summaryrefslogtreecommitdiff
path: root/frontends/gtk
diff options
context:
space:
mode:
Diffstat (limited to 'frontends/gtk')
-rw-r--r--frontends/gtk/Makefile205
-rw-r--r--frontends/gtk/Makefile.defaults42
-rw-r--r--frontends/gtk/about.c157
-rw-r--r--frontends/gtk/about.h24
-rw-r--r--frontends/gtk/bitmap.c540
-rw-r--r--frontends/gtk/bitmap.h35
-rw-r--r--frontends/gtk/compat.c640
-rw-r--r--frontends/gtk/compat.h296
-rw-r--r--frontends/gtk/completion.c157
-rw-r--r--frontends/gtk/completion.h46
-rw-r--r--frontends/gtk/cookies.c219
-rw-r--r--frontends/gtk/cookies.h37
-rw-r--r--frontends/gtk/download.c865
-rw-r--r--frontends/gtk/download.h41
-rw-r--r--frontends/gtk/fetch.c260
-rw-r--r--frontends/gtk/fetch.h28
-rw-r--r--frontends/gtk/gdk.c124
-rw-r--r--frontends/gtk/gdk.h35
-rw-r--r--frontends/gtk/gettext.c43
-rw-r--r--frontends/gtk/gettext.h32
-rw-r--r--frontends/gtk/gui.c1161
-rw-r--r--frontends/gtk/gui.h45
-rw-r--r--frontends/gtk/history.c273
-rw-r--r--frontends/gtk/history.h39
-rw-r--r--frontends/gtk/hotlist.c280
-rw-r--r--frontends/gtk/hotlist.h40
-rw-r--r--frontends/gtk/layout_pango.c309
-rw-r--r--frontends/gtk/layout_pango.h43
-rw-r--r--frontends/gtk/login.c232
-rw-r--r--frontends/gtk/login.h31
-rw-r--r--frontends/gtk/menu.c585
-rw-r--r--frontends/gtk/menu.h227
-rw-r--r--frontends/gtk/options.h72
-rw-r--r--frontends/gtk/plotters.c538
-rw-r--r--frontends/gtk/plotters.h40
-rw-r--r--frontends/gtk/preferences.c1030
-rw-r--r--frontends/gtk/preferences.h28
-rw-r--r--frontends/gtk/print.c613
-rw-r--r--frontends/gtk/print.h50
l---------frontends/gtk/res/Messages1
l---------frontends/gtk/res/SearchEngines1
l---------frontends/gtk/res/adblock.css1
-rw-r--r--frontends/gtk/res/arrow_down_8x32.pngbin0 -> 206 bytes
l---------frontends/gtk/res/ca-bundle.txt1
-rw-r--r--frontends/gtk/res/cookies.gtk2.ui174
-rw-r--r--frontends/gtk/res/cookies.gtk3.ui206
l---------frontends/gtk/res/credits.html1
l---------frontends/gtk/res/de/welcome.html1
l---------frontends/gtk/res/default.css1
-rw-r--r--frontends/gtk/res/default.icobin0 -> 1406 bytes
-rw-r--r--frontends/gtk/res/downloads.gtk2.ui175
-rw-r--r--frontends/gtk/res/downloads.gtk3.ui175
l---------frontends/gtk/res/en/credits.html1
l---------frontends/gtk/res/en/licence.html1
l---------frontends/gtk/res/en/maps.html1
l---------frontends/gtk/res/en/welcome.html1
l---------frontends/gtk/res/favicon.png1
-rw-r--r--frontends/gtk/res/history.gtk2.ui242
-rw-r--r--frontends/gtk/res/history.gtk3.ui238
-rw-r--r--frontends/gtk/res/hotlist.gtk2.ui217
-rw-r--r--frontends/gtk/res/hotlist.gtk3.ui255
l---------frontends/gtk/res/icons1
l---------frontends/gtk/res/internal.css1
l---------frontends/gtk/res/it/credits.html1
l---------frontends/gtk/res/it/licence.html1
l---------frontends/gtk/res/it/welcome.html1
l---------frontends/gtk/res/ja/welcome.html1
-rw-r--r--frontends/gtk/res/languages261
l---------frontends/gtk/res/licence.html1
-rw-r--r--frontends/gtk/res/login.gtk2.ui223
-rw-r--r--frontends/gtk/res/login.gtk3.ui223
l---------frontends/gtk/res/maps.html1
-rw-r--r--frontends/gtk/res/menu_cursor.pngbin0 -> 255 bytes
-rw-r--r--frontends/gtk/res/menu_cursor.xbm6
-rw-r--r--frontends/gtk/res/menu_cursor_mask.xbm6
-rw-r--r--frontends/gtk/res/menu_cursor_mask.xpm22
-rw-r--r--frontends/gtk/res/messages.gresource.xml10
-rw-r--r--frontends/gtk/res/netsurf-16x16.xpm211
-rw-r--r--frontends/gtk/res/netsurf-gtk.desktop68
-rw-r--r--frontends/gtk/res/netsurf.gresource.xml70
-rw-r--r--frontends/gtk/res/netsurf.gtk2.ui212
-rw-r--r--frontends/gtk/res/netsurf.gtk3.ui207
l---------frontends/gtk/res/netsurf.png1
-rw-r--r--frontends/gtk/res/netsurf.xpm317
l---------frontends/gtk/res/nl/credits.html1
l---------frontends/gtk/res/nl/licence.html1
l---------frontends/gtk/res/nl/welcome.html1
-rw-r--r--frontends/gtk/res/options.gtk2.ui3004
-rw-r--r--frontends/gtk/res/options.gtk3.ui3057
-rw-r--r--frontends/gtk/res/password.gtk2.ui415
-rw-r--r--frontends/gtk/res/password.gtk3.ui415
l---------frontends/gtk/res/quirks.css1
-rw-r--r--frontends/gtk/res/ssl.gtk2.ui202
-rw-r--r--frontends/gtk/res/ssl.gtk3.ui181
-rw-r--r--frontends/gtk/res/tabcontents.gtk2.ui92
-rw-r--r--frontends/gtk/res/tabcontents.gtk3.ui92
-rw-r--r--frontends/gtk/res/throbber/throbber0.pngbin0 -> 507 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber1.pngbin0 -> 802 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber2.pngbin0 -> 790 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber3.pngbin0 -> 808 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber4.pngbin0 -> 797 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber5.pngbin0 -> 797 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber6.pngbin0 -> 819 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber7.pngbin0 -> 792 bytes
-rw-r--r--frontends/gtk/res/throbber/throbber8.pngbin0 -> 814 bytes
-rw-r--r--frontends/gtk/res/toolbar.gtk2.ui189
-rw-r--r--frontends/gtk/res/toolbar.gtk3.ui189
-rw-r--r--frontends/gtk/res/viewdata.gtk2.ui204
-rw-r--r--frontends/gtk/res/viewdata.gtk3.ui239
-rw-r--r--frontends/gtk/res/warning.gtk2.ui77
-rw-r--r--frontends/gtk/res/warning.gtk3.ui77
l---------frontends/gtk/res/welcome.html1
-rw-r--r--frontends/gtk/resources.c600
-rw-r--r--frontends/gtk/resources.h109
-rw-r--r--frontends/gtk/scaffolding.c2811
-rw-r--r--frontends/gtk/scaffolding.h252
-rw-r--r--frontends/gtk/schedule.c136
-rw-r--r--frontends/gtk/schedule.h26
-rw-r--r--frontends/gtk/search.c245
-rw-r--r--frontends/gtk/search.h36
-rw-r--r--frontends/gtk/selection.c96
-rw-r--r--frontends/gtk/selection.h24
-rw-r--r--frontends/gtk/sexy_icon_entry.c982
-rw-r--r--frontends/gtk/sexy_icon_entry.h100
-rw-r--r--frontends/gtk/ssl_cert.c135
-rw-r--r--frontends/gtk/ssl_cert.h36
-rw-r--r--frontends/gtk/tabs.c416
-rw-r--r--frontends/gtk/tabs.h42
-rw-r--r--frontends/gtk/throbber.c91
-rw-r--r--frontends/gtk/throbber.h35
-rw-r--r--frontends/gtk/toolbar.c1416
-rw-r--r--frontends/gtk/toolbar.h96
-rw-r--r--frontends/gtk/treeview.c586
-rw-r--r--frontends/gtk/treeview.h38
-rw-r--r--frontends/gtk/viewdata.c990
-rw-r--r--frontends/gtk/viewdata.h48
-rw-r--r--frontends/gtk/viewsource.c82
-rw-r--r--frontends/gtk/viewsource.h25
-rw-r--r--frontends/gtk/warn.h32
-rw-r--r--frontends/gtk/window.c1337
-rw-r--r--frontends/gtk/window.h48
141 files changed, 32309 insertions, 0 deletions
diff --git a/frontends/gtk/Makefile b/frontends/gtk/Makefile
new file mode 100644
index 000000000..7f8ffc16a
--- /dev/null
+++ b/frontends/gtk/Makefile
@@ -0,0 +1,205 @@
+#
+# Makefile for NetSurf GTK target
+#
+# This file is part of NetSurf
+#
+# ----------------------------------------------------------------------------
+# GTK flag setup (using pkg-config)
+# ----------------------------------------------------------------------------
+
+# define additional CFLAGS and LDFLAGS requirements for pkg-configed libs here
+NETSURF_FEATURE_RSVG_CFLAGS := -DWITH_RSVG
+NETSURF_FEATURE_VIDEO_CFLAGS := -DWITH_VIDEO
+
+$(eval $(call pkg_config_find_and_add_enabled,RSVG,librsvg-2.0,SVG))
+$(eval $(call pkg_config_find_and_add_enabled,VIDEO,gstreamer-0.10,Video))
+
+# GTK and GLIB flags to disable depricated usage
+GTKDEPFLAGS := -DG_DISABLE_SINGLE_INCLUDES \
+ -DG_DISABLE_DEPRECATED \
+ -DGTK_DISABLE_SINGLE_INCLUDES \
+ -DGTK_MULTIHEAD_SAFE \
+ -DPANGO_DISABLE_DEPRECATED
+
+# later editions of gtk 2 deprecate interfaces we rely upon for cursors
+# -DGDK_PIXBUF_DISABLE_DEPRECATED
+
+# libsexy currently means we cannot enable this
+# -DGDK_DISABLE_DEPRECATED
+
+# gtk3 is depricating interfaces we use a lot
+ifeq ($(NETSURF_GTK_MAJOR),2)
+GTKDEPFLAGS += -DGTK_DISABLE_DEPRECATED
+endif
+
+
+GTKCFLAGS := -std=c99 -Dgtk -Dnsgtk -g \
+ $(GTKDEPFLAGS) \
+ -D_BSD_SOURCE \
+ -D_DEFAULT_SOURCE \
+ -D_XOPEN_SOURCE=600 \
+ -D_POSIX_C_SOURCE=200809L \
+ -D_NETBSD_SOURCE \
+ -DGTK_RESPATH=\"$(NETSURF_GTK_RESOURCES)\"
+
+# non optional pkg-configed libs
+$(eval $(call pkg_config_find_and_add,gtk+-$(NETSURF_GTK_MAJOR).0,GTK-$(NETSURF_GTK_MAJOR)))
+$(eval $(call pkg_config_find_and_add,gthread-2.0,GThread2))
+$(eval $(call pkg_config_find_and_add,gmodule-2.0,GModule2))
+
+
+CFLAGS += $(GTKCFLAGS)
+LDFLAGS += -lm
+
+# ---------------------------------------------------------------------------
+# Target setup
+# ---------------------------------------------------------------------------
+
+# Path to GTK resources
+NSGTK_RESOURCES_DIR := $(FRONTEND_RESOURCES_DIR)
+
+# The gtk binary target
+EXETARGET := nsgtk
+
+# The filter and target for split messages
+MESSAGES_FILTER=gtk
+MESSAGES_TARGET=$(NSGTK_RESOURCES_DIR)
+
+# ---------------------------------------------------------------------------
+# Windows flag setup
+# ---------------------------------------------------------------------------
+
+ifeq ($(HOST),Windows_NT)
+ CFLAGS += -U__STRICT_ANSI__
+endif
+
+# ----------------------------------------------------------------------------
+# Builtin resource handling
+# ----------------------------------------------------------------------------
+
+# builtin resource sources
+S_RESOURCE :=
+
+# Glib prior to 2.32 does not have GResource handling.
+#
+# This uses pkg-config to check for the minimum required version for
+# this feature in a way similar to the pkg_config_find_and_add_enabled
+# macro. Note we check for gmodule-2.0 which is a specific part of
+# glib we require.
+#
+# It would be nice if we could check for this functionality rather
+# than "knowing" the version but there does not appear to be a simple
+# way to implement that.
+#
+NETSURF_FEATURE_GRESOURCE_AVAILABLE := $(shell $(PKG_CONFIG) --atleast-version=2.32 gmodule-2.0 && echo yes)
+ifneq (,$(filter $(NETSURF_USE_GRESOURCE),AUTO YES))
+ifeq ($(NETSURF_FEATURE_GRESOURCE_AVAILABLE),yes)
+
+# Gresource use has been enabled
+NETSURF_FEATURE_GRESOURCE_ENABLED := yes
+
+#resource compiler tool
+GLIB_COMPILE_RESOURCES := glib-compile-resources
+CFLAGS += -DWITH_GRESOURCE
+
+NETSURF_GRESOURCE_XML := $(NSGTK_RESOURCES_DIR)/netsurf.gresource.xml
+MESSAGES_GRESOURCE_XML := $(NSGTK_RESOURCES_DIR)/messages.gresource.xml
+
+# generate the netsurf gresource source files
+$(OBJROOT)/netsurf_gresource.c: $(NETSURF_GRESOURCE_XML) $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir $(NSGTK_RESOURCES_DIR) --generate-dependencies $(NETSURF_GRESOURCE_XML))
+ $(VQ)echo "GRESORCE: $<"
+ $(Q)$(GLIB_COMPILE_RESOURCES) --generate-source --sourcedir $(NSGTK_RESOURCES_DIR) --target=$@ $<
+
+S_RESOURCE += $(OBJROOT)/netsurf_gresource.c
+
+# generate the messages gresource source file
+$(OBJROOT)/messages_gresource.c: $(MESSAGES_GRESOURCE_XML) $(addsuffix /Messages,$(addprefix $(MESSAGES_TARGET)/,$(MESSAGES_LANGUAGES)))
+ $(VQ)echo "GRESORCE: $<"
+ $(Q)$(GLIB_COMPILE_RESOURCES) --generate-source --sourcedir $(NSGTK_RESOURCES_DIR) --target=$@ $<
+
+S_RESOURCE += $(OBJROOT)/messages_gresource.c
+
+
+endif
+endif
+
+# Build pixbufs as inlines if enabled
+ifneq (,$(filter $(NETSURF_USE_INLINE_PIXBUF),AUTO YES))
+ifneq ($(NETSURF_FEATURE_GRESOURCE_ENABLED),yes)
+
+CFLAGS += -DWITH_BUILTIN_PIXBUF
+
+GTK_IMAGE_favicon := favicon.png
+GTK_IMAGE_netsurf := netsurf.xpm
+GTK_IMAGE_menu_cursor := menu_cursor.png
+
+# 1: input file
+# 2: output file
+# 3: bitmap name
+define convert_image
+
+# add converted pixbuf to builtin resource sources
+S_RESOURCE += $(2)
+
+$(2): $(1)
+ $(VQ)echo " INLINE: ${3}"
+ $(Q)echo "#include <gdk-pixbuf/gdk-pixdata.h>" > $(2)
+ $(Q)gdk-pixbuf-csource --extern --raw --name=$(3) $(1) >> $(2) || \
+ ( rm -f $(2) && false )
+
+endef
+
+$(eval $(foreach V,$(filter GTK_IMAGE_%,$(.VARIABLES)),$(call convert_image,$(addprefix $(NSGTK_RESOURCES_DIR)/,$($(V))),$(OBJROOT)/$(patsubst GTK_IMAGE_%,%,$(V)).c,$(patsubst GTK_IMAGE_%,%,$(V))_pixdata)))
+endif
+endif
+
+# ----------------------------------------------------------------------------
+# Source file setup
+# ----------------------------------------------------------------------------
+
+# S_FRONTEND are sources purely for the GTK frontend
+S_FRONTEND := gui.c schedule.c layout_pango.c bitmap.c plotters.c \
+ treeview.c scaffolding.c gdk.c completion.c login.c throbber.c \
+ selection.c history.c window.c fetch.c download.c menu.c \
+ print.c search.c tabs.c toolbar.c gettext.c \
+ compat.c cookies.c hotlist.c viewdata.c viewsource.c \
+ preferences.c about.c ssl_cert.c resources.c
+
+# This is the final source build list
+# Note this is deliberately *not* expanded here as common and image
+# are not yet available
+SOURCES = $(S_COMMON) $(S_IMAGE) $(S_BROWSER) $(S_RESOURCE) $(S_FRONTEND)
+
+# ----------------------------------------------------------------------------
+# Install target
+# ----------------------------------------------------------------------------
+
+GTK_RESOURCES_LIST := \
+ languages SearchEngines toolbarIndices ca-bundle.txt \
+ default.css adblock.css quirks.css internal.css \
+ credits.html licence.html welcome.html maps.html Messages \
+ default.ico favicon.png netsurf.png netsurf.xpm netsurf-16x16.xpm \
+ arrow_down_8x32.png
+
+GTK_RESOURCES_LIST := \
+ $(addprefix $(NSGTK_RESOURCES_DIR)/, $(GTK_RESOURCES_LIST)) \
+ $(wildcard $(NSGTK_RESOURCES_DIR)/*.gtk$(NETSURF_GTK_MAJOR).ui)
+
+# translations with more than just Messages files
+GTK_TRANSLATIONS_HTML := de en fr it ja nl
+
+install-gtk:
+ $(Q)mkdir -p $(DESTDIR)$(NETSURF_GTK_BIN)
+ $(Q)install nsgtk $(DESTDIR)$(NETSURF_GTK_BIN)netsurf
+ $(Q)mkdir -p $(DESTDIR)$(NETSURF_GTK_RESOURCES)icons
+ $(Q)install -m 0644 $(NSGTK_RESOURCES_DIR)/icons/*.png $(DESTDIR)$(NETSURF_GTK_RESOURCES)/icons
+ $(Q)mkdir -p $(DESTDIR)$(NETSURF_GTK_RESOURCES)throbber
+ $(Q)install -m 0644 $(NSGTK_RESOURCES_DIR)/throbber/*.png $(DESTDIR)$(NETSURF_GTK_RESOURCES)/throbber
+ $(Q)tar -c -h -C $(NSGTK_RESOURCES_DIR) -f - $(GTK_TRANSLATIONS_HTML) | tar -xv -C $(DESTDIR)$(NETSURF_GTK_RESOURCES) -f -
+ $(Q)install -m 0644 $(GTK_RESOURCES_LIST) $(DESTDIR)$(NETSURF_GTK_RESOURCES)
+
+# ----------------------------------------------------------------------------
+# Package target
+# ----------------------------------------------------------------------------
+
+package-gtk:
diff --git a/frontends/gtk/Makefile.defaults b/frontends/gtk/Makefile.defaults
new file mode 100644
index 000000000..fc352a020
--- /dev/null
+++ b/frontends/gtk/Makefile.defaults
@@ -0,0 +1,42 @@
+# ----------------------------------------------------------------------------
+# GTK-specific options
+# ----------------------------------------------------------------------------
+
+# Where to search for NetSurf's resources after looking in ~/.netsurf and
+# $NETSURFRES. It must have a trailing /
+NETSURF_GTK_RESOURCES := $(PREFIX)/share/netsurf/:./frontends/gtk/res/
+
+# Where to install the netsurf binary
+NETSURF_GTK_BIN := $(PREFIX)/bin/
+
+# Enable NetSurf's use of librsvg in conjunction with Cairo to display SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_RSVG := AUTO
+
+# Enable NetSurf's use of libsvgtiny for displaying SVGs
+# Valid options: YES, NO, AUTO
+NETSURF_USE_NSSVG := AUTO
+
+# Enable NetSurf's use of librosprite for displaying RISC OS Sprites
+# Valid options: YES, NO, AUTO
+NETSURF_USE_ROSPRITE := AUTO
+
+# Enable the use of GLib compiled in resource handling. This requires
+# GLib 2.32 or later
+# Valid options: YES, NO, AUTO
+NETSURF_USE_GRESOURCE := AUTO
+
+# Enable the use of compiled in inline pixbuf. This is depricated
+# since GLib 2.32. The automatic selection is disabled if GRESOURCE
+# handling is enabled
+# Valid options: YES, NO, AUTO
+NETSURF_USE_INLINE_PIXBUF := AUTO
+
+# Enable building the source object cache filesystem based backing store.
+NETSURF_FS_BACKING_STORE := YES
+
+# Set default GTK version to build for (2 or 3)
+NETSURF_GTK_MAJOR ?= 2
+
+# Optimisation levels
+CFLAGS += -O2
diff --git a/frontends/gtk/about.c b/frontends/gtk/about.c
new file mode 100644
index 000000000..d57afea7f
--- /dev/null
+++ b/frontends/gtk/about.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file gtk/about.c
+ *
+ * Implementation of gtk about dialog.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "utils/nsurl.h"
+#include "desktop/browser.h"
+#include "desktop/version.h"
+
+#include "gtk/warn.h"
+#include "gtk/compat.h"
+#include "gtk/about.h"
+
+#define ABOUT_RESPONSE_ID_LICENCE 1
+#define ABOUT_RESPONSE_ID_CREDITS 2
+
+
+/**
+ * Open a url and a browser window/tab
+ *
+ * \param url_text The text of the url to open
+ */
+static void about_open(const char *url_text)
+{
+ nsurl *url;
+ nserror ret;
+ enum browser_window_create_flags flags = BW_CREATE_HISTORY;
+
+ if (nsoption_bool(show_single_tab) == true) {
+ flags |= BW_CREATE_TAB;
+ }
+
+ ret = nsurl_create(url_text, &url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_create(flags, url, NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+}
+
+/**
+ * About dialog response handling.
+ *
+ * \param dialog The dialog widget
+ * \param response_id The response ID from the user clicking.
+ * \param user_data The value from the signal connection.
+ */
+static void
+nsgtk_about_dialog_response(GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ switch (response_id) {
+
+ case ABOUT_RESPONSE_ID_LICENCE:
+ about_open("about:credits");
+ break;
+
+ case ABOUT_RESPONSE_ID_CREDITS:
+ about_open("about:licence");
+ break;
+ }
+
+ /* close about dialog */
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+}
+
+void nsgtk_about_dialog_init(GtkWindow *parent)
+{
+ GtkWidget *dialog, *vbox, *label;
+ gchar *name_string;
+ GList *pixbufs;
+
+ /* Create the dialog */
+ dialog = gtk_dialog_new_with_buttons("About NetSurf",
+ parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ "Licence", ABOUT_RESPONSE_ID_LICENCE,
+ "Credits", ABOUT_RESPONSE_ID_CREDITS,
+ "Close", GTK_RESPONSE_CANCEL,
+ NULL, NULL);
+
+ vbox = nsgtk_vbox_new(FALSE, 8);
+
+ gtk_box_pack_start(GTK_BOX(nsgtk_dialog_get_content_area(GTK_DIALOG(dialog))), vbox, TRUE, TRUE, 0);
+
+ /* NetSurf icon */
+ pixbufs = gtk_window_get_default_icon_list();
+ if (pixbufs != NULL) {
+ GtkWidget *image;
+
+ image = nsgtk_image_new_from_pixbuf_icon(GDK_PIXBUF(g_list_nth_data(pixbufs, 0)),
+ GTK_ICON_SIZE_DIALOG);
+ g_list_free(pixbufs);
+
+ gtk_box_pack_start(GTK_BOX(vbox), image, FALSE, FALSE, 0);
+ }
+
+ /* version string */
+ label = gtk_label_new (NULL);
+ name_string = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"bold\">NetSurf %s</span>", netsurf_version);
+ gtk_label_set_markup (GTK_LABEL (label), name_string);
+ g_free(name_string);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ label = gtk_label_new(messages_get("AboutDesc"));
+ gtk_label_set_selectable(GTK_LABEL (label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ label = gtk_label_new(messages_get("NetSurfCopyright"));
+ gtk_label_set_selectable(GTK_LABEL(label), TRUE);
+ gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
+ gtk_box_pack_start(GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ /* Remove separator */
+ nsgtk_dialog_set_has_separator(GTK_DIALOG (dialog), FALSE);
+
+ /* Ensure that the dialog box response is processed. */
+ g_signal_connect_swapped(dialog,
+ "response",
+ G_CALLBACK(nsgtk_about_dialog_response),
+ dialog);
+
+ /* Add the label, and show everything we've added to the dialog. */
+ gtk_widget_show_all(dialog);
+}
diff --git a/frontends/gtk/about.h b/frontends/gtk/about.h
new file mode 100644
index 000000000..bf3c9f58d
--- /dev/null
+++ b/frontends/gtk/about.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2008 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NETSURF_GTK_ABOUT_H
+#define NETSURF_GTK_ABOUT_H
+
+void nsgtk_about_dialog_init(GtkWindow *parent);
+
+#endif
diff --git a/frontends/gtk/bitmap.c b/frontends/gtk/bitmap.c
new file mode 100644
index 000000000..f8dcf1d17
--- /dev/null
+++ b/frontends/gtk/bitmap.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright 2004 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Generic bitmap handling (GDK / GTK+ implementation).
+ *
+ * This implements the interface given by desktop/bitmap.h using GdkPixbufs.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <cairo.h>
+#include <gtk/gtk.h>
+
+#include "utils/utils.h"
+#include "utils/log.h"
+#include "content/content.h"
+#include "image/bitmap.h"
+#include "desktop/plotters.h"
+
+#include "gtk/scaffolding.h"
+#include "gtk/plotters.h"
+#include "gtk/bitmap.h"
+
+
+/**
+ * Create a bitmap.
+ *
+ * \param width width of image in pixels
+ * \param height width of image in pixels
+ * \param state a flag word indicating the initial state
+ * \return an opaque struct bitmap, or NULL on memory exhaustion
+ */
+static void *bitmap_create(int width, int height, unsigned int state)
+{
+ struct bitmap *gbitmap;
+
+ gbitmap = calloc(1, sizeof(struct bitmap));
+ if (gbitmap != NULL) {
+ if ((state & BITMAP_OPAQUE) != 0) {
+ gbitmap->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
+ } else {
+ gbitmap->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ }
+
+ if (cairo_surface_status(gbitmap->surface) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(gbitmap->surface);
+ free(gbitmap);
+ gbitmap = NULL;
+ }
+ }
+
+ return gbitmap;
+}
+
+
+/**
+ * Sets whether a bitmap should be plotted opaque
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \param opaque whether the bitmap should be plotted opaque
+ */
+static void bitmap_set_opaque(void *vbitmap, bool opaque)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ cairo_format_t fmt;
+ cairo_surface_t *nsurface = NULL;
+
+ assert(gbitmap);
+
+ fmt = cairo_image_surface_get_format(gbitmap->surface);
+ if (fmt == CAIRO_FORMAT_RGB24) {
+ if (opaque == false) {
+ /* opaque to transparent */
+ nsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ cairo_image_surface_get_width(gbitmap->surface),
+ cairo_image_surface_get_height(gbitmap->surface));
+
+ }
+
+ } else {
+ if (opaque == true) {
+ /* transparent to opaque */
+ nsurface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+ cairo_image_surface_get_width(gbitmap->surface),
+ cairo_image_surface_get_height(gbitmap->surface));
+
+ }
+ }
+
+ if (nsurface != NULL) {
+ if (cairo_surface_status(nsurface) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(nsurface);
+ } else {
+ memcpy(cairo_image_surface_get_data(nsurface),
+ cairo_image_surface_get_data(gbitmap->surface),
+ cairo_image_surface_get_stride(gbitmap->surface) * cairo_image_surface_get_height(gbitmap->surface));
+ cairo_surface_destroy(gbitmap->surface);
+ gbitmap->surface = nsurface;
+
+ cairo_surface_mark_dirty(gbitmap->surface);
+
+ }
+
+ }
+}
+
+
+/**
+ * Tests whether a bitmap has an opaque alpha channel
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return whether the bitmap is opaque
+ */
+static bool bitmap_test_opaque(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ unsigned char *pixels;
+ int pcount;
+ int ploop;
+
+ assert(gbitmap);
+
+ pixels = cairo_image_surface_get_data(gbitmap->surface);
+
+ pcount = cairo_image_surface_get_stride(gbitmap->surface) *
+ cairo_image_surface_get_height(gbitmap->surface);
+
+ for (ploop = 3; ploop < pcount; ploop += 4) {
+ if (pixels[ploop] != 0xff) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Gets whether a bitmap should be plotted opaque
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static bool bitmap_get_opaque(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ cairo_format_t fmt;
+
+ assert(gbitmap);
+
+ fmt = cairo_image_surface_get_format(gbitmap->surface);
+ if (fmt == CAIRO_FORMAT_RGB24) {
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Return a pointer to the pixel data in a bitmap.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return pointer to the pixel buffer
+ *
+ * The pixel data is packed as BITMAP_FORMAT, possibly with padding at the end
+ * of rows. The width of a row in bytes is given by bitmap_get_rowstride().
+ */
+static unsigned char *bitmap_get_buffer(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ int pixel_loop;
+ int pixel_count;
+ uint8_t *pixels;
+ uint32_t t, r, g, b;
+ cairo_format_t fmt;
+
+ assert(gbitmap);
+
+ cairo_surface_flush(gbitmap->surface);
+ pixels = cairo_image_surface_get_data(gbitmap->surface);
+
+ if (!gbitmap->converted)
+ return pixels;
+
+ fmt = cairo_image_surface_get_format(gbitmap->surface);
+ pixel_count = cairo_image_surface_get_width(gbitmap->surface) *
+ cairo_image_surface_get_height(gbitmap->surface);
+
+ if (fmt == CAIRO_FORMAT_RGB24) {
+ /* Opaque image */
+ for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) {
+ /* Cairo surface is ARGB, written in native endian */
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ b = pixels[4 * pixel_loop + 0];
+ g = pixels[4 * pixel_loop + 1];
+ r = pixels[4 * pixel_loop + 2];
+ t = pixels[4 * pixel_loop + 3];
+#else
+ t = pixels[4 * pixel_loop + 0];
+ r = pixels[4 * pixel_loop + 1];
+ g = pixels[4 * pixel_loop + 2];
+ b = pixels[4 * pixel_loop + 3];
+#endif
+
+ /* Core bitmaps always have a component order of rgba,
+ * regardless of system endianness */
+ pixels[4 * pixel_loop + 0] = r;
+ pixels[4 * pixel_loop + 1] = g;
+ pixels[4 * pixel_loop + 2] = b;
+ pixels[4 * pixel_loop + 3] = t;
+ }
+ } else {
+ /* Alpha image: de-multiply alpha */
+ for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ b = pixels[4 * pixel_loop + 0];
+ g = pixels[4 * pixel_loop + 1];
+ r = pixels[4 * pixel_loop + 2];
+ t = pixels[4 * pixel_loop + 3];
+#else
+ t = pixels[4 * pixel_loop + 0];
+ r = pixels[4 * pixel_loop + 1];
+ g = pixels[4 * pixel_loop + 2];
+ b = pixels[4 * pixel_loop + 3];
+#endif
+
+ if (t != 0) {
+ r = (r << 8) / t;
+ g = (g << 8) / t;
+ b = (b << 8) / t;
+
+ r = (r > 255) ? 255 : r;
+ g = (g > 255) ? 255 : g;
+ b = (b > 255) ? 255 : b;
+ } else {
+ r = g = b = 0;
+ }
+
+ pixels[4 * pixel_loop + 0] = r;
+ pixels[4 * pixel_loop + 1] = g;
+ pixels[4 * pixel_loop + 2] = b;
+ pixels[4 * pixel_loop + 3] = t;
+ }
+ }
+
+ gbitmap->converted = false;
+
+ return (unsigned char *) pixels;
+}
+
+
+/**
+ * Find the width of a pixel row in bytes.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return width of a pixel row in the bitmap
+ */
+static size_t bitmap_get_rowstride(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return cairo_image_surface_get_stride(gbitmap->surface);
+}
+
+
+/**
+ * Find the bytes per pixel of a bitmap
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \return bytes per pixel
+ */
+static size_t bitmap_get_bpp(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return 4;
+}
+
+
+
+/**
+ * Free a bitmap.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_destroy(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ if (gbitmap->surface != NULL) {
+ cairo_surface_destroy(gbitmap->surface);
+ }
+ if (gbitmap->scsurface != NULL) {
+ cairo_surface_destroy(gbitmap->scsurface);
+ }
+ free(gbitmap);
+}
+
+
+/**
+ * Save a bitmap in the platform's native format.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ * \param path pathname for file
+ * \param flags modify the behaviour of the save
+ * \return true on success, false on error and error reported
+ */
+static bool bitmap_save(void *vbitmap, const char *path, unsigned flags)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return false;
+}
+
+
+/**
+ * The bitmap image has changed, so flush any persistant cache.
+ *
+ * \param vbitmap a bitmap, as returned by bitmap_create()
+ */
+static void bitmap_modified(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ int pixel_loop;
+ int pixel_count;
+ uint8_t *pixels;
+ uint32_t t, r, g, b;
+ cairo_format_t fmt;
+
+ assert(gbitmap);
+
+ fmt = cairo_image_surface_get_format(gbitmap->surface);
+
+ pixel_count = cairo_image_surface_get_width(gbitmap->surface) *
+ cairo_image_surface_get_height(gbitmap->surface);
+ pixels = cairo_image_surface_get_data(gbitmap->surface);
+
+ if (gbitmap->converted) {
+ cairo_surface_mark_dirty(gbitmap->surface);
+ return;
+ }
+
+ if (fmt == CAIRO_FORMAT_RGB24) {
+ /* Opaque image */
+ for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) {
+ /* Core bitmaps always have a component order of rgba,
+ * regardless of system endianness */
+ r = pixels[4 * pixel_loop + 0];
+ g = pixels[4 * pixel_loop + 1];
+ b = pixels[4 * pixel_loop + 2];
+ t = pixels[4 * pixel_loop + 3];
+
+ /* Cairo surface is ARGB, written in native endian */
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ pixels[4 * pixel_loop + 0] = b;
+ pixels[4 * pixel_loop + 1] = g;
+ pixels[4 * pixel_loop + 2] = r;
+ pixels[4 * pixel_loop + 3] = t;
+#else
+ pixels[4 * pixel_loop + 0] = t;
+ pixels[4 * pixel_loop + 1] = r;
+ pixels[4 * pixel_loop + 2] = g;
+ pixels[4 * pixel_loop + 3] = b;
+#endif
+ }
+ } else {
+ /* Alpha image: pre-multiply alpha */
+ for (pixel_loop=0; pixel_loop < pixel_count; pixel_loop++) {
+ r = pixels[4 * pixel_loop + 0];
+ g = pixels[4 * pixel_loop + 1];
+ b = pixels[4 * pixel_loop + 2];
+ t = pixels[4 * pixel_loop + 3];
+
+ if (t != 0) {
+ r = ((r * (t + 1)) >> 8) & 0xff;
+ g = ((g * (t + 1)) >> 8) & 0xff;
+ b = ((b * (t + 1)) >> 8) & 0xff;
+ } else {
+ r = g = b = 0;
+ }
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ pixels[4 * pixel_loop + 0] = b;
+ pixels[4 * pixel_loop + 1] = g;
+ pixels[4 * pixel_loop + 2] = r;
+ pixels[4 * pixel_loop + 3] = t;
+#else
+ pixels[4 * pixel_loop + 0] = t;
+ pixels[4 * pixel_loop + 1] = r;
+ pixels[4 * pixel_loop + 2] = g;
+ pixels[4 * pixel_loop + 3] = b;
+#endif
+ }
+ }
+
+ cairo_surface_mark_dirty(gbitmap->surface);
+
+ gbitmap->converted = true;
+}
+
+/* exported interface documented in gtk/bitmap.h */
+int nsgtk_bitmap_get_width(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return cairo_image_surface_get_width(gbitmap->surface);
+}
+
+/* exported interface documented in gtk/bitmap.h */
+int nsgtk_bitmap_get_height(void *vbitmap)
+{
+ struct bitmap *gbitmap = (struct bitmap *)vbitmap;
+ assert(gbitmap);
+
+ return cairo_image_surface_get_height(gbitmap->surface);
+}
+
+/**
+ * Render content into a bitmap.
+ *
+ * \param bitmap The bitmap to draw to
+ * \param content The content to render
+ * \return true on success and bitmap updated else false
+ */
+static nserror
+bitmap_render(struct bitmap *bitmap, struct hlcache_handle *content)
+{
+ cairo_surface_t *dsurface = bitmap->surface;
+ cairo_surface_t *surface;
+ cairo_t *old_cr;
+ gint dwidth, dheight;
+ int cwidth, cheight;
+ struct redraw_context ctx = {
+ .interactive = false,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+
+ assert(content);
+ assert(bitmap);
+
+ dwidth = cairo_image_surface_get_width(dsurface);
+ dheight = cairo_image_surface_get_height(dsurface);
+
+ /* Calculate size of buffer to render the content into */
+ /* Get the width from the content width, unless it exceeds 1024,
+ * in which case we use 1024. This means we never create excessively
+ * large render buffers for huge contents, which would eat memory and
+ * cripple performance.
+ */
+ cwidth = min(max(content_get_width(content), dwidth), 1024);
+
+ /* The height is set in proportion with the width, according to the
+ * aspect ratio of the required thumbnail. */
+ cheight = ((cwidth * dheight) + (dwidth / 2)) / dwidth;
+
+ /* Create surface to render into */
+ surface = cairo_surface_create_similar(dsurface, CAIRO_CONTENT_COLOR_ALPHA, cwidth, cheight);
+
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(surface);
+ return false;
+ }
+
+ old_cr = current_cr;
+ current_cr = cairo_create(surface);
+
+ /* render the content */
+ content_scaled_redraw(content, cwidth, cheight, &ctx);
+
+ cairo_destroy(current_cr);
+ current_cr = old_cr;
+
+ cairo_t *cr = cairo_create(dsurface);
+
+ /* Scale *before* setting the source surface (1) */
+ cairo_scale (cr, (double)dwidth / cwidth, (double)dheight / cheight);
+ cairo_set_source_surface (cr, surface, 0, 0);
+
+ /* To avoid getting the edge pixels blended with 0 alpha,
+ * which would occur with the default EXTEND_NONE. Use
+ * EXTEND_PAD for 1.2 or newer (2)
+ */
+ cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REFLECT);
+
+ /* Replace the destination with the source instead of overlaying */
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ /* Do the actual drawing */
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+
+ cairo_surface_destroy(surface);
+
+ return NSERROR_OK;
+}
+
+
+static struct gui_bitmap_table bitmap_table = {
+ .create = bitmap_create,
+ .destroy = bitmap_destroy,
+ .set_opaque = bitmap_set_opaque,
+ .get_opaque = bitmap_get_opaque,
+ .test_opaque = bitmap_test_opaque,
+ .get_buffer = bitmap_get_buffer,
+ .get_rowstride = bitmap_get_rowstride,
+ .get_width = nsgtk_bitmap_get_width,
+ .get_height = nsgtk_bitmap_get_height,
+ .get_bpp = bitmap_get_bpp,
+ .save = bitmap_save,
+ .modified = bitmap_modified,
+ .render = bitmap_render,
+};
+
+struct gui_bitmap_table *nsgtk_bitmap_table = &bitmap_table;
diff --git a/frontends/gtk/bitmap.h b/frontends/gtk/bitmap.h
new file mode 100644
index 000000000..0f46d19a8
--- /dev/null
+++ b/frontends/gtk/bitmap.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NS_GTK_BITMAP_H
+#define NS_GTK_BITMAP_H
+
+#include <cairo.h>
+
+extern struct gui_bitmap_table *nsgtk_bitmap_table;
+
+struct bitmap {
+ cairo_surface_t *surface; /* original cairo surface */
+ cairo_surface_t *scsurface; /* scaled surface */
+ bool converted; /** set if the surface data has been converted */
+};
+
+int nsgtk_bitmap_get_width(void *vbitmap);
+int nsgtk_bitmap_get_height(void *vbitmap);
+
+#endif /* NS_GTK_BITMAP_H */
diff --git a/frontends/gtk/compat.c b/frontends/gtk/compat.c
new file mode 100644
index 000000000..4c5524b0e
--- /dev/null
+++ b/frontends/gtk/compat.c
@@ -0,0 +1,640 @@
+/*
+ * Copyright 2010 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Compatibility functions for older GTK versions implementation
+ */
+
+#include <stdint.h>
+
+#include "gtk/compat.h"
+
+#ifdef _SEXY_ICON_ENTRY_H_
+#include "gtk/sexy_icon_entry.c"
+
+/*
+ * exported interface documented in gtk/compat.h
+ *
+ * Only required for the lib sexy interface before 2.16
+ */
+GtkStateType nsgtk_widget_get_state(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,18,0)
+ return gtk_widget_get_state(widget);
+#else
+ return GTK_WIDGET_STATE(widget);
+#endif
+}
+
+
+#endif
+
+void nsgtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus)
+{
+#if GTK_CHECK_VERSION(2,22,0)
+ gtk_widget_set_can_focus(widget, can_focus);
+#else
+ if (can_focus == TRUE)
+ GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_FOCUS);
+ else
+ GTK_WIDGET_UNSET_FLAGS(widget, GTK_CAN_FOCUS);
+#endif
+}
+
+gboolean nsgtk_widget_has_focus(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,20,0)
+ return gtk_widget_has_focus(widget);
+#else
+ return GTK_WIDGET_HAS_FOCUS(widget);
+#endif
+}
+
+gboolean nsgtk_widget_get_visible(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,20,0)
+ return gtk_widget_get_visible(widget);
+#else
+ return GTK_WIDGET_VISIBLE(widget);
+#endif
+}
+
+gboolean nsgtk_widget_get_realized(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,20,0)
+ return gtk_widget_get_realized(widget);
+#else
+ return GTK_WIDGET_REALIZED(widget);
+#endif
+}
+
+gboolean nsgtk_widget_get_mapped(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,20,0)
+ return gtk_widget_get_mapped(widget);
+#else
+ return GTK_WIDGET_MAPPED(widget);
+#endif
+}
+
+gboolean nsgtk_widget_is_drawable(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,18,0)
+ return gtk_widget_is_drawable(widget);
+#else
+ return GTK_WIDGET_DRAWABLE(widget);
+#endif
+}
+
+void nsgtk_dialog_set_has_separator(GtkDialog *dialog, gboolean setting)
+{
+#if GTK_CHECK_VERSION(2,21,8)
+ /* Deprecated */
+#else
+ gtk_dialog_set_has_separator(dialog, setting);
+#endif
+}
+
+GtkWidget *nsgtk_combo_box_text_new(void)
+{
+#if GTK_CHECK_VERSION(2,24,0)
+ return gtk_combo_box_text_new();
+#else
+ return gtk_combo_box_new_text();
+#endif
+}
+
+void nsgtk_combo_box_text_append_text(GtkWidget *combo_box, const gchar *text)
+{
+#if GTK_CHECK_VERSION(2,24,0)
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box), text);
+#else
+ gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), text);
+#endif
+
+}
+
+gchar *nsgtk_combo_box_text_get_active_text(GtkWidget *combo_box)
+{
+#if GTK_CHECK_VERSION(2,24,0)
+ return gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo_box));
+#else
+ return gtk_combo_box_get_active_text(GTK_COMBO_BOX(combo_box));
+#endif
+}
+
+GtkWidget *nsgtk_entry_new(void)
+{
+#if GTK_CHECK_VERSION(2,16,0)
+ return gtk_entry_new();
+#else
+ return GTK_WIDGET(sexy_icon_entry_new());
+#endif
+}
+
+void nsgtk_entry_set_icon_from_pixbuf(GtkWidget *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkPixbuf *pixbuf)
+{
+#if GTK_CHECK_VERSION(2,16,0)
+ gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(entry), icon_pos, pixbuf);
+#else
+ GtkImage *image = GTK_IMAGE(gtk_image_new_from_pixbuf(pixbuf));
+
+ if (image != NULL) {
+ sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(entry),
+ (SexyIconEntryPosition)icon_pos,
+ image);
+
+ g_object_unref(image);
+ }
+
+#endif
+}
+
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_entry_set_icon_from_stock(GtkWidget *entry,
+ GtkEntryIconPosition icon_pos,
+ const gchar *id)
+{
+#ifdef NSGTK_USE_ICON_NAME
+ gtk_entry_set_icon_from_icon_name(GTK_ENTRY(entry), icon_pos, id);
+#else
+#if GTK_CHECK_VERSION(2,16,0)
+ gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), icon_pos, id);
+#else
+ GtkImage *image = GTK_IMAGE(gtk_image_new_from_stock(id,
+ GTK_ICON_SIZE_LARGE_TOOLBAR));
+
+ if (image != NULL) {
+ sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(entry),
+ (SexyIconEntryPosition)icon_pos,
+ image);
+ g_object_unref(image);
+ }
+#endif
+#endif
+}
+
+
+/* exported interface documented in gtk/compat.h */
+GtkWidget *nsgtk_image_new_from_stock(const gchar *id, GtkIconSize size)
+{
+#ifdef NSGTK_USE_ICON_NAME
+ return gtk_image_new_from_icon_name(id, size);
+#else
+ return gtk_image_new_from_stock(id, size);
+#endif
+}
+
+
+/* exported interface documented in gtk/compat.h */
+GtkWidget *nsgtk_button_new_from_stock(const gchar *stock_id)
+{
+#ifdef NSGTK_USE_ICON_NAME
+ return gtk_button_new_with_label(stock_id);
+#else
+ return gtk_button_new_from_stock(stock_id);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+gboolean nsgtk_stock_lookup(const gchar *stock_id, GtkStockItem *item)
+{
+#ifdef NSGTK_USE_ICON_NAME
+ return FALSE;
+#else
+ return gtk_stock_lookup(stock_id, item);
+#endif
+}
+
+
+void nsgtk_widget_override_background_color(GtkWidget *widget,
+ GtkStateFlags state,
+ uint16_t a,
+ uint16_t r,
+ uint16_t g,
+ uint16_t b)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA colour;
+ colour.alpha = (double)a / 0xffff;
+ colour.red = (double)r / 0xffff;
+ colour.green = (double)g / 0xffff;
+ colour.blue = (double)b / 0xffff;
+ gtk_widget_override_background_color(widget, state, &colour);
+#else
+ GdkColor colour;
+ colour.pixel = a;
+ colour.red = r;
+ colour.green = g;
+ colour.blue = b;
+ gtk_widget_modify_bg(widget, state, &colour );
+#endif
+}
+
+GtkAdjustment *nsgtk_layout_get_vadjustment(GtkLayout *layout)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(layout));
+#else
+ return gtk_layout_get_vadjustment(layout);
+#endif
+}
+
+GtkAdjustment *nsgtk_layout_get_hadjustment(GtkLayout *layout)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(layout));
+#else
+ return gtk_layout_get_hadjustment(layout);
+#endif
+}
+
+static void nsgtk_layout_set_adjustment_step_increment(GtkAdjustment *adj,
+ int value)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ gtk_adjustment_set_step_increment(adj, value);
+#else
+ adj->step_increment = value;
+#endif
+}
+
+void nsgtk_layout_set_hadjustment(GtkLayout *layout, GtkAdjustment *adj)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_scrollable_set_hadjustment(GTK_SCROLLABLE(layout), adj);
+#else
+ gtk_layout_set_hadjustment(layout, adj);
+#endif
+ nsgtk_layout_set_adjustment_step_increment(adj, 8);
+}
+
+void nsgtk_layout_set_vadjustment(GtkLayout *layout, GtkAdjustment *adj)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_scrollable_set_vadjustment(GTK_SCROLLABLE(layout), adj);
+#else
+ gtk_layout_set_vadjustment(layout, adj);
+#endif
+ nsgtk_layout_set_adjustment_step_increment(adj, 8);
+}
+
+GtkWidget *nsgtk_hbox_new(gboolean homogeneous, gint spacing)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing);
+#else
+ return gtk_hbox_new(homogeneous, spacing);
+#endif
+}
+
+GtkWidget *nsgtk_vbox_new(gboolean homogeneous, gint spacing)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing);
+#else
+ return gtk_vbox_new(homogeneous, spacing);
+#endif
+}
+
+GtkStateFlags nsgtk_widget_get_state_flags(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_widget_get_state_flags(widget);
+#else
+#if GTK_CHECK_VERSION(2,18,0)
+ return gtk_widget_get_state(widget);
+#else
+ return 0; /* FIXME */
+#endif
+#endif
+}
+
+GtkStyleContext *nsgtk_widget_get_style_context(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_widget_get_style_context(widget);
+#else
+ return widget->style;
+#endif
+}
+
+const PangoFontDescription* nsgtk_style_context_get_font(GtkStyleContext *style,
+ GtkStateFlags state)
+{
+#if GTK_CHECK_VERSION(3,8,0)
+ const PangoFontDescription* fontdesc = NULL;
+ gtk_style_context_get(style, state, GTK_STYLE_PROPERTY_FONT, &fontdesc, NULL);
+ return fontdesc;
+#else
+#if GTK_CHECK_VERSION(3,0,0)
+ return gtk_style_context_get_font(style, state);
+#else
+ return style->font_desc;
+#endif
+#endif
+}
+
+gulong nsgtk_connect_draw_event(GtkWidget *widget,
+ GCallback callback,
+ gpointer g)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ return g_signal_connect(G_OBJECT(widget), "draw", callback, g);
+#else
+ return g_signal_connect(G_OBJECT(widget), "expose_event", callback, g);
+#endif
+}
+
+void nsgdk_cursor_unref(GdkCursor *cursor)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ g_object_unref(cursor);
+#else
+ gdk_cursor_unref(cursor);
+#endif
+}
+
+void nsgtk_widget_modify_font(GtkWidget *widget,
+ PangoFontDescription *font_desc)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+/* FIXME */
+ return;
+#else
+ gtk_widget_modify_font(widget, font_desc);
+#endif
+}
+
+GdkWindow *nsgtk_widget_get_window(GtkWidget *widget)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_widget_get_window(widget);
+#else
+ return widget->window;
+#endif
+}
+
+GtkWidget *nsgtk_dialog_get_content_area(GtkDialog *dialog)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_dialog_get_content_area(dialog);
+#else
+ return dialog->vbox;
+#endif
+}
+
+gboolean nsgtk_show_uri(GdkScreen *screen,
+ const gchar *uri,
+ guint32 timestamp,
+ GError **error)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_show_uri(screen, uri, timestamp, error);
+#else
+ return FALSE; /* FIXME */
+#endif
+}
+
+GdkWindow *nsgtk_layout_get_bin_window(GtkLayout *layout)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_layout_get_bin_window(layout);
+#else
+ return layout->bin_window;
+#endif
+}
+
+gdouble nsgtk_adjustment_get_step_increment(GtkAdjustment *adjustment)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_adjustment_get_step_increment(adjustment);
+#else
+ return adjustment->step_increment;
+#endif
+}
+
+gdouble nsgtk_adjustment_get_upper(GtkAdjustment *adjustment)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_adjustment_get_upper(adjustment);
+#else
+ return adjustment->upper;
+#endif
+}
+
+gdouble nsgtk_adjustment_get_lower(GtkAdjustment *adjustment)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_adjustment_get_lower(adjustment);
+#else
+ return adjustment->lower;
+#endif
+}
+
+gdouble nsgtk_adjustment_get_page_increment(GtkAdjustment *adjustment)
+{
+#if GTK_CHECK_VERSION(2,14,0)
+ return gtk_adjustment_get_page_increment(adjustment);
+#else
+ return adjustment->page_increment;
+#endif
+}
+
+void nsgtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation)
+{
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_get_allocation(widget, allocation);
+#else
+ allocation->x = widget->allocation.x;
+ allocation->y = widget->allocation.y;
+ allocation->width = widget->allocation.width;
+ allocation->height = widget->allocation.height;
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+GtkWidget *nsgtk_image_new_from_pixbuf_icon(GdkPixbuf *pixbuf, GtkIconSize size)
+{
+#if GTK_CHECK_VERSION(3,10,0)
+ return gtk_image_new_from_pixbuf(pixbuf);
+#else
+ GtkIconSet *icon_set;
+ GtkWidget *image;
+
+ icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
+
+ image = gtk_image_new_from_icon_set(icon_set, size);
+
+ gtk_icon_set_unref(icon_set);
+
+ return image;
+#endif
+}
+
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_window_set_opacity(GtkWindow *window, gdouble opacity)
+{
+#if GTK_CHECK_VERSION(3,8,0)
+ gtk_widget_set_opacity(GTK_WIDGET(window), opacity);
+#else
+ gtk_window_set_opacity(window, opacity);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_scrolled_window_add_with_viewport(GtkScrolledWindow *window,
+ GtkWidget *child)
+{
+#if GTK_CHECK_VERSION(3,8,0)
+ gtk_container_add(GTK_CONTAINER(window), child);
+#else
+ gtk_scrolled_window_add_with_viewport(window, child);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+GtkWidget *nsgtk_image_menu_item_new_with_mnemonic(const gchar *label)
+{
+#if GTK_CHECK_VERSION(3,10,0)
+ return gtk_menu_item_new_with_mnemonic(label);
+#else
+ return gtk_image_menu_item_new_with_mnemonic(label);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_image_menu_item_set_image(GtkWidget *image_menu_item, GtkWidget *image)
+{
+#if !GTK_CHECK_VERSION(3,10,0)
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(image_menu_item), image);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+gboolean nsgtk_icon_size_lookup_for_settings(GtkSettings *settings,
+ GtkIconSize size,
+ gint *width,
+ gint *height)
+{
+#if GTK_CHECK_VERSION(3,10,0)
+ return gtk_icon_size_lookup(size, width, height);
+#else
+ return gtk_icon_size_lookup_for_settings(settings, size, width, height);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_widget_set_alignment(GtkWidget *widget, GtkAlign halign, GtkAlign valign)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_set_halign(widget, halign);
+ gtk_widget_set_valign(widget, valign);
+#else
+ gfloat x, y;
+ switch(halign) {
+ case GTK_ALIGN_START:
+ x = 0.0;
+ break;
+
+ case GTK_ALIGN_END:
+ x = 1.0;
+ break;
+
+ default:
+ x = 0.5;
+ break;
+ }
+
+ switch(valign) {
+ case GTK_ALIGN_START:
+ y = 0.0;
+ break;
+
+ case GTK_ALIGN_END:
+ y = 1.0;
+ break;
+
+ default:
+ y = 0.5;
+ break;
+ }
+
+ gtk_misc_set_alignment(GTK_MISC(widget), x, y);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+void nsgtk_widget_set_margins(GtkWidget *widget, gint hmargin, gint vmargin)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+#if GTK_CHECK_VERSION(3,12,0)
+ gtk_widget_set_margin_start(widget, hmargin);
+ gtk_widget_set_margin_end(widget, hmargin);
+#else
+ gtk_widget_set_margin_left(widget, hmargin);
+ gtk_widget_set_margin_right(widget, hmargin);
+#endif
+ gtk_widget_set_margin_top(widget, vmargin);
+ gtk_widget_set_margin_bottom(widget, vmargin);
+#else
+ gtk_misc_set_padding(GTK_MISC(widget), hmargin, vmargin);
+#endif
+}
+
+/* exported interface documented in gtk/compat.h */
+guint
+nsgtk_builder_add_from_resource(GtkBuilder *builder,
+ const gchar *resource_path,
+ GError **error)
+{
+ guint ret;
+
+#ifdef WITH_GRESOURCE
+#if GTK_CHECK_VERSION(3,4,0)
+ ret = gtk_builder_add_from_resource(builder, resource_path, error);
+#else
+ GBytes *data;
+ const gchar *buffer;
+ gsize buffer_length;
+
+ g_assert(error && *error == NULL);
+
+ data = g_resources_lookup_data(resource_path, 0, error);
+ if (data == NULL) {
+ return 0;
+ }
+
+ buffer_length = 0;
+ buffer = g_bytes_get_data(data, &buffer_length);
+ g_assert(buffer != NULL);
+
+ ret = gtk_builder_add_from_string(builder, buffer, buffer_length, error);
+
+ g_bytes_unref(data);
+#endif
+#else
+ ret = 0; /* return an error as GResource not supported before GLIB 2.32 */
+#endif
+ return ret;
+}
diff --git a/frontends/gtk/compat.h b/frontends/gtk/compat.h
new file mode 100644
index 000000000..9554b0cba
--- /dev/null
+++ b/frontends/gtk/compat.h
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2010 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Compatibility functions for older GTK versions (interface)
+ */
+
+#ifndef NETSURF_GTK_COMPAT_H_
+#define NETSURF_GTK_COMPAT_H_
+
+#include <stdint.h>
+
+#include <gtk/gtk.h>
+
+/* gtk 3.10 depricated the use of stock names */
+#if GTK_CHECK_VERSION(3,10,0)
+#define NSGTK_USE_ICON_NAME
+#else
+#undef NSGTK_USE_ICON_NAME
+#endif
+
+/* icon names instead of stock */
+#ifdef NSGTK_USE_ICON_NAME
+#define NSGTK_STOCK_ADD "list-add"
+#define NSGTK_STOCK_CANCEL "_Cancel"
+#define NSGTK_STOCK_CLEAR "edit-clear"
+#define NSGTK_STOCK_CLOSE "window-close"
+#define NSGTK_STOCK_FIND "edit-find"
+#define NSGTK_STOCK_GO_BACK "go-previous"
+#define NSGTK_STOCK_GO_FORWARD "go-next"
+#define NSGTK_STOCK_HOME "go-home"
+#define NSGTK_STOCK_INFO "dialog-information"
+#define NSGTK_STOCK_REFRESH "view-refresh"
+#define NSGTK_STOCK_SAVE "document-save"
+#define NSGTK_STOCK_SAVE_AS "document-save-as"
+#define NSGTK_STOCK_STOP "process-stop"
+#define NSGTK_STOCK_OK "_OK"
+#define NSGTK_STOCK_OPEN "_Open"
+#else
+#define NSGTK_STOCK_ADD GTK_STOCK_ADD
+#define NSGTK_STOCK_CANCEL GTK_STOCK_CANCEL
+#define NSGTK_STOCK_CLEAR GTK_STOCK_CLEAR
+#define NSGTK_STOCK_CLOSE GTK_STOCK_CLOSE
+#define NSGTK_STOCK_FIND GTK_STOCK_FIND
+#define NSGTK_STOCK_GO_BACK GTK_STOCK_GO_BACK
+#define NSGTK_STOCK_GO_FORWARD GTK_STOCK_GO_FORWARD
+#define NSGTK_STOCK_HOME GTK_STOCK_HOME
+#define NSGTK_STOCK_INFO GTK_STOCK_INFO
+#define NSGTK_STOCK_REFRESH GTK_STOCK_REFRESH
+#define NSGTK_STOCK_SAVE GTK_STOCK_SAVE
+#define NSGTK_STOCK_SAVE_AS GTK_STOCK_SAVE_AS
+#define NSGTK_STOCK_STOP GTK_STOCK_STOP
+#define NSGTK_STOCK_OK GTK_STOCK_OK
+#define NSGTK_STOCK_OPEN GTK_STOCK_OPEN
+#endif
+
+/* widget alignment only available since 3.0 */
+#if !GTK_CHECK_VERSION(3,0,0)
+typedef enum {
+ GTK_ALIGN_FILL,
+ GTK_ALIGN_START,
+ GTK_ALIGN_END,
+ GTK_ALIGN_CENTER,
+ GTK_ALIGN_BASELINE
+} GtkAlign;
+#endif
+
+/**
+ * Set the alignment of a widget.
+ *
+ * sets both the horizontal and vertical alignement of a widget
+ *
+ * @note this type of alignemnt was not available prior to GTK 3.0 so
+ * we emulate it using gtk_misc_set_alignment.
+ *
+ * \param widget The widget to set alignent on.
+ * \param halign The horizontal alignment to set.
+ * \param valign The vertical alignment to set
+ */
+void nsgtk_widget_set_alignment(GtkWidget *widget, GtkAlign halign, GtkAlign valign);
+
+/**
+ * Set the margins of a widget
+ *
+ * Sets the margin all round a widget.
+ *
+ * @note this type of margin was not available prior to GTK 3.0 so
+ * we emulate it using gtk_misc_set_padding.
+ *
+ * \param widget The widget to set alignent on.
+ * \param hmargin The horizontal margin.
+ * \param vmargin The vertical margin.
+ */
+void nsgtk_widget_set_margins(GtkWidget *widget, gint hmargin, gint vmargin);
+
+void nsgtk_widget_set_can_focus(GtkWidget *widget, gboolean can_focus);
+gboolean nsgtk_widget_has_focus(GtkWidget *widget);
+gboolean nsgtk_widget_get_visible(GtkWidget *widget);
+gboolean nsgtk_widget_get_realized(GtkWidget *widget);
+gboolean nsgtk_widget_get_mapped(GtkWidget *widget);
+gboolean nsgtk_widget_is_drawable(GtkWidget *widget);
+void nsgtk_dialog_set_has_separator(GtkDialog *dialog, gboolean setting);
+GtkWidget *nsgtk_combo_box_text_new(void);
+void nsgtk_combo_box_text_append_text(GtkWidget *combo_box, const gchar *text);
+gchar *nsgtk_combo_box_text_get_active_text(GtkWidget *combo_box);
+
+/**
+ * creates a new image widget of an appropriate icon size from a pixbuf.
+ *
+ * \param pixbuf The pixbuf to use as a source.
+ * \param size The size of icon to create
+ * \return An image widget.
+ */
+GtkWidget *nsgtk_image_new_from_pixbuf_icon(GdkPixbuf *pixbuf, GtkIconSize size);
+
+/* GTK prior to 2.16 needs the sexy interface for icons */
+#if !GTK_CHECK_VERSION(2,16,0)
+
+#include "gtk/sexy_icon_entry.h"
+
+typedef enum {
+ GTK_ENTRY_ICON_PRIMARY = SEXY_ICON_ENTRY_PRIMARY,
+ GTK_ENTRY_ICON_SECONDARY = SEXY_ICON_ENTRY_SECONDARY
+} GtkEntryIconPosition;
+
+GtkStateType nsgtk_widget_get_state(GtkWidget *widget);
+
+#endif
+
+#if GTK_CHECK_VERSION (2, 90, 7)
+#define GDK_KEY(symbol) GDK_KEY_##symbol
+#else
+#include <gdk/gdkkeysyms.h>
+#define GDK_KEY(symbol) GDK_##symbol
+#endif
+
+#if !GTK_CHECK_VERSION(3,0,0)
+typedef GtkStateType GtkStateFlags;
+typedef GtkStyle GtkStyleContext;
+
+#if GTK_CHECK_VERSION(2,22,0)
+enum {
+ GTK_IN_DESTRUCTION = 1 << 0,
+};
+#define GTK_OBJECT_FLAGS(obj) (GTK_OBJECT (obj)->flags)
+#endif
+
+#define gtk_widget_in_destruction(widget) \
+ (GTK_OBJECT_FLAGS(GTK_OBJECT(widget)) & GTK_IN_DESTRUCTION)
+
+#endif
+
+
+/**
+ * Sets the icon shown in the entry at the specified position from a
+ * stock image.
+ *
+ * Compatability interface for original deprecated in GTK 3.10
+ *
+ * \param entry The entry widget to set the icon on.
+ * \param icon_pos The position of the icon.
+ * \param stock_id the name of the stock item.
+ */
+void nsgtk_entry_set_icon_from_stock(GtkWidget *entry, GtkEntryIconPosition icon_pos, const gchar *stock_id);
+
+/**
+ * Creates a GtkImage displaying a stock icon.
+ *
+ * Compatability interface for original deprecated in GTK 3.10
+ *
+ * \param stock_id the name of the stock item.
+ * \param size The size of icon to create.
+ * \return The created image widget or NULL on error
+ */
+GtkWidget *nsgtk_image_new_from_stock(const gchar *stock_id, GtkIconSize size);
+
+/**
+ * Creates a new GtkButton containing the image and text from a stock item.
+ *
+ * Compatability interface for original deprecated in GTK 3.10
+ *
+ * \param stock_id the name of the stock item
+ */
+GtkWidget *nsgtk_button_new_from_stock(const gchar *stock_id);
+
+/**
+ * Fills item with the registered values for stock_id.
+ *
+ * Compatability interface for original deprecated in GTK 3.10
+ *
+ * \param stock_id the name of the stock item.
+ * \param item The structure to update if the stock_id was known.
+ * \return TRUE if stock_id was known.
+ */
+gboolean nsgtk_stock_lookup(const gchar *stock_id, GtkStockItem *item);
+
+void nsgtk_window_set_opacity(GtkWindow *window, gdouble opacity);
+
+void nsgtk_scrolled_window_add_with_viewport(GtkScrolledWindow *window, GtkWidget *child);
+
+GtkWidget *nsgtk_entry_new(void);
+
+void nsgtk_entry_set_icon_from_pixbuf(GtkWidget *entry, GtkEntryIconPosition icon_pos, GdkPixbuf *pixbuf);
+
+void nsgtk_widget_override_background_color(GtkWidget *widget, GtkStateFlags state, uint16_t a, uint16_t r, uint16_t g, uint16_t b);
+GtkWidget* nsgtk_hbox_new(gboolean homogeneous, gint spacing);
+GtkWidget* nsgtk_vbox_new(gboolean homogeneous, gint spacing);
+GtkStateFlags nsgtk_widget_get_state_flags(GtkWidget *widget);
+GtkStyleContext* nsgtk_widget_get_style_context(GtkWidget *widget);
+const PangoFontDescription* nsgtk_style_context_get_font(GtkStyleContext *style, GtkStateFlags state);
+gulong nsgtk_connect_draw_event(GtkWidget *widget, GCallback callback, gpointer g);
+void nsgdk_cursor_unref(GdkCursor *cursor);
+void nsgtk_widget_modify_font(GtkWidget *widget, PangoFontDescription *font_desc);
+GdkWindow *nsgtk_widget_get_window(GtkWidget *widget);
+GtkWidget *nsgtk_dialog_get_content_area(GtkDialog *dialog);
+gboolean nsgtk_show_uri(GdkScreen *screen, const gchar *uri, guint32 timestamp, GError **error);
+GdkWindow *nsgtk_layout_get_bin_window(GtkLayout *layout);
+void nsgtk_widget_get_allocation(GtkWidget *widget, GtkAllocation *allocation);
+
+gboolean nsgtk_icon_size_lookup_for_settings (GtkSettings *settings, GtkIconSize size, gint *width, gint *height);
+
+GtkAdjustment *nsgtk_layout_get_vadjustment(GtkLayout *layout);
+GtkAdjustment *nsgtk_layout_get_hadjustment(GtkLayout *layout);
+void nsgtk_layout_set_hadjustment(GtkLayout *layout, GtkAdjustment *adj);
+void nsgtk_layout_set_vadjustment(GtkLayout *layout, GtkAdjustment *adj);
+gdouble nsgtk_adjustment_get_step_increment(GtkAdjustment *adjustment);
+gdouble nsgtk_adjustment_get_upper(GtkAdjustment *adjustment);
+gdouble nsgtk_adjustment_get_lower(GtkAdjustment *adjustment);
+gdouble nsgtk_adjustment_get_page_increment(GtkAdjustment *adjustment);
+
+/* menu compatability */
+
+/**
+ * Creates a new GtkImageMenuItem containing a label.
+ *
+ * Compatability interface for original deprecated in GTK 3.10.
+ * @note post 3.10 this creates a GtkMenuItem.
+ *
+ * \param label The text of the button, with an underscore in front of
+ * the mnemonic character.
+ * \return a new GtkMenuItem
+ */
+GtkWidget *nsgtk_image_menu_item_new_with_mnemonic(const gchar *label);
+
+/**
+ * Sets the image of image_menu_item to the given widget.
+ *
+ * Compatability interface for original deprecated in GTK 3.10.
+ * @note post 3.10 this is empty as menu creation generates GtkMenuItem.
+ *
+ * \param image_menu_item The image menu entry item.
+ * \param image The image to set.
+ */
+void nsgtk_image_menu_item_set_image(GtkWidget *image_menu_item, GtkWidget *image);
+
+
+/**
+ * Parses a resource file containing a GtkBuilder UI definition and
+ * merges it with the current contents of builder.
+ *
+ * Compatability interface as this did not exist prior to GTK 3.4
+ *
+ * GTK prior to 3.4 can have the resources in a GResource but
+ * gtk_builder cannot directly instantiate from them
+ *
+ * GTK 3.4 onwards can use gtk_builder_add_from_resource() to add
+ * directly from resources. The gtk_builder_new_ type operations
+ * cannot be used because they are only available post 3.10 and handle
+ * all errors by aborting the application
+ *
+ * @note prior to GLIB 2.32 resources did not exist and this wrapper
+ * returns the error code.
+ *
+ * \param builder a GtkBuilder
+ * \param resource_path the path of the resource file to parse
+ * \param error return location for an error, or NULL.
+ * \return A positive value on success, 0 if an error occurred.
+ */
+guint nsgtk_builder_add_from_resource(GtkBuilder *builder, const gchar *resource_path, GError **error);
+
+#endif /* NETSURF_GTK_COMPAT_H */
diff --git a/frontends/gtk/completion.c b/frontends/gtk/completion.c
new file mode 100644
index 000000000..6dde728b3
--- /dev/null
+++ b/frontends/gtk/completion.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Implementation of url entry completion.
+ */
+
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "content/urldb.h"
+#include "desktop/searchweb.h"
+#include "desktop/browser.h"
+
+#include "gtk/warn.h"
+#include "gtk/window.h"
+#include "gtk/completion.h"
+
+GtkListStore *nsgtk_completion_list;
+
+/**
+ * completion row matcher
+ */
+static gboolean nsgtk_completion_match(GtkEntryCompletion *completion,
+ const gchar *key,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ /* the completion list is modified to only contain valid
+ * entries so this simply returns TRUE to indicate all rows
+ * are in the list should be shown.
+ */
+ return TRUE;
+
+}
+
+
+/**
+ * callback for each entry to add to completion list
+ */
+static bool
+nsgtk_completion_udb_callback(nsurl *url, const struct url_data *data)
+{
+ GtkTreeIter iter;
+
+ if (data->visits != 0) {
+ gtk_list_store_append(nsgtk_completion_list, &iter);
+ gtk_list_store_set(nsgtk_completion_list, &iter, 0,
+ nsurl_access(url), -1);
+ }
+ return true;
+}
+
+/**
+ * event handler for when a completion suggestion is selected.
+ */
+static gboolean
+nsgtk_completion_match_select(GtkEntryCompletion *widget,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GValue value = {0, };
+ struct nsgtk_scaffolding *g = user_data;
+ struct browser_window *bw = nsgtk_get_browser_window(nsgtk_scaffolding_top_level(g));
+ nserror ret;
+ nsurl *url;
+
+ gtk_tree_model_get_value(model, iter, 0, &value);
+
+ ret = search_web_omni(g_value_get_string(&value),
+ SEARCH_WEB_OMNI_NONE,
+ &url);
+
+ g_value_unset(&value);
+
+ if (ret == NSERROR_OK) {
+ ret = browser_window_navigate(bw,
+ url, NULL, BW_NAVIGATE_HISTORY,
+ NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+
+ return TRUE;
+}
+
+/* exported interface documented in completion.h */
+void nsgtk_completion_init(void)
+{
+ nsgtk_completion_list = gtk_list_store_new(1, G_TYPE_STRING);
+
+}
+
+/* exported interface documented in completion.h */
+gboolean nsgtk_completion_update(GtkEntry *entry)
+{
+ gtk_list_store_clear(nsgtk_completion_list);
+
+ if (nsoption_bool(url_suggestion) == true) {
+ urldb_iterate_partial(gtk_entry_get_text(entry),
+ nsgtk_completion_udb_callback);
+ }
+
+ return TRUE;
+}
+
+/* exported interface documented in completion.h */
+GtkEntryCompletion *nsgtk_url_entry_completion_new(struct nsgtk_scaffolding *gs)
+{
+ GtkEntryCompletion *completion;
+
+ completion = gtk_entry_completion_new();
+ gtk_entry_completion_set_match_func(completion,
+ nsgtk_completion_match, NULL, NULL);
+
+ gtk_entry_completion_set_model(completion,
+ GTK_TREE_MODEL(nsgtk_completion_list));
+
+ gtk_entry_completion_set_text_column(completion, 0);
+
+ gtk_entry_completion_set_minimum_key_length(completion, 1);
+
+ /* enable popup for completion */
+ gtk_entry_completion_set_popup_completion(completion, TRUE);
+
+ /* when selected callback */
+ g_signal_connect(G_OBJECT(completion), "match-selected",
+ G_CALLBACK(nsgtk_completion_match_select), gs);
+
+ g_object_set(G_OBJECT(completion),
+ "popup-set-width", TRUE,
+ "popup-single-match", TRUE,
+ NULL);
+
+ return completion;
+}
diff --git a/frontends/gtk/completion.h b/frontends/gtk/completion.h
new file mode 100644
index 000000000..9a1db293d
--- /dev/null
+++ b/frontends/gtk/completion.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Interface to url entry completion.
+ */
+
+#ifndef _NETSURF_GTK_COMPLETION_H_
+#define _NETSURF_GTK_COMPLETION_H_
+
+struct nsgtk_scaffolding;
+
+/**
+ * initialise completion list store
+ */
+void nsgtk_completion_init(void);
+
+/**
+ * update completion list store.
+ */
+gboolean nsgtk_completion_update(GtkEntry *entry);
+
+/**
+ * create a new entry completion on a scaffold.
+ *
+ * \param gs The scaffoliding which the url entry is in.
+ */
+GtkEntryCompletion *nsgtk_url_entry_completion_new(struct nsgtk_scaffolding *gs);
+
+#endif
diff --git a/frontends/gtk/cookies.c b/frontends/gtk/cookies.c
new file mode 100644
index 000000000..f8f989347
--- /dev/null
+++ b/frontends/gtk/cookies.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Cookies (implementation).
+ */
+
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "desktop/cookie_manager.h"
+#include "desktop/plot_style.h"
+#include "desktop/tree.h"
+#include "desktop/textinput.h"
+
+#include "gtk/cookies.h"
+#include "gtk/plotters.h"
+#include "gtk/scaffolding.h"
+#include "gtk/treeview.h"
+#include "gtk/resources.h"
+
+#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
+ GtkMenuItem *widget, gpointer g)
+#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
+#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \
+ gpointer g)
+
+struct menu_events {
+ const char *widget;
+ GCallback handler;
+};
+
+/* edit menu */
+MENUPROTO(delete_selected);
+MENUPROTO(delete_all);
+MENUPROTO(select_all);
+MENUPROTO(clear_selection);
+
+/* view menu*/
+MENUPROTO(expand_all);
+MENUPROTO(expand_domains);
+MENUPROTO(expand_cookies);
+MENUPROTO(collapse_all);
+MENUPROTO(collapse_domains);
+MENUPROTO(collapse_cookies);
+
+
+static struct menu_events menu_events[] = {
+
+ /* edit menu */
+ MENUEVENT(delete_selected),
+ MENUEVENT(delete_all),
+ MENUEVENT(select_all),
+ MENUEVENT(clear_selection),
+
+ /* view menu*/
+ MENUEVENT(expand_all),
+ MENUEVENT(expand_domains),
+ MENUEVENT(expand_cookies),
+ MENUEVENT(collapse_all),
+ MENUEVENT(collapse_domains),
+ MENUEVENT(collapse_cookies),
+
+ {NULL, NULL}
+};
+
+static struct nsgtk_treeview *cookies_treeview;
+static GtkBuilder *cookie_builder;
+GtkWindow *wndCookies;
+
+/**
+ * Connects menu events in the cookies window.
+ */
+static void nsgtk_cookies_init_menu(void)
+{
+ struct menu_events *event = menu_events;
+ GtkWidget *w;
+
+ while (event->widget != NULL) {
+ w = GTK_WIDGET(gtk_builder_get_object(cookie_builder, event->widget));
+ if (w == NULL) {
+ LOG("Unable to connect menu widget ""%s""", event->widget); } else {
+ g_signal_connect(G_OBJECT(w), "activate", event->handler, cookies_treeview);
+ }
+ event++;
+ }
+}
+
+/* exported interface documented in gtk/cookies.h */
+nserror nsgtk_cookies_init(void)
+{
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("cookies", &cookie_builder);
+ if (res != NSERROR_OK) {
+ LOG("Cookie UI builder init failed");
+ return res;
+ }
+
+ gtk_builder_connect_signals(cookie_builder, NULL);
+
+ wndCookies = GTK_WINDOW(gtk_builder_get_object(cookie_builder,
+ "wndCookies"));
+
+ scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(cookie_builder,
+ "cookiesScrolled"));
+
+ drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(cookie_builder,
+ "cookiesDrawingArea"));
+
+ cookies_treeview = nsgtk_treeview_create(TREE_COOKIES,
+ wndCookies,
+ scrolled,
+ drawing_area);
+ if (cookies_treeview == NULL) {
+ return NSERROR_INIT_FAILED;
+ }
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ CONNECT(wndCookies, "delete_event", gtk_widget_hide_on_delete, NULL);
+ CONNECT(wndCookies, "hide", nsgtk_tree_window_hide, cookies_treeview);
+
+ nsgtk_cookies_init_menu();
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Destroys the cookies window and performs any other necessary cleanup actions.
+ */
+void nsgtk_cookies_destroy(void)
+{
+ /** \todo what about cookie_builder? */
+ nsgtk_treeview_destroy(cookies_treeview);
+}
+
+
+/* edit menu */
+MENUHANDLER(delete_selected)
+{
+ cookie_manager_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(delete_all)
+{
+ cookie_manager_keypress(NS_KEY_SELECT_ALL);
+ cookie_manager_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(select_all)
+{
+ cookie_manager_keypress(NS_KEY_SELECT_ALL);
+ return TRUE;
+}
+
+MENUHANDLER(clear_selection)
+{
+ cookie_manager_keypress(NS_KEY_CLEAR_SELECTION);
+ return TRUE;
+}
+
+/* view menu*/
+MENUHANDLER(expand_all)
+{
+ cookie_manager_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(expand_domains)
+{
+ cookie_manager_expand(true);
+ return TRUE;
+}
+
+MENUHANDLER(expand_cookies)
+{
+ cookie_manager_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_all)
+{
+ cookie_manager_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_domains)
+{
+ cookie_manager_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_cookies)
+{
+ cookie_manager_contract(false);
+ return TRUE;
+}
diff --git a/frontends/gtk/cookies.h b/frontends/gtk/cookies.h
new file mode 100644
index 000000000..2d5c56d52
--- /dev/null
+++ b/frontends/gtk/cookies.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Cookies (interface).
+ */
+
+#ifndef __NSGTK_COOKIES_H__
+#define __NSGTK_COOKIES_H__
+
+extern GtkWindow *wndCookies;
+
+/**
+ * Creates the window for the cookies tree.
+ *
+ * \return NSERROR_OK on success else appropriate error code on faliure.
+ */
+nserror nsgtk_cookies_init(void);
+
+void nsgtk_cookies_destroy(void);
+
+#endif /* __NSGTK_COOKIES_H__ */
diff --git a/frontends/gtk/download.c b/frontends/gtk/download.c
new file mode 100644
index 000000000..b7eea2584
--- /dev/null
+++ b/frontends/gtk/download.c
@@ -0,0 +1,865 @@
+/*
+ * Copyright 2008 Michael Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+#include <glib/gstdio.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "utils/string.h"
+#include "desktop/download.h"
+#include "desktop/gui_download.h"
+
+#include "gtk/warn.h"
+#include "gtk/scaffolding.h"
+#include "gtk/window.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/download.h"
+
+#define UPDATE_RATE 500 /* In milliseconds */
+
+struct download_context;
+
+enum {
+ NSGTK_DOWNLOAD_PROGRESS,
+ NSGTK_DOWNLOAD_INFO,
+ NSGTK_DOWNLOAD_REMAINING,
+ NSGTK_DOWNLOAD_SPEED,
+ NSGTK_DOWNLOAD_PULSE,
+ NSGTK_DOWNLOAD_STATUS,
+ NSGTK_DOWNLOAD,
+
+ NSGTK_DOWNLOAD_N_COLUMNS
+};
+
+typedef enum {
+ NSGTK_DOWNLOAD_NONE,
+ NSGTK_DOWNLOAD_WORKING,
+ NSGTK_DOWNLOAD_ERROR,
+ NSGTK_DOWNLOAD_COMPLETE,
+ NSGTK_DOWNLOAD_CANCELED
+} nsgtk_download_status;
+
+typedef enum {
+ NSGTK_DOWNLOAD_PAUSE = 1 << 0,
+ NSGTK_DOWNLOAD_RESUME = 1 << 1,
+ NSGTK_DOWNLOAD_CANCEL = 1 << 2,
+ NSGTK_DOWNLOAD_CLEAR = 1 << 3
+} nsgtk_download_actions;
+
+struct gui_download_window {
+ struct download_context *ctx;
+ nsgtk_download_actions sensitivity;
+ nsgtk_download_status status;
+
+ GString *name;
+ GString *time_left;
+ gint size_total;
+ gint size_downloaded;
+ gint progress;
+ gfloat time_remaining;
+ gfloat start_time;
+ gfloat speed;
+
+ GtkTreeRowReference *row;
+ GIOChannel *write;
+ GError *error;
+};
+
+typedef void (*nsgtk_download_selection_action)(struct gui_download_window *dl);
+
+static GtkWindow *nsgtk_download_window, *nsgtk_download_parent;
+static GtkProgressBar *nsgtk_download_progress_bar;
+
+static GtkTreeView *nsgtk_download_tree;
+static GtkListStore *nsgtk_download_store;
+static GtkTreeSelection *nsgtk_download_selection;
+static GtkTreeIter nsgtk_download_iter;
+
+static GTimer *nsgtk_downloads_timer;
+static GList *nsgtk_downloads_list;
+static GtkButton *nsgtk_download_button_pause;
+static GtkButton *nsgtk_download_button_clear;
+static GtkButton *nsgtk_download_button_cancel;
+static GtkButton *nsgtk_download_button_resume;
+static gint nsgtk_downloads_num_active;
+static const gchar* status_messages[] = { NULL, "gtkWorking", "gtkError",
+ "gtkComplete", "gtkCanceled" };
+
+
+
+static GtkTreeView* nsgtk_download_tree_view_new(GtkBuilder *gladeFile)
+{
+ GtkTreeView *treeview;
+ GtkCellRenderer *renderer;
+
+ treeview = GTK_TREE_VIEW(gtk_builder_get_object(gladeFile, "treeDownloads"));
+
+ /* Progress column */
+ renderer = gtk_cell_renderer_progress_new();
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkProgress"), renderer, "value",
+ NSGTK_DOWNLOAD_PROGRESS, "pulse", NSGTK_DOWNLOAD_PULSE,
+ "text", NSGTK_DOWNLOAD_STATUS, NULL);
+
+ /* Information column */
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "wrap-mode", PANGO_WRAP_WORD_CHAR,
+ "wrap-width", 300, NULL);
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkDetails"), renderer, "text",
+ NSGTK_DOWNLOAD_INFO, NULL);
+ gtk_tree_view_column_set_expand(gtk_tree_view_get_column(treeview,
+ NSGTK_DOWNLOAD_INFO), TRUE);
+
+ /* Time remaining column */
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkRemaining"), renderer, "text",
+ NSGTK_DOWNLOAD_REMAINING, NULL);
+
+ /* Speed column */
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes (treeview, -1,
+ messages_get("gtkSpeed"), renderer, "text",
+ NSGTK_DOWNLOAD_SPEED, NULL);
+
+ return treeview;
+}
+
+static gint
+nsgtk_download_sort(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer userdata)
+{
+ struct gui_download_window *dl1, *dl2;
+
+ gtk_tree_model_get(model, a, NSGTK_DOWNLOAD, &dl1, -1);
+ gtk_tree_model_get(model, b, NSGTK_DOWNLOAD, &dl2, -1);
+
+ return dl1->status - dl2->status;
+}
+
+static void
+nsgtk_download_sensitivity_update_buttons(nsgtk_download_actions sensitivity)
+{
+ /* Glade seems to pack the buttons in an arbitrary order */
+ enum { PAUSE_BUTTON, CLEAR_BUTTON, CANCEL_BUTTON, RESUME_BUTTON };
+
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_pause),
+ sensitivity & NSGTK_DOWNLOAD_PAUSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_clear),
+ sensitivity & NSGTK_DOWNLOAD_CLEAR);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_cancel),
+ sensitivity & NSGTK_DOWNLOAD_CANCEL);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_download_button_resume),
+ sensitivity & NSGTK_DOWNLOAD_RESUME);
+}
+
+static void nsgtk_download_sensitivity_evaluate(GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GList *rows;
+ gboolean selected = gtk_tree_selection_count_selected_rows(selection);
+ GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store);
+ nsgtk_download_actions sensitivity = 0;
+ struct gui_download_window *dl;
+
+ if (selected) {
+ rows = gtk_tree_selection_get_selected_rows(selection, &model);
+ while (rows != NULL) {
+ gtk_tree_model_get_iter(model, &iter,
+ (GtkTreePath*)rows->data);
+ gtk_tree_model_get(model, &iter, NSGTK_DOWNLOAD,
+ &dl, -1);
+ sensitivity |= dl->sensitivity;
+ rows = rows->next;
+ }
+ } else {
+ rows = nsgtk_downloads_list;
+ while (rows != NULL) {
+ dl = rows->data;
+ sensitivity |= (dl->sensitivity & NSGTK_DOWNLOAD_CLEAR);
+ rows = rows->next;
+ }
+ }
+
+
+ nsgtk_download_sensitivity_update_buttons(sensitivity);
+}
+
+static void nsgtk_download_do(nsgtk_download_selection_action action)
+{
+ GList *rows, *dls = NULL;
+ GtkTreeModel *model = GTK_TREE_MODEL(nsgtk_download_store);
+ gboolean selection_exists = gtk_tree_selection_count_selected_rows(
+ nsgtk_download_selection);
+
+ if (selection_exists) {
+ rows = gtk_tree_selection_get_selected_rows(
+ nsgtk_download_selection, &model);
+ while (rows != NULL) {
+ struct gui_download_window *dl;
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(
+ nsgtk_download_store),
+ &nsgtk_download_iter,
+ (GtkTreePath*)rows->data);
+ gtk_tree_model_get(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter, NSGTK_DOWNLOAD,
+ &dl, -1);
+ dls = g_list_prepend(dls, dl);
+
+ rows = rows->next;
+ }
+ g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_foreach(rows, (GFunc)g_free, NULL);
+ g_list_free(rows);
+ } else
+ dls = g_list_copy(nsgtk_downloads_list);
+
+ g_list_foreach(dls, (GFunc)action, NULL);
+ g_list_free(dls);
+}
+
+static gchar* nsgtk_download_info_to_string(struct gui_download_window *dl)
+{
+ gchar *size_info = g_strdup_printf(messages_get("gtkSizeInfo"),
+ human_friendly_bytesize(dl->size_downloaded),
+ dl->size_total == 0 ? messages_get("gtkUnknownSize") :
+ human_friendly_bytesize(dl->size_total));
+
+ gchar *r;
+
+ if (dl->status != NSGTK_DOWNLOAD_ERROR)
+ r = g_strdup_printf("%s\n%s", dl->name->str, size_info);
+ else
+ r = g_strdup_printf("%s\n%s", dl->name->str,
+ dl->error->message);
+
+ g_free(size_info);
+
+ return r;
+}
+
+static gchar* nsgtk_download_time_to_string(gint seconds)
+{
+ gint hours, minutes;
+
+ if (seconds < 0)
+ return g_strdup("-");
+
+ hours = seconds / 3600;
+ seconds -= hours * 3600;
+ minutes = seconds / 60;
+ seconds -= minutes * 60;
+
+ if (hours > 0)
+ return g_strdup_printf("%u:%02u:%02u", hours, minutes,
+ seconds);
+ else
+ return g_strdup_printf("%u:%02u", minutes, seconds);
+}
+
+static void nsgtk_download_store_update_item (struct gui_download_window *dl)
+{
+ gchar *info = nsgtk_download_info_to_string(dl);
+ char *human = human_friendly_bytesize(dl->speed);
+ char speed[strlen(human) + SLEN("/s") + 1];
+ sprintf(speed, "%s/s", human);
+ gchar *time = nsgtk_download_time_to_string(dl->time_remaining);
+ gboolean pulse = dl->status == NSGTK_DOWNLOAD_WORKING;
+
+ /* Updates iter (which is needed to set and get data) with the dl row */
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter,
+ gtk_tree_row_reference_get_path(dl->row));
+
+ gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter,
+ NSGTK_DOWNLOAD_PULSE, pulse ? dl->progress : -1,
+ NSGTK_DOWNLOAD_PROGRESS, pulse ? 0 : dl->progress,
+ NSGTK_DOWNLOAD_INFO, info,
+ NSGTK_DOWNLOAD_SPEED, dl->speed == 0 ? "-" : speed,
+ NSGTK_DOWNLOAD_REMAINING, time,
+ NSGTK_DOWNLOAD, dl,
+ -1);
+
+ g_free(info);
+ g_free(time);
+}
+
+static gboolean nsgtk_download_update(gboolean force_update)
+{
+ /* Be sure we need to update */
+ if (!nsgtk_widget_get_visible(GTK_WIDGET(nsgtk_download_window)))
+ return TRUE;
+
+ GList *list;
+ gchar *text;
+ gboolean update, pulse_mode = FALSE;
+ gint downloaded = 0, total = 0, dls = 0;
+ gfloat percent, elapsed = g_timer_elapsed(nsgtk_downloads_timer, NULL);
+ nsgtk_downloads_num_active = 0;
+
+ for (list = nsgtk_downloads_list; list != NULL; list = list->next) {
+ struct gui_download_window *dl = list->data;
+ update = force_update;
+
+ switch (dl->status) {
+ case NSGTK_DOWNLOAD_WORKING:
+ pulse_mode = TRUE;
+
+ case NSGTK_DOWNLOAD_NONE:
+ dl->speed = dl->size_downloaded /
+ (elapsed - dl->start_time);
+ if (dl->status == NSGTK_DOWNLOAD_NONE) {
+ dl->time_remaining = (dl->size_total -
+ dl->size_downloaded)/
+ dl->speed;
+ dl->progress = (gfloat)
+ dl->size_downloaded /
+ dl->size_total * 100;
+ } else
+ dl->progress++;
+
+ nsgtk_downloads_num_active++;
+ update = TRUE;
+
+ case NSGTK_DOWNLOAD_COMPLETE:
+ downloaded += dl->size_downloaded;
+ total += dl->size_total;
+ dls++;
+
+ default:
+ ;//Do nothing
+
+ }
+ if (update)
+ nsgtk_download_store_update_item(dl);
+ }
+
+ if (pulse_mode) {
+ text = g_strdup_printf(
+ messages_get(nsgtk_downloads_num_active > 1 ?
+ "gtkProgressBarPulse" :
+ "gtkProgressBarPulseSingle"),
+ nsgtk_downloads_num_active);
+ gtk_progress_bar_pulse(nsgtk_download_progress_bar);
+ gtk_progress_bar_set_text(nsgtk_download_progress_bar, text);
+ } else {
+ percent = total != 0 ? (gfloat)downloaded / total : 0;
+ text = g_strdup_printf(messages_get("gtkProgressBar"),
+ floor(percent*100), dls);
+ gtk_progress_bar_set_fraction(nsgtk_download_progress_bar,
+ percent);
+ gtk_progress_bar_set_text(nsgtk_download_progress_bar, text);
+ }
+
+ g_free(text);
+
+ if (nsgtk_downloads_num_active == 0)
+ return FALSE; /* Returning FALSE here cancels the g_timeout */
+ else
+ return TRUE;
+}
+
+static void nsgtk_download_store_clear_item(struct gui_download_window *dl)
+{
+ if (dl->sensitivity & NSGTK_DOWNLOAD_CLEAR) {
+ nsgtk_downloads_list = g_list_remove(nsgtk_downloads_list, dl);
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter,
+ gtk_tree_row_reference_get_path(dl->row));
+ gtk_list_store_remove(nsgtk_download_store,
+ &nsgtk_download_iter);
+
+ download_context_destroy(dl->ctx);
+ g_string_free(dl->name, TRUE);
+ g_string_free(dl->time_left, TRUE);
+ g_free(dl);
+
+ nsgtk_download_sensitivity_evaluate(nsgtk_download_selection);
+ nsgtk_download_update(FALSE);
+ }
+}
+
+static void nsgtk_download_tree_view_row_activated(GtkTreeView *tree,
+ GtkTreePath *path, GtkTreeViewColumn *column, gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model(tree);
+
+ if (gtk_tree_model_get_iter(model, &iter, path)) {
+ /* TODO: This will be a context action (pause, start, clear) */
+ nsgtk_download_do(nsgtk_download_store_clear_item);
+ }
+}
+
+static void nsgtk_download_change_sensitivity(struct gui_download_window *dl,
+ nsgtk_download_actions sensitivity)
+{
+ dl->sensitivity = sensitivity;
+ nsgtk_download_sensitivity_evaluate(nsgtk_download_selection);
+}
+
+static void nsgtk_download_change_status (
+ struct gui_download_window *dl, nsgtk_download_status status)
+{
+ dl->status = status;
+ if (status != NSGTK_DOWNLOAD_NONE) {
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter,
+ gtk_tree_row_reference_get_path(dl->row));
+
+ gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter,
+ NSGTK_DOWNLOAD_STATUS,
+ messages_get(status_messages[status]), -1);
+ }
+}
+
+static void nsgtk_download_store_cancel_item (struct gui_download_window *dl)
+{
+ if (dl->sensitivity & NSGTK_DOWNLOAD_CANCEL) {
+ dl->speed = 0;
+ dl->size_downloaded = 0;
+ dl->progress = 0;
+ dl->time_remaining = -1;
+ nsgtk_download_change_sensitivity(dl, NSGTK_DOWNLOAD_CLEAR);
+ nsgtk_download_change_status(dl, NSGTK_DOWNLOAD_CANCELED);
+
+ download_context_abort(dl->ctx);
+
+ g_unlink(download_context_get_filename(dl->ctx));
+
+ nsgtk_download_update(TRUE);
+ }
+}
+
+static gboolean nsgtk_download_hide(GtkWidget *window)
+{
+ gtk_widget_hide(window);
+ return TRUE;
+}
+
+/* exported interface documented in gtk/download.h */
+nserror nsgtk_download_init(void)
+{
+ GtkBuilder* builder;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("downloads", &builder);
+ if (res != NSERROR_OK) {
+ LOG("Download UI builder init failed");
+ return res;
+ }
+
+ gtk_builder_connect_signals(builder, NULL);
+
+ nsgtk_download_button_pause = GTK_BUTTON(gtk_builder_get_object(builder, "buttonPause"));
+ nsgtk_download_button_clear = GTK_BUTTON(gtk_builder_get_object(builder, "buttonClear"));
+ nsgtk_download_button_cancel = GTK_BUTTON(gtk_builder_get_object(builder, "buttonCancel"));
+ nsgtk_download_button_resume = GTK_BUTTON(gtk_builder_get_object(builder, "buttonPlay"));
+
+ nsgtk_download_progress_bar = GTK_PROGRESS_BAR(gtk_builder_get_object(builder, "progressBar"));
+ nsgtk_download_window = GTK_WINDOW(gtk_builder_get_object(builder, "wndDownloads"));
+ nsgtk_download_parent = NULL;
+
+ gtk_window_set_transient_for(GTK_WINDOW(nsgtk_download_window),
+ nsgtk_download_parent);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(nsgtk_download_window),
+ FALSE);
+
+ nsgtk_downloads_timer = g_timer_new();
+
+ nsgtk_download_tree = nsgtk_download_tree_view_new(builder);
+
+ nsgtk_download_store = gtk_list_store_new(NSGTK_DOWNLOAD_N_COLUMNS,
+ G_TYPE_INT, /* % complete */
+ G_TYPE_STRING, /* Description */
+ G_TYPE_STRING, /* Time remaining */
+ G_TYPE_STRING, /* Speed */
+ G_TYPE_INT, /* Pulse */
+ G_TYPE_STRING, /* Status */
+ G_TYPE_POINTER /* Download structure */
+ );
+
+
+ gtk_tree_view_set_model(nsgtk_download_tree,
+ GTK_TREE_MODEL(nsgtk_download_store));
+
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(nsgtk_download_store),
+ NSGTK_DOWNLOAD_STATUS,
+ (GtkTreeIterCompareFunc) nsgtk_download_sort, NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id(
+ GTK_TREE_SORTABLE(nsgtk_download_store),
+ NSGTK_DOWNLOAD_STATUS, GTK_SORT_ASCENDING);
+
+ g_object_unref(nsgtk_download_store);
+
+ nsgtk_download_selection =
+ gtk_tree_view_get_selection(nsgtk_download_tree);
+ gtk_tree_selection_set_mode(nsgtk_download_selection,
+ GTK_SELECTION_MULTIPLE);
+
+ g_signal_connect(G_OBJECT(nsgtk_download_selection), "changed",
+ G_CALLBACK(nsgtk_download_sensitivity_evaluate), NULL);
+ g_signal_connect(nsgtk_download_tree, "row-activated",
+ G_CALLBACK(nsgtk_download_tree_view_row_activated),
+ NULL);
+ g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonClear"),
+ "clicked",
+ G_CALLBACK(nsgtk_download_do),
+ nsgtk_download_store_clear_item);
+ g_signal_connect_swapped(gtk_builder_get_object(builder, "buttonCancel"),
+ "clicked",
+ G_CALLBACK(nsgtk_download_do),
+ nsgtk_download_store_cancel_item);
+ g_signal_connect(G_OBJECT(nsgtk_download_window), "delete-event",
+ G_CALLBACK(nsgtk_download_hide), NULL);
+
+ return NSERROR_OK;
+}
+
+void nsgtk_download_destroy ()
+{
+ nsgtk_download_do(nsgtk_download_store_cancel_item);
+}
+
+bool nsgtk_check_for_downloads (GtkWindow *parent)
+{
+ if (nsgtk_downloads_num_active != 0) {
+ GtkWidget *dialog;
+ dialog = gtk_message_dialog_new_with_markup(parent,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE,
+ "<big><b>%s</b></big>\n\n"
+ "<small>%s</small>", messages_get("gtkQuit"),
+ messages_get("gtkDownloadsRunning"));
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog), "gtk-cancel",
+ GTK_RESPONSE_CANCEL, "gtk-quit",
+ GTK_RESPONSE_CLOSE, NULL);
+
+ gint response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ if (response == GTK_RESPONSE_CANCEL)
+ return true;
+ }
+
+ return false;
+}
+
+void nsgtk_download_show(GtkWindow *parent)
+{
+ gtk_window_set_transient_for(nsgtk_download_window,
+ nsgtk_download_parent);
+ gtk_window_present(nsgtk_download_window);
+}
+
+static gchar* nsgtk_download_dialog_show (const gchar *filename, const gchar *domain,
+ const gchar *size)
+{
+ enum { GTK_RESPONSE_DOWNLOAD, GTK_RESPONSE_SAVE_AS };
+ GtkWidget *dialog;
+ char *destination = NULL;
+ gchar *message = g_strdup(messages_get("gtkStartDownload"));
+ gchar *info = g_strdup_printf(messages_get("gtkInfo"), filename,
+ domain, size);
+
+ dialog = gtk_message_dialog_new_with_markup(nsgtk_download_parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
+ "<span size=\"x-large\" weight=\"ultrabold\">%s</span>"
+ "\n\n<small>%s</small>",
+ message, info);
+
+ gtk_dialog_add_buttons(GTK_DIALOG(dialog), NSGTK_STOCK_SAVE,
+ GTK_RESPONSE_DOWNLOAD, NSGTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, NSGTK_STOCK_SAVE_AS,
+ GTK_RESPONSE_SAVE_AS, NULL);
+
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ g_free(message);
+ g_free(info);
+
+ switch (result) {
+ case GTK_RESPONSE_SAVE_AS: {
+ dialog = gtk_file_chooser_dialog_new
+ (messages_get("gtkSave"),
+ nsgtk_download_parent,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_file_chooser_set_current_name
+ (GTK_FILE_CHOOSER(dialog), filename);
+ gtk_file_chooser_set_current_folder
+ (GTK_FILE_CHOOSER(dialog),
+ nsoption_charp(downloads_directory));
+ gtk_file_chooser_set_do_overwrite_confirmation
+ (GTK_FILE_CHOOSER(dialog),
+ nsoption_bool(request_overwrite));
+
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (result == GTK_RESPONSE_ACCEPT)
+ destination = gtk_file_chooser_get_filename
+ (GTK_FILE_CHOOSER(dialog));
+ gtk_widget_destroy(dialog);
+ break;
+ }
+ case GTK_RESPONSE_DOWNLOAD: {
+ destination = malloc(strlen(nsoption_charp(downloads_directory))
+ + strlen(filename) + SLEN("/") + 1);
+ if (destination == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ break;
+ }
+ sprintf(destination, "%s/%s",
+ nsoption_charp(downloads_directory), filename);
+ /* Test if file already exists and display overwrite
+ * confirmation if needed */
+ if (g_file_test(destination, G_FILE_TEST_EXISTS) &&
+ nsoption_bool(request_overwrite)) {
+ message = g_strdup_printf(messages_get(
+ "gtkOverwrite"), filename);
+ info = g_strdup_printf(messages_get(
+ "gtkOverwriteInfo"),
+ nsoption_charp(downloads_directory));
+
+ dialog = gtk_message_dialog_new_with_markup(
+ nsgtk_download_parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_CANCEL,
+ "<b>%s</b>",message);
+ gtk_message_dialog_format_secondary_markup(
+ GTK_MESSAGE_DIALOG(dialog),
+ "%s", info);
+
+ GtkWidget *button = gtk_dialog_add_button(
+ GTK_DIALOG(dialog),
+ "_Replace",
+ GTK_RESPONSE_DOWNLOAD);
+ gtk_button_set_image(GTK_BUTTON(button),
+ nsgtk_image_new_from_stock(
+ NSGTK_STOCK_SAVE,
+ GTK_ICON_SIZE_BUTTON));
+
+ gint result = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (result == GTK_RESPONSE_CANCEL)
+ destination = NULL;
+
+ gtk_widget_destroy(dialog);
+ g_free(message);
+ g_free(info);
+ }
+ break;
+ }
+ }
+ return destination;
+}
+
+
+static gboolean nsgtk_download_handle_error (GError *error)
+{
+ if (error != NULL) {
+ GtkWidget*dialog;
+ gchar *message = g_strdup_printf(messages_get("gtkFileError"),
+ error->message);
+
+ dialog = gtk_message_dialog_new_with_markup
+ (nsgtk_download_parent,
+ GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>\n\n"
+ "<small>%s</small>", messages_get("gtkFailed"),
+ message);
+
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void nsgtk_download_store_create_item (struct gui_download_window *dl)
+{
+ nsgtk_download_store_update_item(dl);
+ /* The iter has already been updated to this row */
+ gtk_list_store_set(nsgtk_download_store, &nsgtk_download_iter,
+ NSGTK_DOWNLOAD, dl, -1);
+}
+
+static struct gui_download_window *
+gui_download_window_create(download_context *ctx, struct gui_window *gui)
+{
+ nsurl *url = download_context_get_url(ctx);
+ unsigned long total_size = download_context_get_total_length(ctx);
+ gchar *domain;
+ gchar *destination;
+ gboolean unknown_size = total_size == 0;
+ const char *size = (total_size == 0 ?
+ messages_get("gtkUnknownSize") :
+ human_friendly_bytesize(total_size));
+
+ nsgtk_download_parent =
+ nsgtk_scaffolding_window(nsgtk_get_scaffold(gui));
+
+ struct gui_download_window *download = malloc(sizeof *download);
+ if (download == NULL) {
+ return NULL;
+ }
+
+ /* set the domain to the host component of the url if it exists */
+ if (nsurl_has_component(url, NSURL_HOST)) {
+ domain = g_strdup(lwc_string_data(nsurl_get_component(url, NSURL_HOST)));
+ } else {
+ domain = g_strdup(messages_get("gtkUnknownHost"));
+ }
+ if (domain == NULL) {
+ free(download);
+ return NULL;
+ }
+
+ /* show the dialog */
+ destination = nsgtk_download_dialog_show(
+ download_context_get_filename(ctx), domain, size);
+ if (destination == NULL) {
+ g_free(domain);
+ free(download);
+ return NULL;
+ }
+
+ /* Add the new row and store the reference to it (which keeps track of
+ * the tree changes) */
+ gtk_list_store_prepend(nsgtk_download_store, &nsgtk_download_iter);
+ download->row = gtk_tree_row_reference_new(
+ GTK_TREE_MODEL(nsgtk_download_store),
+ gtk_tree_model_get_path(
+ GTK_TREE_MODEL(nsgtk_download_store),
+ &nsgtk_download_iter));
+
+ download->ctx = ctx;
+ download->name = g_string_new(download_context_get_filename(ctx));
+ download->time_left = g_string_new("");
+ download->size_total = total_size;
+ download->size_downloaded = 0;
+ download->speed = 0;
+ download->start_time = g_timer_elapsed(nsgtk_downloads_timer, NULL);
+ download->time_remaining = -1;
+ download->status = NSGTK_DOWNLOAD_NONE;
+ download->progress = 0;
+ download->error = NULL;
+ download->write =
+ g_io_channel_new_file(destination, "w", &download->error);
+
+ if (nsgtk_download_handle_error(download->error)) {
+ g_string_free(download->name, TRUE);
+ g_string_free(download->time_left, TRUE);
+ free(download);
+ return NULL;
+ }
+ g_io_channel_set_encoding(download->write, NULL, &download->error);
+
+ nsgtk_download_change_sensitivity(download, NSGTK_DOWNLOAD_CANCEL);
+
+ nsgtk_download_store_create_item(download);
+ nsgtk_download_show(nsgtk_download_parent);
+
+ if (unknown_size)
+ nsgtk_download_change_status(download, NSGTK_DOWNLOAD_WORKING);
+
+ if (nsgtk_downloads_num_active == 0) {
+ g_timeout_add(UPDATE_RATE,
+ (GSourceFunc) nsgtk_download_update, FALSE);
+ }
+
+ nsgtk_downloads_list = g_list_prepend(nsgtk_downloads_list, download);
+
+ return download;
+}
+
+
+static nserror gui_download_window_data(struct gui_download_window *dw,
+ const char *data, unsigned int size)
+{
+ g_io_channel_write_chars(dw->write, data, size, NULL, &dw->error);
+ if (dw->error != NULL) {
+ dw->speed = 0;
+ dw->time_remaining = -1;
+
+ nsgtk_download_change_sensitivity(dw, NSGTK_DOWNLOAD_CLEAR);
+ nsgtk_download_change_status(dw, NSGTK_DOWNLOAD_ERROR);
+
+ nsgtk_download_update(TRUE);
+
+ gtk_window_present(nsgtk_download_window);
+
+ return NSERROR_SAVE_FAILED;
+ }
+ dw->size_downloaded += size;
+
+ return NSERROR_OK;
+}
+
+
+static void gui_download_window_error(struct gui_download_window *dw,
+ const char *error_msg)
+{
+}
+
+
+static void gui_download_window_done(struct gui_download_window *dw)
+{
+ g_io_channel_shutdown(dw->write, TRUE, &dw->error);
+ g_io_channel_unref(dw->write);
+
+ dw->speed = 0;
+ dw->time_remaining = -1;
+ dw->progress = 100;
+ dw->size_total = dw->size_downloaded;
+ nsgtk_download_change_sensitivity(dw, NSGTK_DOWNLOAD_CLEAR);
+ nsgtk_download_change_status(dw, NSGTK_DOWNLOAD_COMPLETE);
+
+ if (nsoption_bool(downloads_clear))
+ nsgtk_download_store_clear_item(dw);
+ else
+ nsgtk_download_update(TRUE);
+}
+
+
+static struct gui_download_table download_table = {
+ .create = gui_download_window_create,
+ .data = gui_download_window_data,
+ .error = gui_download_window_error,
+ .done = gui_download_window_done,
+};
+
+struct gui_download_table *nsgtk_download_table = &download_table;
diff --git a/frontends/gtk/download.h b/frontends/gtk/download.h
new file mode 100644
index 000000000..0b1097655
--- /dev/null
+++ b/frontends/gtk/download.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2008 Michael Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTK_DOWNLOAD_H
+#define GTK_DOWNLOAD_H
+
+#include <gtk/gtk.h>
+
+/**
+ * download operation table for gtk
+ */
+struct gui_download_table *nsgtk_download_table;
+
+/**
+ * Initialise download window ready for use.
+ *
+ * \return NSERROR_OK on success else appropriate error code on faliure.
+ */
+nserror nsgtk_download_init(void);
+
+void nsgtk_download_destroy (void);
+bool nsgtk_check_for_downloads(GtkWindow *parent);
+void nsgtk_download_show(GtkWindow *parent);
+void nsgtk_download_add(gchar *url, gchar *destination);
+
+#endif
diff --git a/frontends/gtk/fetch.c b/frontends/gtk/fetch.c
new file mode 100644
index 000000000..06770b6b2
--- /dev/null
+++ b/frontends/gtk/fetch.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2007, 2014 Vincent Sanders <vince@netsurf-browser.org>
+ * Copyright 2007 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <gtk/gtk.h>
+
+#include "utils/hashtable.h"
+#include "utils/log.h"
+#include "utils/filepath.h"
+#include "utils/file.h"
+#include "utils/nsurl.h"
+#include "desktop/gui_fetch.h"
+
+#include "gtk/gui.h"
+#include "gtk/resources.h"
+#include "gtk/fetch.h"
+
+#define HASH_SIZE 117
+#define MAX_LINE_LEN 256
+
+static struct hash_table *mime_hash = NULL;
+
+void gtk_fetch_filetype_init(const char *mimefile)
+{
+ struct stat statbuf;
+ FILE *fh = NULL;
+
+ mime_hash = hash_create(HASH_SIZE);
+
+ /* first, check to see if /etc/mime.types in preference */
+
+ if ((stat("/etc/mime.types", &statbuf) == 0) &&
+ S_ISREG(statbuf.st_mode)) {
+ mimefile = "/etc/mime.types";
+ }
+
+ fh = fopen(mimefile, "r");
+
+ /* Some OSes (mentioning no Solarises) have a worthlessly tiny
+ * /etc/mime.types that don't include essential things, so we
+ * pre-seed our hash with the essentials. These will get
+ * over-ridden if they are mentioned in the mime.types file.
+ */
+
+ hash_add(mime_hash, "css", "text/css");
+ hash_add(mime_hash, "htm", "text/html");
+ hash_add(mime_hash, "html", "text/html");
+ hash_add(mime_hash, "jpg", "image/jpeg");
+ hash_add(mime_hash, "jpeg", "image/jpeg");
+ hash_add(mime_hash, "gif", "image/gif");
+ hash_add(mime_hash, "png", "image/png");
+ hash_add(mime_hash, "jng", "image/jng");
+ hash_add(mime_hash, "mng", "image/mng");
+ hash_add(mime_hash, "webp", "image/webp");
+ hash_add(mime_hash, "spr", "image/x-riscos-sprite");
+
+ if (fh == NULL) {
+ LOG("Unable to open a mime.types file, so using a minimal one for you.");
+ return;
+ }
+
+ while (feof(fh) == 0) {
+ char line[MAX_LINE_LEN], *ptr, *type, *ext;
+
+ if (fgets(line, sizeof(line), fh) == NULL)
+ break;
+
+ if ((feof(fh) == 0) && line[0] != '#') {
+ ptr = line;
+
+ /* search for the first non-whitespace character */
+ while (isspace(*ptr)) {
+ ptr++;
+ }
+
+ /* is this line empty other than leading whitespace? */
+ if (*ptr == '\n' || *ptr == '\0') {
+ continue;
+ }
+
+ type = ptr;
+
+ /* search for the first non-whitespace char or NUL or
+ * NL */
+ while (*ptr && (!isspace(*ptr)) && *ptr != '\n') {
+ ptr++;
+ }
+
+ if (*ptr == '\0' || *ptr == '\n') {
+ /* this mimetype has no extensions - read next
+ * line.
+ */
+ continue;
+ }
+
+ *ptr++ = '\0';
+
+ /* search for the first non-whitespace character which
+ * will be the first filename extenion */
+ while (isspace(*ptr)) {
+ ptr++;
+ }
+
+ while (true) {
+ ext = ptr;
+
+ /* search for the first whitespace char or
+ * NUL or NL which is the end of the ext.
+ */
+ while (*ptr &&
+ (!isspace(*ptr)) &&
+ *ptr != '\n') {
+ ptr++;
+ }
+
+ if (*ptr == '\0' || *ptr == '\n') {
+ /* special case for last extension on
+ * the line
+ */
+ *ptr = '\0';
+ hash_add(mime_hash, ext, type);
+ break;
+ }
+
+ *ptr++ = '\0';
+ hash_add(mime_hash, ext, type);
+
+ /* search for the first non-whitespace char or
+ * NUL or NL, to find start of next ext.
+ */
+ while (*ptr &&
+ (isspace(*ptr)) &&
+ *ptr != '\n') {
+ ptr++;
+ }
+ }
+ }
+ }
+
+ fclose(fh);
+}
+
+void gtk_fetch_filetype_fin(void)
+{
+ hash_destroy(mime_hash);
+}
+
+const char *fetch_filetype(const char *unix_path)
+{
+ struct stat statbuf;
+ char *ext;
+ const char *ptr;
+ char *lowerchar;
+ const char *type;
+ int l;
+
+ /* stat the path to attempt to determine if the file is special */
+ if (stat(unix_path, &statbuf) == 0) {
+ /* stat suceeded so can check for directory */
+
+ if (S_ISDIR(statbuf.st_mode)) {
+ return "application/x-netsurf-directory";
+ }
+ }
+
+ l = strlen(unix_path);
+
+ /* Hacky RISC OS compatibility */
+ if ((3 < l) && (strcasecmp(unix_path + l - 4, ",f79") == 0)) {
+ return "text/css";
+ } else if ((3 < l) && (strcasecmp(unix_path + l - 4, ",faf") == 0)) {
+ return "text/html";
+ } else if ((3 < l) && (strcasecmp(unix_path + l - 4, ",b60") == 0)) {
+ return "image/png";
+ } else if ((3 < l) && (strcasecmp(unix_path + l - 4, ",ff9") == 0)) {
+ return "image/x-riscos-sprite";
+ }
+
+ if (strchr(unix_path, '.') == NULL) {
+ /* no extension anywhere! */
+ return "text/plain";
+ }
+
+ ptr = unix_path + strlen(unix_path);
+ while (*ptr != '.' && *ptr != '/') {
+ ptr--;
+ }
+
+ if (*ptr != '.') {
+ return "text/plain";
+ }
+
+ ext = strdup(ptr + 1); /* skip the . */
+
+ /* the hash table only contains lower-case versions - make sure this
+ * copy is lower case too.
+ */
+ lowerchar = ext;
+ while (*lowerchar) {
+ *lowerchar = tolower(*lowerchar);
+ lowerchar++;
+ }
+
+ type = hash_get(mime_hash, ext);
+ free(ext);
+
+ if (type == NULL) {
+ type = "text/plain";
+ }
+
+ return type;
+}
+
+
+static nsurl *nsgtk_get_resource_url(const char *path)
+{
+ char buf[PATH_MAX];
+ nsurl *url = NULL;
+
+ /* favicon.ico -> favicon.png */
+ if (strcmp(path, "favicon.ico") == 0) {
+ nsurl_create("resource:favicon.png", &url);
+ } else {
+ netsurf_path_to_nsurl(filepath_sfind(respaths, buf, path), &url);
+ }
+
+ return url;
+}
+
+static struct gui_fetch_table fetch_table = {
+ .filetype = fetch_filetype,
+
+ .get_resource_url = nsgtk_get_resource_url,
+ .get_resource_data = nsgtk_data_from_resname,
+};
+
+struct gui_fetch_table *nsgtk_fetch_table = &fetch_table;
diff --git a/frontends/gtk/fetch.h b/frontends/gtk/fetch.h
new file mode 100644
index 000000000..a095adbf9
--- /dev/null
+++ b/frontends/gtk/fetch.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NETSURF_GTK_FETCH_H
+#define NETSURF_GTK_FETCH_H
+
+struct gui_fetch_table *nsgtk_fetch_table;
+
+void gtk_fetch_filetype_init(const char *mimefile);
+void gtk_fetch_filetype_fin(void);
+const char *fetch_filetype(const char *unix_path);
+
+#endif
diff --git a/frontends/gtk/gdk.c b/frontends/gtk/gdk.c
new file mode 100644
index 000000000..9ed90bd8e
--- /dev/null
+++ b/frontends/gtk/gdk.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "utils/log.h"
+
+#include "gtk/gdk.h"
+
+static void
+convert_alpha(guchar *dest_data,
+ int dest_stride,
+ guchar *src_data,
+ int src_stride,
+ int width,
+ int height)
+{
+ int x, y;
+
+ for (y = 0; y < height; y++) {
+ guint32 *src = (guint32 *) src_data;
+
+ for (x = 0; x < width; x++) {
+ guint alpha = src[x] >> 24;
+
+ if (alpha == 0) {
+ dest_data[x * 4 + 0] = 0;
+ dest_data[x * 4 + 1] = 0;
+ dest_data[x * 4 + 2] = 0;
+ } else {
+ dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+ dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
+ dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
+ }
+ dest_data[x * 4 + 3] = alpha;
+ }
+
+ src_data += src_stride;
+ dest_data += dest_stride;
+ }
+}
+
+
+GdkPixbuf *
+nsgdk_pixbuf_get_from_surface(cairo_surface_t *surface, int scwidth, int scheight)
+{
+ int width, height; /* source width and height */
+ cairo_surface_t *scsurface; /* scaled surface */
+ cairo_t *cr; /* cairo context for scaled surface */
+ GdkPixbuf *pixbuf; /* The result pixel buffer */
+
+ /* create pixmap */
+ pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, scwidth, scheight);
+ if (pixbuf == NULL) {
+ return NULL;
+ }
+
+ memset(gdk_pixbuf_get_pixels(pixbuf),
+ 0xff,
+ gdk_pixbuf_get_rowstride(pixbuf) * scheight);
+
+ /* scale cairo surface into new surface the target size */
+ cairo_surface_flush(surface); /* ensure source surface is ready */
+
+ /* get source surface dimensions */
+ width = cairo_image_surface_get_width(surface);
+ height = cairo_image_surface_get_height(surface);
+
+ /* scaled surface always has an alpha chanel for ease */
+ scsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, scwidth, scheight);
+ if (cairo_surface_status(scsurface) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(scsurface);
+ g_object_unref(pixbuf);
+ LOG("Surface creation failed");
+ return NULL;
+ }
+
+ cr = cairo_create(scsurface);
+
+ /* Scale *before* setting the source surface */
+ cairo_scale(cr, (double)scwidth / width, (double)scheight / height);
+ cairo_set_source_surface(cr, surface, 0, 0);
+
+ /* To avoid getting the edge pixels blended with 0
+ * alpha, which would occur with the default
+ * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer
+ */
+ cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REFLECT);
+
+ /* Replace the destination with the source instead of overlaying */
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ /* Do the actual drawing */
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+
+ /* copy data from surface into pixmap */
+ convert_alpha(gdk_pixbuf_get_pixels(pixbuf),
+ gdk_pixbuf_get_rowstride(pixbuf),
+ cairo_image_surface_get_data(scsurface),
+ cairo_image_surface_get_stride(scsurface),
+ scwidth, scheight);
+
+ cairo_surface_destroy(scsurface);
+
+ return pixbuf;
+}
+
diff --git a/frontends/gtk/gdk.h b/frontends/gtk/gdk.h
new file mode 100644
index 000000000..2fcee07f1
--- /dev/null
+++ b/frontends/gtk/gdk.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * GDK support functions for missing interfaces
+ */
+
+#ifndef NETSURF_GTK_GDK_H_
+#define NETSURF_GTK_GDK_H_
+
+#include <gtk/gtk.h>
+
+/** obtain a pixbuf of the specified size from a cairo surface.
+ *
+ * This is the same as the GTK+ 3 gdk_pixbuf_get_from_surface but
+ * actually works and is available on gtk 2
+ */
+GdkPixbuf *nsgdk_pixbuf_get_from_surface(cairo_surface_t *surface, int width, int height);
+
+#endif /* NETSURF_GTK_GDK_H */
diff --git a/frontends/gtk/gettext.c b/frontends/gtk/gettext.c
new file mode 100644
index 000000000..a9f6f48be
--- /dev/null
+++ b/frontends/gtk/gettext.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 Vincent Sanders <vince@kyllikki.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Localised gettext message support (implementation).
+ *
+ * Wrappers for gettext to the internal native language message support.
+ */
+
+#include <stdlib.h>
+
+#include "utils/messages.h"
+#include "gtk/gettext.h"
+
+char *gettext(const char *msgid)
+{
+ return dcgettext(NULL, msgid, 0);
+}
+
+char *dgettext(const char *domainname, const char *msgid)
+{
+ return dcgettext(domainname, msgid, 0);
+}
+
+char *dcgettext(const char *domainname, const char *msgid, int category)
+{
+ return (void *)messages_get(msgid);
+}
diff --git a/frontends/gtk/gettext.h b/frontends/gtk/gettext.h
new file mode 100644
index 000000000..726ba356e
--- /dev/null
+++ b/frontends/gtk/gettext.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Localised message support (interface).
+ *
+ * Provide gettext interface to the utility localisation routines
+ */
+
+#ifndef _NETSURF_GTK_GETTEXT_MESSAGES_H_
+#define _NETSURF_GTK_GETTEXT_MESSAGES_H_
+
+char *gettext(const char *msgid);
+char *dgettext(const char *domainname, const char *msgid);
+char *dcgettext(const char *domainname, const char *msgid, int category);
+
+#endif
diff --git a/frontends/gtk/gui.c b/frontends/gtk/gui.c
new file mode 100644
index 000000000..e705918bc
--- /dev/null
+++ b/frontends/gtk/gui.c
@@ -0,0 +1,1161 @@
+/*
+ * Copyright 2004-2010 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2010-2016 Vincent Sanders <vince@netsurf-browser.org>
+ * Copyright 2004-2009 John-Mark Bell <jmb@netsurf-browser.org>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ * Copyright 2006-2009 Daniel Silverstone <dsilvers@netsurf-browser.org>
+ * Copyright 2006-2008 Rob Kendrick <rjek@netsurf-browser.org>
+ * Copyright 2008 John Tytgat <joty@netsurf-browser.org>
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <gtk/gtk.h>
+
+#include "utils/filepath.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/file.h"
+#include "utils/nsoption.h"
+#include "content/fetchers.h"
+#include "content/hlcache.h"
+#include "content/urldb.h"
+#include "content/backing_store.h"
+#include "desktop/browser.h"
+#include "desktop/save_complete.h"
+#include "desktop/save_pdf.h"
+#include "desktop/searchweb.h"
+#include "desktop/textinput.h"
+#include "desktop/tree.h"
+#include "desktop/gui_misc.h"
+#include "desktop/netsurf.h"
+
+#include "gtk/compat.h"
+#include "gtk/warn.h"
+#include "gtk/completion.h"
+#include "gtk/cookies.h"
+#include "gtk/download.h"
+#include "gtk/fetch.h"
+#include "gtk/gui.h"
+#include "gtk/history.h"
+#include "gtk/hotlist.h"
+#include "gtk/throbber.h"
+#include "gtk/treeview.h"
+#include "gtk/window.h"
+#include "gtk/schedule.h"
+#include "gtk/selection.h"
+#include "gtk/search.h"
+#include "gtk/ssl_cert.h"
+#include "gtk/bitmap.h"
+#include "gtk/resources.h"
+#include "gtk/login.h"
+#include "gtk/layout_pango.h"
+
+bool nsgtk_complete = false;
+
+char *toolbar_indices_file_location;
+
+char *nsgtk_config_home; /* exported global defined in gtk/gui.h */
+
+GdkPixbuf *favicon_pixbuf; /** favicon default pixbuf */
+GdkPixbuf *win_default_icon_pixbuf; /** default window icon pixbuf */
+GdkPixbuf *arrow_down_pixbuf; /** arrow down pixbuf */
+
+GtkBuilder *warning_builder;
+
+char **respaths; /** resource search path vector */
+
+/**
+ * Cause an abnormal program termination.
+ *
+ * \note This never returns and is intended to terminate without any cleanup.
+ *
+ * \param error The message to display to the user.
+ */
+static void die(const char * const error)
+{
+ fprintf(stderr, "%s", error);
+ exit(EXIT_FAILURE);
+}
+
+/**
+ * Create an array of valid paths to search for resources.
+ *
+ * The idea is that all the complex path computation to find resources
+ * is performed here, once, rather than every time a resource is
+ * searched for.
+ */
+static char **
+nsgtk_init_resource_path(const char *config_home)
+{
+ char *resource_path;
+ int resource_path_len;
+ const gchar * const *langv;
+ char **pathv; /* resource path string vector */
+ char **respath; /* resource paths vector */
+
+ if (config_home != NULL) {
+ resource_path_len = snprintf(NULL, 0,
+ "%s:${NETSURFRES}:%s",
+ config_home,
+ GTK_RESPATH);
+ resource_path = malloc(resource_path_len + 1);
+ if (resource_path == NULL) {
+ return NULL;
+ }
+ snprintf(resource_path, resource_path_len + 1,
+ "%s:${NETSURFRES}:%s",
+ config_home,
+ GTK_RESPATH);
+ } else {
+ resource_path_len = snprintf(NULL, 0,
+ "${NETSURFRES}:%s",
+ GTK_RESPATH);
+ resource_path = malloc(resource_path_len + 1);
+ if (resource_path == NULL) {
+ return NULL;
+ }
+ snprintf(resource_path,
+ resource_path_len + 1,
+ "${NETSURFRES}:%s",
+ GTK_RESPATH);
+ }
+
+ pathv = filepath_path_to_strvec(resource_path);
+
+ langv = g_get_language_names();
+
+ respath = filepath_generate(pathv, langv);
+
+ filepath_free_strvec(pathv);
+
+ free(resource_path);
+
+ return respath;
+}
+
+
+/**
+ * Set option defaults for gtk frontend.
+ *
+ * @param defaults The option table to update.
+ * @return error status.
+ */
+static nserror set_defaults(struct nsoption_s *defaults)
+{
+ char *fname;
+
+ /* cookie file default */
+ fname = NULL;
+ netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Cookies");
+ if (fname != NULL) {
+ nsoption_setnull_charp(cookie_file, fname);
+ }
+
+ /* cookie jar default */
+ fname = NULL;
+ netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Cookies");
+ if (fname != NULL) {
+ nsoption_setnull_charp(cookie_jar, fname);
+ }
+
+ /* url database default */
+ fname = NULL;
+ netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "URLs");
+ if (fname != NULL) {
+ nsoption_setnull_charp(url_file, fname);
+ }
+
+ /* bookmark database default */
+ fname = NULL;
+ netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Hotlist");
+ if (fname != NULL) {
+ nsoption_setnull_charp(hotlist_path, fname);
+ }
+
+ /* download directory default */
+ fname = getenv("HOME");
+ if (fname != NULL) {
+ nsoption_setnull_charp(downloads_directory, strdup(fname));
+ }
+
+ /* default path to certificates */
+ nsoption_setnull_charp(ca_path, strdup("/etc/ssl/certs"));
+
+ if ((nsoption_charp(cookie_file) == NULL) ||
+ (nsoption_charp(cookie_jar) == NULL) ||
+ (nsoption_charp(url_file) == NULL) ||
+ (nsoption_charp(hotlist_path) == NULL) ||
+ (nsoption_charp(downloads_directory) == NULL) ||
+ (nsoption_charp(ca_path) == NULL)) {
+ LOG("Failed initialising default resource paths");
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ /* set default font names */
+ nsoption_set_charp(font_sans, strdup("Sans"));
+ nsoption_set_charp(font_serif, strdup("Serif"));
+ nsoption_set_charp(font_mono, strdup("Monospace"));
+ nsoption_set_charp(font_cursive, strdup("Serif"));
+ nsoption_set_charp(font_fantasy, strdup("Serif"));
+
+ return NSERROR_OK;
+}
+
+
+
+
+/**
+ * Initialize GTK interface.
+ */
+static nserror nsgtk_init(int argc, char** argv, char **respath)
+{
+ char buf[PATH_MAX];
+ char *resource_filename;
+ char *addr = NULL;
+ nsurl *url;
+ nserror error;
+
+ error = nsgtk_builder_new_from_resname("warning", &warning_builder);
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise warning dialog");
+ return error;
+ }
+
+ gtk_builder_connect_signals(warning_builder, NULL);
+
+ /* set default icon if its available */
+ error = nsgdk_pixbuf_new_from_resname("netsurf.xpm",
+ &win_default_icon_pixbuf);
+ if (error == NSERROR_OK) {
+ LOG("Seting default window icon");
+ gtk_window_set_default_icon(win_default_icon_pixbuf);
+ }
+
+ /* Search engine sources */
+ resource_filename = filepath_find(respath, "SearchEngines");
+ search_web_init(resource_filename);
+ if (resource_filename != NULL) {
+ LOG("Using '%s' as Search Engines file", resource_filename);
+ free(resource_filename);
+ }
+
+ /* Default favicon */
+ error = nsgdk_pixbuf_new_from_resname("favicon.png", &favicon_pixbuf);
+ if (error != NSERROR_OK) {
+ favicon_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
+ false, 8, 16, 16);
+ }
+
+ /* arrow down icon */
+ error = nsgdk_pixbuf_new_from_resname("arrow_down_8x32.png",
+ &arrow_down_pixbuf);
+ if (error != NSERROR_OK) {
+ arrow_down_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
+ false, 8, 8, 32);
+ }
+
+ /* Toolbar inicies file */
+ toolbar_indices_file_location = filepath_find(respath,
+ "toolbarIndices");
+ LOG("Using '%s' as custom toolbar settings file",
+ toolbar_indices_file_location);
+
+ /* initialise throbber */
+ error = nsgtk_throbber_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise throbber.");
+ return error;
+ }
+
+ /* Initialise completions - cannot fail */
+ nsgtk_completion_init();
+
+ filepath_sfinddef(respath, buf, "mime.types", "/etc/");
+ gtk_fetch_filetype_init(buf);
+
+ save_complete_init();
+
+ urldb_load(nsoption_charp(url_file));
+ urldb_load_cookies(nsoption_charp(cookie_file));
+
+ /* The tree view system needs to know the screen's DPI, so we
+ * find that out here, rather than when we create a first browser
+ * window.
+ */
+ browser_set_dpi(gdk_screen_get_resolution(gdk_screen_get_default()));
+ LOG("Set CSS DPI to %d", browser_get_dpi());
+
+ /* Initialise top level UI elements */
+ error = nsgtk_history_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise global history window.");
+ return error;
+ }
+
+ error = nsgtk_download_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise download window.");
+ return error;
+ }
+
+ error = nsgtk_cookies_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise cookies window.");
+ return error;
+ }
+
+ error = nsgtk_hotlist_init();
+ if (error != NSERROR_OK) {
+ LOG("Unable to initialise hotlist window.");
+ return error;
+ }
+
+ /* If there is a url specified on the command line use it */
+ if (argc > 1) {
+ struct stat fs;
+ if (stat(argv[1], &fs) == 0) {
+ size_t addrlen;
+ char *rp = realpath(argv[1], NULL);
+ assert(rp != NULL);
+
+ /* calculate file url length including terminator */
+ addrlen = SLEN("file://") + strlen(rp) + 1;
+ addr = malloc(addrlen);
+ assert(addr != NULL);
+ snprintf(addr, addrlen, "file://%s", rp);
+ free(rp);
+ } else {
+ addr = strdup(argv[1]);
+ }
+ }
+ if (addr != NULL) {
+ /* managed to set up based on local launch */
+ } else if (nsoption_charp(homepage_url) != NULL) {
+ addr = strdup(nsoption_charp(homepage_url));
+ } else {
+ addr = strdup(NETSURF_HOMEPAGE);
+ }
+
+ /* create an initial browser window */
+ error = nsurl_create(addr, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ free(addr);
+
+ return error;
+}
+
+
+
+/**
+ * Ensures output logging stream is correctly configured
+ */
+static bool nslog_stream_configure(FILE *fptr)
+{
+ /* set log stream to be non-buffering */
+ setbuf(fptr, NULL);
+
+ return true;
+}
+
+
+/**
+ * Run the gtk event loop.
+ *
+ * The same as the standard gtk_main loop except this ensures active
+ * FD are added to the gtk poll event set.
+ */
+static void nsgtk_main(void)
+{
+ fd_set read_fd_set, write_fd_set, exc_fd_set;
+ int max_fd;
+ GPollFD *fd_list[1000];
+ unsigned int fd_count;
+
+ while (!nsgtk_complete) {
+ max_fd = -1;
+ fd_count = 0;
+ FD_ZERO(&read_fd_set);
+ FD_ZERO(&write_fd_set);
+ FD_ZERO(&exc_fd_set);
+
+ fetcher_fdset(&read_fd_set, &write_fd_set, &exc_fd_set, &max_fd);
+ for (int i = 0; i <= max_fd; i++) {
+ if (FD_ISSET(i, &read_fd_set)) {
+ GPollFD *fd = malloc(sizeof *fd);
+ fd->fd = i;
+ fd->events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+ g_main_context_add_poll(0, fd, 0);
+ fd_list[fd_count++] = fd;
+ }
+ if (FD_ISSET(i, &write_fd_set)) {
+ GPollFD *fd = malloc(sizeof *fd);
+ fd->fd = i;
+ fd->events = G_IO_OUT | G_IO_ERR;
+ g_main_context_add_poll(0, fd, 0);
+ fd_list[fd_count++] = fd;
+ }
+ if (FD_ISSET(i, &exc_fd_set)) {
+ GPollFD *fd = malloc(sizeof *fd);
+ fd->fd = i;
+ fd->events = G_IO_ERR;
+ g_main_context_add_poll(0, fd, 0);
+ fd_list[fd_count++] = fd;
+ }
+ }
+
+ schedule_run();
+
+ gtk_main_iteration();
+
+ for (unsigned int i = 0; i != fd_count; i++) {
+ g_main_context_remove_poll(0, fd_list[i]);
+ free(fd_list[i]);
+ }
+ }
+}
+
+
+static void gui_quit(void)
+{
+ LOG("Quitting GUI");
+
+ /* Ensure all scaffoldings are destroyed before we go into exit */
+ nsgtk_download_destroy();
+ urldb_save_cookies(nsoption_charp(cookie_jar));
+ urldb_save(nsoption_charp(url_file));
+ nsgtk_cookies_destroy();
+ nsgtk_history_destroy();
+ nsgtk_hotlist_destroy();
+
+ free(toolbar_indices_file_location);
+
+ free(nsgtk_config_home);
+
+ gtk_fetch_filetype_fin();
+}
+
+static nserror gui_launch_url(struct nsurl *url)
+{
+ gboolean ok;
+ GError *error = NULL;
+
+ ok = nsgtk_show_uri(NULL, nsurl_access(url), GDK_CURRENT_TIME, &error);
+ if (ok == TRUE) {
+ return NSERROR_OK;
+ }
+
+ if (error) {
+ nsgtk_warning(messages_get("URIOpenError"), error->message);
+ g_error_free(error);
+ }
+ return NSERROR_NO_FETCH_HANDLER;
+}
+
+/* exported function documented in gtk/warn.h */
+nserror nsgtk_warning(const char *warning, const char *detail)
+{
+ char buf[300]; /* 300 is the size the RISC OS GUI uses */
+ static GtkWindow *nsgtk_warning_window;
+ GtkLabel *WarningLabel;
+
+ LOG("%s %s", warning, detail ? detail : "");
+ fflush(stdout);
+
+ nsgtk_warning_window = GTK_WINDOW(gtk_builder_get_object(warning_builder, "wndWarning"));
+ WarningLabel = GTK_LABEL(gtk_builder_get_object(warning_builder,
+ "labelWarning"));
+
+ snprintf(buf, sizeof(buf), "%s %s", messages_get(warning),
+ detail ? detail : "");
+ buf[sizeof(buf) - 1] = 0;
+
+ gtk_label_set_text(WarningLabel, buf);
+
+ gtk_widget_show_all(GTK_WIDGET(nsgtk_warning_window));
+
+ return NSERROR_OK;
+}
+
+
+static void nsgtk_PDF_set_pass(GtkButton *w, gpointer data)
+{
+ char **owner_pass = ((void **)data)[0];
+ char **user_pass = ((void **)data)[1];
+ GtkWindow *wnd = ((void **)data)[2];
+ GtkBuilder *password_builder = ((void **)data)[3];
+ char *path = ((void **)data)[4];
+
+ char *op, *op1;
+ char *up, *up1;
+
+ op = strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_builder_get_object(password_builder,
+ "entryPDFOwnerPassword"))));
+ op1 = strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_builder_get_object(password_builder,
+ "entryPDFOwnerPassword1"))));
+ up = strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_builder_get_object(password_builder,
+ "entryPDFUserPassword"))));
+ up1 = strdup(gtk_entry_get_text(
+ GTK_ENTRY(gtk_builder_get_object(password_builder,
+ "entryPDFUserPassword1"))));
+
+
+ if (op[0] == '\0') {
+ gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(password_builder,
+ "labelInfo")),
+ "Owner password must be at least 1 character long:");
+ free(op);
+ free(up);
+ } else if (!strcmp(op, up)) {
+ gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(password_builder,
+ "labelInfo")),
+ "User and owner passwords must be different:");
+ free(op);
+ free(up);
+ } else if (!strcmp(op, op1) && !strcmp(up, up1)) {
+
+ *owner_pass = op;
+ if (up[0] == '\0')
+ free(up);
+ else
+ *user_pass = up;
+
+ free(data);
+ gtk_widget_destroy(GTK_WIDGET(wnd));
+ g_object_unref(G_OBJECT(password_builder));
+
+ save_pdf(path);
+
+ free(path);
+ } else {
+ gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(password_builder,
+ "labelInfo")), "Passwords not confirmed:");
+ free(op);
+ free(up);
+ }
+
+ free(op1);
+ free(up1);
+}
+
+static void nsgtk_PDF_no_pass(GtkButton *w, gpointer data)
+{
+ GtkWindow *wnd = ((void **)data)[2];
+ GtkBuilder *password_builder = ((void **)data)[3];
+ char *path = ((void **)data)[4];
+
+ free(data);
+
+ gtk_widget_destroy(GTK_WIDGET(wnd));
+ g_object_unref(G_OBJECT(password_builder));
+
+ save_pdf(path);
+
+ free(path);
+}
+
+static void nsgtk_pdf_password(char **owner_pass, char **user_pass, char *path)
+{
+ GtkButton *ok, *no;
+ GtkWindow *wnd;
+ void **data;
+ GtkBuilder *password_builder;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("password", &password_builder);
+ if (res != NSERROR_OK) {
+ LOG("Password UI builder init failed");
+ return;
+ }
+
+ gtk_builder_connect_signals(password_builder, NULL);
+
+ wnd = GTK_WINDOW(gtk_builder_get_object(password_builder,
+ "wndPDFPassword"));
+
+ data = malloc(5 * sizeof(void *));
+
+ *owner_pass = NULL;
+ *user_pass = NULL;
+
+ data[0] = owner_pass;
+ data[1] = user_pass;
+ data[2] = wnd;
+ data[3] = password_builder;
+ data[4] = path;
+
+ ok = GTK_BUTTON(gtk_builder_get_object(password_builder,
+ "buttonPDFSetPassword"));
+ no = GTK_BUTTON(gtk_builder_get_object(password_builder,
+ "buttonPDFNoPassword"));
+
+ g_signal_connect(G_OBJECT(ok), "clicked",
+ G_CALLBACK(nsgtk_PDF_set_pass), (gpointer)data);
+ g_signal_connect(G_OBJECT(no), "clicked",
+ G_CALLBACK(nsgtk_PDF_no_pass), (gpointer)data);
+
+ gtk_widget_show(GTK_WIDGET(wnd));
+}
+
+
+uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *key)
+{
+ /* this function will need to become much more complex to support
+ * everything that the RISC OS version does. But this will do for
+ * now. I hope.
+ */
+ switch (key->keyval) {
+
+ case GDK_KEY(Tab):
+ return NS_KEY_TAB;
+
+ case GDK_KEY(BackSpace):
+ if (key->state & GDK_SHIFT_MASK)
+ return NS_KEY_DELETE_LINE_START;
+ else
+ return NS_KEY_DELETE_LEFT;
+ case GDK_KEY(Delete):
+ if (key->state & GDK_SHIFT_MASK)
+ return NS_KEY_DELETE_LINE_END;
+ else
+ return NS_KEY_DELETE_RIGHT;
+ case GDK_KEY(Linefeed): return 13;
+ case GDK_KEY(Return): return 10;
+ case GDK_KEY(Left): return NS_KEY_LEFT;
+ case GDK_KEY(Right): return NS_KEY_RIGHT;
+ case GDK_KEY(Up): return NS_KEY_UP;
+ case GDK_KEY(Down): return NS_KEY_DOWN;
+ case GDK_KEY(Home):
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_TEXT_START;
+ else
+ return NS_KEY_LINE_START;
+ case GDK_KEY(End):
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_TEXT_END;
+ else
+ return NS_KEY_LINE_END;
+ case GDK_KEY(Page_Up):
+ return NS_KEY_PAGE_UP;
+ case GDK_KEY(Page_Down):
+ return NS_KEY_PAGE_DOWN;
+ case 'a':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_SELECT_ALL;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'u':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_DELETE_LINE;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'c':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_COPY_SELECTION;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'v':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_PASTE;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'x':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_CUT_SELECTION;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'Z':
+ case 'y':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_REDO;
+ return gdk_keyval_to_unicode(key->keyval);
+ case 'z':
+ if (key->state & GDK_CONTROL_MASK)
+ return NS_KEY_UNDO;
+ return gdk_keyval_to_unicode(key->keyval);
+ case GDK_KEY(Escape):
+ return NS_KEY_ESCAPE;
+
+ /* Modifiers - do nothing for now */
+ case GDK_KEY(Shift_L):
+ case GDK_KEY(Shift_R):
+ case GDK_KEY(Control_L):
+ case GDK_KEY(Control_R):
+ case GDK_KEY(Caps_Lock):
+ case GDK_KEY(Shift_Lock):
+ case GDK_KEY(Meta_L):
+ case GDK_KEY(Meta_R):
+ case GDK_KEY(Alt_L):
+ case GDK_KEY(Alt_R):
+ case GDK_KEY(Super_L):
+ case GDK_KEY(Super_R):
+ case GDK_KEY(Hyper_L):
+ case GDK_KEY(Hyper_R):
+ return 0;
+
+ default:
+ return gdk_keyval_to_unicode(key->keyval);
+ }
+}
+
+
+/**
+ * create directory name and check it is acessible and a directory.
+ */
+static nserror
+check_dirname(const char *path, const char *leaf, char **dirname_out)
+{
+ nserror ret;
+ char *dirname = NULL;
+ struct stat dirname_stat;
+
+ ret = netsurf_mkpath(&dirname, NULL, 2, path, leaf);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ /* ensure access is possible and the entry is actualy
+ * a directory.
+ */
+ if (stat(dirname, &dirname_stat) == 0) {
+ if (S_ISDIR(dirname_stat.st_mode)) {
+ if (access(dirname, R_OK | W_OK) == 0) {
+ *dirname_out = dirname;
+ return NSERROR_OK;
+ } else {
+ ret = NSERROR_PERMISSION;
+ }
+ } else {
+ ret = NSERROR_NOT_DIRECTORY;
+ }
+ } else {
+ ret = NSERROR_NOT_FOUND;
+ }
+
+ free(dirname);
+
+ return ret;
+}
+
+/**
+ * Get the path to the config directory.
+ *
+ * @param config_home_out Path to configuration directory.
+ * @return NSERROR_OK on sucess and \a config_home_out updated else error code.
+ */
+static nserror get_config_home(char **config_home_out)
+{
+ nserror ret;
+ char *home_dir;
+ char *xdg_config_dir;
+ char *config_home;
+
+ home_dir = getenv("HOME");
+
+ /* The old $HOME/.netsurf/ directory should be used if it
+ * exists and is accessible.
+ */
+ if (home_dir != NULL) {
+ ret = check_dirname(home_dir, ".netsurf", &config_home);
+ if (ret == NSERROR_OK) {
+ LOG("\"%s\"", config_home);
+ *config_home_out = config_home;
+ return ret;
+ }
+ }
+
+ /* $XDG_CONFIG_HOME defines the base directory
+ * relative to which user specific configuration files
+ * should be stored.
+ */
+ xdg_config_dir = getenv("XDG_CONFIG_HOME");
+
+ if ((xdg_config_dir == NULL) || (*xdg_config_dir == 0)) {
+ /* If $XDG_CONFIG_HOME is either not set or empty, a
+ * default equal to $HOME/.config should be used.
+ */
+
+ /** @todo the meaning of empty is never defined so I
+ * am assuming it is a zero length string but is it
+ * supposed to mean "whitespace" and if so what counts
+ * as whitespace? (are tabs etc. counted or should
+ * isspace() be used)
+ */
+
+ /* the HOME envvar is required */
+ if (home_dir == NULL) {
+ return NSERROR_NOT_DIRECTORY;
+ }
+
+ ret = check_dirname(home_dir, ".config/netsurf", &config_home);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ } else {
+ ret = check_dirname(xdg_config_dir, "netsurf", &config_home);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ }
+
+ LOG("\"%s\"", config_home);
+
+ *config_home_out = config_home;
+ return NSERROR_OK;
+}
+
+static nserror create_config_home(char **config_home_out)
+{
+ char *config_home = NULL;
+ char *home_dir;
+ char *xdg_config_dir;
+ nserror ret;
+
+ LOG("Attempting to create configuration directory");
+
+ /* $XDG_CONFIG_HOME defines the base directory
+ * relative to which user specific configuration files
+ * should be stored.
+ */
+ xdg_config_dir = getenv("XDG_CONFIG_HOME");
+
+ if ((xdg_config_dir == NULL) || (*xdg_config_dir == 0)) {
+ home_dir = getenv("HOME");
+
+ if ((home_dir == NULL) || (*home_dir == 0)) {
+ return NSERROR_NOT_DIRECTORY;
+ }
+
+ ret = netsurf_mkpath(&config_home, NULL, 4, home_dir, ".config","netsurf", "/");
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ } else {
+ ret = netsurf_mkpath(&config_home, NULL, 3, xdg_config_dir, "netsurf", "/");
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ }
+
+ /* ensure all elements of path exist (the trailing / is required) */
+ ret = netsurf_mkdir_all(config_home);
+ if (ret != NSERROR_OK) {
+ free(config_home);
+ return ret;
+ }
+
+ /* strip the trailing separator */
+ config_home[strlen(config_home) - 1] = 0;
+
+ LOG("\"%s\"", config_home);
+
+ *config_home_out = config_home;
+
+ return NSERROR_OK;
+}
+
+/**
+ * Get the path to the cache directory.
+ *
+ * @param cache_home_out Path to cache directory.
+ * @return NSERROR_OK on sucess and \a cache_home_out updated else error code.
+ */
+static nserror get_cache_home(char **cache_home_out)
+{
+ nserror ret;
+ char *xdg_cache_dir;
+ char *cache_home;
+ char *home_dir;
+
+ /* $XDG_CACHE_HOME defines the base directory relative to
+ * which user specific non-essential data files should be
+ * stored.
+ */
+ xdg_cache_dir = getenv("XDG_CACHE_HOME");
+
+ if ((xdg_cache_dir == NULL) || (*xdg_cache_dir == 0)) {
+ /* If $XDG_CACHE_HOME is either not set or empty, a
+ * default equal to $HOME/.cache should be used.
+ */
+
+ home_dir = getenv("HOME");
+
+ /* the HOME envvar is required */
+ if (home_dir == NULL) {
+ return NSERROR_NOT_DIRECTORY;
+ }
+
+ ret = check_dirname(home_dir, ".cache/netsurf", &cache_home);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ } else {
+ ret = check_dirname(xdg_cache_dir, "netsurf", &cache_home);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ }
+
+ LOG("\"%s\"", cache_home);
+
+ *cache_home_out = cache_home;
+ return NSERROR_OK;
+}
+
+static nserror create_cache_home(char **cache_home_out)
+{
+ char *cache_home = NULL;
+ char *home_dir;
+ char *xdg_cache_dir;
+ nserror ret;
+
+ LOG("Attempting to create configuration directory");
+
+ /* $XDG_CACHE_HOME defines the base directory
+ * relative to which user specific cache files
+ * should be stored.
+ */
+ xdg_cache_dir = getenv("XDG_CACHE_HOME");
+
+ if ((xdg_cache_dir == NULL) || (*xdg_cache_dir == 0)) {
+ home_dir = getenv("HOME");
+
+ if ((home_dir == NULL) || (*home_dir == 0)) {
+ return NSERROR_NOT_DIRECTORY;
+ }
+
+ ret = netsurf_mkpath(&cache_home, NULL, 4, home_dir, ".cache", "netsurf", "/");
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ } else {
+ ret = netsurf_mkpath(&cache_home, NULL, 3, xdg_cache_dir, "netsurf", "/");
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+ }
+
+ /* ensure all elements of path exist (the trailing / is required) */
+ ret = netsurf_mkdir_all(cache_home);
+ if (ret != NSERROR_OK) {
+ free(cache_home);
+ return ret;
+ }
+
+ /* strip the trailing separator */
+ cache_home[strlen(cache_home) - 1] = 0;
+
+ LOG("\"%s\"", cache_home);
+
+ *cache_home_out = cache_home;
+
+ return NSERROR_OK;
+}
+
+static nserror nsgtk_option_init(int *pargc, char** argv)
+{
+ nserror ret;
+ char *choices = NULL;
+
+ /* user options setup */
+ ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ /* Attempt to load the user choices */
+ ret = netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (ret == NSERROR_OK) {
+ nsoption_read(choices, nsoptions);
+ free(choices);
+ }
+
+ /* overide loaded options with those from commandline */
+ nsoption_commandline(pargc, argv, nsoptions);
+
+ /* ensure all options fall within sensible bounds */
+
+ /* Attempt to handle nonsense status bar widths. These may exist
+ * in people's Choices as the GTK front end used to abuse the
+ * status bar width option by using it for an absolute value in px.
+ * The GTK front end now correctly uses it as a proportion of window
+ * width. Here we assume that a value of less than 15% is wrong
+ * and set to the default two thirds. */
+ if (nsoption_int(toolbar_status_size) < 1500) {
+ nsoption_set_int(toolbar_status_size, 6667);
+ }
+
+ return NSERROR_OK;
+}
+
+static struct gui_misc_table nsgtk_misc_table = {
+ .schedule = nsgtk_schedule,
+ .warning = nsgtk_warning,
+
+ .quit = gui_quit,
+ .launch_url = gui_launch_url,
+ .cert_verify = gtk_cert_verify,
+ .login = gui_401login_open,
+ .pdf_password = nsgtk_pdf_password,
+};
+
+
+static nserror nsgtk_messages_init(char **respaths)
+{
+ const char *messages;
+ nserror ret;
+ const uint8_t *data;
+ size_t data_size;
+
+ ret = nsgtk_data_from_resname("Messages", &data, &data_size);
+ if (ret == NSERROR_OK) {
+ ret = messages_add_from_inline(data, data_size);
+ } else {
+ /* Obtain path to messages */
+ ret = nsgtk_path_from_resname("Messages", &messages);
+ if (ret == NSERROR_OK) {
+ ret = messages_add_from_file(messages);
+ }
+ }
+ return ret;
+}
+
+/**
+ * Main entry point from OS.
+ */
+int main(int argc, char** argv)
+{
+ char *cache_home = NULL;
+ nserror ret;
+ struct netsurf_table nsgtk_table = {
+ .misc = &nsgtk_misc_table,
+ .window = nsgtk_window_table,
+ .clipboard = nsgtk_clipboard_table,
+ .download = nsgtk_download_table,
+ .fetch = nsgtk_fetch_table,
+ .llcache = filesystem_llcache_table,
+ .search = nsgtk_search_table,
+ .search_web = nsgtk_search_web_table,
+ .bitmap = nsgtk_bitmap_table,
+ .layout = nsgtk_layout_table,
+ };
+
+ ret = netsurf_register(&nsgtk_table);
+ if (ret != NSERROR_OK) {
+ die("NetSurf operation table failed registration\n");
+ }
+
+ /* Locate the correct user configuration directory path */
+ ret = get_config_home(&nsgtk_config_home);
+ if (ret == NSERROR_NOT_FOUND) {
+ /* no config directory exists yet so try to create one */
+ ret = create_config_home(&nsgtk_config_home);
+ }
+ if (ret != NSERROR_OK) {
+ LOG("Unable to locate a configuration directory.");
+ nsgtk_config_home = NULL;
+ }
+
+ /* Initialise gtk */
+ gtk_init(&argc, &argv);
+
+ /* initialise logging. Not fatal if it fails but not much we
+ * can do about it either.
+ */
+ nslog_init(nslog_stream_configure, &argc, argv);
+
+ /* build the common resource path list */
+ respaths = nsgtk_init_resource_path(nsgtk_config_home);
+ if (respaths == NULL) {
+ fprintf(stderr, "Unable to locate resources\n");
+ return 1;
+ }
+
+ /* initialise the gtk resource handling */
+ ret = nsgtk_init_resources(respaths);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "GTK resources failed to initialise (%s)\n",
+ messages_get_errorcode(ret));
+ return 1;
+ }
+
+ /* Initialise user options */
+ ret = nsgtk_option_init(&argc, argv);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "Options failed to initialise (%s)\n",
+ messages_get_errorcode(ret));
+ return 1;
+ }
+
+ /* Initialise translated messages */
+ ret = nsgtk_messages_init(respaths);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "Unable to load translated messages (%s)\n",
+ messages_get_errorcode(ret));
+ LOG("Unable to load translated messages");
+ /** \todo decide if message load faliure should be fatal */
+ }
+
+ /* Locate the correct user cache directory path */
+ ret = get_cache_home(&cache_home);
+ if (ret == NSERROR_NOT_FOUND) {
+ /* no cache directory exists yet so try to create one */
+ ret = create_cache_home(&cache_home);
+ }
+ if (ret != NSERROR_OK) {
+ LOG("Unable to locate a cache directory.");
+ }
+
+ /* core initialisation */
+ ret = netsurf_init(cache_home);
+ free(cache_home);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "NetSurf core failed to initialise (%s)\n",
+ messages_get_errorcode(ret));
+ return 1;
+ }
+
+ /* run the browser */
+ ret = nsgtk_init(argc, argv, respaths);
+ if (ret != NSERROR_OK) {
+ fprintf(stderr, "NetSurf gtk initialise failed (%s)\n",
+ messages_get_errorcode(ret));
+ } else {
+ nsgtk_main();
+ }
+
+ /* common finalisation */
+ netsurf_exit();
+
+ /* finalise options */
+ nsoption_finalise(nsoptions, nsoptions_default);
+
+ return 0;
+}
diff --git a/frontends/gtk/gui.h b/frontends/gtk/gui.h
new file mode 100644
index 000000000..b6a6dc994
--- /dev/null
+++ b/frontends/gtk/gui.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTK_GUI_H
+#define GTK_GUI_H
+
+struct nsurl;
+
+/** toolbar arrangement file path. */
+extern char *toolbar_indices_file_location;
+
+/** Directory where all configuration files are held. */
+extern char *nsgtk_config_home;
+
+/** favicon default pixbuf */
+extern GdkPixbuf *favicon_pixbuf;
+
+/** arrow down pixbuf */
+extern GdkPixbuf *arrow_down_pixbuf;
+
+/** resource search path vector */
+extern char **respaths;
+
+/** input conversion. */
+uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *eventkey);
+
+/** set when no windows remain open. */
+extern bool nsgtk_complete;
+
+#endif /* GTK_GUI_H */
diff --git a/frontends/gtk/history.c b/frontends/gtk/history.c
new file mode 100644
index 000000000..9c5c0b5e4
--- /dev/null
+++ b/frontends/gtk/history.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "desktop/global_history.h"
+#include "desktop/plot_style.h"
+#include "desktop/tree.h"
+#include "desktop/textinput.h"
+
+#include "gtk/plotters.h"
+#include "gtk/scaffolding.h"
+#include "gtk/treeview.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/history.h"
+
+#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
+ GtkMenuItem *widget, gpointer g)
+#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
+#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \
+ gpointer g)
+
+struct menu_events {
+ const char *widget;
+ GCallback handler;
+};
+
+/* file menu*/
+MENUPROTO(export);
+
+/* edit menu */
+MENUPROTO(delete_selected);
+MENUPROTO(delete_all);
+MENUPROTO(select_all);
+MENUPROTO(clear_selection);
+
+/* view menu*/
+MENUPROTO(expand_all);
+MENUPROTO(expand_directories);
+MENUPROTO(expand_addresses);
+MENUPROTO(collapse_all);
+MENUPROTO(collapse_directories);
+MENUPROTO(collapse_addresses);
+
+MENUPROTO(launch);
+
+static struct menu_events menu_events[] = {
+
+ /* file menu*/
+ MENUEVENT(export),
+
+ /* edit menu */
+ MENUEVENT(delete_selected),
+ MENUEVENT(delete_all),
+ MENUEVENT(select_all),
+ MENUEVENT(clear_selection),
+
+ /* view menu*/
+ MENUEVENT(expand_all),
+ MENUEVENT(expand_directories),
+ MENUEVENT(expand_addresses),
+ MENUEVENT(collapse_all),
+ MENUEVENT(collapse_directories),
+ MENUEVENT(collapse_addresses),
+
+ MENUEVENT(launch),
+ {NULL, NULL}
+};
+
+static struct nsgtk_treeview *global_history_window;
+static GtkBuilder *history_builder;
+GtkWindow *wndHistory;
+
+/**
+ * Connects menu events in the global history window.
+ */
+static void nsgtk_history_init_menu(void)
+{
+ struct menu_events *event = menu_events;
+ GtkWidget *w;
+
+ while (event->widget != NULL) {
+ w = GTK_WIDGET(gtk_builder_get_object(history_builder,
+ event->widget));
+ if (w == NULL) {
+ LOG("Unable to connect menu widget ""%s""",
+ event->widget);
+ } else {
+ g_signal_connect(G_OBJECT(w),
+ "activate",
+ event->handler,
+ global_history_window);
+ }
+ event++;
+ }
+}
+
+/* exported interface, documented in gtk/history.h */
+nserror nsgtk_history_init(void)
+{
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("history", &history_builder);
+ if (res != NSERROR_OK) {
+ LOG("History UI builder init failed");
+ return res;
+ }
+ gtk_builder_connect_signals(history_builder, NULL);
+
+ wndHistory = GTK_WINDOW(gtk_builder_get_object(history_builder,
+ "wndHistory"));
+
+ window = wndHistory;
+
+ scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(history_builder,
+ "globalHistoryScrolled"));
+
+ drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(history_builder,
+ "globalHistoryDrawingArea"));
+
+ global_history_window = nsgtk_treeview_create(TREE_HISTORY,
+ window,
+ scrolled,
+ drawing_area);
+ if (global_history_window == NULL) {
+ return NSERROR_INIT_FAILED;
+ }
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL);
+ CONNECT(window, "hide", nsgtk_tree_window_hide, global_history_window);
+
+ nsgtk_history_init_menu();
+
+ return NSERROR_OK;
+}
+
+
+
+
+/**
+ * Destroys the global history window and performs any other necessary cleanup
+ * actions.
+ */
+void nsgtk_history_destroy(void)
+{
+ /** \todo what about history_builder? */
+ nsgtk_treeview_destroy(global_history_window);
+}
+
+
+/* file menu */
+MENUHANDLER(export)
+{
+ GtkWidget *save_dialog;
+ save_dialog = gtk_file_chooser_dialog_new("Save File",
+ wndHistory,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog),
+ getenv("HOME") ? getenv("HOME") : "/");
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog),
+ "history.html");
+
+ if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) {
+ gchar *filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(save_dialog));
+
+ global_history_export(filename, NULL);
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(save_dialog);
+
+ return TRUE;
+}
+
+/* edit menu */
+MENUHANDLER(delete_selected)
+{
+ global_history_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(delete_all)
+{
+ global_history_keypress(NS_KEY_SELECT_ALL);
+ global_history_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(select_all)
+{
+ global_history_keypress(NS_KEY_SELECT_ALL);
+ return TRUE;
+}
+
+MENUHANDLER(clear_selection)
+{
+ global_history_keypress(NS_KEY_CLEAR_SELECTION);
+ return TRUE;
+}
+
+/* view menu*/
+MENUHANDLER(expand_all)
+{
+ global_history_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(expand_directories)
+{
+ global_history_expand(true);
+ return TRUE;
+}
+
+MENUHANDLER(expand_addresses)
+{
+ global_history_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_all)
+{
+ global_history_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_directories)
+{
+ global_history_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_addresses)
+{
+ global_history_contract(false);
+ return TRUE;
+}
+
+MENUHANDLER(launch)
+{
+ global_history_keypress(NS_KEY_CR);
+ return TRUE;
+}
diff --git a/frontends/gtk/history.h b/frontends/gtk/history.h
new file mode 100644
index 000000000..c0f7db2bd
--- /dev/null
+++ b/frontends/gtk/history.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __NSGTK_HISTORY_H__
+#define __NSGTK_HISTORY_H__
+
+#include <gtk/gtk.h>
+
+extern GtkWindow *wndHistory;
+
+/**
+ * Creates the window for the global history tree.
+ *
+ * \return NSERROR_OK on sucess else appropriate error code.
+ */
+nserror nsgtk_history_init(void);
+
+/**
+ * Free global resources associated with the gtk history window.
+ */
+void nsgtk_history_destroy(void);
+
+#endif /* __NSGTK_HISTORY_H__ */
diff --git a/frontends/gtk/hotlist.c b/frontends/gtk/hotlist.c
new file mode 100644
index 000000000..06fd5cd69
--- /dev/null
+++ b/frontends/gtk/hotlist.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "desktop/hotlist.h"
+#include "desktop/tree.h"
+
+#include "gtk/plotters.h"
+#include "gtk/scaffolding.h"
+#include "gtk/treeview.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/hotlist.h"
+
+#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
+ GtkMenuItem *widget, gpointer g)
+#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
+#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \
+ gpointer g)
+
+struct menu_events {
+ const char *widget;
+ GCallback handler;
+};
+
+
+/* file menu*/
+MENUPROTO(export);
+MENUPROTO(new_folder);
+MENUPROTO(new_entry);
+
+/* edit menu */
+MENUPROTO(edit_selected);
+MENUPROTO(delete_selected);
+MENUPROTO(select_all);
+MENUPROTO(clear_selection);
+
+/* view menu*/
+MENUPROTO(expand_all);
+MENUPROTO(expand_directories);
+MENUPROTO(expand_addresses);
+MENUPROTO(collapse_all);
+MENUPROTO(collapse_directories);
+MENUPROTO(collapse_addresses);
+
+MENUPROTO(launch);
+
+static struct menu_events menu_events[] = {
+
+ /* file menu*/
+ MENUEVENT(export),
+ MENUEVENT(new_folder),
+ MENUEVENT(new_entry),
+
+ /* edit menu */
+ MENUEVENT(edit_selected),
+ MENUEVENT(delete_selected),
+ MENUEVENT(select_all),
+ MENUEVENT(clear_selection),
+
+ /* view menu*/
+ MENUEVENT(expand_all),
+ MENUEVENT(expand_directories),
+ MENUEVENT(expand_addresses),
+ MENUEVENT(collapse_all),
+ MENUEVENT(collapse_directories),
+ MENUEVENT(collapse_addresses),
+
+ MENUEVENT(launch),
+ {NULL, NULL}
+};
+
+static struct nsgtk_treeview *hotlist_treeview;
+static GtkBuilder *hotlist_builder;
+GtkWindow *wndHotlist;
+
+/**
+ * Connects menu events in the hotlist window.
+ */
+static void nsgtk_hotlist_init_menu(void)
+{
+ struct menu_events *event = menu_events;
+ GtkWidget *w;
+
+ while (event->widget != NULL) {
+ w = GTK_WIDGET(gtk_builder_get_object(hotlist_builder, event->widget));
+ if (w == NULL) {
+ LOG("Unable to connect menu widget ""%s""", event->widget); } else {
+ g_signal_connect(G_OBJECT(w), "activate", event->handler, hotlist_treeview);
+ }
+ event++;
+ }
+}
+
+/* exported interface docuemnted in gtk/hotlist.h */
+nserror nsgtk_hotlist_init(void)
+{
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("hotlist", &hotlist_builder);
+ if (res != NSERROR_OK) {
+ LOG("Cookie UI builder init failed");
+ return res;
+ }
+
+ gtk_builder_connect_signals(hotlist_builder, NULL);
+
+ wndHotlist = GTK_WINDOW(gtk_builder_get_object(hotlist_builder, "wndHotlist"));
+ window = wndHotlist;
+
+ scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(hotlist_builder,
+ "hotlistScrolled"));
+
+ drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(hotlist_builder,
+ "hotlistDrawingArea"));
+
+
+ tree_hotlist_path = nsoption_charp(hotlist_path);
+ hotlist_treeview = nsgtk_treeview_create(TREE_HOTLIST, window,
+ scrolled, drawing_area);
+
+ if (hotlist_treeview == NULL) {
+ return NSERROR_INIT_FAILED;
+ }
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL);
+ CONNECT(window, "hide", nsgtk_tree_window_hide, hotlist_treeview);
+
+ nsgtk_hotlist_init_menu();
+
+ return NSERROR_OK;
+}
+
+
+
+
+/**
+ * Destroys the hotlist window and performs any other necessary cleanup actions.
+ */
+void nsgtk_hotlist_destroy(void)
+{
+ /** \todo what about hotlist_builder? */
+ nsgtk_treeview_destroy(hotlist_treeview);
+}
+
+
+/* file menu*/
+MENUHANDLER(export)
+{
+ GtkWidget *save_dialog;
+ save_dialog = gtk_file_chooser_dialog_new("Save File",
+ wndHotlist,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog),
+ getenv("HOME") ? getenv("HOME") : "/");
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog),
+ "hotlist.html");
+
+ if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) {
+ gchar *filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(save_dialog));
+
+ hotlist_export(filename, NULL);
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(save_dialog);
+
+ return TRUE;
+}
+
+MENUHANDLER(new_folder)
+{
+ hotlist_add_folder(NULL, false, 0);
+ return TRUE;
+}
+
+MENUHANDLER(new_entry)
+{
+ hotlist_add_entry(NULL, NULL, false, 0);
+ return TRUE;
+}
+
+/* edit menu */
+MENUHANDLER(edit_selected)
+{
+ hotlist_edit_selection();
+ return TRUE;
+}
+
+MENUHANDLER(delete_selected)
+{
+ hotlist_keypress(NS_KEY_DELETE_LEFT);
+ return TRUE;
+}
+
+MENUHANDLER(select_all)
+{
+ hotlist_keypress(NS_KEY_SELECT_ALL);
+ return TRUE;
+}
+
+MENUHANDLER(clear_selection)
+{
+ hotlist_keypress(NS_KEY_CLEAR_SELECTION);
+ return TRUE;
+}
+
+/* view menu*/
+MENUHANDLER(expand_all)
+{
+ hotlist_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(expand_directories)
+{
+ hotlist_expand(true);
+ return TRUE;
+}
+
+MENUHANDLER(expand_addresses)
+{
+ hotlist_expand(false);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_all)
+{
+ hotlist_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_directories)
+{
+ hotlist_contract(true);
+ return TRUE;
+}
+
+MENUHANDLER(collapse_addresses)
+{
+ hotlist_contract(false);
+ return TRUE;
+}
+
+MENUHANDLER(launch)
+{
+ hotlist_keypress(NS_KEY_CR);
+ return TRUE;
+}
diff --git a/frontends/gtk/hotlist.h b/frontends/gtk/hotlist.h
new file mode 100644
index 000000000..01e5a86c5
--- /dev/null
+++ b/frontends/gtk/hotlist.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * GTK hotlist (interface).
+ */
+
+#ifndef __NSGTK_HOTLIST_H__
+#define __NSGTK_HOTLIST_H__
+
+#include <gtk/gtk.h>
+
+extern GtkWindow *wndHotlist;
+
+/**
+ * Initialise the gtk specific hotlist (bookmarks) display.
+ *
+ * \return NSERROR_OK on success else appropriate error code on faliure.
+ */
+nserror nsgtk_hotlist_init(void);
+
+
+void nsgtk_hotlist_destroy(void);
+
+#endif /* __NSGTK_HOTLIST_H__ */
diff --git a/frontends/gtk/layout_pango.c b/frontends/gtk/layout_pango.c
new file mode 100644
index 000000000..49b629399
--- /dev/null
+++ b/frontends/gtk/layout_pango.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * GTK implementation of layout handling using pango.
+ *
+ * Pango is used handle and render fonts.
+ */
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "desktop/gui_layout.h"
+
+#include "gtk/layout_pango.h"
+#include "gtk/plotters.h"
+
+static PangoContext *nsfont_pango_context = NULL;
+static PangoLayout *nsfont_pango_layout = NULL;
+
+static inline void nsfont_pango_check(void)
+{
+ if (nsfont_pango_context == NULL) {
+ LOG("Creating nsfont_pango_context.");
+ nsfont_pango_context = gdk_pango_context_get();
+ }
+
+ if (nsfont_pango_layout == NULL) {
+ LOG("Creating nsfont_pango_layout.");
+ nsfont_pango_layout = pango_layout_new(nsfont_pango_context);
+ }
+}
+
+/**
+ * Measure the width of a string.
+ *
+ * \param[in] fstyle plot style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[out] width updated to width of string[0..length)
+ * \return NSERROR_OK and width updated or appropriate error code on faliure
+ */
+static nserror
+nsfont_width(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int *width)
+{
+ PangoFontDescription *desc;
+
+ if (length == 0) {
+ *width = 0;
+ return NSERROR_OK;
+ }
+
+ nsfont_pango_check();
+
+ desc = nsfont_style_to_description(fstyle);
+ pango_layout_set_font_description(nsfont_pango_layout, desc);
+ pango_font_description_free(desc);
+
+ pango_layout_set_text(nsfont_pango_layout, string, length);
+
+ pango_layout_get_pixel_size(nsfont_pango_layout, width, 0);
+
+ /* LOG("fstyle: %p string:\"%.*s\", length: %u, width: %dpx",
+ fstyle, length, string, length, *width);
+ */
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find the position in a string where an x coordinate falls.
+ *
+ * \param[in] fstyle style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[in] x coordinate to search for
+ * \param[out] char_offset updated to offset in string of actual_x, [0..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK and char_offset and actual_x updated or appropriate
+ * error code on faliure
+ */
+static nserror
+nsfont_position_in_string(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ int index;
+ PangoFontDescription *desc;
+ PangoRectangle pos;
+
+ nsfont_pango_check();
+
+ desc = nsfont_style_to_description(fstyle);
+ pango_layout_set_font_description(nsfont_pango_layout, desc);
+ pango_font_description_free(desc);
+
+ pango_layout_set_text(nsfont_pango_layout, string, length);
+
+ if (pango_layout_xy_to_index(nsfont_pango_layout,
+ x * PANGO_SCALE,
+ 0, &index, 0) == FALSE) {
+ index = length;
+ }
+
+ pango_layout_index_to_pos(nsfont_pango_layout, index, &pos);
+
+ *char_offset = index;
+ *actual_x = PANGO_PIXELS(pos.x);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Find where to split a string to make it fit a width.
+ *
+ * \param[in] fstyle style for this text
+ * \param[in] string UTF-8 string to measure
+ * \param[in] length length of string, in bytes
+ * \param[in] x width available
+ * \param[out] char_offset updated to offset in string of actual_x, [1..length]
+ * \param[out] actual_x updated to x coordinate of character closest to x
+ * \return NSERROR_OK or appropriate error code on faliure
+ *
+ * On exit, char_offset indicates first character after split point.
+ *
+ * \note char_offset of 0 must never be returned.
+ *
+ * Returns:
+ * char_offset giving split point closest to x, where actual_x <= x
+ * else
+ * char_offset giving split point closest to x, where actual_x > x
+ *
+ * Returning char_offset == length means no split possible
+ */
+static nserror
+nsfont_split(const plot_font_style_t *fstyle,
+ const char *string,
+ size_t length,
+ int x,
+ size_t *char_offset,
+ int *actual_x)
+{
+ int index = length;
+ PangoFontDescription *desc;
+ PangoContext *context;
+ PangoLayout *layout;
+ PangoLayoutLine *line;
+
+ context = gdk_pango_context_get();
+ layout = pango_layout_new(context);
+
+ desc = nsfont_style_to_description(fstyle);
+ pango_layout_set_font_description(layout, desc);
+ pango_font_description_free(desc);
+
+ pango_layout_set_text(layout, string, length);
+
+ /* Limit width of layout to the available width */
+ pango_layout_set_width(layout, x * PANGO_SCALE);
+
+ /* Request word wrapping */
+ pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
+
+ /* Prevent pango treating linebreak characters as line breaks */
+ pango_layout_set_single_paragraph_mode(layout, TRUE);
+
+ /* Obtain the second line of the layout (if there is one) */
+ line = pango_layout_get_line(layout, 1);
+ if (line != NULL) {
+ /* Pango split the text. The line's start_index indicates the
+ * start of the character after the line break. */
+ index = line->start_index;
+ }
+
+ g_object_unref(layout);
+ g_object_unref(context);
+
+ *char_offset = index;
+ /* Obtain the pixel offset of the split character */
+ nsfont_width(fstyle, string, index, actual_x);
+
+ return NSERROR_OK;
+}
+
+
+/**
+ * Render a string.
+ *
+ * \param x x coordinate
+ * \param y y coordinate
+ * \param string UTF-8 string to measure
+ * \param length length of string
+ * \param fstyle plot style for this text
+ * \return true on success, false on error and error reported
+ */
+bool nsfont_paint(int x, int y, const char *string, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ PangoLayoutLine *line;
+
+ if (length == 0)
+ return true;
+
+ layout = pango_cairo_create_layout(current_cr);
+
+ desc = nsfont_style_to_description(fstyle);
+ pango_layout_set_font_description(layout, desc);
+ pango_font_description_free(desc);
+
+ pango_layout_set_text(layout, string, length);
+
+ line = pango_layout_get_line_readonly(layout, 0);
+ cairo_move_to(current_cr, x, y);
+ nsgtk_set_colour(fstyle->foreground);
+ pango_cairo_show_layout_line(current_cr, line);
+
+ g_object_unref(layout);
+
+ return true;
+}
+
+
+/* exported interface documented in gtk/layout_pango.h */
+PangoFontDescription *
+nsfont_style_to_description(const plot_font_style_t *fstyle)
+{
+ unsigned int size;
+ PangoFontDescription *desc;
+ PangoStyle style = PANGO_STYLE_NORMAL;
+
+ switch (fstyle->family) {
+ case PLOT_FONT_FAMILY_SERIF:
+ desc = pango_font_description_from_string(nsoption_charp(font_serif));
+ break;
+ case PLOT_FONT_FAMILY_MONOSPACE:
+ desc = pango_font_description_from_string(nsoption_charp(font_mono));
+ break;
+ case PLOT_FONT_FAMILY_CURSIVE:
+ desc = pango_font_description_from_string(nsoption_charp(font_cursive));
+ break;
+ case PLOT_FONT_FAMILY_FANTASY:
+ desc = pango_font_description_from_string(nsoption_charp(font_fantasy));
+ break;
+ case PLOT_FONT_FAMILY_SANS_SERIF:
+ default:
+ desc = pango_font_description_from_string(nsoption_charp(font_sans));
+ break;
+ }
+
+ size = (fstyle->size * PANGO_SCALE) / FONT_SIZE_SCALE;
+
+ if (fstyle->flags & FONTF_ITALIC)
+ style = PANGO_STYLE_ITALIC;
+ else if (fstyle->flags & FONTF_OBLIQUE)
+ style = PANGO_STYLE_OBLIQUE;
+
+ pango_font_description_set_style(desc, style);
+
+ pango_font_description_set_weight(desc, (PangoWeight) fstyle->weight);
+
+ pango_font_description_set_size(desc, size);
+
+ if (fstyle->flags & FONTF_SMALLCAPS) {
+ pango_font_description_set_variant(desc,
+ PANGO_VARIANT_SMALL_CAPS);
+ } else {
+ pango_font_description_set_variant(desc, PANGO_VARIANT_NORMAL);
+ }
+
+ return desc;
+}
+
+static struct gui_layout_table layout_table = {
+ .width = nsfont_width,
+ .position = nsfont_position_in_string,
+ .split = nsfont_split,
+};
+
+struct gui_layout_table *nsgtk_layout_table = &layout_table;
diff --git a/frontends/gtk/layout_pango.h b/frontends/gtk/layout_pango.h
new file mode 100644
index 000000000..137cebe68
--- /dev/null
+++ b/frontends/gtk/layout_pango.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Interface to GTK layout handling using pango.
+ */
+
+#ifndef _NETSURF_GTK_LAYOUT_PANGO_H_
+#define _NETSURF_GTK_LAYOUT_PANGO_H_
+
+#include <stdbool.h>
+
+struct plot_font_style;
+
+extern struct gui_layout_table *nsgtk_layout_table;
+
+bool nsfont_paint(int x, int y, const char *string, size_t length, const struct plot_font_style *fstyle);
+
+/**
+ * Convert a plot style to a PangoFontDescription.
+ *
+ * \param fstyle plot style for this text
+ * \return A new Pango font description
+ */
+PangoFontDescription *nsfont_style_to_description(const struct plot_font_style *fstyle);
+
+#endif
diff --git a/frontends/gtk/login.c b/frontends/gtk/login.c
new file mode 100644
index 000000000..ee77052d7
--- /dev/null
+++ b/frontends/gtk/login.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsurl.h"
+#include "content/urldb.h"
+
+#include "gtk/resources.h"
+#include "gtk/login.h"
+
+/** login window session data */
+struct session_401 {
+ nsurl *url; /**< URL being fetched */
+ lwc_string *host; /**< Host for user display */
+ char *realm; /**< Authentication realm */
+ nserror (*cb)(bool proceed, void *pw); /**< Continuation callback */
+ void *cbpw; /**< Continuation data */
+ GtkBuilder *x; /**< Our builder windows */
+ GtkWindow *wnd; /**< The login window itself */
+ GtkEntry *user; /**< Widget with username */
+ GtkEntry *pass; /**< Widget with password */
+};
+
+/**
+ * Destroy login window and free all associated resources
+ *
+ * \param session The login window session to destroy.
+ */
+static void destroy_login_window(struct session_401 *session)
+{
+ nsurl_unref(session->url);
+ lwc_string_unref(session->host);
+ free(session->realm);
+ gtk_widget_destroy(GTK_WIDGET(session->wnd));
+ g_object_unref(G_OBJECT(session->x));
+ free(session);
+}
+
+
+/**
+ * process next signal in entry widgets.
+ *
+ * \param w current widget
+ * \param data next widget
+ */
+static void nsgtk_login_next(GtkWidget *w, gpointer data)
+{
+ gtk_widget_grab_focus(GTK_WIDGET(data));
+}
+
+
+/**
+ * handler called when navigation is continued
+ *
+ * \param w current widget
+ * \param data login window session
+ */
+static void nsgtk_login_ok_clicked(GtkButton *w, gpointer data)
+{
+ /* close the window and destroy it, having continued the fetch
+ * assoicated with it.
+ */
+
+ struct session_401 *session = (struct session_401 *)data;
+ const gchar *user = gtk_entry_get_text(session->user);
+ const gchar *pass = gtk_entry_get_text(session->pass);
+ char *auth;
+
+ auth = malloc(strlen(user) + strlen(pass) + 2);
+ sprintf(auth, "%s:%s", user, pass);
+ urldb_set_auth_details(session->url, session->realm, auth);
+ free(auth);
+
+ session->cb(true, session->cbpw);
+
+ destroy_login_window(session);
+}
+
+
+/**
+ * handler called when navigation is cancelled
+ *
+ * \param w widget
+ * \param data login window session
+ */
+static void nsgtk_login_cancel_clicked(GtkButton *w, gpointer data)
+{
+ struct session_401 *session = (struct session_401 *) data;
+
+ session->cb(false, session->cbpw);
+
+ /* close and destroy the window */
+ destroy_login_window(session);
+}
+
+
+/**
+ * create a new instance of the login window
+ *
+ * creates login window and handles to all the widgets we're
+ * interested in.
+ *
+ * \param url The url causing the login.
+ * \param host the host being logged into
+ * \param realm realmm the login relates to
+ * \param cb callback when complete
+ * \param cbpw data to pass to callback
+ * \return NSERROR_OK on sucessful window creation or error code on faliure.
+ */
+static nserror
+create_login_window(nsurl *url,
+ lwc_string *host,
+ const char *realm,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
+{
+ struct session_401 *session;
+ GtkWindow *wnd;
+ GtkLabel *lhost, *lrealm;
+ GtkEntry *euser, *epass;
+ GtkButton *bok, *bcan;
+ GtkBuilder* builder;
+ nserror res;
+
+ session = calloc(1, sizeof(struct session_401));
+ if (session == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ res = nsgtk_builder_new_from_resname("login", &builder);
+ if (res != NSERROR_OK) {
+ free(session);
+ return res;
+ }
+
+ gtk_builder_connect_signals(builder, NULL);
+
+ wnd = GTK_WINDOW(gtk_builder_get_object(builder, "wndLogin"));
+ lhost = GTK_LABEL(gtk_builder_get_object(builder, "labelLoginHost"));
+ lrealm = GTK_LABEL(gtk_builder_get_object(builder, "labelLoginRealm"));
+ euser = GTK_ENTRY(gtk_builder_get_object(builder, "entryLoginUser"));
+ epass = GTK_ENTRY(gtk_builder_get_object(builder, "entryLoginPass"));
+ bok = GTK_BUTTON(gtk_builder_get_object(builder, "buttonLoginOK"));
+ bcan = GTK_BUTTON(gtk_builder_get_object(builder, "buttonLoginCan"));
+
+ /* create and fill in our session structure */
+ session->url = nsurl_ref(url);
+ session->host = lwc_string_ref(host);
+ session->realm = strdup(realm ? realm : "Secure Area");
+ session->cb = cb;
+ session->cbpw = cbpw;
+ session->x = builder;
+ session->wnd = wnd;
+ session->user = euser;
+ session->pass = epass;
+
+ /* fill in our new login window */
+
+ gtk_label_set_text(GTK_LABEL(lhost), lwc_string_data(host));
+ gtk_label_set_text(lrealm, realm);
+ gtk_entry_set_text(euser, "");
+ gtk_entry_set_text(epass, "");
+
+ /* attach signal handlers to the Login and Cancel buttons in our new
+ * window to call functions in this file to process the login
+ */
+ g_signal_connect(G_OBJECT(bok), "clicked",
+ G_CALLBACK(nsgtk_login_ok_clicked), (gpointer)session);
+ g_signal_connect(G_OBJECT(bcan), "clicked",
+ G_CALLBACK(nsgtk_login_cancel_clicked),
+ (gpointer)session);
+
+ /* attach signal handlers to the entry boxes such that pressing
+ * enter in one progresses the focus onto the next widget.
+ */
+
+ g_signal_connect(G_OBJECT(euser), "activate",
+ G_CALLBACK(nsgtk_login_next), (gpointer)epass);
+ g_signal_connect(G_OBJECT(epass), "activate",
+ G_CALLBACK(nsgtk_login_next), (gpointer)bok);
+
+ /* make sure the username entry box currently has the focus */
+ gtk_widget_grab_focus(GTK_WIDGET(euser));
+
+ /* finally, show the window */
+ gtk_widget_show(GTK_WIDGET(wnd));
+
+ return NSERROR_OK;
+}
+
+
+/* exported function documented in gtk/login.h */
+void gui_401login_open(nsurl *url,
+ const char *realm,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
+{
+ lwc_string *host;
+ nserror res;
+
+ host = nsurl_get_component(url, NSURL_HOST);
+ assert(host != NULL);
+
+ res = create_login_window(url, host, realm, cb, cbpw);
+ if (res != NSERROR_OK) {
+ LOG("Login init failed");
+
+ /* creating login failed so cancel navigation */
+ cb(false, cbpw);
+ }
+
+ lwc_string_unref(host);
+}
diff --git a/frontends/gtk/login.h b/frontends/gtk/login.h
new file mode 100644
index 000000000..00c29000c
--- /dev/null
+++ b/frontends/gtk/login.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Login interfaces.
+ */
+
+#ifndef __NSGTK_LOGIN_H__
+#define __NSGTK_LOGIN_H__
+
+/**
+ * login window request.
+ */
+extern void gui_401login_open(struct nsurl *url, const char *realm, nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+#endif
diff --git a/frontends/gtk/menu.c b/frontends/gtk/menu.c
new file mode 100644
index 000000000..a93ef9385
--- /dev/null
+++ b/frontends/gtk/menu.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "utils/messages.h"
+
+#include "gtk/compat.h"
+#include "gtk/menu.h"
+#include "gtk/warn.h"
+
+/**
+ * Adds image menu item to a menu.
+ *
+ * \param menu the menu to add the item to
+ * \param item_out a pointer to the item's location in the menu struct
+ * \param message the menu item I18n lookup value
+ * \param messageAccel the menu item accelerator I18n lookup value
+ * \param group the 'global' in a gtk sense accelerator group
+ * \return true if sucessful and \a item_out updated else false.
+ */
+
+static bool nsgtk_menu_add_image_item(GtkMenu *menu,
+ GtkWidget **item_out, const char *message,
+ const char *messageAccel, GtkAccelGroup *group)
+{
+ unsigned int key;
+ GdkModifierType mod;
+ GtkWidget *item;
+
+ item = nsgtk_image_menu_item_new_with_mnemonic(messages_get(message));
+ if (item == NULL) {
+ return false;
+ }
+
+ gtk_accelerator_parse(messages_get(messageAccel), &key, &mod);
+ if (key > 0) {
+ gtk_widget_add_accelerator(item, "activate", group, key, mod,
+ GTK_ACCEL_VISIBLE);
+ }
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+ gtk_widget_show(item);
+
+ *item_out = item;
+
+ return true;
+}
+
+#define NEW_MENU(n, m) \
+ n = malloc(sizeof(*n)); \
+ if (n == NULL) { \
+ return NULL; \
+ } \
+ n->m##_menu = GTK_MENU(gtk_menu_new())
+
+#define IMAGE_ITEM(p, q, r, s, t)\
+ nsgtk_menu_add_image_item(s->p##_menu, &(s->q##_menuitem), #r,\
+ #r "Accel", t)
+
+#define CHECK_ITEM(p, q, r, s)\
+ s->q##_menuitem = GTK_CHECK_MENU_ITEM(\
+ gtk_check_menu_item_new_with_mnemonic(\
+ messages_get(#r)));\
+ if ((s->q##_menuitem != NULL) && (s->p##_menu != NULL)) {\
+ gtk_menu_shell_append(GTK_MENU_SHELL(s->p##_menu),\
+ GTK_WIDGET(s->q##_menuitem));\
+ gtk_widget_show(GTK_WIDGET(s->q##_menuitem));\
+ }
+
+#define SET_SUBMENU(q, r) \
+ do { \
+ r->q##_submenu = nsgtk_menu_##q##_submenu(group); \
+ if ((r->q##_submenu != NULL) && \
+ (r->q##_submenu->q##_menu != NULL) && \
+ (r->q##_menuitem != NULL)) { \
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(r->q##_menuitem), \
+ GTK_WIDGET(r->q##_submenu->q##_menu)); \
+ } \
+ } while(0)
+
+#define ADD_NAMED_SEP(q, r, s) \
+ do { \
+ s->r##_separator = gtk_separator_menu_item_new(); \
+ if ((s->r##_separator != NULL) && (s->q##_menu != NULL)) { \
+ gtk_menu_shell_append(GTK_MENU_SHELL(s->q##_menu), s->r##_separator); \
+ gtk_widget_show(s->r##_separator); \
+ } \
+ } while(0)
+
+#define ADD_SEP(q, r) \
+ do { \
+ GtkWidget *w = gtk_separator_menu_item_new(); \
+ if ((w != NULL) && (r->q##_menu != NULL)) { \
+ gtk_menu_shell_append(GTK_MENU_SHELL(r->q##_menu), w); \
+ gtk_widget_show(w); \
+ } \
+ } while(0)
+
+#define ATTACH_PARENT(parent, msgname, menuv, group) \
+ do { \
+ /* create top level menu entry and attach to parent */ \
+ menuv = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(messages_get(#msgname))); \
+ gtk_menu_shell_append(parent, GTK_WIDGET(menuv)); \
+ gtk_widget_show(GTK_WIDGET(menuv)); \
+ /* attach submenu to parent */ \
+ gtk_menu_item_set_submenu(menuv, GTK_WIDGET(menuv##_menu)); \
+ gtk_menu_set_accel_group(menuv##_menu, group); \
+ } while(0)
+
+/**
+* creates an export submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_export_submenu *nsgtk_menu_export_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_export_submenu *ret = malloc(sizeof(struct
+ nsgtk_export_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->export_menu = GTK_MENU(gtk_menu_new());
+ if (ret->export_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(export, plaintext, gtkPlainText, ret, group);
+ IMAGE_ITEM(export, drawfile, gtkDrawFile, ret, group);
+ IMAGE_ITEM(export, postscript, gtkPostScript, ret, group);
+ IMAGE_ITEM(export, pdf, gtkPDF, ret, group);
+ return ret;
+}
+
+/**
+* creates a scaleview submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_scaleview_submenu *nsgtk_menu_scaleview_submenu(
+ GtkAccelGroup *group)
+{
+ struct nsgtk_scaleview_submenu *ret =
+ malloc(sizeof(struct nsgtk_scaleview_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->scaleview_menu = GTK_MENU(gtk_menu_new());
+ if (ret->scaleview_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(scaleview, zoomplus, gtkZoomPlus, ret, group);
+ IMAGE_ITEM(scaleview, zoomnormal, gtkZoomNormal, ret, group);
+ IMAGE_ITEM(scaleview, zoomminus, gtkZoomMinus, ret, group);
+ return ret;
+}
+
+/**
+* creates a tab navigation submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_tabs_submenu *nsgtk_menu_tabs_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_tabs_submenu *ret = malloc(sizeof(struct nsgtk_tabs_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->tabs_menu = GTK_MENU(gtk_menu_new());
+ if (ret->tabs_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(tabs, nexttab, gtkNextTab, ret, group);
+ IMAGE_ITEM(tabs, prevtab, gtkPrevTab, ret, group);
+ IMAGE_ITEM(tabs, closetab, gtkCloseTab, ret, group);
+
+ return ret;
+}
+
+/**
+* creates an images submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_images_submenu *nsgtk_menu_images_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_images_submenu *ret =
+ malloc(sizeof(struct nsgtk_images_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->images_menu = GTK_MENU(gtk_menu_new());
+ if (ret->images_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ CHECK_ITEM(images, foregroundimages, gtkForegroundImages, ret)
+ CHECK_ITEM(images, backgroundimages, gtkBackgroundImages, ret)
+ return ret;
+}
+
+/**
+* creates a toolbars submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_toolbars_submenu *nsgtk_menu_toolbars_submenu(
+ GtkAccelGroup *group)
+{
+ struct nsgtk_toolbars_submenu *ret =
+ malloc(sizeof(struct nsgtk_toolbars_submenu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->toolbars_menu = GTK_MENU(gtk_menu_new());
+ if (ret->toolbars_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ CHECK_ITEM(toolbars, menubar, gtkMenuBar, ret)
+ if (ret->menubar_menuitem != NULL)
+ gtk_check_menu_item_set_active(ret->menubar_menuitem, TRUE);
+ CHECK_ITEM(toolbars, toolbar, gtkToolBar, ret)
+ if (ret->toolbar_menuitem != NULL)
+ gtk_check_menu_item_set_active(ret->toolbar_menuitem, TRUE);
+ return ret;
+}
+
+/**
+* creates a debugging submenu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_developer_submenu *nsgtk_menu_developer_submenu(
+ GtkAccelGroup *group)
+{
+ struct nsgtk_developer_submenu *dmenu =
+ malloc(sizeof(struct nsgtk_developer_submenu));
+ if (dmenu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ dmenu->developer_menu = GTK_MENU(gtk_menu_new());
+ if (dmenu->developer_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(dmenu);
+ return NULL;
+ }
+
+ IMAGE_ITEM(developer, viewsource, gtkPageSource, dmenu, group);
+ IMAGE_ITEM(developer, toggledebugging, gtkToggleDebugging, dmenu, group);
+ IMAGE_ITEM(developer, debugboxtree, gtkDebugBoxTree, dmenu, group);
+ IMAGE_ITEM(developer, debugdomtree, gtkDebugDomTree, dmenu, group);
+
+ return dmenu;
+}
+
+/**
+ * creates the file menu
+ *
+ * \param group The gtk 'global' accelerator reference
+ * \return The new file menu or NULL on error
+ */
+static struct nsgtk_file_menu *nsgtk_menu_file_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_file_menu *fmenu;
+
+ fmenu = malloc(sizeof(struct nsgtk_file_menu));
+ if (fmenu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+
+ fmenu->file_menu = GTK_MENU(gtk_menu_new());
+ if (fmenu->file_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(fmenu);
+ return NULL;
+ }
+
+ IMAGE_ITEM(file, newwindow, gtkNewWindow, fmenu, group);
+ IMAGE_ITEM(file, newtab, gtkNewTab, fmenu, group);
+ IMAGE_ITEM(file, openfile, gtkOpenFile, fmenu, group);
+ IMAGE_ITEM(file, closewindow, gtkCloseWindow, fmenu, group);
+ ADD_SEP(file, fmenu);
+ IMAGE_ITEM(file, savepage, gtkSavePage, fmenu, group);
+ IMAGE_ITEM(file, export, gtkExport, fmenu, group);
+ ADD_SEP(file, fmenu);
+ IMAGE_ITEM(file, printpreview, gtkPrintPreview, fmenu, group);
+ IMAGE_ITEM(file, print, gtkPrint, fmenu, group);
+ ADD_SEP(file, fmenu);
+ IMAGE_ITEM(file, quit, gtkQuitMenu, fmenu, group);
+ SET_SUBMENU(export, fmenu);
+
+ return fmenu;
+}
+
+/**
+* creates an edit menu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_edit_menu *nsgtk_menu_edit_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_edit_menu *ret = malloc(sizeof(struct nsgtk_edit_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->edit_menu = GTK_MENU(gtk_menu_new());
+ if (ret->edit_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(edit, cut, gtkCut, ret, group);
+ IMAGE_ITEM(edit, copy, gtkCopy, ret, group);
+ IMAGE_ITEM(edit, paste, gtkPaste, ret, group);
+ IMAGE_ITEM(edit, delete, gtkDelete, ret, group);
+ ADD_SEP(edit, ret);
+ IMAGE_ITEM(edit, selectall, gtkSelectAll, ret, group);
+ ADD_SEP(edit, ret);
+ IMAGE_ITEM(edit, find, gtkFind, ret, group);
+ ADD_SEP(edit, ret);
+ IMAGE_ITEM(edit, preferences, gtkPreferences, ret, group);
+
+ return ret;
+}
+
+/**
+* creates a view menu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_view_menu *nsgtk_menu_view_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_view_menu *ret = malloc(sizeof(struct nsgtk_view_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->view_menu = GTK_MENU(gtk_menu_new());
+ if (ret->view_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(view, stop, gtkStop, ret, group);
+ IMAGE_ITEM(view, reload, gtkReload, ret, group);
+ ADD_SEP(view, ret);
+ IMAGE_ITEM(view, scaleview, gtkScaleView, ret, group);
+ IMAGE_ITEM(view, fullscreen, gtkFullScreen, ret, group);
+ ADD_SEP(view, ret);
+ IMAGE_ITEM(view, images, gtkImages, ret, group);
+ IMAGE_ITEM(view, toolbars, gtkToolbars, ret, group);
+ IMAGE_ITEM(view, tabs, gtkTabs, ret, group);
+ ADD_SEP(view, ret);
+ IMAGE_ITEM(view, savewindowsize, gtkSaveWindowSize, ret, group);
+ SET_SUBMENU(scaleview, ret);
+ SET_SUBMENU(images, ret);
+ SET_SUBMENU(toolbars, ret);
+ SET_SUBMENU(tabs, ret);
+
+
+ return ret;
+}
+
+/**
+* creates a nav menu
+* \param group the 'global' in a gtk sense accelerator reference
+*/
+
+static struct nsgtk_nav_menu *nsgtk_menu_nav_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_nav_menu *ret = malloc(sizeof(struct nsgtk_nav_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->nav_menu = GTK_MENU(gtk_menu_new());
+ if (ret->nav_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+
+ IMAGE_ITEM(nav, back, gtkBack, ret, group);
+ IMAGE_ITEM(nav, forward, gtkForward, ret, group);
+ IMAGE_ITEM(nav, home, gtkHome, ret, group);
+ ADD_SEP(nav, ret);
+ IMAGE_ITEM(nav, localhistory, gtkLocalHistory, ret, group);
+ IMAGE_ITEM(nav, globalhistory, gtkGlobalHistory, ret, group);
+ ADD_SEP(nav, ret);
+ IMAGE_ITEM(nav, addbookmarks, gtkAddBookMarks, ret, group);
+ IMAGE_ITEM(nav, showbookmarks, gtkShowBookMarks, ret, group);
+ ADD_SEP(nav, ret);
+ IMAGE_ITEM(nav, openlocation, gtkOpenLocation, ret, group);
+
+
+ return ret;
+}
+
+/**
+ * creates the tools menu
+ * \param group the 'global' in a gtk sense accelerator reference
+ */
+static struct nsgtk_tools_menu *nsgtk_menu_tools_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_tools_menu *ret = malloc(sizeof(struct nsgtk_tools_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->tools_menu = GTK_MENU(gtk_menu_new());
+ if (ret->tools_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+
+ IMAGE_ITEM(tools, downloads, gtkDownloads, ret, group);
+ IMAGE_ITEM(tools, showcookies, gtkShowCookies, ret, group);
+ IMAGE_ITEM(tools, developer, gtkDeveloper, ret, group);
+ SET_SUBMENU(developer, ret);
+
+ return ret;
+}
+
+/**
+ * creates a help menu
+ * \param group the 'global' in a gtk sense accelerator reference
+ */
+static struct nsgtk_help_menu *nsgtk_menu_help_submenu(GtkAccelGroup *group)
+{
+ struct nsgtk_help_menu *ret = malloc(sizeof(struct nsgtk_help_menu));
+ if (ret == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+ ret->help_menu = GTK_MENU(gtk_menu_new());
+ if (ret->help_menu == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(ret);
+ return NULL;
+ }
+ IMAGE_ITEM(help, contents, gtkContents, ret, group);
+ IMAGE_ITEM(help, guide, gtkGuide, ret, group);
+ IMAGE_ITEM(help, info, gtkUserInformation, ret, group);
+ ADD_SEP(help, ret);
+ IMAGE_ITEM(help, about, gtkAbout, ret, group);
+
+ return ret;
+}
+
+
+/**
+ * Generate menubar menus.
+ *
+ * Generate the main menu structure and attach it to a menubar widget.
+ */
+struct nsgtk_bar_submenu *
+nsgtk_menu_bar_create(GtkMenuShell *menubar, GtkAccelGroup *group)
+{
+ struct nsgtk_bar_submenu *nmenu;
+
+ nmenu = calloc(1, sizeof(struct nsgtk_bar_submenu));
+ if (nmenu == NULL) {
+ return NULL;
+ }
+
+ /* create sub menus */
+ nmenu->file_submenu = nsgtk_menu_file_submenu(group);
+ nmenu->edit_submenu = nsgtk_menu_edit_submenu(group);
+ nmenu->view_submenu = nsgtk_menu_view_submenu(group);
+ nmenu->nav_submenu = nsgtk_menu_nav_submenu(group);
+ nmenu->tools_submenu = nsgtk_menu_tools_submenu(group);
+ nmenu->help_submenu = nsgtk_menu_help_submenu(group);
+
+ if (menubar != NULL) {
+ nmenu->bar_menu = GTK_MENU_BAR(menubar);
+
+ /* attach menus to menubar */
+ ATTACH_PARENT(menubar, gtkFile, nmenu->file_submenu->file, group);
+ ATTACH_PARENT(menubar, gtkEdit, nmenu->edit_submenu->edit, group);
+ ATTACH_PARENT(menubar, gtkView, nmenu->view_submenu->view, group);
+ ATTACH_PARENT(menubar, gtkNavigate, nmenu->nav_submenu->nav, group);
+ ATTACH_PARENT(menubar, gtkTools, nmenu->tools_submenu->tools, group);
+ ATTACH_PARENT(menubar, gtkHelp, nmenu->help_submenu->help, group);
+ }
+
+ return nmenu;
+}
+
+/* exported function documented in gtk/menu.h */
+struct nsgtk_popup_menu *nsgtk_popup_menu_create(GtkAccelGroup *group)
+{
+ struct nsgtk_popup_menu *nmenu;
+
+ NEW_MENU(nmenu, popup);
+
+ IMAGE_ITEM(popup, file, gtkFile, nmenu, group);
+ SET_SUBMENU(file, nmenu);
+
+ IMAGE_ITEM(popup, edit, gtkEdit, nmenu, group);
+ SET_SUBMENU(edit, nmenu);
+
+ IMAGE_ITEM(popup, view, gtkView, nmenu, group);
+ SET_SUBMENU(view, nmenu);
+
+ IMAGE_ITEM(popup, nav, gtkNavigate, nmenu, group);
+ SET_SUBMENU(nav, nmenu);
+
+ IMAGE_ITEM(popup, tools, gtkTools, nmenu, group);
+ SET_SUBMENU(tools, nmenu);
+
+ IMAGE_ITEM(popup, help, gtkHelp, nmenu, group);
+ SET_SUBMENU(help, nmenu);
+
+ ADD_NAMED_SEP(popup, first, nmenu);
+
+ IMAGE_ITEM(popup, back, gtkBack, nmenu, group);
+ IMAGE_ITEM(popup, forward, gtkForward, nmenu, group);
+
+ ADD_NAMED_SEP(popup, third, nmenu);
+
+ IMAGE_ITEM(popup, stop, gtkStop, nmenu, group);
+ IMAGE_ITEM(popup, reload, gtkReload, nmenu, group);
+ IMAGE_ITEM(popup, cut, gtkCut, nmenu, group);
+ IMAGE_ITEM(popup, copy, gtkCopy, nmenu, group);
+ IMAGE_ITEM(popup, paste, gtkPaste, nmenu, group);
+ IMAGE_ITEM(popup, customize, gtkCustomize, nmenu, group);
+
+ return nmenu;
+}
+
+
+/* exported function documented in gtk/menu.h */
+struct nsgtk_link_menu *
+nsgtk_link_menu_create(GtkAccelGroup *group)
+{
+ struct nsgtk_link_menu *nmenu;
+
+ NEW_MENU(nmenu, link);
+
+ IMAGE_ITEM(link, opentab, gtkOpentab, nmenu, group);
+ IMAGE_ITEM(link, openwin, gtkOpenwin, nmenu, group);
+
+ ADD_SEP(link, nmenu);
+
+ IMAGE_ITEM(link, save, gtkSavelink, nmenu, group);
+ IMAGE_ITEM(link, bookmark, gtkBookmarklink, nmenu, group);
+ IMAGE_ITEM(link, copy, gtkCopylink, nmenu, group);
+
+ return nmenu;
+}
diff --git a/frontends/gtk/menu.h b/frontends/gtk/menu.h
new file mode 100644
index 000000000..5da5cb1b2
--- /dev/null
+++ b/frontends/gtk/menu.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _NETSURF_GTK_MENU_H_
+#define _NETSURF_GTK_MENU_H_
+
+#include <gtk/gtk.h>
+
+struct nsgtk_file_menu {
+ GtkMenuItem *file; /* File menu item on menubar */
+ GtkMenu *file_menu;
+ GtkWidget *newwindow_menuitem;
+ GtkWidget *newtab_menuitem;
+ GtkWidget *openfile_menuitem;
+ GtkWidget *closewindow_menuitem;
+ GtkWidget *savepage_menuitem;
+ GtkWidget *export_menuitem;
+ struct nsgtk_export_submenu *export_submenu;
+ GtkWidget *printpreview_menuitem;
+ GtkWidget *print_menuitem;
+ GtkWidget *quit_menuitem;
+};
+
+struct nsgtk_edit_menu {
+ GtkMenuItem *edit; /* Edit menu item on menubar */
+ GtkMenu *edit_menu;
+ GtkWidget *cut_menuitem;
+ GtkWidget *copy_menuitem;
+ GtkWidget *paste_menuitem;
+ GtkWidget *delete_menuitem;
+ GtkWidget *selectall_menuitem;
+ GtkWidget *find_menuitem;
+ GtkWidget *preferences_menuitem;
+};
+
+struct nsgtk_view_menu {
+ GtkMenuItem *view; /* View menu item on menubar */
+ GtkMenu *view_menu; /* gtk menu attached to menu item */
+ GtkWidget *stop_menuitem;
+ GtkWidget *reload_menuitem;
+ GtkWidget *scaleview_menuitem;
+ struct nsgtk_scaleview_submenu *scaleview_submenu;
+ GtkWidget *fullscreen_menuitem;
+ GtkWidget *images_menuitem;
+ struct nsgtk_images_submenu *images_submenu;
+ GtkWidget *toolbars_menuitem;
+ struct nsgtk_toolbars_submenu *toolbars_submenu;
+ GtkWidget *tabs_menuitem;
+ struct nsgtk_tabs_submenu *tabs_submenu;
+ GtkWidget *savewindowsize_menuitem;
+};
+
+struct nsgtk_nav_menu {
+ GtkMenuItem *nav; /* Nav menu item on menubar */
+ GtkMenu *nav_menu;
+ GtkWidget *back_menuitem;
+ GtkWidget *forward_menuitem;
+ GtkWidget *home_menuitem;
+ GtkWidget *localhistory_menuitem;
+ GtkWidget *globalhistory_menuitem;
+ GtkWidget *addbookmarks_menuitem;
+ GtkWidget *showbookmarks_menuitem;
+ GtkWidget *openlocation_menuitem;
+};
+
+struct nsgtk_tools_menu {
+ GtkMenuItem *tools; /* Tools menu item on menubar */
+ GtkMenu *tools_menu;
+
+ GtkWidget *showcookies_menuitem;
+ GtkWidget *downloads_menuitem;
+ GtkWidget *developer_menuitem;
+ struct nsgtk_developer_submenu *developer_submenu;
+};
+
+struct nsgtk_help_menu {
+ GtkMenuItem *help; /* Help menu item on menubar */
+ GtkMenu *help_menu;
+ GtkWidget *contents_menuitem;
+ GtkWidget *guide_menuitem;
+ GtkWidget *info_menuitem;
+ GtkWidget *about_menuitem;
+};
+
+
+struct nsgtk_export_submenu {
+ GtkMenu *export_menu;
+ GtkWidget *plaintext_menuitem;
+ GtkWidget *drawfile_menuitem;
+ GtkWidget *postscript_menuitem;
+ GtkWidget *pdf_menuitem;
+};
+
+struct nsgtk_scaleview_submenu {
+ GtkMenu *scaleview_menu;
+ GtkWidget *zoomplus_menuitem;
+ GtkWidget *zoomminus_menuitem;
+ GtkWidget *zoomnormal_menuitem;
+};
+
+struct nsgtk_tabs_submenu {
+ GtkMenu *tabs_menu;
+ GtkWidget *nexttab_menuitem;
+ GtkWidget *prevtab_menuitem;
+ GtkWidget *closetab_menuitem;
+};
+
+struct nsgtk_images_submenu {
+ GtkMenu *images_menu;
+ GtkCheckMenuItem *foregroundimages_menuitem;
+ GtkCheckMenuItem *backgroundimages_menuitem;
+};
+
+struct nsgtk_toolbars_submenu {
+ GtkMenu *toolbars_menu;
+ GtkCheckMenuItem *menubar_menuitem;
+ GtkCheckMenuItem *toolbar_menuitem;
+};
+
+struct nsgtk_developer_submenu {
+ GtkMenu *developer_menu;
+
+ GtkWidget *viewsource_menuitem;
+ GtkWidget *toggledebugging_menuitem;
+ GtkWidget *debugboxtree_menuitem;
+ GtkWidget *debugdomtree_menuitem;
+};
+
+
+struct nsgtk_bar_submenu {
+ GtkMenuBar *bar_menu;
+ struct nsgtk_file_menu *file_submenu;
+ struct nsgtk_edit_menu *edit_submenu;
+ struct nsgtk_view_menu *view_submenu;
+ struct nsgtk_nav_menu *nav_submenu;
+ struct nsgtk_tabs_submenu *tabs_submenu;
+ struct nsgtk_tools_menu *tools_submenu;
+ struct nsgtk_help_menu *help_submenu;
+};
+
+struct nsgtk_popup_menu {
+ GtkMenu *popup_menu;
+
+ GtkWidget *file_menuitem;
+ struct nsgtk_file_menu *file_submenu;
+
+ GtkWidget *edit_menuitem;
+ struct nsgtk_edit_menu *edit_submenu;
+
+ GtkWidget *view_menuitem;
+ struct nsgtk_view_menu *view_submenu;
+
+ GtkWidget *nav_menuitem;
+ struct nsgtk_nav_menu *nav_submenu;
+
+ GtkWidget *tabs_menuitem;
+ struct nsgtk_tabs_submenu *tabs_submenu;
+
+ GtkWidget *tools_menuitem;
+ struct nsgtk_tools_menu *tools_submenu;
+
+ GtkWidget *help_menuitem;
+ struct nsgtk_help_menu *help_submenu;
+
+ GtkWidget *first_separator;
+
+ /* navigation entries */
+ GtkWidget *back_menuitem;
+ GtkWidget *forward_menuitem;
+
+ GtkWidget *third_separator;
+
+ /* view entries */
+ GtkWidget *stop_menuitem;
+ GtkWidget *reload_menuitem;
+
+ GtkWidget *cut_menuitem;
+ GtkWidget *copy_menuitem;
+ GtkWidget *paste_menuitem;
+ GtkWidget *customize_menuitem;
+
+};
+
+struct nsgtk_link_menu {
+ GtkMenu *link_menu;
+
+ GtkWidget *opentab_menuitem;
+ GtkWidget *openwin_menuitem;
+
+ GtkWidget *save_menuitem;
+ GtkWidget *bookmark_menuitem;
+ GtkWidget *copy_menuitem;
+};
+
+/**
+ * Create main menu bar.
+ */
+struct nsgtk_bar_submenu *nsgtk_menu_bar_create(GtkMenuShell *menubar, GtkAccelGroup *group);
+
+/**
+ * Generate right click menu menu.
+ *
+ */
+struct nsgtk_popup_menu *nsgtk_popup_menu_create(GtkAccelGroup *group);
+
+/**
+ * Generate context sensitive popup menu for link.
+ *
+ */
+struct nsgtk_link_menu *nsgtk_link_menu_create(GtkAccelGroup *group);
+
+
+#endif
diff --git a/frontends/gtk/options.h b/frontends/gtk/options.h
new file mode 100644
index 000000000..ac642c153
--- /dev/null
+++ b/frontends/gtk/options.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NETSURF_GTK_OPTIONS_H_
+#define _NETSURF_GTK_OPTIONS_H_
+
+/* currently nothing here */
+
+#endif
+
+/* High quality image scaling */
+NSOPTION_BOOL(render_resample, true)
+
+/* clear downloads */
+NSOPTION_BOOL(downloads_clear, false)
+
+/* prompt before overwriting downloads */
+NSOPTION_BOOL(request_overwrite, true)
+
+/* location to download files to */
+NSOPTION_STRING(downloads_directory, NULL)
+
+/* where to store URL database */
+NSOPTION_STRING(url_file, NULL)
+
+/* Always show tabs even if there is only one */
+NSOPTION_BOOL(show_single_tab, false)
+
+/* size of buttons */
+NSOPTION_INTEGER(button_type, 0)
+
+/* disallow popup windows */
+NSOPTION_BOOL(disable_popups, false)
+
+/* disable content plugins */
+NSOPTION_BOOL(disable_plugins, false)
+
+/* number of days to keep history data */
+NSOPTION_INTEGER(history_age, 0)
+
+/* show urls in local history browser */
+NSOPTION_BOOL(hover_urls, false)
+
+/* bring new tabs to front */
+NSOPTION_BOOL(focus_new, false)
+
+/* new tabs are blank instead of homepage */
+NSOPTION_BOOL(new_blank, false)
+
+/* path to save hotlist file */
+NSOPTION_STRING(hotlist_path, NULL)
+
+/* Developer information viewer display method */
+NSOPTION_INTEGER(developer_view, 0)
+
+/* where tabs are positioned */
+NSOPTION_INTEGER(position_tab, 0)
diff --git a/frontends/gtk/plotters.c b/frontends/gtk/plotters.c
new file mode 100644
index 000000000..1d8c19827
--- /dev/null
+++ b/frontends/gtk/plotters.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * GTK and Cairo plotter implementations.
+ *
+ * Uses Cairo drawing primitives to render browser output.
+ * \todo remove the use of the gdk structure for clipping
+ */
+
+#include <math.h>
+#include <assert.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "desktop/plotters.h"
+#include "utils/nsoption.h"
+
+#include "gtk/layout_pango.h"
+#include "gtk/plotters.h"
+#include "gtk/scaffolding.h"
+#include "gtk/bitmap.h"
+
+GtkWidget *current_widget;
+cairo_t *current_cr;
+
+static GdkRectangle cliprect;
+
+struct plotter_table plot;
+
+/** Set cairo context colour to nsgtk colour. */
+void nsgtk_set_colour(colour c)
+{
+ cairo_set_source_rgba(current_cr,
+ (c & 0xff) / 255.0,
+ ((c & 0xff00) >> 8) / 255.0,
+ ((c & 0xff0000) >> 16) / 255.0,
+ 1.0);
+}
+
+/** Set cairo context to solid plot operation. */
+static inline void nsgtk_set_solid(void)
+{
+ double dashes = 0;
+ cairo_set_dash(current_cr, &dashes, 0, 0);
+}
+
+/** Set cairo context to dotted plot operation. */
+static inline void nsgtk_set_dotted(void)
+{
+ double cdashes[] = { 1.0, 2.0 };
+ cairo_set_dash(current_cr, cdashes, 2, 0);
+}
+
+/** Set cairo context to dashed plot operation. */
+static inline void nsgtk_set_dashed(void)
+{
+ double cdashes[] = { 8.0, 2.0 };
+ cairo_set_dash(current_cr, cdashes, 2, 0);
+}
+
+/** Set clipping area for subsequent plot operations. */
+static bool nsgtk_plot_clip(const struct rect *clip)
+{
+ cairo_reset_clip(current_cr);
+ cairo_rectangle(current_cr, clip->x0, clip->y0,
+ clip->x1 - clip->x0, clip->y1 - clip->y0);
+ cairo_clip(current_cr);
+
+ cliprect.x = clip->x0;
+ cliprect.y = clip->y0;
+ cliprect.width = clip->x1 - clip->x0;
+ cliprect.height = clip->y1 - clip->y0;
+
+ return true;
+}
+
+
+static bool nsgtk_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
+{
+ nsgtk_set_colour(style->fill_colour);
+ nsgtk_set_solid();
+
+ cairo_set_line_width(current_cr, 1);
+ cairo_arc(current_cr, x, y, radius,
+ (angle1 + 90) * (M_PI / 180),
+ (angle2 + 90) * (M_PI / 180));
+ cairo_stroke(current_cr);
+
+ return true;
+}
+
+static bool nsgtk_plot_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->fill_colour);
+ nsgtk_set_solid();
+ cairo_set_line_width(current_cr, 0);
+ cairo_arc(current_cr, x, y, radius, 0, M_PI * 2);
+ cairo_fill(current_cr);
+ cairo_stroke(current_cr);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(current_cr, 1);
+ else
+ cairo_set_line_width(current_cr, style->stroke_width);
+
+ cairo_arc(current_cr, x, y, radius, 0, M_PI * 2);
+
+ cairo_stroke(current_cr);
+ }
+
+ return true;
+}
+
+static bool
+nsgtk_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ nsgtk_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_set_dashed();
+ break;
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->stroke_colour);
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(current_cr, 1);
+ else
+ cairo_set_line_width(current_cr, style->stroke_width);
+
+ /* core expects horizontal and vertical lines to be on pixels, not
+ * between pixels */
+ cairo_move_to(current_cr, (x0 == x1) ? x0 + 0.5 : x0,
+ (y0 == y1) ? y0 + 0.5 : y0);
+ cairo_line_to(current_cr, (x0 == x1) ? x1 + 0.5 : x1,
+ (y0 == y1) ? y1 + 0.5 : y1);
+ cairo_stroke(current_cr);
+
+ return true;
+}
+
+/** Plot a caret.
+ *
+ * @note It is assumed that the plotters have been set up.
+ */
+void nsgtk_plot_caret(int x, int y, int h)
+{
+ nsgtk_set_solid(); /* solid line */
+ nsgtk_set_colour(0); /* black */
+ cairo_set_line_width(current_cr, 1); /* thin line */
+
+ /* core expects horizontal and vertical lines to be on pixels, not
+ * between pixels */
+ cairo_move_to(current_cr, x + 0.5, y);
+ cairo_line_to(current_cr, x + 0.5, y + h - 1);
+ cairo_stroke(current_cr);
+}
+
+static bool nsgtk_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->fill_colour);
+ nsgtk_set_solid();
+
+ cairo_set_line_width(current_cr, 0);
+ cairo_rectangle(current_cr, x0, y0, x1 - x0, y1 - y0);
+ cairo_fill(current_cr);
+ cairo_stroke(current_cr);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(current_cr, 1);
+ else
+ cairo_set_line_width(current_cr, style->stroke_width);
+
+ cairo_rectangle(current_cr, x0 + 0.5, y0 + 0.5, x1 - x0, y1 - y0);
+ cairo_stroke(current_cr);
+ }
+ return true;
+}
+
+static bool nsgtk_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ unsigned int i;
+
+ nsgtk_set_colour(style->fill_colour);
+ nsgtk_set_solid();
+
+ cairo_set_line_width(current_cr, 0);
+ cairo_move_to(current_cr, p[0], p[1]);
+ for (i = 1; i != n; i++) {
+ cairo_line_to(current_cr, p[i * 2], p[i * 2 + 1]);
+ }
+ cairo_fill(current_cr);
+ cairo_stroke(current_cr);
+
+ return true;
+}
+
+
+
+
+static bool nsgtk_plot_text(int x, int y, const char *text, size_t length,
+ const struct plot_font_style *fstyle)
+{
+ return nsfont_paint(x, y, text, length, fstyle);
+}
+
+
+
+static bool nsgtk_plot_pixbuf(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg)
+{
+ int x0, y0, x1, y1;
+ int dsrcx, dsrcy, dwidth, dheight;
+ int bmwidth, bmheight;
+
+ cairo_surface_t *bmsurface = bitmap->surface;
+
+ /* Bail early if we can */
+ if (width == 0 || height == 0)
+ /* Nothing to plot */
+ return true;
+ if ((x > (cliprect.x + cliprect.width)) ||
+ ((x + width) < cliprect.x) ||
+ (y > (cliprect.y + cliprect.height)) ||
+ ((y + height) < cliprect.y)) {
+ /* Image completely outside clip region */
+ return true;
+ }
+
+ /* Get clip rectangle / image rectangle edge differences */
+ x0 = cliprect.x - x;
+ y0 = cliprect.y - y;
+ x1 = (x + width) - (cliprect.x + cliprect.width);
+ y1 = (y + height) - (cliprect.y + cliprect.height);
+
+ /* Set initial draw geometry */
+ dsrcx = x;
+ dsrcy = y;
+ dwidth = width;
+ dheight = height;
+
+ /* Manually clip draw coordinates to area of image to be rendered */
+ if (x0 > 0) {
+ /* Clip left */
+ dsrcx += x0;
+ dwidth -= x0;
+ }
+ if (y0 > 0) {
+ /* Clip top */
+ dsrcy += y0;
+ dheight -= y0;
+ }
+ if (x1 > 0) {
+ /* Clip right */
+ dwidth -= x1;
+ }
+ if (y1 > 0) {
+ /* Clip bottom */
+ dheight -= y1;
+ }
+
+ if (dwidth == 0 || dheight == 0)
+ /* Nothing to plot */
+ return true;
+
+ bmwidth = cairo_image_surface_get_width(bmsurface);
+ bmheight = cairo_image_surface_get_height(bmsurface);
+
+ /* Render the bitmap */
+ if ((bmwidth == width) && (bmheight == height)) {
+ /* Bitmap is not scaled */
+ /* Plot the bitmap */
+ cairo_set_source_surface(current_cr, bmsurface, x, y);
+ cairo_rectangle(current_cr, dsrcx, dsrcy, dwidth, dheight);
+ cairo_fill(current_cr);
+
+ } else {
+ /* Bitmap is scaled */
+ if ((bitmap->scsurface != NULL) &&
+ ((cairo_image_surface_get_width(bitmap->scsurface) != width) ||
+ (cairo_image_surface_get_height(bitmap->scsurface) != height))){
+ cairo_surface_destroy(bitmap->scsurface);
+ bitmap->scsurface = NULL;
+ }
+
+ if (bitmap->scsurface == NULL) {
+ bitmap->scsurface = cairo_surface_create_similar(bmsurface,CAIRO_CONTENT_COLOR_ALPHA, width, height);
+ cairo_t *cr = cairo_create(bitmap->scsurface);
+
+ /* Scale *before* setting the source surface (1) */
+ cairo_scale(cr, (double)width / bmwidth, (double)height / bmheight);
+ cairo_set_source_surface(cr, bmsurface, 0, 0);
+
+ /* To avoid getting the edge pixels blended with 0
+ * alpha, which would occur with the default
+ * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer (2)
+ */
+ cairo_pattern_set_extend(cairo_get_source(cr),
+ CAIRO_EXTEND_REFLECT);
+
+ /* Replace the destination with the source instead of
+ * overlaying
+ */
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ /* Do the actual drawing */
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+
+ }
+ /* Plot the scaled bitmap */
+ cairo_set_source_surface(current_cr, bitmap->scsurface, x, y);
+ cairo_rectangle(current_cr, dsrcx, dsrcy, dwidth, dheight);
+ cairo_fill(current_cr);
+
+
+ }
+
+ return true;
+}
+
+static bool nsgtk_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ int doneheight = 0, donewidth = 0;
+ bool repeat_x = (flags & BITMAPF_REPEAT_X);
+ bool repeat_y = (flags & BITMAPF_REPEAT_Y);
+
+ /* Bail early if we can */
+ if (width == 0 || height == 0)
+ /* Nothing to plot */
+ return true;
+
+ if (!(repeat_x || repeat_y)) {
+ /* Not repeating at all, so just pass it on */
+ return nsgtk_plot_pixbuf(x, y, width, height, bitmap, bg);
+ }
+
+ if (y > cliprect.y) {
+ doneheight = (cliprect.y - height) + ((y - cliprect.y) % height);
+ } else {
+ doneheight = y;
+ }
+
+ while (doneheight < (cliprect.y + cliprect.height)) {
+ if (x > cliprect.x) {
+ donewidth = (cliprect.x - width) + ((x - cliprect.x) % width);
+ } else {
+ donewidth = x;
+ }
+
+ while (donewidth < (cliprect.x + cliprect.width)) {
+ nsgtk_plot_pixbuf(donewidth, doneheight,
+ width, height, bitmap, bg);
+ donewidth += width;
+ if (!repeat_x)
+ break;
+ }
+ doneheight += height;
+
+ if (!repeat_y)
+ break;
+ }
+
+ return true;
+}
+
+static bool nsgtk_plot_path(const float *p, unsigned int n, colour fill, float width,
+ colour c, const float transform[6])
+{
+ unsigned int i;
+ cairo_matrix_t old_ctm, n_ctm;
+
+ if (n == 0)
+ return true;
+
+ if (p[0] != PLOTTER_PATH_MOVE) {
+ LOG("Path does not start with move");
+ return false;
+ }
+
+
+ /* Save CTM */
+ cairo_get_matrix(current_cr, &old_ctm);
+
+ /* Set up line style and width */
+ cairo_set_line_width(current_cr, 1);
+ nsgtk_set_solid();
+
+ /* Load new CTM */
+ n_ctm.xx = transform[0];
+ n_ctm.yx = transform[1];
+ n_ctm.xy = transform[2];
+ n_ctm.yy = transform[3];
+ n_ctm.x0 = transform[4];
+ n_ctm.y0 = transform[5];
+
+ cairo_set_matrix(current_cr, &n_ctm);
+
+ /* Construct path */
+ for (i = 0; i < n; ) {
+ if (p[i] == PLOTTER_PATH_MOVE) {
+ cairo_move_to(current_cr, p[i+1], p[i+2]);
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_CLOSE) {
+ cairo_close_path(current_cr);
+ i++;
+ } else if (p[i] == PLOTTER_PATH_LINE) {
+ cairo_line_to(current_cr, p[i+1], p[i+2]);
+ i += 3;
+ } else if (p[i] == PLOTTER_PATH_BEZIER) {
+ cairo_curve_to(current_cr, p[i+1], p[i+2],
+ p[i+3], p[i+4],
+ p[i+5], p[i+6]);
+ i += 7;
+ } else {
+ LOG("bad path command %f", p[i]);
+ /* Reset matrix for safety */
+ cairo_set_matrix(current_cr, &old_ctm);
+ return false;
+ }
+ }
+
+ /* Restore original CTM */
+ cairo_set_matrix(current_cr, &old_ctm);
+
+ /* Now draw path */
+ if (fill != NS_TRANSPARENT) {
+ nsgtk_set_colour(fill);
+
+ if (c != NS_TRANSPARENT) {
+ /* Fill & Stroke */
+ cairo_fill_preserve(current_cr);
+ nsgtk_set_colour(c);
+ cairo_stroke(current_cr);
+ } else {
+ /* Fill only */
+ cairo_fill(current_cr);
+ }
+ } else if (c != NS_TRANSPARENT) {
+ /* Stroke only */
+ nsgtk_set_colour(c);
+ cairo_stroke(current_cr);
+ }
+
+ return true;
+}
+
+/** GTK plotter table */
+const struct plotter_table nsgtk_plotters = {
+ .clip = nsgtk_plot_clip,
+ .arc = nsgtk_plot_arc,
+ .disc = nsgtk_plot_disc,
+ .line = nsgtk_plot_line,
+ .rectangle = nsgtk_plot_rectangle,
+ .polygon = nsgtk_plot_polygon,
+ .path = nsgtk_plot_path,
+ .bitmap = nsgtk_plot_bitmap,
+ .text = nsgtk_plot_text,
+ .option_knockout = true
+};
+
+
+
diff --git a/frontends/gtk/plotters.h b/frontends/gtk/plotters.h
new file mode 100644
index 000000000..c88a8da0c
--- /dev/null
+++ b/frontends/gtk/plotters.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Target independent plotting (GDK / GTK+ interface).
+ */
+
+#ifndef NETSURF_GTK_PLOTTERS_H
+#define NETSURF_GTK_PLOTTERS_H 1
+
+#include <gtk/gtk.h>
+
+struct plotter_table;
+
+extern const struct plotter_table nsgtk_plotters;
+
+/* make sure this is NULL if no redraw is in progress */
+extern GtkWidget *current_widget;
+extern cairo_t *current_cr;
+
+void nsgtk_set_colour(colour c);
+void nsgtk_plot_caret(int x, int y, int h);
+
+#endif /* NETSURF_GTK_PLOTTERS_H */
+
diff --git a/frontends/gtk/preferences.c b/frontends/gtk/preferences.c
new file mode 100644
index 000000000..3efb9eddd
--- /dev/null
+++ b/frontends/gtk/preferences.c
@@ -0,0 +1,1030 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+#include <string.h>
+
+#include "utils/messages.h"
+#include "utils/nsoption.h"
+#include "utils/file.h"
+#include "utils/log.h"
+#include "utils/nsurl.h"
+#include "desktop/browser.h"
+#include "desktop/searchweb.h"
+
+#include "gtk/compat.h"
+#include "gtk/window.h"
+#include "gtk/gui.h"
+#include "gtk/scaffolding.h"
+#include "gtk/resources.h"
+#include "gtk/preferences.h"
+
+/* private prefs */
+struct ppref {
+ /** dialog handle created when window first accessed */
+ GObject *dialog;
+
+ struct browser_window *bw;
+
+ /* widgets which are accessed from outside their own signal handlers */
+ GtkEntry *entryHomePageURL;
+ GtkEntry *entryProxyHost;
+ GtkEntry *entryProxyUser;
+ GtkEntry *entryProxyPassword;
+ GtkEntry *entryProxyNoproxy;
+ GtkSpinButton *spinProxyPort;
+
+ /* dynamic list stores */
+ GtkListStore *content_language;
+ GtkListStore *search_providers;
+};
+
+static struct ppref ppref;
+
+
+/* Set netsurf option based on toggle button state
+ *
+ * This works for any widget which subclasses togglebutton (checkbox,
+ * radiobutton etc.)
+ */
+#define TOGGLEBUTTON_SIGNALS(WIDGET, OPTION) \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \
+ struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_toggled(GtkToggleButton *togglebutton, \
+ struct ppref *priv) \
+{ \
+ nsoption_set_bool(OPTION, \
+ gtk_toggle_button_get_active(togglebutton)); \
+} \
+ \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \
+ struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, \
+ struct ppref *priv) \
+{ \
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), \
+ nsoption_bool(OPTION)); \
+}
+
+#define SPINBUTTON_SIGNALS(WIDGET, OPTION, MULTIPLIER) \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
+ struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
+ struct ppref *priv) \
+{ \
+ nsoption_set_int(OPTION, \
+ round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \
+} \
+ \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \
+{ \
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \
+ ((gdouble)nsoption_int(OPTION)) / MULTIPLIER); \
+}
+
+#define SPINBUTTON_UINT_SIGNALS(WIDGET, OPTION, MULTIPLIER) \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
+ struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_valuechanged(GtkSpinButton *spinbutton, \
+ struct ppref *priv) \
+{ \
+ nsoption_set_uint(OPTION, \
+ round(gtk_spin_button_get_value(spinbutton) * MULTIPLIER)); \
+} \
+ \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \
+{ \
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(widget), \
+ ((gdouble)nsoption_uint(OPTION)) / MULTIPLIER); \
+}
+
+#define ENTRY_SIGNALS(WIDGET, OPTION) \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_changed(GtkEditable *editable, struct ppref *priv)\
+{ \
+ nsoption_set_charp(OPTION, \
+ strdup(gtk_entry_get_text(GTK_ENTRY(editable)))); \
+} \
+ \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv); \
+G_MODULE_EXPORT void \
+nsgtk_preferences_##WIDGET##_realize(GtkWidget *widget, struct ppref *priv) \
+{ \
+ const char *OPTION; \
+ OPTION = nsoption_charp(OPTION); \
+ if (OPTION != NULL) { \
+ gtk_entry_set_text(GTK_ENTRY(widget), OPTION); \
+ } \
+}
+
+/* GTK module requires these to be exported symbols so these all need
+ * forward declaring to avoid warnings
+ */
+G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboDeveloperView_changed(GtkComboBox *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboDeveloperView_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboButtonType_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid);
+G_MODULE_EXPORT gboolean nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, struct ppref *priv);
+G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv);
+
+
+/********* PDF **********/
+
+/* Appearance */
+
+/* no images in output */
+TOGGLEBUTTON_SIGNALS(checkSuppressImages, suppress_images)
+
+/* no background images */
+TOGGLEBUTTON_SIGNALS(checkRemoveBackgrounds, remove_backgrounds)
+
+/* scale to fit page */
+TOGGLEBUTTON_SIGNALS(checkFitPage, enable_loosening)
+
+/* port */
+SPINBUTTON_SIGNALS(spinExportScale, export_scale, 1.0)
+
+/* Margins */
+SPINBUTTON_SIGNALS(spinMarginTop, margin_top, 1.0)
+SPINBUTTON_SIGNALS(spinMarginBottom, margin_bottom, 1.0)
+SPINBUTTON_SIGNALS(spinMarginLeft, margin_left, 1.0)
+SPINBUTTON_SIGNALS(spinMarginRight, margin_right, 1.0)
+
+
+/* Generation */
+
+/* output is compressed */
+TOGGLEBUTTON_SIGNALS(checkCompressPDF, enable_PDF_compression)
+
+/* output has a password */
+TOGGLEBUTTON_SIGNALS(checkPasswordPDF, enable_PDF_password)
+
+/********* Network **********/
+
+/* HTTP proxy */
+static void set_proxy_widgets_sensitivity(int proxyval, struct ppref *priv)
+{
+ gboolean host;
+ gboolean port;
+ gboolean user;
+ gboolean pass;
+ gboolean noproxy;
+
+ switch (proxyval) {
+ case 0: /* no proxy */
+ host = FALSE;
+ port = FALSE;
+ user = FALSE;
+ pass = FALSE;
+ noproxy = FALSE;
+ break;
+
+ case 1: /* proxy with no auth */
+ host = TRUE;
+ port = TRUE;
+ user = FALSE;
+ pass = FALSE;
+ noproxy = TRUE;
+ break;
+
+ case 2: /* proxy with basic auth */
+ host = TRUE;
+ port = TRUE;
+ user = TRUE;
+ pass = TRUE;
+ noproxy = TRUE;
+ break;
+
+ case 3: /* proxy with ntlm auth */
+ host = TRUE;
+ port = TRUE;
+ user = TRUE;
+ pass = TRUE;
+ noproxy = TRUE;
+ break;
+
+ case 4: /* system proxy */
+ host = FALSE;
+ port = FALSE;
+ user = FALSE;
+ pass = FALSE;
+ noproxy = FALSE;
+ break;
+
+ default:
+ return;
+ }
+
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyHost), host);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->spinProxyPort), port);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyUser), user);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyPassword), pass);
+ gtk_widget_set_sensitive(GTK_WIDGET(priv->entryProxyNoproxy), noproxy);
+
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboProxyType_changed(GtkComboBox *combo, struct ppref *priv)
+{
+ int proxy_sel;
+ proxy_sel = gtk_combo_box_get_active(combo);
+
+ switch (proxy_sel) {
+ case 0: /* no proxy */
+ nsoption_set_bool(http_proxy, false);
+ break;
+
+ case 1: /* proxy with no auth */
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE);
+ break;
+
+ case 2: /* proxy with basic auth */
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_BASIC);
+ break;
+
+ case 3: /* proxy with ntlm auth */
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NTLM);
+ break;
+
+ case 4: /* system proxy */
+ nsoption_set_bool(http_proxy, true);
+ nsoption_set_int(http_proxy_auth, OPTION_HTTP_PROXY_AUTH_NONE);
+ break;
+ }
+
+ set_proxy_widgets_sensitivity(proxy_sel, priv);
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboProxyType_realize(GtkWidget *widget, struct ppref *priv)
+{
+ int proxytype = 0; /* no proxy by default */
+
+ if (nsoption_bool(http_proxy) == true) {
+ /* proxy type combo box starts with disabled, to allow
+ * for this the http_proxy option needs combining with
+ * the http_proxy_auth option
+ */
+ proxytype = nsoption_int(http_proxy_auth) + 1;
+ if (nsoption_charp(http_proxy_host) == NULL) {
+ /* set to use a proxy without a host, turn proxy off */
+ proxytype = 0;
+ } else if (((proxytype == 2) ||
+ (proxytype == 3)) &&
+ ((nsoption_charp(http_proxy_auth_user) == NULL) ||
+ (nsoption_charp(http_proxy_auth_pass) == NULL))) {
+ /* authentication selected with empty credentials, turn proxy off */
+ proxytype = 0;
+ }
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), proxytype);
+
+ set_proxy_widgets_sensitivity(proxytype, priv);
+}
+
+/* host */
+ENTRY_SIGNALS(entryProxyHost, http_proxy_host)
+
+/* port */
+SPINBUTTON_SIGNALS(spinProxyPort, http_proxy_port, 1.0)
+
+/* user */
+ENTRY_SIGNALS(entryProxyUser, http_proxy_auth_user)
+
+/* password */
+ENTRY_SIGNALS(entryProxyPassword, http_proxy_auth_pass)
+
+/* no proxy */
+ENTRY_SIGNALS(entryProxyNoproxy, http_proxy_noproxy)
+
+/* Fetching */
+
+/* maximum fetchers */
+SPINBUTTON_SIGNALS(spinMaxFetchers, max_fetchers, 1.0)
+
+/* fetches per host */
+SPINBUTTON_SIGNALS(spinFetchesPerHost, max_fetchers_per_host, 1.0)
+
+/* cached connections */
+SPINBUTTON_SIGNALS(spinCachedConnections, max_cached_fetch_handles, 1.0)
+
+
+/********* Privacy **********/
+
+/* General */
+
+/* enable referral submission */
+TOGGLEBUTTON_SIGNALS(checkSendReferer, send_referer)
+
+/* send do not track */
+TOGGLEBUTTON_SIGNALS(checkSendDNT, do_not_track)
+
+/* History */
+
+/* local history shows url tooltips */
+TOGGLEBUTTON_SIGNALS(checkHoverURLs, hover_urls)
+
+/* remember browsing history */
+SPINBUTTON_SIGNALS(spinHistoryAge, history_age, 1.0)
+
+/* Cache */
+
+/* memory cache size */
+SPINBUTTON_SIGNALS(spinMemoryCacheSize, memory_cache_size, (1024*1024))
+
+/* disc cache size */
+SPINBUTTON_UINT_SIGNALS(spinDiscCacheSize, disc_cache_size, (1024*1024))
+
+
+/* disc cache age */
+SPINBUTTON_SIGNALS(spinDiscCacheAge, disc_cache_age, 1.0)
+
+
+/********* Content **********/
+
+/* Control */
+
+
+/* prevent popups */
+TOGGLEBUTTON_SIGNALS(checkDisablePopups, disable_popups)
+
+/* hide adverts */
+TOGGLEBUTTON_SIGNALS(checkHideAdverts, block_advertisements)
+
+/* enable javascript */
+TOGGLEBUTTON_SIGNALS(checkEnableJavascript, enable_javascript)
+
+/* disable plugins */
+TOGGLEBUTTON_SIGNALS(checkDisablePlugins, disable_plugins)
+
+/* high quality image scaling */
+TOGGLEBUTTON_SIGNALS(checkResampleImages, render_resample)
+
+/* load and display of images */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboboxLoadImages_changed(GtkComboBox *combo,
+ struct ppref *priv)
+{
+ int img_sel;
+ /* get the row number for the selection */
+ img_sel = gtk_combo_box_get_active(combo);
+ switch (img_sel) {
+ case 0:
+ /* background and foreground */
+ nsoption_set_bool(foreground_images, true);
+ nsoption_set_bool(background_images, true);
+ break;
+
+ case 1:
+ /* foreground only */
+ nsoption_set_bool(foreground_images, true);
+ nsoption_set_bool(background_images, false);
+ break;
+
+ case 2:
+ /* background only */
+ nsoption_set_bool(foreground_images, false);
+ nsoption_set_bool(background_images, true);
+ break;
+
+ case 3:
+ /* no images */
+ nsoption_set_bool(foreground_images, false);
+ nsoption_set_bool(background_images, false);
+ break;
+ }
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboboxLoadImages_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ if (nsoption_bool(foreground_images)) {
+ if (nsoption_bool(background_images)) {
+ /* background and foreground */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0);
+ } else {
+ /* foreground only */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 1);
+ }
+ } else {
+ if (nsoption_bool(background_images)) {
+ /* background only */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 2);
+ } else {
+ /* no images */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 3);
+ }
+ }
+}
+
+/* Animation */
+
+/* enable animation */
+TOGGLEBUTTON_SIGNALS(checkEnableAnimations, animate_images)
+
+/* frame time */
+SPINBUTTON_SIGNALS(spinAnimationSpeed, minimum_gif_delay, 100.0)
+
+/* Fonts */
+
+/* default font */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboDefault_changed(GtkComboBox *combo, struct ppref *priv)
+{
+ int font_sel;
+ /* get the row number for the selection */
+ font_sel = gtk_combo_box_get_active(combo);
+ if ((font_sel >= 0) && (font_sel <= 4)) {
+ nsoption_set_int(font_default, font_sel);
+ }
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboDefault_realize(GtkWidget *widget, struct ppref *priv)
+{
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
+ nsoption_int(font_default));
+}
+
+/* default font size */
+SPINBUTTON_SIGNALS(spinDefaultSize, font_size, 10.0)
+
+/* preview - actually reflow all views */
+G_MODULE_EXPORT void
+nsgtk_preferences_fontPreview_clicked(GtkButton *button, struct ppref *priv)
+{
+ nsgtk_reflow_all_windows();
+}
+
+
+/* Language */
+
+/* accept language */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboboxLanguage_changed(GtkComboBox *combo,
+ struct ppref *priv)
+{
+ gchar *lang = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ /* Obtain currently selected item from combo box.
+ * If nothing is selected, do nothing.
+ */
+ if (gtk_combo_box_get_active_iter(combo, &iter)) {
+ /* Obtain data model from combo box. */
+ model = gtk_combo_box_get_model(combo);
+
+ /* Obtain string from model. */
+ gtk_tree_model_get(model, &iter, 0, &lang, -1);
+ }
+
+ if (lang != NULL) {
+ nsoption_set_charp(accept_language, strdup(lang));
+ g_free(lang);
+ }
+}
+
+/**
+ * populate language combo from data
+ */
+static nserror
+comboboxLanguage_add_from_data(GtkListStore *liststore,
+ GtkComboBox *combobox,
+ const char *accept_language,
+ const uint8_t *data,
+ size_t data_size)
+{
+ int active_language = -1;
+ GtkTreeIter iter;
+ int combo_row_count = 0;
+ const uint8_t *s;
+ const uint8_t *nl;
+ char buf[50];
+ int bufi = 0;
+
+ gtk_list_store_clear(liststore);
+ s = data;
+
+ while (s < (data + data_size)) {
+ /* find nl and copy buffer */
+ for (nl = s; nl < data + data_size; nl++) {
+ if ((*nl == '\n') || (bufi == (sizeof(buf) - 2))) {
+ buf[bufi] = 0; /* null terminate */
+ break;
+ }
+ buf[bufi++] = *nl;
+ }
+ if (bufi > 0) {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, 0, buf, -1 );
+
+ if (strcmp(buf, accept_language) == 0) {
+ active_language = combo_row_count;
+ }
+
+ combo_row_count++;
+ }
+ bufi = 0;
+ s = nl + 1; /* skip newline */
+ }
+
+ /* if configured language was not in list, add it */
+ if (active_language == -1) {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, 0, accept_language, -1);
+ active_language = combo_row_count;
+ }
+
+ gtk_combo_box_set_active(combobox, active_language);
+
+ return NSERROR_OK;
+}
+
+/**
+ * populate language combo from file
+ */
+static nserror
+comboboxLanguage_add_from_file(GtkListStore *liststore,
+ GtkComboBox *combobox,
+ const char *accept_language,
+ const char *file_location)
+{
+ int active_language = -1;
+ GtkTreeIter iter;
+ int combo_row_count = 0;
+ FILE *fp;
+ char buf[50];
+
+ fp = fopen(file_location, "r");
+ if (fp == NULL) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ gtk_list_store_clear(liststore);
+
+ LOG("Used %s for languages", file_location);
+ while (fgets(buf, sizeof(buf), fp)) {
+ /* Ignore blank lines */
+ if (buf[0] == '\0')
+ continue;
+
+ /* Remove trailing \n */
+ buf[strlen(buf) - 1] = '\0';
+
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, 0, buf, -1 );
+
+ if (strcmp(buf, accept_language) == 0) {
+ active_language = combo_row_count;
+ }
+
+ combo_row_count++;
+ }
+
+ /* if configured language was not in list, add it */
+ if (active_language == -1) {
+ gtk_list_store_append(liststore, &iter);
+ gtk_list_store_set(liststore, &iter, 0, accept_language, -1);
+ active_language = combo_row_count;
+ }
+
+ fclose(fp);
+
+ gtk_combo_box_set_active(combobox, active_language);
+
+ return NSERROR_OK;
+}
+
+/**
+ * Fill content language list store.
+ */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboboxLanguage_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ nserror res;
+ const uint8_t *data;
+ size_t data_size;
+ const char *languages_file;
+ const char *accept_language;
+
+ if (priv->content_language == NULL) {
+ LOG("content language list store unavailable");
+ return;
+ }
+
+ /* get current accept language */
+ accept_language = nsoption_charp(accept_language);
+ if (accept_language == NULL) {
+ accept_language = "en";
+ }
+
+ /* attempt to read languages from inline resource */
+ res = nsgtk_data_from_resname("languages", &data, &data_size);
+ if (res == NSERROR_OK) {
+ res = comboboxLanguage_add_from_data(priv->content_language,
+ GTK_COMBO_BOX(widget),
+ accept_language,
+ data,
+ data_size);
+ } else {
+ /* attempt to read languages from file */
+ res = nsgtk_path_from_resname("languages", &languages_file);
+ if (res == NSERROR_OK) {
+ res = comboboxLanguage_add_from_file(priv->content_language,
+ GTK_COMBO_BOX(widget),
+ accept_language,
+ languages_file);
+ }
+ }
+ if (res != NSERROR_OK) {
+ LOG("error populatiung languages combo");
+ }
+}
+
+
+/********* Apperance **********/
+
+/* Tabs */
+
+/* always show tab bar */
+G_MODULE_EXPORT void
+nsgtk_preferences_checkShowSingleTab_toggled(GtkToggleButton *togglebutton,
+ struct ppref *priv)
+{
+ nsoption_set_bool(show_single_tab,
+ gtk_toggle_button_get_active(togglebutton));
+ nsgtk_reflow_all_windows();
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_checkShowSingleTab_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
+ nsoption_bool(show_single_tab));
+}
+
+/* switch to newly opened tabs immediately */
+TOGGLEBUTTON_SIGNALS(checkFocusNew, focus_new)
+
+/* newly opened tabs are blank */
+TOGGLEBUTTON_SIGNALS(checkNewBlank, new_blank)
+
+/* tab position */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboTabPosition_changed(GtkComboBox *widget,
+ struct ppref *priv)
+{
+ struct nsgtk_scaffolding *current;
+
+ /* set the option */
+ nsoption_set_int(position_tab, gtk_combo_box_get_active(widget));
+
+ /* update all notebooks in all scaffolds */
+ current = nsgtk_scaffolding_iterate(NULL);
+ while (current) {
+ nsgtk_scaffolding_reset_offset(current);
+
+ nsgtk_reflow_all_windows();
+
+ current = nsgtk_scaffolding_iterate(current);
+ }
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboTabPosition_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
+ nsoption_int(position_tab));
+}
+
+/* Tools */
+
+/* developer view opening */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboDeveloperView_changed(GtkComboBox *widget,
+ struct ppref *priv)
+{
+ /* set the option */
+ nsoption_set_int(developer_view, gtk_combo_box_get_active(widget));
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboDeveloperView_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
+ nsoption_int(developer_view));
+}
+
+
+/* URLbar */
+
+/* show recently visited urls as you type */
+TOGGLEBUTTON_SIGNALS(checkDisplayRecentURLs, url_suggestion)
+
+/* Toolbar */
+
+/* button position */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboButtonType_changed(GtkComboBox *widget,
+ struct ppref *priv)
+{
+ struct nsgtk_scaffolding *current;
+
+ nsoption_set_int(button_type, gtk_combo_box_get_active(widget) + 1);
+
+ current = nsgtk_scaffolding_iterate(NULL);
+ while (current != NULL) {
+ nsgtk_scaffolding_reset_offset(current);
+
+ nsgtk_scaffolding_toolbars(current, nsoption_int(button_type));
+
+ current = nsgtk_scaffolding_iterate(current);
+ }
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboButtonType_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget),
+ nsoption_int(button_type) - 1);
+}
+
+
+
+/************ Main ************/
+
+/* Startup */
+
+/* entry HomePageURL widget */
+ENTRY_SIGNALS(entryHomePageURL, homepage_url)
+
+/* put current page into homepage url */
+G_MODULE_EXPORT void
+nsgtk_preferences_setCurrentPage_clicked(GtkButton *button, struct ppref *priv)
+{
+ const gchar *url = nsurl_access(browser_window_get_url(priv->bw));
+
+ if (priv->entryHomePageURL != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url);
+ nsoption_set_charp(homepage_url, strdup(url));
+ }
+}
+
+/* put default page into homepage */
+G_MODULE_EXPORT void
+nsgtk_preferences_setDefaultPage_clicked(GtkButton *button, struct ppref *priv)
+{
+ const gchar *url = NETSURF_HOMEPAGE;
+
+ if (priv->entryHomePageURL != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(priv->entryHomePageURL), url);
+ nsoption_set_charp(homepage_url, strdup(url));
+ }
+}
+
+/* Search */
+
+/* Url Search widget */
+TOGGLEBUTTON_SIGNALS(checkUrlSearch, search_url_bar)
+
+/* provider combo */
+G_MODULE_EXPORT void
+nsgtk_preferences_comboSearch_changed(GtkComboBox *widget, struct ppref *priv)
+{
+ int provider;
+
+ provider = gtk_combo_box_get_active(widget);
+
+ /* set the option */
+ nsoption_set_int(search_provider, provider);
+
+ /* set search provider */
+ search_web_select_provider(provider);
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_comboSearch_realize(GtkWidget *widget, struct ppref *priv)
+{
+ int iter;
+ const char *name;
+ int provider = nsoption_int(search_provider);
+
+ if (priv->search_providers != NULL) {
+ gtk_list_store_clear(priv->search_providers);
+ for (iter = search_web_iterate_providers(0, &name);
+ iter != -1;
+ iter = search_web_iterate_providers(iter, &name)) {
+ gtk_list_store_insert_with_values(priv->search_providers,
+ NULL, -1,
+ 0, name, -1);
+ }
+ }
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(widget), provider);
+}
+
+
+/* Downloads */
+
+/* clear downloads */
+TOGGLEBUTTON_SIGNALS(checkClearDownloads, downloads_clear)
+
+/* request overwite */
+TOGGLEBUTTON_SIGNALS(checkRequestOverwrite, request_overwrite)
+
+/* download location
+ *
+ * note selection-changed is used instead of file-set as the returned
+ * filename when that signal are used is incorrect. Though this signal
+ * does update frequently often with the same data.
+ */
+G_MODULE_EXPORT void
+nsgtk_preferences_fileChooserDownloads_selectionchanged(GtkFileChooser *chooser,
+ struct ppref *priv)
+{
+ gchar *dir;
+ dir = gtk_file_chooser_get_filename(chooser);
+ nsoption_set_charp(downloads_directory, strdup(dir));
+ g_free(dir);
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget,
+ struct ppref *priv)
+{
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(widget),
+ nsoption_charp(downloads_directory));
+}
+
+
+/************* Dialog window ***********/
+
+/* dialog close and destroy events */
+G_MODULE_EXPORT void
+nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid)
+{
+ char *choices = NULL;
+
+ if (resid == GTK_RESPONSE_CLOSE) {
+ netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (choices != NULL) {
+ nsoption_write(choices, NULL, NULL);
+ free(choices);
+ }
+ gtk_widget_hide(GTK_WIDGET(dlg));
+ }
+}
+
+G_MODULE_EXPORT gboolean
+nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg,
+ struct ppref *priv)
+{
+ char *choices = NULL;
+
+ netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (choices != NULL) {
+ nsoption_write(choices, NULL, NULL);
+ free(choices);
+ }
+
+ gtk_widget_hide(GTK_WIDGET(dlg));
+
+ /* Delt with it by hiding window, no need to destory widget by
+ * default.
+ */
+ return TRUE;
+}
+
+G_MODULE_EXPORT void
+nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv)
+{
+ char *choices = NULL;
+
+ netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (choices != NULL) {
+ nsoption_write(choices, NULL, NULL);
+ free(choices);
+ }
+}
+
+
+/* exported interface documented in gtk/preferences.h */
+GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent)
+{
+ GtkBuilder *preferences_builder;
+ struct ppref *priv = &ppref;
+ nserror res;
+
+ priv->bw = bw; /* for setting "current" page */
+
+ /* memoised dialog creation */
+ if (priv->dialog != NULL) {
+ gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent);
+ return GTK_WIDGET(priv->dialog);
+ }
+
+ res = nsgtk_builder_new_from_resname("options", &preferences_builder);
+ if (res != NSERROR_OK) {
+ LOG("Preferences UI builder init failed");
+ return NULL;
+ }
+
+ priv->dialog = gtk_builder_get_object(preferences_builder,
+ "dialogPreferences");
+ if (priv->dialog == NULL) {
+ LOG("Unable to get object for preferences dialog");
+ /* release builder as were done with it */
+ g_object_unref(G_OBJECT(preferences_builder));
+ return NULL;
+ }
+
+ /* need to explicitly obtain handles for some widgets enabling
+ * updates by other widget events
+ */
+#define GB(TYPE, NAME) GTK_##TYPE(gtk_builder_get_object(preferences_builder, #NAME))
+ priv->entryHomePageURL = GB(ENTRY, entryHomePageURL);
+ priv->content_language = GB(LIST_STORE, liststore_content_language);
+ priv->search_providers = GB(LIST_STORE, liststore_search_provider);
+ priv->entryProxyHost = GB(ENTRY, entryProxyHost);
+ priv->spinProxyPort = GB(SPIN_BUTTON, spinProxyPort);
+ priv->entryProxyUser = GB(ENTRY, entryProxyUser);
+ priv->entryProxyPassword = GB(ENTRY, entryProxyPassword);
+ priv->entryProxyNoproxy = GB(ENTRY, entryProxyNoproxy);
+#undef GB
+
+ /* connect all signals ready to use */
+ gtk_builder_connect_signals(preferences_builder, priv);
+
+ /* release builder as were done with it */
+ g_object_unref(G_OBJECT(preferences_builder));
+
+ /* mark dialog as transient on parent */
+ gtk_window_set_transient_for(GTK_WINDOW(priv->dialog), parent);
+
+ return GTK_WIDGET(priv->dialog);
+}
+
diff --git a/frontends/gtk/preferences.h b/frontends/gtk/preferences.h
new file mode 100644
index 000000000..9fe469e24
--- /dev/null
+++ b/frontends/gtk/preferences.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NETSURF_GTK_PREFERENCES_H
+#define NETSURF_GTK_PREFERENCES_H
+
+#include <gtk/gtk.h>
+
+/** Initialise prefernces window
+ */
+GtkWidget* nsgtk_preferences(struct browser_window *bw, GtkWindow *parent);
+
+#endif
diff --git a/frontends/gtk/print.c b/frontends/gtk/print.c
new file mode 100644
index 000000000..a6e639996
--- /dev/null
+++ b/frontends/gtk/print.c
@@ -0,0 +1,613 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ /** \file
+ * GTK printing (implementation).
+ * All the functions and structures necessary for printing( signal handlers,
+ * plotters, printer) are here.
+ * Most of the plotters have been copied from the gtk_plotters.c file.
+ */
+
+#include "utils/config.h"
+
+#include <math.h>
+#include <assert.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "utils/nsoption.h"
+#include "desktop/plotters.h"
+#include "desktop/print.h"
+#include "desktop/printer.h"
+
+#include "gtk/layout_pango.h"
+#include "gtk/bitmap.h"
+#include "gtk/print.h"
+#include "gtk/scaffolding.h"
+
+/* Globals */
+cairo_t *gtk_print_current_cr;
+static struct print_settings* settings;
+hlcache_handle *content_to_print;
+static GdkRectangle cliprect;
+
+static inline void nsgtk_print_set_colour(colour c)
+{
+ int r, g, b;
+
+ r = c & 0xff;
+ g = (c & 0xff00) >> 8;
+ b = (c & 0xff0000) >> 16;
+
+#ifdef FIXME
+ GdkColor colour;
+ colour.red = r | (r << 8);
+ colour.green = g | (g << 8);
+ colour.blue = b | (b << 8);
+ colour.pixel = (r << 16) | (g << 8) | b;
+ gdk_colormap_alloc_color(gdk_colormap_get_system(), &colour, true, true);
+#endif
+ cairo_set_source_rgba(gtk_print_current_cr, r / 255.0,
+ g / 255.0, b / 255.0, 1.0);
+}
+
+
+
+static bool gtk_print_font_paint(int x, int y,
+ const char *string, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ PangoFontDescription *desc;
+ PangoLayout *layout;
+ gint size;
+ PangoLayoutLine *line;
+
+ if (length == 0)
+ return true;
+
+ desc = nsfont_style_to_description(fstyle);
+ size = (gint) ((double) pango_font_description_get_size(desc) *
+ settings->scale);
+
+ if (pango_font_description_get_size_is_absolute(desc))
+ pango_font_description_set_absolute_size(desc, size);
+ else
+ pango_font_description_set_size(desc, size);
+
+ layout = pango_cairo_create_layout(gtk_print_current_cr);
+
+ pango_layout_set_font_description(layout, desc);
+ pango_layout_set_text(layout, string, length);
+
+ line = pango_layout_get_line(layout, 0);
+
+ cairo_move_to(gtk_print_current_cr, x, y);
+ nsgtk_print_set_colour(fstyle->foreground);
+ pango_cairo_show_layout_line(gtk_print_current_cr, line);
+
+ g_object_unref(layout);
+ pango_font_description_free(desc);
+
+ return true;
+}
+
+
+/** Set cairo context to solid plot operation. */
+static inline void nsgtk_print_set_solid(void)
+{
+ double dashes = 0;
+ cairo_set_dash(gtk_print_current_cr, &dashes, 0, 0);
+}
+
+/** Set cairo context to dotted plot operation. */
+static inline void nsgtk_print_set_dotted(void)
+{
+ double cdashes[] = { 1.0, 2.0 };
+ cairo_set_dash(gtk_print_current_cr, cdashes, 1, 0);
+}
+
+/** Set cairo context to dashed plot operation. */
+static inline void nsgtk_print_set_dashed(void)
+{
+ double cdashes[] = { 8.0, 2.0 };
+ cairo_set_dash(gtk_print_current_cr, cdashes, 1, 0);
+}
+
+/** Set clipping area for subsequent plot operations. */
+static bool nsgtk_print_plot_clip(const struct rect *clip)
+{
+ LOG("Clipping. x0: %i ;\t y0: %i ;\t x1: %i ;\t y1: %i", clip->x0, clip->y0, clip->x1, clip->y1);
+
+ /* Normalize cllipping area - to prevent overflows.
+ * See comment in pdf_plot_fill. */
+ int clip_x0 = min(max(clip->x0, 0), settings->page_width);
+ int clip_y0 = min(max(clip->y0, 0), settings->page_height);
+ int clip_x1 = min(max(clip->x1, 0), settings->page_width);
+ int clip_y1 = min(max(clip->y1, 0), settings->page_height);
+
+ cairo_reset_clip(gtk_print_current_cr);
+ cairo_rectangle(gtk_print_current_cr, clip_x0, clip_y0,
+ clip_x1 - clip_x0, clip_y1 - clip_y0);
+ cairo_clip(gtk_print_current_cr);
+
+ cliprect.x = clip_x0;
+ cliprect.y = clip_y0;
+ cliprect.width = clip_x1 - clip_x0;
+ cliprect.height = clip_y1 - clip_y0;
+
+ return true;
+}
+
+static bool nsgtk_print_plot_arc(int x, int y, int radius, int angle1, int angle2, const plot_style_t *style)
+{
+ nsgtk_print_set_colour(style->fill_colour);
+ nsgtk_print_set_solid();
+
+ cairo_set_line_width(gtk_print_current_cr, 1);
+ cairo_arc(gtk_print_current_cr, x, y, radius,
+ (angle1 + 90) * (M_PI / 180),
+ (angle2 + 90) * (M_PI / 180));
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+static bool nsgtk_print_plot_disc(int x, int y, int radius, const plot_style_t *style)
+{
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_print_set_colour(style->fill_colour);
+ nsgtk_print_set_solid();
+ cairo_set_line_width(gtk_print_current_cr, 0);
+ cairo_arc(gtk_print_current_cr, x, y, radius, 0, M_PI * 2);
+ cairo_fill(gtk_print_current_cr);
+ cairo_stroke(gtk_print_current_cr);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_print_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_print_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_print_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_print_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(gtk_print_current_cr, 1);
+ else
+ cairo_set_line_width(gtk_print_current_cr, style->stroke_width);
+
+ cairo_arc(gtk_print_current_cr, x, y, radius, 0, M_PI * 2);
+
+ cairo_stroke(gtk_print_current_cr);
+ }
+ return true;
+}
+
+static bool nsgtk_print_plot_line(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ nsgtk_print_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_print_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_print_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_print_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(gtk_print_current_cr, 1);
+ else
+ cairo_set_line_width(gtk_print_current_cr, style->stroke_width);
+
+ cairo_move_to(gtk_print_current_cr, x0 + 0.5, y0 + 0.5);
+ cairo_line_to(gtk_print_current_cr, x1 + 0.5, y1 + 0.5);
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+static bool nsgtk_print_plot_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style)
+{
+ LOG("x0: %i ;\t y0: %i ;\t x1: %i ;\t y1: %i", x0, y0, x1, y1);
+
+ if (style->fill_type != PLOT_OP_TYPE_NONE) {
+
+ nsgtk_print_set_colour(style->fill_colour);
+ nsgtk_print_set_solid();
+
+ /* Normalize boundaries of the area - to prevent overflows.
+ * See comment in pdf_plot_fill. */
+ x0 = min(max(x0, 0), settings->page_width);
+ y0 = min(max(y0, 0), settings->page_height);
+ x1 = min(max(x1, 0), settings->page_width);
+ y1 = min(max(y1, 0), settings->page_height);
+
+ cairo_set_line_width(gtk_print_current_cr, 0);
+ cairo_rectangle(gtk_print_current_cr, x0, y0, x1 - x0, y1 - y0);
+ cairo_fill(gtk_print_current_cr);
+ cairo_stroke(gtk_print_current_cr);
+ }
+
+ if (style->stroke_type != PLOT_OP_TYPE_NONE) {
+ nsgtk_print_set_colour(style->stroke_colour);
+
+ switch (style->stroke_type) {
+ case PLOT_OP_TYPE_SOLID: /**< Solid colour */
+ default:
+ nsgtk_print_set_solid();
+ break;
+
+ case PLOT_OP_TYPE_DOT: /**< Doted plot */
+ nsgtk_print_set_dotted();
+ break;
+
+ case PLOT_OP_TYPE_DASH: /**< dashed plot */
+ nsgtk_print_set_dashed();
+ break;
+ }
+
+ if (style->stroke_width == 0)
+ cairo_set_line_width(gtk_print_current_cr, 1);
+ else
+ cairo_set_line_width(gtk_print_current_cr, style->stroke_width);
+
+ cairo_rectangle(gtk_print_current_cr, x0, y0, x1 - x0, y1 - y0);
+ cairo_stroke(gtk_print_current_cr);
+ }
+
+ return true;
+}
+
+static bool nsgtk_print_plot_polygon(const int *p, unsigned int n, const plot_style_t *style)
+{
+ unsigned int i;
+
+ LOG("Plotting polygon.");
+
+ nsgtk_print_set_colour(style->fill_colour);
+ nsgtk_print_set_solid();
+
+ cairo_set_line_width(gtk_print_current_cr, 0);
+ cairo_move_to(gtk_print_current_cr, p[0], p[1]);
+
+ LOG("Starting line at: %i\t%i", p[0], p[1]);
+
+ for (i = 1; i != n; i++) {
+ cairo_line_to(gtk_print_current_cr, p[i * 2], p[i * 2 + 1]);
+ LOG("Drawing line to: %i\t%i", p[i * 2], p[i * 2 + 1]);
+ }
+
+ cairo_fill(gtk_print_current_cr);
+ cairo_stroke(gtk_print_current_cr);
+
+ return true;
+}
+
+
+static bool nsgtk_print_plot_path(const float *p, unsigned int n, colour fill,
+ float width, colour c, const float transform[6])
+{
+ /* Only the internal SVG renderer uses this plot call currently,
+ * and the GTK version uses librsvg. Thus, we ignore this complexity,
+ * and just return true obliviously. */
+
+ return true;
+}
+
+
+static bool nsgtk_print_plot_pixbuf(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg)
+{
+ int x0, y0, x1, y1;
+ int dsrcx, dsrcy, dwidth, dheight;
+ int bmwidth, bmheight;
+
+ cairo_surface_t *bmsurface = bitmap->surface;
+
+ /* Bail early if we can */
+ if (width == 0 || height == 0)
+ /* Nothing to plot */
+ return true;
+ if ((x > (cliprect.x + cliprect.width)) ||
+ ((x + width) < cliprect.x) ||
+ (y > (cliprect.y + cliprect.height)) ||
+ ((y + height) < cliprect.y)) {
+ /* Image completely outside clip region */
+ return true;
+ }
+
+ /* Get clip rectangle / image rectangle edge differences */
+ x0 = cliprect.x - x;
+ y0 = cliprect.y - y;
+ x1 = (x + width) - (cliprect.x + cliprect.width);
+ y1 = (y + height) - (cliprect.y + cliprect.height);
+
+ /* Set initial draw geometry */
+ dsrcx = x;
+ dsrcy = y;
+ dwidth = width;
+ dheight = height;
+
+ /* Manually clip draw coordinates to area of image to be rendered */
+ if (x0 > 0) {
+ /* Clip left */
+ dsrcx += x0;
+ dwidth -= x0;
+ }
+ if (y0 > 0) {
+ /* Clip top */
+ dsrcy += y0;
+ dheight -= y0;
+ }
+ if (x1 > 0) {
+ /* Clip right */
+ dwidth -= x1;
+ }
+ if (y1 > 0) {
+ /* Clip bottom */
+ dheight -= y1;
+ }
+
+ if (dwidth == 0 || dheight == 0)
+ /* Nothing to plot */
+ return true;
+
+ bmwidth = cairo_image_surface_get_width(bmsurface);
+ bmheight = cairo_image_surface_get_height(bmsurface);
+
+ /* Render the bitmap */
+ if ((bmwidth == width) && (bmheight == height)) {
+ /* Bitmap is not scaled */
+ /* Plot the bitmap */
+ cairo_set_source_surface(gtk_print_current_cr, bmsurface, x, y);
+ cairo_rectangle(gtk_print_current_cr, dsrcx, dsrcy, dwidth, dheight);
+ cairo_fill(gtk_print_current_cr);
+
+ } else {
+ /* Bitmap is scaled */
+ if ((bitmap->scsurface != NULL) &&
+ ((cairo_image_surface_get_width(bitmap->scsurface) != width) ||
+ (cairo_image_surface_get_height(bitmap->scsurface) != height))){
+ cairo_surface_destroy(bitmap->scsurface);
+ bitmap->scsurface = NULL;
+ }
+
+ if (bitmap->scsurface == NULL) {
+ bitmap->scsurface = cairo_surface_create_similar(bmsurface,CAIRO_CONTENT_COLOR_ALPHA, width, height);
+ cairo_t *cr = cairo_create(bitmap->scsurface);
+
+ /* Scale *before* setting the source surface (1) */
+ cairo_scale(cr, (double)width / bmwidth, (double)height / bmheight);
+ cairo_set_source_surface(cr, bmsurface, 0, 0);
+
+ /* To avoid getting the edge pixels blended with 0
+ * alpha, which would occur with the default
+ * EXTEND_NONE. Use EXTEND_PAD for 1.2 or newer (2)
+ */
+ cairo_pattern_set_extend(cairo_get_source(cr),
+ CAIRO_EXTEND_REFLECT);
+
+ /* Replace the destination with the source instead of
+ * overlaying
+ */
+ cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+ /* Do the actual drawing */
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+
+ }
+ /* Plot the scaled bitmap */
+ cairo_set_source_surface(gtk_print_current_cr, bitmap->scsurface, x, y);
+ cairo_rectangle(gtk_print_current_cr, dsrcx, dsrcy, dwidth, dheight);
+ cairo_fill(gtk_print_current_cr);
+
+ }
+
+ return true;
+}
+
+
+static bool nsgtk_print_plot_bitmap(int x, int y, int width, int height,
+ struct bitmap *bitmap, colour bg,
+ bitmap_flags_t flags)
+{
+ int doneheight = 0, donewidth = 0;
+ bool repeat_x = (flags & BITMAPF_REPEAT_X);
+ bool repeat_y = (flags & BITMAPF_REPEAT_Y);
+
+ if (!(repeat_x || repeat_y)) {
+ /* Not repeating at all, so just pass it on */
+ return nsgtk_print_plot_pixbuf(x, y, width, height, bitmap, bg);
+ }
+
+ width = nsgtk_bitmap_get_width(bitmap);
+ height = nsgtk_bitmap_get_height(bitmap);
+
+ /* Bail early if we can */
+ if (width == 0 || height == 0)
+ /* Nothing to plot */
+ return true;
+
+ if (y > cliprect.y) {
+ doneheight = (cliprect.y - height) + ((y - cliprect.y) % height);
+ } else {
+ doneheight = y;
+ }
+
+ while (doneheight < (cliprect.y + cliprect.height)) {
+ if (x > cliprect.x) {
+ donewidth = (cliprect.x - width) + ((x - cliprect.x) % width);
+ } else {
+ donewidth = x;
+ }
+
+ while (donewidth < (cliprect.x + cliprect.width)) {
+ nsgtk_print_plot_pixbuf(donewidth, doneheight,
+ width, height, bitmap, bg);
+ donewidth += width;
+ if (!repeat_x)
+ break;
+ }
+ doneheight += height;
+
+ if (!repeat_y)
+ break;
+ }
+
+ return true;
+}
+
+static bool nsgtk_print_plot_text(int x, int y, const char *text, size_t length,
+ const plot_font_style_t *fstyle)
+{
+ return gtk_print_font_paint(x, y, text, length, fstyle);
+}
+
+/** GTK print plotter table */
+static const struct plotter_table nsgtk_print_plotters = {
+ .clip = nsgtk_print_plot_clip,
+ .arc = nsgtk_print_plot_arc,
+ .disc = nsgtk_print_plot_disc,
+ .line = nsgtk_print_plot_line,
+ .rectangle = nsgtk_print_plot_rectangle,
+ .polygon = nsgtk_print_plot_polygon,
+ .path = nsgtk_print_plot_path,
+ .bitmap = nsgtk_print_plot_bitmap,
+ .text = nsgtk_print_plot_text,
+ .option_knockout = false,
+};
+
+static bool gtk_print_begin(struct print_settings* settings)
+{
+ return true;
+}
+
+static bool gtk_print_next_page(void)
+{
+ return true;
+}
+
+static void gtk_print_end(void)
+{
+}
+
+static const struct printer gtk_printer = {
+ &nsgtk_print_plotters,
+ gtk_print_begin,
+ gtk_print_next_page,
+ gtk_print_end
+};
+
+/**
+ * Handle the begin_print signal from the GtkPrintOperation
+ *
+ * \param operation the operation which emited the signal
+ * \param context the print context used to set up the pages
+ * \param user_data nothing in here
+ */
+void gtk_print_signal_begin_print (GtkPrintOperation *operation,
+ GtkPrintContext *context, gpointer user_data)
+{
+ int page_number;
+ double height_on_page, height_to_print;
+
+ LOG("Begin print");
+
+ settings = user_data;
+
+ settings->margins[MARGINTOP] = 0;
+ settings->margins[MARGINLEFT] = 0;
+ settings->margins[MARGINBOTTOM] = 0;
+ settings->margins[MARGINRIGHT] = 0;
+ settings->page_width = gtk_print_context_get_width(context);
+ settings->page_height = gtk_print_context_get_height(context);
+ settings->scale = 0.7; /* at 0.7 the pages look the best */
+ settings->font_func = nsgtk_layout_table;
+
+ if (print_set_up(content_to_print, &gtk_printer,
+ settings, &height_to_print) == false) {
+ gtk_print_operation_cancel(operation);
+
+ } else {
+
+ LOG("page_width: %f ;page_height: %f; content height: %lf", settings->page_width, settings->page_height, height_to_print);
+
+ height_on_page = settings->page_height;
+ height_on_page = height_on_page -
+ FIXTOFLT(FSUB(settings->margins[MARGINTOP],
+ settings->margins[MARGINBOTTOM]));
+ height_to_print *= settings->scale;
+
+ page_number = height_to_print / height_on_page;
+
+ if (height_to_print - page_number * height_on_page > 0)
+ page_number += 1;
+
+ gtk_print_operation_set_n_pages(operation, page_number);
+ }
+}
+
+/**
+ * Handle the draw_page signal from the GtkPrintOperation.
+ * This function changes only the cairo context to print on.
+ */
+void gtk_print_signal_draw_page(GtkPrintOperation *operation,
+ GtkPrintContext *context, gint page_nr, gpointer user_data)
+{
+ LOG("Draw Page");
+ gtk_print_current_cr = gtk_print_context_get_cairo_context(context);
+ print_draw_next_page(&gtk_printer, settings);
+}
+
+/**
+ * Handle the end_print signal from the GtkPrintOperation.
+ * This functions calls only the print_cleanup function from the print interface
+ */
+void gtk_print_signal_end_print(GtkPrintOperation *operation,
+ GtkPrintContext *context, gpointer user_data)
+{
+ LOG("End print");
+ print_cleanup(content_to_print, &gtk_printer, user_data);
+}
+
+
diff --git a/frontends/gtk/print.h b/frontends/gtk/print.h
new file mode 100644
index 000000000..d44fad31f
--- /dev/null
+++ b/frontends/gtk/print.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2008 Adam Blokus <adamblokus@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+ /** \file
+ * GTK printing (interface).
+ */
+
+#ifndef NETSURF_GTK_PRINT_PLOTTERS_H
+#define NETSURF_GTK_PRINT_PLOTTERS_H
+
+
+#include <gtk/gtk.h>
+
+struct hlcache_handle;
+
+extern cairo_t *gtk_print_current_cr;
+
+extern struct hlcache_handle *content_to_print;
+
+
+/*handlers for signals from the GTK print operation*/
+void gtk_print_signal_begin_print(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gpointer user_data);
+
+void gtk_print_signal_draw_page(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr,
+ gpointer user_data);
+
+void gtk_print_signal_end_print(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gpointer user_data);
+
+#endif
diff --git a/frontends/gtk/res/Messages b/frontends/gtk/res/Messages
new file mode 120000
index 000000000..75bfdf53d
--- /dev/null
+++ b/frontends/gtk/res/Messages
@@ -0,0 +1 @@
+en/Messages \ No newline at end of file
diff --git a/frontends/gtk/res/SearchEngines b/frontends/gtk/res/SearchEngines
new file mode 120000
index 000000000..3dc819c2a
--- /dev/null
+++ b/frontends/gtk/res/SearchEngines
@@ -0,0 +1 @@
+../../../resources/SearchEngines \ No newline at end of file
diff --git a/frontends/gtk/res/adblock.css b/frontends/gtk/res/adblock.css
new file mode 120000
index 000000000..ff2485622
--- /dev/null
+++ b/frontends/gtk/res/adblock.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/AdBlock,f79 \ No newline at end of file
diff --git a/frontends/gtk/res/arrow_down_8x32.png b/frontends/gtk/res/arrow_down_8x32.png
new file mode 100644
index 000000000..475b4ff61
--- /dev/null
+++ b/frontends/gtk/res/arrow_down_8x32.png
Binary files differ
diff --git a/frontends/gtk/res/ca-bundle.txt b/frontends/gtk/res/ca-bundle.txt
new file mode 120000
index 000000000..0b0e416ad
--- /dev/null
+++ b/frontends/gtk/res/ca-bundle.txt
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/ca-bundle \ No newline at end of file
diff --git a/frontends/gtk/res/cookies.gtk2.ui b/frontends/gtk/res/cookies.gtk2.ui
new file mode 100644
index 000000000..86f15c765
--- /dev/null
+++ b/frontends/gtk/res/cookies.gtk2.ui
@@ -0,0 +1,174 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.6 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="wndCookies">
+ <property name="title" translatable="yes">Cookies - NetSurf</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ <accelerator key="Delete" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">D_elete all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ <accelerator key="U" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_domains">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Domains</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_cookies">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Cookies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu6">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_domains">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Domains</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_cookies">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Cookies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="cookiesScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="cookiesViewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkDrawingArea" id="cookiesDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/cookies.gtk3.ui b/frontends/gtk/res/cookies.gtk3.ui
new file mode 100644
index 000000000..44dcb80b8
--- /dev/null
+++ b/frontends/gtk/res/cookies.gtk3.ui
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndCookies">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">NetSurf Cookies</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">D_elete all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_domains">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Domains</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_cookies">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Cookies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_domains">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Domains</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_cookies">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Cookies</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="cookiesScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="cookiesViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="cookiesDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/credits.html b/frontends/gtk/res/credits.html
new file mode 120000
index 000000000..ca85d3d27
--- /dev/null
+++ b/frontends/gtk/res/credits.html
@@ -0,0 +1 @@
+en/credits.html \ No newline at end of file
diff --git a/frontends/gtk/res/de/welcome.html b/frontends/gtk/res/de/welcome.html
new file mode 120000
index 000000000..98a53b215
--- /dev/null
+++ b/frontends/gtk/res/de/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/de/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/default.css b/frontends/gtk/res/default.css
new file mode 120000
index 000000000..a8579eb7c
--- /dev/null
+++ b/frontends/gtk/res/default.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/CSS,f79 \ No newline at end of file
diff --git a/frontends/gtk/res/default.ico b/frontends/gtk/res/default.ico
new file mode 100644
index 000000000..1cb432828
--- /dev/null
+++ b/frontends/gtk/res/default.ico
Binary files differ
diff --git a/frontends/gtk/res/downloads.gtk2.ui b/frontends/gtk/res/downloads.gtk2.ui
new file mode 100644
index 000000000..1e71328a4
--- /dev/null
+++ b/frontends/gtk/res/downloads.gtk2.ui
@@ -0,0 +1,175 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="wndDownloads">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes">Downloads - NetSurf</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">500</property>
+ <property name="default_height">300</property>
+ <property name="destroy_with_parent">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="treeDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="search_column">2</property>
+ <property name="show_expanders">False</property>
+ <property name="rubber_banding">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">2</property>
+ <property name="bottom_padding">2</property>
+ <property name="left_padding">2</property>
+ <property name="right_padding">2</property>
+ <child>
+ <object class="GtkProgressBar" id="progressBar">
+ <property name="visible">True</property>
+ <property name="show_text">True</property>
+ <property name="text" translatable="yes">0% of 0 files</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="buttonPause">
+ <property name="label">gtk-media-pause</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonPlay">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-play</property>
+ <property name="icon-size">2</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Resume</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonCancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonClear">
+ <property name="label">gtk-clear</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/downloads.gtk3.ui b/frontends/gtk/res/downloads.gtk3.ui
new file mode 100644
index 000000000..1e71328a4
--- /dev/null
+++ b/frontends/gtk/res/downloads.gtk3.ui
@@ -0,0 +1,175 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="wndDownloads">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes">Downloads - NetSurf</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">500</property>
+ <property name="default_height">300</property>
+ <property name="destroy_with_parent">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="treeDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="search_column">2</property>
+ <property name="show_expanders">False</property>
+ <property name="rubber_banding">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">2</property>
+ <property name="bottom_padding">2</property>
+ <property name="left_padding">2</property>
+ <property name="right_padding">2</property>
+ <child>
+ <object class="GtkProgressBar" id="progressBar">
+ <property name="visible">True</property>
+ <property name="show_text">True</property>
+ <property name="text" translatable="yes">0% of 0 files</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="buttonPause">
+ <property name="label">gtk-media-pause</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonPlay">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-media-play</property>
+ <property name="icon-size">2</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Resume</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonCancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonClear">
+ <property name="label">gtk-clear</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/en/credits.html b/frontends/gtk/res/en/credits.html
new file mode 120000
index 000000000..252516fd7
--- /dev/null
+++ b/frontends/gtk/res/en/credits.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/en/credits.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/en/licence.html b/frontends/gtk/res/en/licence.html
new file mode 120000
index 000000000..79f73669b
--- /dev/null
+++ b/frontends/gtk/res/en/licence.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/en/licence.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/en/maps.html b/frontends/gtk/res/en/maps.html
new file mode 120000
index 000000000..bb3ffcbe7
--- /dev/null
+++ b/frontends/gtk/res/en/maps.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/en/maps.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/en/welcome.html b/frontends/gtk/res/en/welcome.html
new file mode 120000
index 000000000..601099223
--- /dev/null
+++ b/frontends/gtk/res/en/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/en/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/favicon.png b/frontends/gtk/res/favicon.png
new file mode 120000
index 000000000..5a8b3433c
--- /dev/null
+++ b/frontends/gtk/res/favicon.png
@@ -0,0 +1 @@
+../../../resources/favicon.png \ No newline at end of file
diff --git a/frontends/gtk/res/history.gtk2.ui b/frontends/gtk/res/history.gtk2.ui
new file mode 100644
index 000000000..2b89ecb4b
--- /dev/null
+++ b/frontends/gtk/res/history.gtk2.ui
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndHistory">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">NetSurf Global History</property>
+ <property name="window_position">center</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <property name="type_hint">utility</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="export">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Export</property>
+ <property name="use_underline">True</property>
+ <accelerator key="E" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ <accelerator key="Delete" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">D_elete all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ <accelerator key="U" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="launch">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Launch</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="globalHistoryScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkViewport" id="globalHistoryViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkDrawingArea" id="globalHistoryDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/history.gtk3.ui b/frontends/gtk/res/history.gtk3.ui
new file mode 100644
index 000000000..7fa598f1e
--- /dev/null
+++ b/frontends/gtk/res/history.gtk3.ui
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndHistory">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">NetSurf Global History</property>
+ <property name="window_position">center</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <property name="type_hint">utility</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="export">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Export</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">D_elete all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="launch">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Launch</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="globalHistoryScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="globalHistoryViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="globalHistoryDrawingArea">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/hotlist.gtk2.ui b/frontends/gtk/res/hotlist.gtk2.ui
new file mode 100644
index 000000000..af0fd5696
--- /dev/null
+++ b/frontends/gtk/res/hotlist.gtk2.ui
@@ -0,0 +1,217 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.6 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="wndHotlist">
+ <property name="title" translatable="yes">Bookmarks - NetSurf</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="export">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Export</property>
+ <property name="use_underline">True</property>
+ <accelerator key="E" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="new_folder">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">New _folder</property>
+ <property name="use_underline">True</property>
+ <accelerator key="M" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="new_entry">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">New _entry</property>
+ <property name="use_underline">True</property>
+ <accelerator key="N" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="edit_selected">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ <accelerator key="Delete" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ <accelerator key="U" signal="activate" modifiers="GDK_MOD1_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_directories">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_addresses">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu6">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_directories">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_addresses">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="launch">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Launch</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="hotlistScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkViewport" id="hotlistViewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">queue</property>
+ <child>
+ <object class="GtkDrawingArea" id="hotlistDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/hotlist.gtk3.ui b/frontends/gtk/res/hotlist.gtk3.ui
new file mode 100644
index 000000000..b0e075c4b
--- /dev/null
+++ b/frontends/gtk/res/hotlist.gtk3.ui
@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndHotlist">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">NetSurf Bookmarks</property>
+ <property name="window_position">mouse</property>
+ <property name="default_width">600</property>
+ <property name="default_height">500</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="export">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Export</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="new_folder">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">New _folder</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="new_entry">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">New _entry</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="edit_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="delete_selected">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Delete</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="select_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Select all</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="clear_selection">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Clear selection</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Expand</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="expand_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="expand_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Collapse</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="collapse_all">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_All</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_directories">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Directories</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="collapse_addresses">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add_resses</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="launch">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Launch</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="hotlistScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="hotlistViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="hotlistDrawingArea">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/icons b/frontends/gtk/res/icons
new file mode 120000
index 000000000..187efd6f9
--- /dev/null
+++ b/frontends/gtk/res/icons
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/Icons/ \ No newline at end of file
diff --git a/frontends/gtk/res/internal.css b/frontends/gtk/res/internal.css
new file mode 120000
index 000000000..17f9f1504
--- /dev/null
+++ b/frontends/gtk/res/internal.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/internal.css,f79 \ No newline at end of file
diff --git a/frontends/gtk/res/it/credits.html b/frontends/gtk/res/it/credits.html
new file mode 120000
index 000000000..64b78982e
--- /dev/null
+++ b/frontends/gtk/res/it/credits.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/it/credits.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/it/licence.html b/frontends/gtk/res/it/licence.html
new file mode 120000
index 000000000..4abc825d3
--- /dev/null
+++ b/frontends/gtk/res/it/licence.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/it/licence.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/it/welcome.html b/frontends/gtk/res/it/welcome.html
new file mode 120000
index 000000000..59cef0551
--- /dev/null
+++ b/frontends/gtk/res/it/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/it/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/ja/welcome.html b/frontends/gtk/res/ja/welcome.html
new file mode 120000
index 000000000..a2556ee4e
--- /dev/null
+++ b/frontends/gtk/res/ja/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/ja/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/languages b/frontends/gtk/res/languages
new file mode 100644
index 000000000..4927e03a0
--- /dev/null
+++ b/frontends/gtk/res/languages
@@ -0,0 +1,261 @@
+aa
+ab
+ae
+af
+ak
+am
+an
+ar
+ar-ae
+ar-bh
+ar-dz
+ar-eg
+ar-iq
+ar-jo
+ar-kw
+ar-lb
+ar-ly
+ar-ma
+ar-om
+ar-qa
+ar-sa
+ar-sy
+ar-tn
+ar-ye
+as
+ast
+av
+az
+ba
+be
+bg
+bh
+bi
+bm
+bn
+bo
+br
+bs
+ca
+ce
+ch
+co
+cr
+cs
+cu
+cv
+cy
+da
+de
+de-at
+de-ch
+de-de
+de-li
+de-lu
+dv
+dz
+ee
+el
+en
+en-au
+en-bz
+en-ca
+en-gb
+en-ie
+en-jm
+en-nz
+en-ph
+en-tt
+en-us
+en-za
+en-zw
+eo
+es
+es-ar
+es-bo
+es-cl
+es-co
+es-cr
+es-do
+es-ec
+es-es
+es-gt
+es-hn
+es-mx
+es-ni
+es-pa
+es-pe
+es-pr
+es-py
+es-sv
+es-uy
+es-ve
+et
+eu
+fa
+fa-ir
+ff
+fi
+fj
+fo
+fr
+fr-be
+fr-ca
+fr-ch
+fr-fr
+fr-lu
+fr-mc
+fur
+fy
+ga
+gd
+gl
+gn
+gu
+gv
+ha
+he
+hi
+ho
+hsb
+hr
+ht
+hu
+hy
+hz
+ia
+id
+ie
+ig
+ii
+ik
+io
+is
+it
+it-ch
+iu
+ja
+jv
+ka
+kg
+ki
+kk
+kl
+km
+kn
+ko
+ko-kp
+ko-kr
+kok
+kr
+ks
+ku
+kv
+kw
+ky
+la
+lb
+lg
+li
+ln
+lo
+lt
+lu
+lv
+mg
+mh
+mi
+mk
+mk-mk
+ml
+mn
+mo
+mr
+ms
+mt
+my
+na
+nb
+nd
+ne
+ng
+nl
+nl-be
+nn
+no
+nr
+nso
+nv
+ny
+oc
+oj
+om
+or
+os
+pa
+pa-in
+pa-pk
+pi
+pl
+ps
+pt
+pt-br
+qu
+rm
+rn
+ro
+ro-mo
+ru
+ru-mo
+sa
+sc
+sd
+sg
+si
+sk
+sl
+so
+sq
+sr
+ss
+st
+su
+sv
+sv-fi
+sv-se
+sw
+ta
+te
+tg
+th
+ti
+tig
+tk
+tl
+tlh
+tn
+to
+tr
+ts
+tt
+tw
+ty
+ug
+uk
+ur
+uz
+ve
+vi
+vo
+wa
+wo
+xh
+yi
+yo
+za
+zh
+zh-cn
+zh-hk
+zh-sg
+zh-tw
+zu
diff --git a/frontends/gtk/res/licence.html b/frontends/gtk/res/licence.html
new file mode 120000
index 000000000..86f8c54bf
--- /dev/null
+++ b/frontends/gtk/res/licence.html
@@ -0,0 +1 @@
+en/licence.html \ No newline at end of file
diff --git a/frontends/gtk/res/login.gtk2.ui b/frontends/gtk/res/login.gtk2.ui
new file mode 100644
index 000000000..552b173ed
--- /dev/null
+++ b/frontends/gtk/res/login.gtk2.ui
@@ -0,0 +1,223 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkDialog" id="wndLogin">
+ <property name="title" translatable="yes">Site Authentication</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ALWAYS</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="yalign">0.10000000149011612</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table5">
+ <property name="visible">True</property>
+ <property name="border_width">1</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">11</property>
+ <property name="row_spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="labelLoginHost">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">moo.yoo.com</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label57">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label56">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Username</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label54">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Host</property>
+ </object>
+ <packing>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Realm</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="labelLoginRealm">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">my sekr3t area</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryLoginPass">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="activates_default">True</property>
+ <property name="text" translatable="yes">opensesame</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryLoginUser">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="text" translatable="yes">sesame</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="buttonLoginCan">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonLoginOK">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label49">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Login</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">buttonLoginCan</action-widget>
+ <action-widget response="-5">buttonLoginOK</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/login.gtk3.ui b/frontends/gtk/res/login.gtk3.ui
new file mode 100644
index 000000000..552b173ed
--- /dev/null
+++ b/frontends/gtk/res/login.gtk3.ui
@@ -0,0 +1,223 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkDialog" id="wndLogin">
+ <property name="title" translatable="yes">Site Authentication</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ALWAYS</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="yalign">0.10000000149011612</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table5">
+ <property name="visible">True</property>
+ <property name="border_width">1</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">11</property>
+ <property name="row_spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="labelLoginHost">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">moo.yoo.com</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label57">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label56">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Username</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label54">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Host</property>
+ </object>
+ <packing>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Realm</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="labelLoginRealm">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">my sekr3t area</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryLoginPass">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="activates_default">True</property>
+ <property name="text" translatable="yes">opensesame</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryLoginUser">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="text" translatable="yes">sesame</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"/>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="buttonLoginCan">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonLoginOK">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="stock">gtk-ok</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label49">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Login</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">buttonLoginCan</action-widget>
+ <action-widget response="-5">buttonLoginOK</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/maps.html b/frontends/gtk/res/maps.html
new file mode 120000
index 000000000..a32f725fb
--- /dev/null
+++ b/frontends/gtk/res/maps.html
@@ -0,0 +1 @@
+en/maps.html \ No newline at end of file
diff --git a/frontends/gtk/res/menu_cursor.png b/frontends/gtk/res/menu_cursor.png
new file mode 100644
index 000000000..ccbbbd2d4
--- /dev/null
+++ b/frontends/gtk/res/menu_cursor.png
Binary files differ
diff --git a/frontends/gtk/res/menu_cursor.xbm b/frontends/gtk/res/menu_cursor.xbm
new file mode 100644
index 000000000..1257ac1f4
--- /dev/null
+++ b/frontends/gtk/res/menu_cursor.xbm
@@ -0,0 +1,6 @@
+#define menu_cursor_width 16
+#define menu_cursor_height 16
+static char menu_cursor_bits[] = {
+ 0x00, 0x00, 0x80, 0x7F, 0x88, 0x40, 0x9E, 0x5E, 0x88, 0x40, 0x80, 0x56,
+ 0x80, 0x40, 0x80, 0x5A, 0x80, 0x40, 0x80, 0x5E, 0x80, 0x40, 0x80, 0x56,
+ 0x80, 0x40, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00 };
diff --git a/frontends/gtk/res/menu_cursor_mask.xbm b/frontends/gtk/res/menu_cursor_mask.xbm
new file mode 100644
index 000000000..09789d51b
--- /dev/null
+++ b/frontends/gtk/res/menu_cursor_mask.xbm
@@ -0,0 +1,6 @@
+#define menu_cursor_mask_width 16
+#define menu_cursor_mask_height 16
+static char menu_cursor_mask_bits[] = {
+ 0xC0, 0xFF, 0xC8, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xC8, 0xFF,
+ 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF,
+ 0xC0, 0xFF, 0xC0, 0xFF, 0xC0, 0xFF, 0x00, 0x00 };
diff --git a/frontends/gtk/res/menu_cursor_mask.xpm b/frontends/gtk/res/menu_cursor_mask.xpm
new file mode 100644
index 000000000..985d46cc6
--- /dev/null
+++ b/frontends/gtk/res/menu_cursor_mask.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * menu_cursor_mask_xpm[] = {
+"16 16 3 1",
+" c None",
+". c #FFFFFF",
+"+ c #000000",
+" ..........",
+" . .++++++++.",
+"...+. .+......+.",
+".++++..+.++++.+.",
+"...+. .+......+.",
+" . .+.++.+.+.",
+" .+......+.",
+" .+.+.++.+.",
+" .+......+.",
+" .+.++++.+.",
+" .+......+.",
+" .+.++.+.+.",
+" .+......+.",
+" .++++++++.",
+" ..........",
+" "};
diff --git a/frontends/gtk/res/messages.gresource.xml b/frontends/gtk/res/messages.gresource.xml
new file mode 100644
index 000000000..684a10862
--- /dev/null
+++ b/frontends/gtk/res/messages.gresource.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/netsurf">
+ <file>Messages</file>
+ <file>nl/Messages</file>
+ <file>de/Messages</file>
+ <file>fr/Messages</file>
+ <file>it/Messages</file>
+ </gresource>
+</gresources>
diff --git a/frontends/gtk/res/netsurf-16x16.xpm b/frontends/gtk/res/netsurf-16x16.xpm
new file mode 100644
index 000000000..7880f7aae
--- /dev/null
+++ b/frontends/gtk/res/netsurf-16x16.xpm
@@ -0,0 +1,211 @@
+/* XPM */
+static char *netsurf___x__[] = {
+/* columns rows colors chars-per-pixel */
+"16 16 189 2",
+" c #02530B521130",
+". c #02C50E711619",
+"X c #02AF106B198E",
+"o c #02D913641E8A",
+"O c #04D913E91D9D",
+"+ c #042B14C01F86",
+"@ c #124A1BC61F1D",
+"# c #097F17941FC4",
+"$ c #03CE193E2759",
+"% c #04451CED2D68",
+"& c #0BDC1D8727E6",
+"* c #10D2247A2F43",
+"= c #069C27663D09",
+"- c #180C2AED33E3",
+"; c #1AEC2D37353A",
+": c #16F12D9C3927",
+"> c #20AD32C039B7",
+", c #0A2A2E04451D",
+"< c #0C1A306A47D4",
+"1 c #1D6E39A1482F",
+"2 c #060A341B5355",
+"3 c #0B773D945E4D",
+"4 c #20B93C4949C5",
+"5 c #07B03F0A6460",
+"6 c #2332407A4E57",
+"7 c #253040AD4CF8",
+"8 c #33854FAB5A6E",
+"9 c #15494CF97003",
+"0 c #19F855EA7B34",
+"q c #1E7F57237923",
+"w c #21344D986676",
+"e c #422B60856A1E",
+"r c #09DA58208D29",
+"t c #11AC562C82BA",
+"y c #0A6A5CB99496",
+"u c #18A265959712",
+"i c #17B8662E9805",
+"p c #11A66358990E",
+"a c #224A65BB8E98",
+"s c #342C75079947",
+"d c #3487766A9B6F",
+"f c #35B478C99F4B",
+"g c #0BFA6509A06D",
+"h c #0F9175B8BA3A",
+"j c #1AAA7C1DBB21",
+"k c #289374DBA2C1",
+"l c #200E76DFADCA",
+"z c #269D7B1FAF00",
+"x c #32E57D4FA875",
+"c c #299F805AB558",
+"v c #2DF28A1CC027",
+"b c #345A83F3B216",
+"n c #3151843FB58D",
+"m c #59C882138E75",
+"M c #5F5E8AAC9842",
+"N c #51998AF6A4C9",
+"B c #5F5A96AEACF5",
+"V c #43048E07B597",
+"C c #65129940AC1C",
+"Z c #6C1AA2A1B60E",
+"A c #7A87AEC9BD67",
+"S c #125084AAD039",
+"D c #163388A1D450",
+"F c #1BF88E3BD899",
+"G c #17BD8EB6DCB2",
+"H c #220A83D0C214",
+"J c #29678B9EC6ED",
+"K c #3F59A336DDC8",
+"L c #198792ECE247",
+"P c #11B295CDED75",
+"I c #132A98BFF1CB",
+"U c #25099D91EB08",
+"Y c #29299E0AE8BB",
+"T c #1412A137FE21",
+"R c #191EA60AFFFF",
+"E c #1C73A663FFFF",
+"W c #2E68A1C2E7B3",
+"Q c #2C05A14AEAC1",
+"! c #2E32A597F03F",
+"~ c #39BBA7F4E929",
+"^ c #23A0A4D1F872",
+"/ c #25C6A4E4F6A5",
+"( c #2965A4CCF3B1",
+") c #236CA4BAF891",
+"_ c #23A1A6A2FA96",
+"` c #2018A8FDFFFF",
+"' c #25F5AB89FFFF",
+"] c #2B0BACCDFECB",
+"[ c #2AEBAD69FFF4",
+"{ c #2E7EAC78FC88",
+"} c #37FCADAFF4EB",
+"| c #3B7FAF4EF4E9",
+" . c #303DAB56F8E3",
+".. c #3354B0E8FFFF",
+"X. c #3607B294FFCE",
+"o. c #3978B4A2FFF7",
+"O. c #414EA3E7DD4D",
+"+. c #5643A8B7D325",
+"@. c #52B4AADCD9F0",
+"#. c #60F0B145D82C",
+"$. c #6F40B9C2DA92",
+"%. c #71EFBCB5DD40",
+"&. c #482FB26FEEA5",
+"*. c #5E59B526E061",
+"=. c #413CB50EFA06",
+"-. c #40FAB7C7FF64",
+";. c #411FB840FF4A",
+":. c #43C4B9CAFFFA",
+">. c #4AB5BB61FFFF",
+",. c #4A0ABCF3FFFF",
+"<. c #4BB1BD9AFFB4",
+"1. c #4D02BE43FFE0",
+"2. c #542DBA39F2D5",
+"3. c #5682BD17F583",
+"4. c #6545BFABECE0",
+"5. c #7AD1C155DCC5",
+"6. c #512AC23DFFFF",
+"7. c #5584C279FF9A",
+"8. c #5642C44BFFFF",
+"9. c #57E0C1FCF97C",
+"0. c #5C81C618FFB3",
+"q. c #5F65C55DFFFF",
+"w. c #5FA0C7BFFFCB",
+"e. c #6BD0C37FEDBC",
+"r. c #6FB7C6C0EF7B",
+"t. c #7055C5DCEDC7",
+"y. c #7A3ECB76EEA8",
+"u. c #63C4C2A1F626",
+"i. c #64A9C5C0F7C3",
+"p. c #6B3AC4CBF902",
+"a. c #621EC925FFE9",
+"s. c #6557C9F1FE6D",
+"d. c #67BACC2FFFFE",
+"f. c #66B4CD66FFFF",
+"g. c #69E9CD27FFFC",
+"h. c #6E23CD02FFBA",
+"j. c #6E50CFA0FFFF",
+"k. c #7093CC80FFBB",
+"l. c #6E47D0E6FFFF",
+"z. c #7269D1B2FFFF",
+"x. c #73B7D323FFFF",
+"c. c #7383D389FFFF",
+"v. c #7F08D0B1FFA4",
+"b. c #7A4CD5C8FFFF",
+"n. c #7C1BD6A7FFFF",
+"m. c #7CBAD75AFFFF",
+"M. c #8CB9CF6EE5B6",
+"N. c #9678D359E3DC",
+"B. c #8911D5A7F360",
+"V. c #829DD7A7FFFF",
+"C. c #8092DA47FFFF",
+"Z. c #814DDA85FFFF",
+"A. c #839BDA7BFFFF",
+"S. c #85D6DB65FFFF",
+"D. c #86DFDAB4FFFD",
+"F. c #8EE1DDADFC14",
+"G. c #8CC8DC55FFFE",
+"H. c #894DDD3BFFFE",
+"J. c #89CCDDD9FFFF",
+"K. c #8AFCDE12FFFC",
+"L. c #8D41DF48FFF9",
+"P. c #8D5FE05DFFFF",
+"I. c #8FB7E05EFFEA",
+"U. c #9808DD11F202",
+"Y. c #A445E007FF52",
+"T. c #96ADE3E2FFB6",
+"R. c #9705E548FFFF",
+"E. c #9DB5E153FFFD",
+"W. c #9914E507FFB9",
+"Q. c #9A08E660FFF3",
+"!. c #9A40E858FFFF",
+"~. c #A7C7E568F44C",
+"^. c #A7C5E6F4F48B",
+"/. c #A3A6EB6AFFFB",
+"(. c #A652EC53FFFA",
+"). c #A408ED75FFFF",
+"_. c #B2C9E411FFBB",
+"`. c #B047E69AFFFB",
+"'. c #B8E2E8BFFFF5",
+"]. c #D6CBF231FFFF",
+"[. c #DE35F506FFFC",
+"{. c #E029F512FFFF",
+"}. c #E57AF80BFFFE",
+"|. c #E6C5F71DFFE9",
+" X c #E735F794FFF7",
+".X c #E895F7DEFFEE",
+"XX c #E9B0F91EFFFE",
+"oX c gray100",
+"OX c None",
+/* pixels */
+"OXOXOXOXOXOX; 7 6 : OXOXOXOXOXOX",
+"OXOXOXOX> C 5.y.r.*.V w OXOXOXOX",
+"OXOX@ e M ).R.J.m.l.V.u.f & OXOX",
+"OXOXm ^.~.(.W.K.D.`.[.].Y.k OXOX",
+"OX8 U.A N./.T.H.G.}.oXoX{.p.9 OX",
+"OXB !.Z M.Q.I.A.E.XXoXoX|.q.H O ",
+"- %.P.B.F.L.S.n.z.'. X.X_.>.U , ",
+"4 t.C.N $.Z.b.z.g.h.k.v.-...^ 3 ",
+"1 4.c.#.e.x.j.d.w.7.<.;.X.[ F = ",
+"* @.f.i.+.s.a.0.7.1.:.o.{ _ p . ",
+"OXd 9.3.s 2.8.6.,.=.} ! u G h X ",
+"OX# b &.x O.K n | z l / L I 5 OX",
+"OXOXq ~ v a c J .( ) E T y OXOX",
+"OXOXOX0 W Q Y ] ' ` R P r o OXOX",
+"OXOXOXOX< i j t D S g 2 OXOXOXOX",
+"OXOXOXOXOXOX+ % $ OXOXOXOXOXOX"
+};
diff --git a/frontends/gtk/res/netsurf-gtk.desktop b/frontends/gtk/res/netsurf-gtk.desktop
new file mode 100644
index 000000000..4c21d5537
--- /dev/null
+++ b/frontends/gtk/res/netsurf-gtk.desktop
@@ -0,0 +1,68 @@
+[Desktop Entry]
+Name=NetSurf Web Browser
+Name[ca]=Navegador web NetSurf
+Name[cs]=NetSurf Webový prohlížeč
+Name[es]=Navegador web NetSurf
+Name[fa]=مرورگر اینترنتی NetSurf
+Name[fi]=NetSurf-selain
+Name[fr]=Navigateur Web NetSurf
+Name[hu]=NetSurf webböngésző
+Name[it]=NetSurf Browser Web
+Name[ja]=NetSurf ウェブ・ブラウザ
+Name[ko]=NetSurf 웹 브라우저
+Name[nb]=NetSurf Nettleser
+Name[nl]=NetSurf webbrowser
+Name[nn]=NetSurf Nettlesar
+Name[no]=NetSurf Nettleser
+Name[pl]=Przeglądarka WWW NetSurf
+Name[pt]=NetSurf Navegador Web
+Name[pt_BR]=Navegador Web NetSurf
+Name[sk]=Internetový prehliadač NetSurf
+Comment=Browse the World Wide Web
+Comment[ca]=Navegueu per el web
+Comment[cs]=Prohlížení stránek World Wide Webu
+Comment[de]=Im Internet surfen
+Comment[es]=Navegue por la web
+Comment[fa]=صفحات شبکه جهانی اینترنت را مرور نمایید
+Comment[fi]=Selaa Internetin WWW-sivuja
+Comment[fr]=Navigue sur Internet
+Comment[hu]=A világháló böngészése
+Comment[it]=Esplora il web
+Comment[ja]=ウェブを閲覧します
+Comment[ko]=웹을 돌아 다닙니다
+Comment[nb]=Surf på nettet
+Comment[nl]=Verken het internet
+Comment[nn]=Surf på nettet
+Comment[no]=Surf på nettet
+Comment[pl]=Przeglądanie stron WWW
+Comment[pt]=Navegue na Internet
+Comment[pt_BR]=Navegue na Internet
+Comment[sk]=Prehliadanie internetu
+GenericName=Web Browser
+GenericName[ca]=Navegador web
+GenericName[cs]=Webový prohlížeč
+GenericName[es]=Navegador web
+GenericName[fa]=مرورگر اینترنتی
+GenericName[fi]=WWW-selain
+GenericName[fr]=Navigateur Web
+GenericName[hu]=Webböngésző
+GenericName[it]=Browser Web
+GenericName[ja]=ウェブ・ブラウザ
+GenericName[ko]=웹 브라우저
+GenericName[nb]=Nettleser
+GenericName[nl]=Webbrowser
+GenericName[nn]=Nettlesar
+GenericName[no]=Nettleser
+GenericName[pl]=Przeglądarka WWW
+GenericName[pt]=Navegador Web
+GenericName[pt_BR]=Navegador Web
+GenericName[sk]=Internetový prehliadač
+Exec=netsurf-gtk %u
+Terminal=false
+X-MultipleArgs=false
+Type=Application
+Icon=netsurf.png
+Categories=Network;
+MimeType=text/html;text/xml;application/xhtml+xml;application/xml;image/gif;image/jpeg;image/png
+StartupWMClass=NetSurf-bin
+StartupNotify=true
diff --git a/frontends/gtk/res/netsurf.gresource.xml b/frontends/gtk/res/netsurf.gresource.xml
new file mode 100644
index 000000000..c7626b053
--- /dev/null
+++ b/frontends/gtk/res/netsurf.gresource.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/netsurf">
+ <file>cookies.gtk2.ui</file>
+ <file>history.gtk3.ui</file>
+ <file>netsurf.gtk2.ui</file>
+ <file>password.gtk3.ui</file>
+ <file>toolbar.gtk2.ui</file>
+ <file>warning.gtk3.ui</file>
+ <file>cookies.gtk3.ui</file>
+ <file>hotlist.gtk2.ui</file>
+ <file>netsurf.gtk3.ui</file>
+ <file>ssl.gtk2.ui</file>
+ <file>toolbar.gtk3.ui</file>
+ <file>downloads.gtk2.ui</file>
+ <file>hotlist.gtk3.ui</file>
+ <file>options.gtk2.ui</file>
+ <file>ssl.gtk3.ui</file>
+ <file>viewdata.gtk2.ui</file>
+ <file>downloads.gtk3.ui</file>
+ <file>login.gtk2.ui</file>
+ <file>options.gtk3.ui</file>
+ <file>tabcontents.gtk2.ui</file>
+ <file>viewdata.gtk3.ui</file>
+ <file>history.gtk2.ui</file>
+ <file>login.gtk3.ui</file>
+ <file>password.gtk2.ui</file>
+ <file>tabcontents.gtk3.ui</file>
+ <file>warning.gtk2.ui</file>
+ <file preprocess="to-pixdata">favicon.png</file>
+ <file preprocess="to-pixdata">netsurf.xpm</file>
+ <file preprocess="to-pixdata">menu_cursor.png</file>
+ <file preprocess="to-pixdata">throbber/throbber0.png</file>
+ <file preprocess="to-pixdata">throbber/throbber1.png</file>
+ <file preprocess="to-pixdata">throbber/throbber2.png</file>
+ <file preprocess="to-pixdata">throbber/throbber3.png</file>
+ <file preprocess="to-pixdata">throbber/throbber4.png</file>
+ <file preprocess="to-pixdata">throbber/throbber5.png</file>
+ <file preprocess="to-pixdata">throbber/throbber6.png</file>
+ <file preprocess="to-pixdata">throbber/throbber7.png</file>
+ <file preprocess="to-pixdata">throbber/throbber8.png</file>
+ <file>credits.html</file>
+ <file>it/credits.html</file>
+ <file>nl/credits.html</file>
+ <file>licence.html</file>
+ <file>it/licence.html</file>
+ <file>nl/licence.html</file>
+ <file>welcome.html</file>
+ <file>de/welcome.html</file>
+ <file>it/welcome.html</file>
+ <file>ja/welcome.html</file>
+ <file>nl/welcome.html</file>
+ <file>maps.html</file>
+ <file>adblock.css</file>
+ <file>default.css</file>
+ <file>internal.css</file>
+ <file>quirks.css</file>
+ <file>netsurf.png</file>
+ <file>default.ico</file>
+ <file>arrow_down_8x32.png</file>
+ <file>icons/arrow-l.png</file>
+ <file>icons/content.png</file>
+ <file>icons/directory2.png</file>
+ <file>icons/directory.png</file>
+ <file>icons/hotlist-add.png</file>
+ <file>icons/hotlist-rmv.png</file>
+ <file>icons/search.png</file>
+ <file>languages</file>
+ </gresource>
+</gresources>
diff --git a/frontends/gtk/res/netsurf.gtk2.ui b/frontends/gtk/res/netsurf.gtk2.ui
new file mode 100644
index 000000000..68812b364
--- /dev/null
+++ b/frontends/gtk/res/netsurf.gtk2.ui
@@ -0,0 +1,212 @@
+<?xml version="1.0"?>
+<!--Generated with glade3 3.4.5 on Wed Apr 7 17:10:28 2010 -->
+<interface>
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="upper">100</property>
+ <property name="lower">0</property>
+ <property name="page_increment">10</property>
+ <property name="step_increment">26</property>
+ <property name="page_size">10</property>
+ <property name="value">0.5357142857142857</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment2">
+ <property name="upper">100</property>
+ <property name="lower">0</property>
+ <property name="page_increment">10</property>
+ <property name="step_increment">26</property>
+ <property name="page_size">10</property>
+ <property name="value">0</property>
+ </object>
+ <object class="GtkUIManager" id="uimanager1">
+ <ui>
+ <menubar name="menubar"/>
+ </ui>
+ </object>
+ <object class="GtkWindow" id="wndBrowser">
+ <property name="title" translatable="yes">NetSurf</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <child>
+ <object class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuBar" constructor="uimanager1" id="menubar">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="toolbar_style">GTK_TOOLBAR_BOTH_HORIZ</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="searchbar">
+ <property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
+ <child>
+ <object class="GtkToolButton" id="closeSearchButton">
+ <property name="visible">True</property>
+ <property name="stock_id">gtk-close</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="searchLabelItem">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="searchlabel">
+ <property name="visible">True</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Match</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolSearch">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkEntry" id="searchEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="searchBackButton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Search _Back</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-go-back</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="searchForwardButton">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Search _Forward</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-go-forward</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="checkAllSearchItem">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCheckButton" id="checkAllSearch">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip-text" translatable="yes">show all matches</property>
+ <property name="label" translatable="yes">All </property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="caseSensItem">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCheckButton" id="caseSensButton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip-text" translatable="yes">Match case when searching</property>
+ <property name="label" translatable="yes">Case</property>
+ <property name="relief">GTK_RELIEF_NONE</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="scrollable">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/netsurf.gtk3.ui b/frontends/gtk/res/netsurf.gtk3.ui
new file mode 100644
index 000000000..ce47c6370
--- /dev/null
+++ b/frontends/gtk/res/netsurf.gtk3.ui
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="wndBrowser">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="toolbar_style">both</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="searchbar">
+ <property name="can_focus">False</property>
+ <property name="toolbar_style">both</property>
+ <child>
+ <object class="GtkToolButton" id="closeSearchButton">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">gtk-close</property>
+ <property name="stock_id">gtk-close</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="searchLabelItem">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkLabel" id="searchlabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Match</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="toolSearch">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkEntry" id="searchEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="searchBackButton">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">Search _Back</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-go-back</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolButton" id="searchForwardButton">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="label" translatable="yes">Search _Forward</property>
+ <property name="use_underline">True</property>
+ <property name="stock_id">gtk-go-forward</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="checkAllSearchItem">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkCheckButton" id="checkAllSearch">
+ <property name="label" translatable="yes">All</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolItem" id="caseSensItem">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_action_appearance">False</property>
+ <child>
+ <object class="GtkCheckButton" id="caseSensButton">
+ <property name="label" translatable="yes">Case</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="scrollable">True</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/netsurf.png b/frontends/gtk/res/netsurf.png
new file mode 120000
index 000000000..905512c25
--- /dev/null
+++ b/frontends/gtk/res/netsurf.png
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/netsurf.png,b60 \ No newline at end of file
diff --git a/frontends/gtk/res/netsurf.xpm b/frontends/gtk/res/netsurf.xpm
new file mode 100644
index 000000000..7061727d6
--- /dev/null
+++ b/frontends/gtk/res/netsurf.xpm
@@ -0,0 +1,317 @@
+/* XPM */
+static char *netsurf[] = {
+/* columns rows colors chars-per-pixel */
+"132 135 176 2",
+" c black",
+". c #010101",
+"X c #010202",
+"o c #030406",
+"O c #040A0D",
+"+ c #060E14",
+"@ c #08141A",
+"# c #081720",
+"$ c #091B27",
+"% c #0F2430",
+"& c #142E3D",
+"* c #143548",
+"= c #153C53",
+"- c #194662",
+"; c #1E516E",
+": c #1D577A",
+"> c #175B87",
+", c #1A6595",
+"< c #1E6B9C",
+"1 c #26709C",
+"2 c #33789E",
+"3 c #3188BD",
+"4 c #3E89B3",
+"5 c #1F92DD",
+"6 c #0E9CFD",
+"7 c #0E9DFF",
+"8 c #0F9EFF",
+"9 c #1A95E7",
+"0 c #1898ED",
+"q c #1299F4",
+"w c #119BF9",
+"e c #119FFF",
+"r c #14A0FF",
+"t c #15A1FF",
+"y c #17A2FF",
+"u c #18A3FF",
+"i c #1AA3FE",
+"p c #1BA4FE",
+"a c #1CA5FE",
+"s c #1EA5FE",
+"d c #1FA6FE",
+"f c #268AC9",
+"g c #218ED3",
+"h c #20A6FE",
+"j c #21A7FE",
+"k c #23A8FE",
+"l c #24A8FE",
+"z c #25A8FE",
+"x c #26A9FE",
+"c c #27AAFE",
+"v c #28AAFE",
+"b c #2AABFF",
+"n c #2BACFF",
+"m c #2CADFF",
+"M c #2EAEFF",
+"N c #2FAEFF",
+"B c #31AFFE",
+"V c #31AFFF",
+"C c #32B0FF",
+"Z c #34B1FF",
+"A c #35B2FF",
+"S c #36B2FF",
+"D c #37B2FF",
+"F c #3AB2FB",
+"G c #38B3FE",
+"H c #39B3FE",
+"J c #3AB3FC",
+"K c #3BB4FE",
+"L c #3CB5FE",
+"P c #3DB5FE",
+"I c #3EB6FF",
+"U c #3FB7FF",
+"Y c #4182A4",
+"T c #4789AB",
+"R c #43B7FB",
+"E c #40B7FF",
+"W c #44B6FA",
+"Q c #42B8FE",
+"! c #44B9FE",
+"~ c #46BAFF",
+"^ c #47BAFF",
+"/ c #48BBFF",
+"( c #49BCFF",
+") c #4ABCFF",
+"_ c #4CBDFF",
+"` c #4EBEFF",
+"' c #50BFFE",
+"] c #50BFFF",
+"[ c #51BFFF",
+"{ c #5ABFF7",
+"} c #52C0FE",
+"| c #54C1FE",
+" . c #56C2FF",
+".. c #57C3FF",
+"X. c #58C3FE",
+"o. c #58C3FF",
+"O. c #5CC1F8",
+"+. c #5DC4FD",
+"@. c #5CC5FE",
+"#. c #5DC5FE",
+"$. c #5DC6FF",
+"%. c #5EC6FF",
+"&. c #5FC7FF",
+"*. c #61C8FF",
+"=. c #62C8FF",
+"-. c #63C9FF",
+";. c #65CAFF",
+":. c #66CBFF",
+">. c #67CBFF",
+",. c #68CCFF",
+"<. c #69CDFF",
+"1. c #6BCDFF",
+"2. c #6CCEFF",
+"3. c #6DCEFF",
+"4. c #6FCFFF",
+"5. c #70D0FF",
+"6. c #72D1FF",
+"7. c #73D1FF",
+"8. c #74D2FF",
+"9. c #76D3FF",
+"0. c #77D3FF",
+"q. c #78D4FF",
+"w. c #79D5FF",
+"e. c #7AD5FF",
+"r. c #7CD6FF",
+"t. c #7ED7FF",
+"y. c #7FD8FF",
+"u. c #81CBE7",
+"i. c #82CBE7",
+"p. c #83CEEB",
+"a. c #81D4F7",
+"s. c #84D3F4",
+"d. c #80D7FE",
+"f. c #80D8FF",
+"g. c #84DAFF",
+"h. c #85DAFF",
+"j. c #86DBFF",
+"k. c #87DBFF",
+"l. c #89DCFF",
+"z. c #8ADDFF",
+"x. c #8BDEFF",
+"c. c #8DDEFF",
+"v. c #8EDEFF",
+"b. c #8FDFFF",
+"n. c #91E0FF",
+"m. c #92E1FF",
+"M. c #93E2FF",
+"N. c #95E3FF",
+"B. c #97E3FF",
+"V. c #98E4FF",
+"C. c #99E4FE",
+"Z. c #9BE5FE",
+"A. c #9CE6FE",
+"S. c #9EE6FF",
+"D. c #9FE7FF",
+"F. c #A1E8FF",
+"G. c #A3E9FF",
+"H. c #A5EBFF",
+"J. c #A6ECFF",
+"K. c #A8EDFF",
+"L. c #ABEEFF",
+"P. c #ADEFFF",
+"I. c #B0F0FF",
+"U. c #B3F1FF",
+"Y. c #B7F2FF",
+"T. c #C7F3FF",
+"R. c #DCF6FF",
+"E. c #EAF8FF",
+"W. c #F1FAFF",
+"Q. c #F8FCFF",
+"!. c #FCFEFF",
+"~. c #FDFEFF",
+"^. c #FEFEFF",
+"/. c #FEFFFF",
+"(. c gray100",
+"). c None",
+/* pixels */
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). % ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O & ; 2 T O.O.a.r.e.e.e.q.9.9.4.{ { T 2 ; % O ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). @ - T r.l.k.k.k.y.t.t.t.t.r.e.e.q.9.8.8.8.8.4.4.1.1.1.{ T ; # ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). = Y p.v.x.x.s.v.k.k.k.a.g.t.y.t.r.e.e.q.9.8.8.8.2.4.4.4.4.,.,.;.;.;.O.4 = o ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). % 2 u.m.m.v.v.c.x.x.l.k.k.k.k.g.t.y.t.t.r.e.e.q.9.8.8.8.8.4.4.1.1.,.,.;.;.-.-.-.{ 2 & ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O ; u.B.m.m.m.m.m.v.c.c.x.x.x.k.k.k.g.g.g.t.t.r.e.e.q.9.9.8.8.2.2.8.2.1.1.,.,.;.;.-.*.*.$.$.! : + ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O : p.C.C.B.B.B.m.m.m.m.c.c.c.c.x.k.l.k.k.g.y.g.t.t.r.e.e.q.q.8.9.2.2.4.2.1.1.1.,.;.;.-.-.-.&.$.$.+.O.2 @ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).). + 2 s.A.A.C.C.C.B.m.N.m.M.m.m.m.c.l.x.x.l.k.k.g.g.t.f.t.r.r.e.q.9.9.8.8.8.8.8.1.2.,.,.,.;.-.-.&.&.&.$.+... .| 2 @ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).). = p.F.F.A.A.A.C.C.C.C.N.N.m.m.m.m.v.v.c.x.l.l.g.k.g.t.f.f.t.r.e.e.q.9.8.8.8.2.8.2.2.1.,.,.,.;.-.&.-.&.+.+....... .` 1 O ).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).). ; A.F.F.A.A.A.A.C.C.C.N.N.N.m.m.m.v.c.x.x.l.k.k.g.g.f.f.t.r.e.e.q.9.q.8.8.8.2.2.2.1.,.,.;.;.-.-.&.&.$.+.$..... .| | W = ).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).). o T F.F.F.A.F.S.A.A.C.C.C.N.M.M.m.m.c.c.c.x.l.k.k.g.g.f.f.f.t.t.e.e.q.9.9.8.5.5.4.1.1.1.;.,.-.-.-.-.$.*.$.+.o. . . .| | ` T # ).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).). * & p.F.F.F.A.F.F.A.A.A.C.C.N.N.M.M.m.m.c.c.x.x.k.k.k.g.h.f.f.t.r.r.e.q.9.9.8.5.5.4.4.4.1.1.;.,.;.-.-.&.$.$.+. . . . .| | ` ` W ; ).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).). 2 A. % J.J.F.J.F.F.S.F.F.A.A.C.C.C.N.M.m.m.c.c.c.x.x.l.k.k.f.t.t.t.t.r.e.q.0.9.9.8.5.4.4.1.1.,.,.;.-.-.&.-.$.$.$.o.+... .| | ` ) ) ) 2 O ).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).). @ u.J.T ; J.J.J.F.J.F.F.F.A.A.D.C.N.C.N.M.M.m.m.m.x.x.x.l.k.k.k.k.f.f.t.r.r.q.0.9.5.5.5.5.4.4.1.,.,.;.;.-.-.&.$.$.+.o. ... .| | ` | [ ) ( 5 $ ).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).). & p.K.K.: T J.J.J.J.F.G.F.F.F.F.A.A.N.C.C.N.M.m.m.c.c.c.x.x.l.k.g.g.t.t.t.r.r.q.0.0.9.8.5.5.4.3.1.,.,.,.;.-.-.&.h.k.+.$.o. . . .| ` ` ` | ( ( W - ).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).). = F.K.P.K.& p.K.J.J.J.J.F.F.F.A.F.D.A.A.C.C.N.M.M.m.m.c.c.x.l.l.k.k.g.g.g.t.r.r.r.e.0.9.9.5.5.4.4.1.1.,.;.,.-.-.,.Q./.8.o.o.o. .[ ` | | ` ) ) ( ( ( ; ).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).). ; J.K.P.P.F. % K.P.K.J.J.J.G.G.G.F.A.D.A.A.C.C.N.N.M.m.m.m.v.x.x.l.k.g.g.t.y.t.t.r.q.0.0.9.7.9.5.4.4.2.1.,.,.;.;.-.F././.T.$.o.o. . .| ) ` ) ) ) ( ( ! ! , ).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).). : K.K.P.P.P.u. ; P.K.P.K.K.J.J.J.G.F.F.D.D.D.C.C.N.N.M.m.m.c.v.c.x.l.k.k.g.k.y.t.r.r.r.0.0.9.9.5.5.4.4.2.1.,.;.,.;.,.W./././.l.o. . . . .| | ` ) ) ( ( ^ ! Q 1 ).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).). Y K.K.P.P.P.I.Y T P.P.P.K.K.J.J.J.F.G.F.D.D.A.A.N.N.N.N.M.m.m.c.c.c.x.l.k.g.g.y.t.t.t.r.w.q.q.8.7.5.5.4.2.1.,.,.;.;.F././././.E.+. . . .| | ) ` ) ) ) ( / ! ! W 2 ).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).). 2 K.P.P.P.P.P.I.P.u.T ; % i.I.I.P.P.P.K.J.J.J.G.F.D.D.D.A.C.C.N.N.m.m.m.c.c.x.l.l.g.k.g.y.t.t.r.r.w.q.8.8.7.5.5.5.2.1.,.,.;.;.E./././././.P. . . .| | | ) ) ` ( 1.K.0.! E Q 1 ).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).). ; J.K.P.P.P.I.I.U.U.U.U.U.U.U.I.I.P.P.P.K.J.J.J.G.G.D.D.D.A.D.C.C.N.N.m.N.c.c.x.l.l.g.k.g.y.g.t.r.r.r.q.q.8.7.5.5.5.2.2.,.,.;.C./././././././.4.o.o.| | ) | $.C.R././.U.! ! U L > ).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).). * F.J.K.K.P.I.I.U.U.U.U.U.U.U.U.U.I.I.P.K.K.J.J.J.G.G.D.D.D.D.C.N.C.N.m.m.m.c.c.x.x.l.k.k.k.y.t.t.r.r.q.q.8.8.9.5.5.5.2.2.,.,.;.E./././././././.T.o.[ | .v.T./.!././.!.k.! E Q L L - ).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).). & C.J.K.K.P.P.I.I.U.U.U.Y.Y.Y.U.U.U.I.I.P.P.P.K.J.G.J.F.D.D.D.A.C.C.N.N.M.m.m.m.c.x.l.l.k.k.g.y.y.y.r.r.r.M.R.U.C.9.5.5.2.2.1.,.v.!././././././././.k.d.T././././././././.+.! ! E E L G * ).).).).).).).).).).).).).",
+").).).).).).).).).).).).). + p.J.J.K.P.P.P.I.I.U.U.Y.Y.Y.Y.Y.U.U.U.I.P.P.K.K.J.J.J.F.F.D.D.A.D.N.C.N.M.m.m.c.c.x.x.l.z.g.g.y.g.y.r.r.r.L./.!././.R.U.m.2.1.>.R./././././././././././././././././././.W.! ! E E E L L F $ ).).).).).).).).).).).).",
+").).).).).).).).).).).).). T G.J.J.K.K.P.P.I.I.U.U.Y.Y.Y.Y.Y.U.U.I.I.P.P.P.K.J.J.J.G.F.D.D.A.D.C.C.N.M.m.M.c.c.x.x.l.k.k.g.y.t.y.t.r.q.r.!./././././././.R.T././././././././././././././././././././.Y.! ! ! E L L L K g o ).).).).).).).).).).).).",
+").).).).).).).).).).).). ; J.G.J.J.K.K.P.P.I.I.U.U.U.Y.Y.Y.U.U.U.I.I.P.P.P.K.J.G.J.G.F.D.D.D.C.N.C.N.M.m.M.c.c.x.l.l.k.g.g.y.t.y.r.r.r.q.T.!././././././././.!././././././././././././././././././.Q.v.! ! E E E L L K G , ).).).).).).).).).).).",
+").).).).).).).).).).). & A.J.F.J.J.J.J.L.P.P.I.U.U.U.U.U.U.U.U.U.I.I.P.P.P.K.J.J.F.F.F.D.D.A.A.N.C.N.M.m.m.c.c.x.l.l.k.k.h.f.f.f.r.r.r.0.A.^././././././././././././././././././././././././././././.*.! ! E E E L L L G D * ).).).).).).).).).).",
+").).).).).).).).).).). O u.A.F.F.J.J.J.K.K.P.P.I.I.U.U.U.U.U.U.U.I.I.P.P.K.K.J.J.J.F.J.D.D.D.A.A.C.N.N.M.m.m.m.c.x.x.l.k.k.f.f.f.r.r.r.0.0.0.Q./././././././././././././././././././././././././././.W.! ! ! E E L L L P G D B @ ).).).).).).).).).).",
+").).).).).).).).).). Y F.D.F.J.J.J.J.K.L.P.P.P.I.I.I.U.U.U.U.I.I.I.P.P.K.K.J.J.J.G.F.D.D.D.A.C.C.C.N.m.m.m.c.c.x.l.l.k.k.h.f.f.r.r.r.e.0.9.T.^././././././././././././././././././././././././././.T./ ! ! ! E E L L G G D D 1 ).).).).).).).).).",
+").).).).).).).).).). $ N.D.D.F.F.G.J.J.K.K.L.L.P.P.I.I.I.I.I.I.I.I.P.P.P.K.J.J.J.G.G.F.D.D.A.D.C.N.N.M.N.m.m.c.c.x.x.l.k.f.h.f.f.f.r.e.e.e.9.n.^./././././././././././././././././././././././././.Q.K.! ! ! E E L L L G D D B V & ).).).).).).).).).",
+").).).).).).).).). T A.F.D.D.F.F.J.J.J.J.K.L.L.T : T i.J.I.I.P.P.P.K.K.K.J.J.G.G.G.F.D.D.A.C.C.N.N.M.m.m.m.v.v.l.x.k.k.g.g.t.f.f.r.e.e.e.9.9.W./././././././././././././././././././././././././.^.^.R.g.W E E P P P G G D D V g ).).).).).).).).",
+").).).).).).).).). ; C.A.A.D.D.F.F.F.J.J.J.K.K.L.& K.P.P.P.P.K.K.J.J.J.F.G.F.A.D.D.A.C.C.C.N.M.m.m.c.c.x.x.k.k.k.g.f.f.f.r.r.e.0.0.9.8.Y./././././././././././././././././././././././././././././.E.v.W P P G G G D V V N - ).).).).).).).).",
+").).).).).).).). o u.C.C.A.D.D.F.F.F.J.G.J.J.J.K.+ % P.K.K.P.K.K.K.J.J.G.F.F.F.F.D.A.D.C.C.N.M.M.m.m.m.l.x.x.l.k.k.g.f.f.f.r.r.e.9.9.9.8.h./././././././././././././././././././././././././././././././.W.n.^ P G G D D V V b + ).).).).).).).",
+").).).).).).).). ; B.C.C.A.A.D.D.D.F.F.G.J.G.K.A. = K.K.K.K.J.J.K.G.J.G.G.F.F.A.A.A.C.C.N.N.M.m.m.c.v.v.x.l.l.k.k.k.f.f.r.r.r.e.0.9.8.8.B.^./././././././././././././././././././././././././././././././.!.Q.F./ G D V V V N : ).).).).).).).",
+").).).).).).). p.B.N.C.C.D.A.D.D.F.F.G.G.G.J.r. ; J.J.J.J.J.J.J.F.G.G.F.F.S.F.A.C.C.C.N.N.M.m.N.c.x.x.z.z.z.g.g.g.g.t.t.r.r.q.0.0.h.R./././././././././././././././././././././././././././././././././././.Q./.2.A C C V N 0 O ).).).).).).",
+").).).).).).). ; N.N.N.N.C.C.D.D.D.D.F.F.G.G.K.i. 2 J.J.G.J.J.J.G.G.F.F.F.A.A.A.A.C.C.N.N.N.M.m.m.c.c.x.z.z.g.k.g.t.y.y.t.r.r.q.0.Y./././././././././././././././././././././././././././././././././././././.Q.E.&.A C C N N m ; ).).).).).).",
+").).).).).). r.m.B.B.N.C.C.A.A.D.D.D.F.F.G.G.Y X Y J.G.J.J.G.G.G.F.F.S.F.S.A.A.C.C.C.N.M.M.m.M.c.c.x.x.z.k.k.g.g.g.t.t.r.r.q.D.W././././././././././././././././././././././././././././././././././././.^./.G.U A A C C V N m 9 o ).).).).).",
+").).).).).). = n.m.m.m.N.C.C.C.A.A.D.D.A.F.G.G.Y X T G.G.G.F.G.F.F.F.A.F.S.A.A.A.C.C.N.N.M.m.m.c.c.c.x.z.z.k.g.g.g.t.t.t.r.k.R././././././././././././././././././././././././././././././././././././././.T.o.G K A C C V V N m N - ).).).).).",
+").).).).).). X X T n.n.n.n.N.N.C.C.C.A.A.A.F.A.F.A.: u.F.F.F.F.F.A.F.A.A.F.A.A.C.C.N.C.N.M.M.m.m.c.c.x.x.x.k.k.k.g.f.f.t.t.N./.!./././././././././././././././././././././././././././././././././././.Q.W.r.P G G A A D V V V N m b f ).).).).).",
+").).).).). X @ s.n.n.C.n.m.B.N.N.C.C.A.A.Z.F.S.F.; u.F.F.F.F.A.F.F.F.A.A.A.C.C.C.C.N.m.M.m.M.c.c.c.x.l.l.k.k.g.g.f.f.t.r.m.W././././././././././././././././././././././././././././././././././.^.^.G./ L P P G A A D V V N m m b b # ).).).).",
+").).).).). - n.b.M.n.n.m.m.B.m.C.B.C.C.A.A.A.F.= B.A.F.F.A.A.F.A.A.A.C.C.B.C.B.N.M.M.m.m.c.c.c.x.l.l.k.k.g.g.g.f.f.t.r.e.e.J.E././././././././././././././././././././././././././././././././.^.*.E E L L P G A A V V V N m N N b : ).).).).",
+").).).).). e.b.b.s.n.n.m.m.m.B.B.B.C.C.C.C.A.A.* F.F.A.A.A.A.A.A.C.C.B.C.C.m.B.M.M.m.m.c.m.c.x.x.x.l.k.k.g.t.t.f.t.e.e.e.e.9.9.A.R././././././././././././././././././././././././././././././.Q.r.E L L L P G K A A V N N m b b b g ).).).).",
+").).).). % x.l.x.c.c.c.M.m.m.M.M.N.N.C.C.C.C.C.C.D.D.A.A.A.A.A.A.C.A.C.C.C.N.C.N.N.M.M.M.m.m.c.m.l.c.x.x.k.k.k.g.g.g.t.t.t.r.e.e.q.9.8.8.8.m.R./././././././././././././././././././././././././././././.T.Q L L L K K D V V C V N m m b c c % ).).).",
+").).).). 2 l.x.x.x.c.c.c.m.m.m.M.M.N.N.N.C.N.C.N.C.C.C.C.C.C.C.C.C.C.C.C.C.C.N.N.M.m.m.m.m.m.c.v.v.c.l.k.k.k.g.g.y.y.t.t.r.e.e.q.9.9.8.8.4.2.4.l.T././././././././././././././././././././././././././././.) E L J A A D V C V N N m m b c c > ).).).",
+").).).). T k.k.x.x.x.c.c.c.m.m.m.M.M.M.N.N.N.N.C.C.C.C.C.N.C.C.N.C.N.C.N.N.N.M.M.M.m.m.m.c.m.c.c.x.x.l.k.k.k.g.y.g.t.t.r.e.e.e.q.9.8.8.2.8.4.4.1.1.J.!./././././././././././././././././././././././././.!.N.L L L J D A A C V N m m m b c c f ).).).",
+").).).). O a.k.k.l.l.x.x.c.c.c.m.m.m.m.M.N.m.N.N.N.N.N.N.B.B.N.C.N.N.N.N.M.M.M.M.m.m.m.m.c.v.v.l.x.l.l.k.k.g.g.g.y.y.t.t.r.e.e.q.8.8.8.8.8.2.4.1.1.,.T./././././././././././././././././././././././././././.R.L L L A D A C C V N m b b b c c k + ).).",
+").).). * g.k.g.k.l.x.l.x.c.c.c.c.m.m.m.m.m.m.M.M.M.M.M.N.N.N.m.B.M.M.M.M.M.m.m.m.m.c.c.c.x.x.x.x.l.k.k.k.g.g.g.f.t.t.r.r.e.q.9.9.8.8.8.2.4.4.4.1.,.W././././././././././././././././././././././././././././.;.K K A D A V V V N N b m b c c k * ).).",
+").).). 2 k.g.k.k.k.l.l.l.x.x.c.c.m.v.m.m.m.m.m.m.M.M.M.M.M.m.m.m.m.m.m.m.M.m.m.c.m.c.c.c.x.x.x.l.k.k.k.g.k.t.y.t.t.r.r.e.e.q.9.8.8.8.2.8.4.1.1.1.e././././././././././././././././././././././././././././.W.P.K A G D A V V V N m m b c c c k > ).).",
+").).). O.t.g.g.k.k.k.l.l.x.x.x.c.c.v.c.c.m.m.m.m.m.M.M.m.m.m.m.m.m.m.m.m.c.m.c.c.c.c.x.x.l.x.k.k.k.k.g.g.t.f.t.t.t.e.e.e.q.9.9.8.8.4.2.4.4.1.1.;.A.!./././././././././././././././././././.Q././././././././.Q.U K A A A V V V m m m b c c k k g ).).",
+").).). X a.f.t.t.g.g.k.k.k.l.x.l.x.l.v.v.c.c.c.c.m.c.c.M.m.m.m.m.m.c.m.c.c.c.c.c.c.x.x.x.x.l.k.g.k.g.g.g.t.g.t.t.r.r.e.e.q.q.9.8.8.8.4.4.4.1.4.;.1.T././././././././././././././././././././.C.U.W./.W././././.Q.g.K A A V V V N m b b b c c k k k o ).).",
+").).). $ d.f.f.f.f.h.h.k.k.k.k.l.x.z.x.x.x.x.x.c.c.c.c.c.c.c.c.m.c.c.c.c.c.c.x.x.x.x.x.k.l.k.k.k.g.g.g.g.t.y.t.r.e.e.e.q.9.9.8.8.8.8.2.8.1.1.,.,.,.W./././././././././.W.^./././././././.^.T./ ! ! *.C.T./.Q.E.!.T.D A A V V V N b N v v c c k k j $ ).).",
+").). = r.r.f.f.f.f.h.f.k.k.k.k.z.k.z.z.z.x.x.x.x.c.c.c.v.v.v.v.c.c.c.x.x.x.l.x.x.k.k.k.k.k.g.g.k.t.t.y.t.t.r.r.e.q.q.q.9.8.8.8.4.2.8.2.4.1.,.,.9./././././././.E.K.1.8.^./././././././.^.9./ / ! ! ! L Q 8.F.R.R.A A A V V N b N v N v c j k j j * ).",
+").). : r.r.t.t.f.f.f.f.g.g.k.k.k.k.x.k.l.x.x.l.x.x.x.x.x.x.l.x.x.x.x.l.l.x.l.l.z.k.g.k.k.g.g.g.g.t.t.t.r.r.r.r.q.q.9.q.8.8.8.4.2.2.2.2.1.,.,.,.A./././.!.Q.U.w.$. .o. .P./././././././.R./ / ! ! E E E U K K K J D V C C V N b b b v v c j k j d ; ).",
+").). Y e.r.r.t.t.f.f.f.t.g.g.g.k.g.k.k.k.l.l.l.Y ; ; = & s.x.l.l.l.l.l.l.l.k.k.g.k.k.g.g.k.t.t.y.y.t.t.r.r.r.w.q.q.8.8.8.8.8.2.4.8.1.1.,.,.,.;.T././.T.x.&.&.+.+. .o. .+.W././././././.d.! / W ! ! E L U K K A D D C V V V N N b b v c c j k j d , ).",
+").). T e.e.e.e.r.r.t.f.t.g.t.k.g.g.g.k.k.k.k.g.; r.l.l.l.h.k.k.k.k.k.k.k.g.g.g.t.y.y.g.t.t.r.r.r.r.q.q.q.8.8.8.8.4.2.8.4.4.1.,.>.1.;.;.9.A.;.*.&.$.$.$.o.o. . .[ k./././././.E.( / ! ! ! ! L E L L K K A A C C V N N m v v v v j c j d d f ).",
+").). { q.e.e.e.r.r.t.r.t.t.t.g.t.g.g.g.g.g.k.k.; i.h.h.k.k.k.k.k.g.g.g.g.g.g.y.t.g.y.t.t.r.r.w.w.w.q.q.q.8.8.8.4.4.4.4.4.1.1.,.1.>.;.;.*.*.*.*.$.$.+...o. . . . .[ T././././.v./ / ! ! E E E L K K K A A C C V V N m b N v v c z j j d d g ).",
+").). { 9.9.e.e.e.r.r.r.t.t.y.y.y.g.t.t.g.k.g.k.2 T h.h.h.h.f.k.g.g.g.t.t.y.g.t.t.t.t.r.r.r.r.w.q.q.q.8.8.8.8.8.2.4.4.1.1.1.,.,.;.;.;.-.-.*.$.$.+.+.+.o. . . . .[ [ ;./././.Q.) / / ! Q ! L L L U K K A A A C V V m m m v v c c j c j d d 9 ).",
+").). 5.8.9.9.q.e.e.e.r.r.r.t.t.y.y.t.g.t.t.t.t.T Y h.h.f.k.f.f.t.t.y.g.y.y.t.t.t.r.r.r.r.w.q.q.q.8.q.8.8.8.2.2.8.4.4.1.1.,.,.1.;.;.-.-.*.*.*.$.+.+.+.o. . . .[ [ [ [ K.!.!.B./ ! ! ! Q E E E L K A K A A V C V N m m b v c c z j j d d d 0 ).",
+").). 8.8.8.9.8.q.q.q.e.e.r.r.r.r.t.r.f.f.f.f.f.{ ; f.f.f.f.f.f.f.f.t.f.t.t.r.r.r.e.e.e.e.q.q.9.q.8.8.8.8.2.5.5.4.4.1.1.,.,.,.,.-.-.-.-.&.&.$.$.+....... . .| | ) | ) | E.!.[ / / ! Q Q E L L L K A K A A V V V N m b m v c c k k j d d p p ).",
+").). O 8.8.8.8.8.9.9.q.q.e.e.e.e.r.r.r.r.t.t.t.t.u. = f.f.f.t.f.f.t.t.t.t.r.r.r.e.e.e.e.q.q.9.9.8.8.8.8.8.8.8.2.2.1.1.1.,.,.;.;.;.-.-.&.-.&.$.$.$.+. ..... .| | | | ) ) ( ;.-.! ! ! ! Q E L E L L K A A C C V N N m m b b c c c k j d d d i i O ).",
+").). @ 2.4.8.8.8.8.9.9.9.q.q.e.e.e.e.e.r.r.r.r.r.r.O % t.t.t.t.r.r.r.r.r.r.r.e.e.e.e.q.q.9.9.9.8.8.8.8.8.2.2.2.5.2.1.1.,.,.;.,.;.-.-.-.&.&.$.$.$.+...+. . .| ` | ` ` | ) ) ( / / ! ! Q Q E E L L L A K A A C V V N m m m b c c k k j j d p p y + ).",
+").). @ 4.4.4.8.8.8.8.8.8.9.q.q.q.e.e.e.e.e.e.r.r.r.% + r.r.r.r.r.e.r.e.e.e.e.e.q.q.e.9.9.9.8.8.8.8.8.2.8.2.2.8.2.2.1.,.1.;.,.;.-.-.-.&.&.$.$.+.$. ... . . .| | ` ` ) ` ) ) / ( ! ! ! Q E L L L L G K A C C C V V N m b b c c c k k j d d p p i @ ).",
+").). O 4.4.4.2.8.8.8.8.8.8.9.9.9.9.q.q.q.e.e.e.e.e.* r.e.e.e.e.e.e.e.q.q.q.q.9.9.9.9.8.8.9.8.8.8.2.2.8.2.2.2.2.2.,.,.,.;.-.-.-.&.&.&.$.$.+.+..... . . .| | | | ) ) ` ( ( ( ! ! ! W E E E L L P G K A C C V N N m b m b c c k k d d d d p p y O ).",
+").). 4.1.4.8.2.2.8.8.8.8.8.8.8.9.8.9.9.q.q.q.q.e.: Y e.q.e.q.q.e.e.q.q.9.9.9.8.8.8.8.8.8.2.4.2.2.8.2.4.2.2.:.:.,.,.,.;.-.-.-.-.&.&.$.+.+....... . . .| [ ) | [ ) ) ( ( / / ! Q Q E L L L P G G A C C C V N m m b b c c c k k k d d p p y y ).",
+").). O.1.4.4.4.2.8.2.8.8.8.8.8.8.8.8.9.9.9.9.9.9.T O * : Y O.e.9.9.9.9.9.9.9.9.8.8.8.9.8.8.8.4.2.8.2.8.2.2.4.1.:.2.:.:.;.,.-.-.-.-.&.&.&.$.+.+......... .| | | ) | | ) ` ) ( ( ! ! ! Q E L L L P G G G A A C V V N m b m b c c k k k d d d p p p q ).",
+").). { 1.1.1.4.2.8.2.8.2.2.4.8.8.8.8.8.8.8.8.8.9.9.9.9.e.q.q.q.9.9.9.8.8.8.8.8.8.8.8.8.8.8.2.4.4.8.2.2.2.1.1.1.2.:.:.;.-.;.-.-.-.&.&.$.$.$.+...+... ... .| ) [ | ) ) ) ) ( ( ! ! ! ! Q E E P P G G D D C C C V N m m m b c c c k k d d d p p y 9 9 ).",
+").). { ,.,.1.1.2.2.2.2.2.8.2.8.2.8.8.2.9.8.8.8.8.8.8.8.9.9.9.6.9.6.6.9.8.8.8.8.2.8.8.2.4.4.8.2.2.2.2.2.2.,.,.,.,.;.;.;.;.;.;.&.&.&.$.$.$.$...........| | | ` | ) ) ) ) ( ^ ^ ! Q Q Q Q L E L L K A A V V V V V N m b b v c c c j j d d d i p w $ % ).",
+").). T ,.,.,.1.1.2.2.2.4.4.4.8.2.8.2.8.2.8.8.8.8.2.8.8.7.7.6.9.7.7.6.6.8.8.4.4.8.2.2.8.4.4.2.2.2.2.4.1.,.,.,.,.;.;.;.;.-.;.&.&.&.$.$.+.+...........| | ` | ` ` ` ` ( ( ( ( ^ ! ! Q Q L L L L L K A A D V V V V m m m b v c z j j j d d p p y = ).",
+").). Y ,.;.,.,.,.1.1.2.1.1.4.2.4.4.2.8.8.2.2.8.2.2.8.2.7.7.3.3.3.7.3.6.8.2.8.2.4.4.4.2.1.1.4.1.1.1.,.,.,.,.;.;.;.;.&.;.&.&.&.&.&.$.+.+......... .| | | | ` ` ` ) ( ( ( ^ ^ ! ! Q Q Q L L L G G K A V D V V V N N b b b c c j z j j d d p i - ).",
+").). : -.,.;.,.,.,.,.,.1.1.1.1.1.1.4.4.2.8.2.2.8.4.4.4.3.3.3.7.3.3.7.3.2.8.2.2.4.1.1.1.1.1.1.,.,.,.,.,.;.,.;.;.;.;.;.&.-.&.&.$.$.$.+. .......| | | | ` ` | ` ( ` ( ( ( ^ Q Q Q Q U U L L L G G A A A V V V N b b b b c c c c j j d d p p : ).",
+").). = -.;.;.,.,.,.,.,.,.,.,.,.1.1.4.1.2.2.2.2.2.8.2.2.4.4.4.4.7.3.6.3.2.2.2.2.2.1.1.1.,.,.,.,.,.;.,.-.;.;.-.;.;.&.&.&.&.&.$.$.$.+...+... . .| | | ` ` ` ` ` ` ( ( ^ ^ ! ! Q Q Q L U L P P D D D V V V V N m m b b c c z z j j d d d p - ).",
+").).). % -.-.-.;.-.-.,.,.,.,.,.,.,.,.1.1.2.2.2.2.2.2.2.2.2.2.2.1.1.1.3.1.2.1.1.1.1.,.,.,.,.,.,.;.;.,.;.;.-.;.-.&.&.;.&.&.$.$.$.+......... . .| | | ` ` | ` _ _ ( ( ^ ^ ^ ! Q Q Q L U L L P G D D D V V V V N m m m b c c z j c j d d i = ).).",
+").).). o $.&.-.-.-.-.;.;.,.-.,.,.,.,.,.,.,.,.2.:.2.,.:.2.1.1.1.1.,.1.1.,.,.,.,.,.,.,.,.,.,.-.,.,.;.-.-.-.;.&.-.&.&.&.$.$.$.+.+......... . .| | ` ` | ) ` _ _ ( ( ( ^ ^ Q Q Q U U U L K K G G D D V V V V N m m b b c c c z j j d d 0 & O o ).).",
+").).). g -.&.-.-.-.-.-.;.;.-.,.-.,.;.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.;.,.,.-.,.,.;.;.-.-.-.-.-.-.&.&.&.&.$.$.$.$.$....... . . .| | | ` | ) | ) ` ( ( ^ ^ ^ ! Q Q Q U L L K K K G D D B V V V V m m m b b c c k j j d d d - O , ).).",
+").).). 2 $.*.*.*.*.*.;.;.-.-.;.;.;.;.;.,.;.;.,.,.;.,.,.,.,.,.;.;.1.;.;.,.;.;.,.;.;.;.;.;.;.&.;.&.&.&.&.&.&.&.$.$.+.+.$........... .| | | [ [ | ) ) ) ) ) ) / / / ! ! E Q U U U K K K A A A C C V V N N N n N v c c c k c j d d p 0 $ + g , ).).",
+").).). * $.$.$.$.*.*.*.-.&.-.-.-.-.;.;.;.;.;.;.;.;.;.;.,.;.T : # 2 ;.;.;.;.;.;.-.-.-.-.&.;.&.;.&.;.&.&.&.&.$.+.+.+.| ........| .| | | | ) ) [ [ ) ( ( ) ( ! / ! ! E ! U U K K K K K A A A C C V N N m v n v v c c j k j d d p p i g O # 5 e = ).).",
+").).). + $.$.$.$.$.$.*.&.-.-.&.&.;.-.;.;.;.;.&.;.;.;.;.O.O $ -.-.-.-.-.-.-.-.-.&.&.;.&.&.&.&.&.$.$.$.+.$...+...........| | ` ` ` | | ) [ ) ) ) ( ( / ! ! ! Q ! E U L L L K A K A A V C V V N N b N v v v c j k j j d d p i y y < * 0 y e O ).).",
+").).).). T $.$.$.$.$.$.&.&.&.&.&.-.&.&.&.&.&.;.&.;.;.;.-.$ T -.-.-.-.&.&.-.&.-.&.&.&.$.$.$.$.$.+.+........... ...| | | | | ` | ) | ) ) ) ) ( ( / ! ! ! Q Q E L U L L K A K A A A C V V N m b N N v c c c j k j d d d i i y y y y e 8 g ).).).",
+").).).). > o.$.$.+.$.$.$.$.$.$.&.&.&.&.&.;.&.&.;.&.&.&.-.2 - -.&.-.&.&.&.&.&.$.$.$.$.$.$.+.+.+.| ............ .| | ) | ` | ` ) [ ) ) ) ( / ( ! ! ! ! Q Q Q L L L L K K K A A C V C V V N b b b v v c c k k j j d p p i y y y y y e e > ).).).",
+").).).). & o.o.o.o.$.+.+.$.$.$.$.$.$.&.$.&.$.$.&.&.&.&.$.{ o O.&.&.&.$.$.$.$.$.$.+.$.+.+.............| ....| | | | | | | ) | ) ) ) ) ) / ( ! ! ! Q Q Q E L E L L K A J A A A C C V N N b N b b v c z > 5 j d d p d p y y y y e e e 8 & ).).).",
+").).).).). W .o.o. .o...+.$.+.+.+.+.$.$.$.$.$.$.$.&.$.$.$.= 2 &.$.$.$.$.+.$.+.+.$.+.............| ....| | | | ) | | ` ) | ( ) ) ) / ( / ! ! ! Q Q Q L L L L L K K K A A C C C V V N m N b v c c , @ = j d d d i i i y y e y e e 5 ).).).",
+").).).).). : . . .o. ...........$.+.+.$.$.+.$.$.$.+.$.$.$.4 % $.+.+.+.+.$................. . .....| | ` | ` ` | ) ) | ) ` ) ( ( / / ! / ! ! Q Q Q Q L E L L L K A A A A C C V V N m m v v c > + g d d i i i y y y e e e 8 > ).).).).",
+").).).).). # | . . . . . .....+. ......... .$.+.$.+.$.+.+.+.@ 4 ....+...............| ...... .| | | | ` } } ` ) | ) ) ) ) ) / / / / ! ! ! Q Q Q L Q L K K K A K D V C C C V V N m m m 5 ; + * d p i y i i y y e e 8 8 $ ).).).).",
+").).).).).). 4 .| | . . . . .........+. .+............... .: * ..............| ......| | | | | | ` ` } ` ` _ ) ) ) ) ( ( ( / / ! ! ! Q Q Q E L L L L K K A K D D A V C V V N m m 3 = $ 0 p i i y e e e e e 8 g ).).).).).",
+").).).).).). - | | | | | | . . . . . .| .......... . ..... .W o 4 .. . . . . ...| | | | | ` ` ` ` ` } ` ` _ _ ) ) ) ) / / ( ! ! ! ! E W E E L L L L L L K A D A A V C N V N m m b % @ , i p i y y i y e e 8 8 8 - ).).).).).",
+").).).).).). W | ) [ [ | | | | | | . . . . . . . .| ... . .; O : R . . .| | | | | ` | ` | ` | | ` ` ` _ _ _ _ ) ( ( ( / / ! ! ! ! E ! E L E E L L L G A D D D A C C V N N m m b m , $ < d d p i y y y e i e e 8 7 9 O ).).).).).",
+").).).).).).). + 4 ) | | [ [ | | | | | | | | | | | | | | | | | W + : W | .| | | | | | | | ` | | ` ` _ ` ` _ _ _ ( _ ( ( ( ( ! / ! ! ! E ! E E L L L L L L G G G D D V V V V V N m m m b b 0 & < d d d p i y y y y e y e e 8 7 : ).).).).).).",
+").).).).).).). % 2 ( [ ) | ) | ) | | ` | | | | [ | | ` | | .) | | | | ) | ) | | ) ` ` ` ` ` ` ` _ _ _ _ _ _ _ ^ ^ ( / ! ! ! ! Q Q E E E E E L L G P G G G D B 1 3 C V V N m m b m b c c * o - g k d d d p p y y y y e e e 8 8 8 q O ).).).).).).",
+").).).).).).).). + ; 4 | | ) | ` ` ` ) | ) | [ | ) | ` | | [ | ) ) | | | ) | ) | ` ` ` _ _ _ _ _ ( _ ^ _ ^ ^ ^ ^ ! ! ! ! Q Q Q Q E E L L L L P G G G 3 : % * V V N m m m b b b c c 5 0 k k d d d p p i y y y y e e e 8 8 8 > ).).).).).).).",
+").).).).).).).). $ : W ` ` ` | ` ` | | ) ` | ` | ) | ) [ | | ) ) | ) | ) ) _ _ _ _ _ _ _ ( ( ( ^ ^ ^ ^ ^ ! ! ! ! Q Q E E L E L L L P 5 1 ; & o + N N m m m b b c c c k k k k d d d p p i i y i y e e e 8 8 8 w + ).).).).).).).",
+").).).).).).).).). o W ) ) [ ) [ [ ) ) | ) [ ) | [ ) | | ) | [ ) ) ) ) [ ) ) ) ( ( ) ( / / ^ ( ^ ^ ^ ! ^ Q Q Q Q Q Q L L U L K U K , f b N v n v c c c k k k k d d d d i i i y y y e e e e e 8 8 : ).).).).).).).",
+").).).).).).).).). * ( ) ) [ ) ) ) ) [ ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ( ( ) ) ) ( / / / / / ^ ^ ^ Q ! ! Q Q Q G U L L U L L L K K A f > N v N v c c c k k k k d d d p i i y y y y y e e e 8 7 8 5 o ).).).).).).).).",
+").).).).).).).).).). O 1 ( ( ( ( ) ) ) ) ) ) W $ % - ; 1 1 2 f f f W ( ( / / ( / / / ! / 5 f 1 1 > - - % % # : L Q L L K K K K K K V X # - < v b v n v v z z k k d d d d p p i y y y e y e e e 8 7 7 8 * ).).).).).).).).",
+").).).).).).).).).). > : % F / / / / / / / ( ) ) g P / / / ( / / ! / ! ! - - L L L L K J K A A A A % X # = < 5 N N b N b v c c c c j k k d d d p p i i y y i y e e e 8 7 7 7 < ).).).).).).).).).",
+").).).).).).).).).).). O G ! F ! / ! / ! / / ( ( ( ( 3 W / / ! ! ! ! ! ! ! ! ; % L L L K A A A D V A C < > 3 v V N b b N b b v v c c c z j z k d d d p p i y y y e e e e 8 8 7 7 8 q # ).).).).).).).).).).",
+").).).).).).).).).).). & ! ! ! ! ! ! ! ! / ! / ! ! , W ! 2 # 3 ! ! Q Q ! E ; + L G A K K D A D A V C V V V N N b N N b b b v c c c j z j j d d d p p i y y i y e y e e 8 7 7 7 8 - ).).).).).).).).).).",
+").).).).).).).).).).).). : ! Q Q Q ! ! ! ! ! ! ! ! 2 % @ g & @ G ! Q E E E ; % % - F G A A A A A C V V V V V N m N N b N v v c c c j j j j j d d d p p p y y y y e e e e 8 7 7 7 7 , ).).).).).).).).).).).",
+").).).).).).).).).).).).). 3 E Q Q Q Q ! Q Q Q Q Q ! Q Q Q G f 2 1 1 : - - O = E Q L L E 2 : 1 1 f f 9 L J A G G D D A A A C V V V V V N N m m b b b v v c c z z j c j j d j d p p p y y y y e y e e 8 8 7 7 7 g O ).).).).).).).).).).).",
+").).).).).).).).).).).).). + F E L E L Q E E E E ! Q Q Q Q Q Q Q Q Q U Q U 1 1 L L L L L L L K K K K A K K D D A D D V V V V V V N N m m m b m b b b c c c c j j j j d d p p p i y y y e e i e e e 7 7 7 7 w $ ).).).).).).).).).).).).",
+").).).).).).).).).).).).).). & F E E E L L E E L E L U L L Q L L L L L L U U * + G L L L L L K K K D K A A A V D A V V V V V V N N b m m m b b b c c c c k k j j j j j p d i p y y y y y y e e e 7 7 7 7 7 7 - ).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).). * G L L L E L E L L E L U U L Q L L Q U L L L J + * L L K K A A K D D A A A C D V V V V V V V N b N b b m b b b c c c c k k k d j d d p p p i y y y y y e e e e 8 7 7 8 8 8 > ).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).). - L L L L L L L L L L L L K L L L L L L K U K 1 < A K A K A A V D A A C C V V V V V N N N N b b N b b b b c c c k k k k d d d d d d p i i y y y y e y e e 8 7 7 7 8 8 , ).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).). : K K K K L G L P K K K K K K K L L K K K K K = o g D A A A D A A V V V C V V N N N m m m N b N v v v c c c c k k k d k d d d p p i i y i y y y e y e 8 8 7 7 7 7 7 < ).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).). , G G G G G G G K K K D K A K A K D A D A K N @ $ V V D V V V V A V V V V N N m m m b m v N v v v v z c j k k k k d d d d d p p y i y i y e e y e e 8 7 7 7 8 8 f o ).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).). : G D D D G D A A K A D D A K A K D D A A A 3 > D V V V V V V V V N N N m N m m b m b v v v v c c c j j c j j d d d d p p i y y y i e e e e e 8 8 7 7 7 7 7 < o ).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).). - V D V D D A A A A V D A V D V D V A C A C : + 3 V V V V V V V V N N N b m m b m b b b c v c c z c j j j j j d d d d p i p p y y y y i e y 8 e e 8 8 7 7 7 7 , ).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).). = B D V D A C A A A C A V C C C C C C C C V ; 5 N V N V V N N N b b N N v m b m b v c c c c z c z j j j j d d d d d i p p y y y y i y e e e 8 8 7 7 7 8 8 8 > ).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).). & N V V C V V C C V C V C C C V V V V V V V V N N N N N m N b N b b b N b b b c c c c c z j j j c j j d d d d i p p y p y y y y e e e e e 8 7 7 7 7 7 8 - ).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).). + 3 V V V V V V V V V V V V V N V N N N N N m b N N b m b b b N b v v c c c c c c k k j c j j j j d d d p p i i y y y y y e y e e e 8 8 8 7 7 7 7 9 % ).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).). > N N N N V N N N N N N N N m m m m m m m m b b b m b b b b c c c c k c c k k k k j j j j d d d p p p i i i y y y y e y e e e 8 8 7 7 7 7 7 < + ).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).). * 0 N m b N N b m m m m m m N m m b m b b N v b b b c c c c c c c k k k k k d k d d d d p j p p i y y y y i e i e y e e e 8 7 7 7 7 7 7 : ).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).). O , m b b b N b m m b m b b b b m b b v v v v c c c c c c j k k k k k k d d d d d d d p p p i i y y y e i e e e 8 e 8 8 7 7 7 7 7 f $ ).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).). % g b b b m b m b b b b b b c c c c c c c c c c k g k k k k k d k d d d d p d i p i i i i y y y i e y e e e 8 8 7 7 7 7 7 q - ).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).). X ; c v b b c c c b c c c c c c c c c k k k d = o f j k k d d d d d d p i i i i i y y y y i e e e e e e e 8 8 8 7 7 7 , + ).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).). O : 0 c c c c c c c c c c k k k k k k 0 & + 5 d d d p d d d p i i y y y y y y e y e e e e e 8 8 7 7 8 8 8 , $ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). O ; 0 c z j j c k k k k k k k k d @ # 9 d d p p i p i y i y y y y y e y y e e e 8 e 7 7 7 8 7 , $ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). o - f j k k k k d d j k d d d : % 0 i p p y y i y y y y y y e y e e e e 8 8 7 7 7 9 > # ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). $ > 9 k d d k d d d d d d = % w y p y y y y y e e y e y e e e 8 8 8 7 w < * ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). & > g d d d d p d p i & * w y y y y y y e y e e e e 8 8 8 0 < - O ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). + = , 5 i p p i 6 % * w y e e e e e 8 e e 8 q < : % ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). % = : < f # = y e y g f < , - & @ ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). o ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).). ).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
+").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).)."
+};
diff --git a/frontends/gtk/res/nl/credits.html b/frontends/gtk/res/nl/credits.html
new file mode 120000
index 000000000..9c983987a
--- /dev/null
+++ b/frontends/gtk/res/nl/credits.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/nl/credits.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/nl/licence.html b/frontends/gtk/res/nl/licence.html
new file mode 120000
index 000000000..8a10d2073
--- /dev/null
+++ b/frontends/gtk/res/nl/licence.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/nl/licence.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/nl/welcome.html b/frontends/gtk/res/nl/welcome.html
new file mode 120000
index 000000000..6b2ba7742
--- /dev/null
+++ b/frontends/gtk/res/nl/welcome.html
@@ -0,0 +1 @@
+../../../../!NetSurf/Resources/nl/welcome.html,faf \ No newline at end of file
diff --git a/frontends/gtk/res/options.gtk2.ui b/frontends/gtk/res/options.gtk2.ui
new file mode 100644
index 000000000..d5542ba5d
--- /dev/null
+++ b/frontends/gtk/res/options.gtk2.ui
@@ -0,0 +1,3004 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="dialogPreferences">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">preferencesTitle</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <signal name="destroy" handler="nsgtk_preferences_dialogPreferences_destroy" swapped="no"/>
+ <signal name="delete-event" handler="nsgtk_preferences_dialogPreferences_deleteevent" swapped="no"/>
+ <signal name="response" handler="nsgtk_preferences_dialogPreferences_response" swapped="no"/>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="vbox_dialog">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">edge</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox_main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_main_startup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_startup_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesStartupPage</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryHomePageURL">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryHomePageURL_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryHomePageURL_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="setDefaultPage">
+ <property name="label" translatable="yes">preferencesStartupPageDefault</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="nsgtk_preferences_setDefaultPage_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="setCurrentPage">
+ <property name="label" translatable="yes">preferencesStartupPageCurrent</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="nsgtk_preferences_setCurrentPage_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label_main_startup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesStartup</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_main_search">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkUrlSearch">
+ <property name="label" translatable="yes">preferencesSearchURLBar</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkUrlSearch_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkUrlSearch_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesSearchProvider</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboSearch">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_search_provider</property>
+ <signal name="changed" handler="nsgtk_preferences_comboSearch_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboSearch_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesSearch</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_main_downloads">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkClearDownloads">
+ <property name="label" translatable="yes">preferencesDownloadsRemove</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkClearDownloads_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkClearDownloads_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkRequestOverwrite">
+ <property name="label" translatable="yes">preferencesDownloadsConfirm</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkRequestOverwrite_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkRequestOverwrite_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDownloadsLocation</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserButton" id="fileChooserDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="action">select-folder</property>
+ <signal name="selection-changed" handler="nsgtk_preferences_fileChooserDownloads_selectionchanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_fileChooserDownloads_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDownloads</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMainTabtitle</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_appearance">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_tabs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkShowSingleTab">
+ <property name="label" translatable="yes">preferencesTabsAlways</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkShowSingleTab_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkShowSingleTab_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkFocusNew">
+ <property name="label" translatable="yes">preferencesTabsSwitch</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkFocusNew_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkFocusNew_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkNewBlank">
+ <property name="label" translatable="yes">preferencesTabsNewly</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkNewBlank_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkNewBlank_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTabsPosition</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboTabPosition">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_tab_position</property>
+ <signal name="changed" handler="nsgtk_preferences_comboTabPosition_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboTabPosition_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTabs</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_tools">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDeveloperView</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxDeveloperView">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_developer_view</property>
+ <signal name="changed" handler="nsgtk_preferences_comboDeveloperView_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboDeveloperView_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext10"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTools</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_url">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkDisplayRecentURLs">
+ <property name="label" translatable="yes">preferencesURLbarDisplay</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisplayRecentURLs_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisplayRecentURLs_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesURLbar</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">7</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesToolbarButtons</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboButtonType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_toolbar_buttontype</property>
+ <signal name="changed" handler="nsgtk_preferences_comboButtonType_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboButtonType_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesToolbar</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearanceTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_content">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_content_control">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkDisablePopups">
+ <property name="label" translatable="yes">preferencesControlPrevent</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisablePopups_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisablePopups_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkHideAdverts">
+ <property name="label" translatable="yes">preferencesControlHide</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkHideAdverts_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkHideAdverts_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkEnableJavascript">
+ <property name="label" translatable="yes">preferencesControlEnable</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkEnableJavascript_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkEnableJavascript_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkDisablePlugins">
+ <property name="label" translatable="yes">preferencesControlDisable</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisablePlugins_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisablePlugins_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkResampleImages">
+ <property name="label" translatable="yes">preferencesControlHigh</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkResampleImages_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkResampleImages_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesControlLoad</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxLoadImages">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_image_loading</property>
+ <signal name="changed" handler="nsgtk_preferences_comboboxLoadImages_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboboxLoadImages_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext4"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesControl</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_animation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkEnableAnimations">
+ <property name="label" translatable="yes">preferencesAnimationEnable</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkEnableAnimations_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkEnableAnimations_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAnimationMinimum</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinAnimationSpeed">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesAnimationMinimumTooltip</property>
+ <property name="invisible_char">●</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_animation_time</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinAnimationSpeed_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinAnimationSpeed_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAnimation</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_fonts">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label26">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFontsDefault</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboDefault">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_defaultfont</property>
+ <signal name="changed" handler="nsgtk_preferences_comboDefault_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboDefault_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext5"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label27">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFontsSize</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDefaultSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFontsSizeTooltip</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">4</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_font_default_size</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDefaultSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDefaultSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="fontPreview">
+ <property name="label" translatable="yes">preferencesFontsPreview</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="image">image1</property>
+ <property name="use_underline">True</property>
+ <signal name="clicked" handler="nsgtk_preferences_fontPreview_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFonts</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_font">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label21">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesLanguagePreferred</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxLanguage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesLanguagePreferredTooltip</property>
+ <property name="model">liststore_content_language</property>
+ <signal name="changed" handler="nsgtk_preferences_comboboxLanguage_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboboxLanguage_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext7">
+ <property name="xalign">0</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext8">
+ <property name="xalign">1</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label25">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesLanguage</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesContentTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_privacy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_general">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkSendReferer">
+ <property name="label" translatable="yes">preferencesGeneralReferral</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSendReferer_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSendReferer_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkSendDNT">
+ <property name="label" translatable="yes">preferencesGeneralDNT</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSendDNT_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSendDNT_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesGeneral</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_history">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkHoverURLs">
+ <property name="label" translatable="yes">preferencesHistoryShow</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkHoverURLs_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkHoverURLs_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label28">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistoryRemember</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinHistoryAge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">4</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_history_age</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinHistoryAge_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinHistoryAge_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistoryDays</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistory</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_cache">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheMemory</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label31">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheDisc</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label34">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheExpire</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMemoryCacheSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_cache_memory_size</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMemoryCacheSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMemoryCacheSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDiscCacheSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_cache_disc_size</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDiscCacheSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDiscCacheSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDiscCacheAge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">3</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_disc_cache_age</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDiscCacheAge_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDiscCacheAge_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label">MB</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label">MB</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label35">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesHistoryDays</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonCacheMaintenance">
+ <property name="label" translatable="yes">preferencesCacheMaintenance</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label24">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesCache</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label22">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesPrivacyTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_network">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_network_proxy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label42">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyType</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label43">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyHost</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label44">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyUsername</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label45">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyPassword</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboProxyType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyTypeTooltip</property>
+ <property name="model">liststore_proxy_type</property>
+ <signal name="changed" handler="nsgtk_preferences_comboProxyType_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboProxyType_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext6"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="entryProxyHost">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyHostTooltip</property>
+ <property name="invisible_char">●</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyHost_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyHost_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label46">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label">:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinProxyPort">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyPortTooltip</property>
+ <property name="max_length">5</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_proxy_port</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinProxyPort_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinProxyPort_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyUser">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyUsernameTooltip</property>
+ <property name="invisible_char">●</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyUser_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyUser_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyPasswordTooltip</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">●</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyPassword_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyPassword_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyNoproxy</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyNoproxy">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyNoproxyTooltip</property>
+ <property name="caps_lock_warning">False</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyNoproxy_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyNoproxy_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesProxy</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_network_fetching">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label39">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingMax</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label40">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingPerhost</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label41">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingCached</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMaxFetchers">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingMaxTooltip</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">3</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_fetching_max</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMaxFetchers_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMaxFetchers_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinFetchesPerHost">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingPerhostTooltip</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">3</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_fetching_perhost</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinFetchesPerHost_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinFetchesPerHost_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinCachedConnections">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingCachedTooltip.</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">3</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_fetching_cached</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinCachedConnections_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinCachedConnections_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label38">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFetching</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesNetworkTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_pdfexport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_appearance">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkSuppressImages">
+ <property name="label" translatable="yes">preferencesAppearanceImages</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSuppressImages_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSuppressImages_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkRemoveBackgrounds">
+ <property name="label" translatable="yes">preferencesAppearanceBackground</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkRemoveBackgrounds_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkRemoveBackgrounds_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkFitPage">
+ <property name="label" translatable="yes">preferencesAppearanceScalefit</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkFitPage_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkFitPage_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label59">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearanceScale</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinExportScale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">4</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_pdf_scale</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinExportScale_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinExportScale_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label60">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label">%</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label48">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearance</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_margins">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox22">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label62">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMarginsMeasurements</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label51">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Top</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginTop">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginTop_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginTop_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label52">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Left</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginLeft">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="adjustment">adjustment_pdf_lmargin</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginLeft_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginLeft_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label53">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Bottom</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginBottom">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginBottom_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginBottom_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox21">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label54">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Right</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginRight">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="secondary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">True</property>
+ <property name="secondary_icon_sensitive">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginRight_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginRight_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label49">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMargins</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_generation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">7</property>
+ <child>
+ <object class="GtkCheckButton" id="checkCompressPDF">
+ <property name="label" translatable="yes">preferencesGenerationCompressed</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkCompressPDF_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkCompressPDF_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkPasswordPDF">
+ <property name="label" translatable="yes">preferencesGenerationPassword</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkPasswordPDF_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkPasswordPDF_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label50">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesGeneration</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label47">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesPDFTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">help</action-widget>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkListStore" id="liststore_search_provider">
+ <columns>
+ <!-- column-name Provider -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">Google</col>
+ </row>
+ <row>
+ <col id="0">Yahoo!</col>
+ </row>
+ <row>
+ <col id="0">Microsoft Live</col>
+ </row>
+ <row>
+ <col id="0">Buisiness.com</col>
+ </row>
+ <row>
+ <col id="0">Omgili</col>
+ </row>
+ <row>
+ <col id="0">BBC News</col>
+ </row>
+ <row>
+ <col id="0">Ubuntu Packages</col>
+ </row>
+ <row>
+ <col id="0">Creative Commons</col>
+ </row>
+ <row>
+ <col id="0">Ask</col>
+ </row>
+ <row>
+ <col id="0">Answers</col>
+ </row>
+ <row>
+ <col id="0">Dictionary.com</col>
+ </row>
+ <row>
+ <col id="0">YouTube</col>
+ </row>
+ <row>
+ <col id="0">AeroMP3</col>
+ </row>
+ <row>
+ <col id="0">AOL</col>
+ </row>
+ <row>
+ <col id="0">Baidu</col>
+ </row>
+ <row>
+ <col id="0">Amazon</col>
+ </row>
+ <row>
+ <col id="0">Ebay</col>
+ </row>
+ <row>
+ <col id="0">IMBD</col>
+ </row>
+ <row>
+ <col id="0">ESPN</col>
+ </row>
+ <row>
+ <col id="0">Wikipedia</col>
+ </row>
+ <row>
+ <col id="0">DuckDuckGo</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_tab_position">
+ <columns>
+ <!-- column-name Position -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocTop</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocLeft</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocRight</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocBottom</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_toolbar_buttontype">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeSmall</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeLarge</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeLargeText</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeText</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_image_loading">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadBoth</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadFore</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadBack</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadNone</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_defaultfont">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeSans</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeSerif</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeMonospace</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeCursive</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeFantasy</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">3</property>
+ <property name="stock">gtk-apply</property>
+ </object>
+ <object class="GtkListStore" id="liststore_proxy_type">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeDirect</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeManual</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeBasic</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeNLTM</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeSystem</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_animation_time">
+ <property name="value">0.10000000000000001</property>
+ <property name="lower">0.10000000000000001</property>
+ <property name="upper">10</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">1</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_font_default_size">
+ <property name="value">16</property>
+ <property name="lower">1</property>
+ <property name="upper">99.900000000000006</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">2</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_history_age">
+ <property name="value">28</property>
+ <property name="upper">999</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">28</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_cache_memory_size">
+ <property name="value">16</property>
+ <property name="upper">2048</property>
+ <property name="step_increment">4</property>
+ <property name="page_increment">16</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_cache_disc_size">
+ <property name="value">1024</property>
+ <property name="upper">4096</property>
+ <property name="step_increment">32</property>
+ <property name="page_increment">256</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_disc_cache_age">
+ <property name="value">28</property>
+ <property name="upper">999</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_proxy_port">
+ <property name="value">3128</property>
+ <property name="lower">1</property>
+ <property name="upper">65535</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_max">
+ <property name="value">10</property>
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_perhost">
+ <property name="value">1</property>
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_cached">
+ <property name="value">1</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_pdf_scale">
+ <property name="value">100</property>
+ <property name="lower">1</property>
+ <property name="upper">1000</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_pdf_lmargin">
+ <property name="upper">999</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkListStore" id="liststore_content_language">
+ <columns>
+ <!-- column-name Code -->
+ <column type="gchararray"/>
+ <!-- column-name Description -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">en</col>
+ <col id="1" translatable="yes">English</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_developer_view">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewWindow</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewTab</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewEditor</col>
+ </row>
+ </data>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/options.gtk3.ui b/frontends/gtk/res/options.gtk3.ui
new file mode 100644
index 000000000..2a3516f09
--- /dev/null
+++ b/frontends/gtk/res/options.gtk3.ui
@@ -0,0 +1,3057 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkAdjustment" id="adjustment_animation_time">
+ <property name="lower">0.10000000000000001</property>
+ <property name="upper">10</property>
+ <property name="value">0.10000000000000001</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">1</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_cache_disc_size">
+ <property name="upper">4096</property>
+ <property name="value">1024</property>
+ <property name="step_increment">4</property>
+ <property name="page_increment">16</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_cache_memory_size">
+ <property name="upper">1024</property>
+ <property name="value">16</property>
+ <property name="step_increment">4</property>
+ <property name="page_increment">16</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_disc_cache_age">
+ <property name="upper">999</property>
+ <property name="value">28</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_cached">
+ <property name="upper">100</property>
+ <property name="value">1</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_max">
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="value">10</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_fetching_perhost">
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="value">1</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_font_default_size">
+ <property name="lower">1</property>
+ <property name="upper">99.900000000000006</property>
+ <property name="value">16</property>
+ <property name="step_increment">0.10000000000000001</property>
+ <property name="page_increment">2</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_history_age">
+ <property name="upper">999</property>
+ <property name="value">28</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">28</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_pdf_lmargin">
+ <property name="upper">999</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_pdf_scale">
+ <property name="lower">1</property>
+ <property name="upper">1000</property>
+ <property name="value">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment_proxy_port">
+ <property name="lower">1</property>
+ <property name="upper">65535</property>
+ <property name="value">3128</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkDialog" id="dialogPreferences">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">preferencesTitle</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <signal name="destroy" handler="nsgtk_preferences_dialogPreferences_destroy" swapped="no"/>
+ <signal name="delete-event" handler="nsgtk_preferences_dialogPreferences_deleteevent" swapped="no"/>
+ <signal name="response" handler="nsgtk_preferences_dialogPreferences_response" swapped="no"/>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="vbox_dialog">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label">gtk-close</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="notebook1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox_main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_main_startup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_startup_page">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesStartupPage</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryHomePageURL">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesStartupPageTooltip</property>
+ <property name="invisible_char">●</property>
+ <signal name="changed" handler="nsgtk_preferences_entryHomePageURL_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryHomePageURL_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="setDefaultPage">
+ <property name="label" translatable="yes">preferencesStartupPageDefault</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="nsgtk_preferences_setDefaultPage_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="setCurrentPage">
+ <property name="label" translatable="yes">preferencesStartupPageCurrent</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="nsgtk_preferences_setCurrentPage_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label_main_startup">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesStartup</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_main_search">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkUrlSearch">
+ <property name="label" translatable="yes">preferencesSearchURLBar</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkUrlSearch_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkUrlSearch_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesSearchProvider</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboSearch">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">preferencesSearchProviderTooltip</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_search_provider</property>
+ <signal name="changed" handler="nsgtk_preferences_comboSearch_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboSearch_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesSearch</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_main_downloads">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkClearDownloads">
+ <property name="label" translatable="yes">preferencesDownloadsRemove</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkClearDownloads_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkClearDownloads_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkRequestOverwrite">
+ <property name="label" translatable="yes">preferencesDownloadsConfirm</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkRequestOverwrite_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkRequestOverwrite_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDownloadsLocation</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserButton" id="fileChooserDownloads">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">preferencesDownloadsLocationTooltip</property>
+ <property name="halign">start</property>
+ <property name="action">select-folder</property>
+ <signal name="selection-changed" handler="nsgtk_preferences_fileChooserDownloads_selectionchanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_fileChooserDownloads_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDownloads</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMainTabtitle</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_appearance">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_tabs">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkShowSingleTab">
+ <property name="label" translatable="yes">preferencesTabsAlways</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkShowSingleTab_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkShowSingleTab_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkFocusNew">
+ <property name="label" translatable="yes">preferencesTabsSwitch</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkFocusNew_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkFocusNew_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkNewBlank">
+ <property name="label" translatable="yes">preferencesTabsNewly</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkNewBlank_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkNewBlank_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTabsPosition</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboTabPosition">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_tab_position</property>
+ <signal name="changed" handler="nsgtk_preferences_comboTabPosition_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboTabPosition_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext2"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTabs</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_tools">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesDeveloperView</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxDeveloperView">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_developer_view</property>
+ <signal name="changed" handler="nsgtk_preferences_comboDeveloperView_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboDeveloperView_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext10"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesTools</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_url">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkDisplayRecentURLs">
+ <property name="label" translatable="yes">preferencesURLbarDisplay</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisplayRecentURLs_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisplayRecentURLs_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesURLbar</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">7</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_appearance_toolbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesToolbarButtons</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboButtonType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_toolbar_buttontype</property>
+ <signal name="changed" handler="nsgtk_preferences_comboButtonType_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboButtonType_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext3"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesToolbar</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearanceTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_content">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_content_control">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkDisablePopups">
+ <property name="label" translatable="yes">preferencesControlPrevent</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisablePopups_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisablePopups_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkHideAdverts">
+ <property name="label" translatable="yes">preferencesControlHide</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkHideAdverts_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkHideAdverts_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkEnableJavascript">
+ <property name="label" translatable="yes">preferencesControlEnable</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkEnableJavascript_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkEnableJavascript_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkDisablePlugins">
+ <property name="label" translatable="yes">preferencesControlDisable</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkDisablePlugins_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkDisablePlugins_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkResampleImages">
+ <property name="label" translatable="yes">preferencesControlHigh</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkResampleImages_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkResampleImages_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesControlLoad</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxLoadImages">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_image_loading</property>
+ <signal name="changed" handler="nsgtk_preferences_comboboxLoadImages_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboboxLoadImages_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext4"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesControl</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_animation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkEnableAnimations">
+ <property name="label" translatable="yes">preferencesAnimationEnable</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkEnableAnimations_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkEnableAnimations_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAnimationMinimum</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinAnimationSpeed">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesAnimationMinimumTooltip</property>
+ <property name="invisible_char">●</property>
+ <property name="adjustment">adjustment_animation_time</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinAnimationSpeed_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinAnimationSpeed_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAnimation</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_fonts">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkHBox" id="hbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkHBox" id="hbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label26">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFontsDefault</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboDefault">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="model">liststore_defaultfont</property>
+ <signal name="changed" handler="nsgtk_preferences_comboDefault_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboDefault_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext5"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label27">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFontsSize</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDefaultSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFontsSizeTooltip</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">4</property>
+ <property name="adjustment">adjustment_font_default_size</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDefaultSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDefaultSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="fontPreview">
+ <property name="label" translatable="yes">preferencesFontsPreview</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="image">image1</property>
+ <property name="use_underline">True</property>
+ <signal name="clicked" handler="nsgtk_preferences_fontPreview_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFonts</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_content_font">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label21">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesLanguagePreferred</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboboxLanguage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesLanguagePreferredTooltip</property>
+ <property name="halign">start</property>
+ <property name="model">liststore_content_language</property>
+ <signal name="changed" handler="nsgtk_preferences_comboboxLanguage_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboboxLanguage_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext7">
+ <property name="xalign">0</property>
+ </object>
+ <attributes>
+ <attribute name="text">1</attribute>
+ </attributes>
+ </child>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext8">
+ <property name="xalign">1</property>
+ </object>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label25">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesLanguage</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesContentTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_privacy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_general">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkSendReferer">
+ <property name="label" translatable="yes">preferencesGeneralReferral</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSendReferer_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSendReferer_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkSendDNT">
+ <property name="label" translatable="yes">preferencesGeneralDNT</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSendDNT_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSendDNT_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesGeneral</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_history">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkHoverURLs">
+ <property name="label" translatable="yes">preferencesHistoryShow</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkHoverURLs_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkHoverURLs_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label28">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistoryRemember</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinHistoryAge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">4</property>
+ <property name="adjustment">adjustment_history_age</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinHistoryAge_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinHistoryAge_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistoryDays</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label23">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesHistory</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_privacy_cache">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkTable" id="table3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheMemory</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label31">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheDisc</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label34">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheExpire</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMemoryCacheSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="adjustment">adjustment_cache_memory_size</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMemoryCacheSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMemoryCacheSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDiscCacheSize">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="adjustment">adjustment_cache_disc_size</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDiscCacheSize_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDiscCacheSize_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinDiscCacheAge">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">3</property>
+ <property name="adjustment">adjustment_disc_cache_age</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinDiscCacheAge_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinDiscCacheAge_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">MB</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="no">MB</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label35">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesCacheDays</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonCacheMaintenance">
+ <property name="label" translatable="yes">preferencesCacheMaintenance</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label24">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesCache</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label22">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesPrivacyTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_network">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_network_proxy">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label42">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyType</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label43">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyHost</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label44">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyUsername</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label45">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyPassword</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="comboProxyType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyTypeTooltip</property>
+ <property name="model">liststore_proxy_type</property>
+ <signal name="changed" handler="nsgtk_preferences_comboProxyType_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_comboProxyType_realize" swapped="no"/>
+ <child>
+ <object class="GtkCellRendererText" id="cellrenderertext6"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="entryProxyHost">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyHostTooltip</property>
+ <property name="invisible_char">●</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyHost_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyHost_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label46">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="no">:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinProxyPort">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyPortTooltip</property>
+ <property name="max_length">5</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="adjustment">adjustment_proxy_port</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">if-valid</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinProxyPort_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinProxyPort_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyUser">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyUsernameTooltip</property>
+ <property name="invisible_char">●</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyUser_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyUser_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyPasswordTooltip</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">●</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyPassword_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyPassword_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label55">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesProxyNoproxy</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryProxyNoproxy">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesProxyNoproxyTooltip</property>
+ <property name="caps_lock_warning">False</property>
+ <signal name="changed" handler="nsgtk_preferences_entryProxyNoproxy_changed" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_entryProxyNoproxy_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesProxy</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_network_fetching">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">6</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label39">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingMax</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label40">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingPerhost</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label41">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">preferencesFetchingCached</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMaxFetchers">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingMaxTooltip</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">3</property>
+ <property name="adjustment">adjustment_fetching_max</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMaxFetchers_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMaxFetchers_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinFetchesPerHost">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingPerhostTooltip</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">3</property>
+ <property name="adjustment">adjustment_fetching_perhost</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinFetchesPerHost_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinFetchesPerHost_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinCachedConnections">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">preferencesFetchingCachedTooltip</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">3</property>
+ <property name="adjustment">adjustment_fetching_cached</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinCachedConnections_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinCachedConnections_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label38">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesFetching</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesNetworkTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox_pdfexport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_appearance">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment17">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="checkSuppressImages">
+ <property name="label" translatable="yes">preferencesAppearanceImages</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkSuppressImages_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkSuppressImages_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkRemoveBackgrounds">
+ <property name="label" translatable="yes">preferencesAppearanceBackground</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkRemoveBackgrounds_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkRemoveBackgrounds_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkFitPage">
+ <property name="label" translatable="yes">preferencesAppearanceScalefit</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkFitPage_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkFitPage_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label59">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearanceScale</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinExportScale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">4</property>
+ <property name="xalign">1</property>
+ <property name="adjustment">adjustment_pdf_scale</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinExportScale_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinExportScale_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label60">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="no">%</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label48">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesAppearance</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_margins">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox22">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="label62">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMarginsMeasurements</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkTable" id="table4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label51">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Top</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginTop">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginTop_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginTop_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox18">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label52">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Left</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginLeft">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="adjustment">adjustment_pdf_lmargin</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginLeft_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginLeft_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox20">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label53">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Bottom</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginBottom">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginBottom_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginBottom_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox21">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label54">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Right</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="spinMarginRight">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="max_length">4</property>
+ <property name="invisible_char">●</property>
+ <property name="width_chars">5</property>
+ <property name="xalign">1</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <signal name="value-changed" handler="nsgtk_preferences_spinMarginRight_valuechanged" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_spinMarginRight_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_EXPAND</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label49">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesMargins</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_pdfexport_generation">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment19">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">12</property>
+ <child>
+ <object class="GtkVBox" id="vbox14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">7</property>
+ <child>
+ <object class="GtkCheckButton" id="checkCompressPDF">
+ <property name="label" translatable="yes">preferencesGenerationCompressed</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkCompressPDF_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkCompressPDF_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="checkPasswordPDF">
+ <property name="label" translatable="yes">preferencesGenerationPassword</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="halign">start</property>
+ <property name="use_action_appearance">False</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="nsgtk_preferences_checkPasswordPDF_toggled" swapped="no"/>
+ <signal name="realize" handler="nsgtk_preferences_checkPasswordPDF_realize" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label50">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesGeneration</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">6</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label47">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">preferencesPDFTabtitle</property>
+ </object>
+ <packing>
+ <property name="position">5</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">help</action-widget>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xpad">3</property>
+ <property name="stock">gtk-apply</property>
+ </object>
+ <object class="GtkListStore" id="liststore_content_language">
+ <columns>
+ <!-- column-name Code -->
+ <column type="gchararray"/>
+ <!-- column-name Description -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">en</col>
+ <col id="1" translatable="yes">English</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_defaultfont">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeSans</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeSerif</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeMonospace</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeCursive</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesFonttypeFantasy</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_developer_view">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewWindow</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewTab</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesDeveloperViewEditor</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_image_loading">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadBoth</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadFore</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadBack</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesImageLoadNone</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_proxy_type">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeDirect</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeManual</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeBasic</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeNLTM</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesProxyTypeSystem</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_search_provider">
+ <columns>
+ <!-- column-name Provider -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="no">Google</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Yahoo!</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Microsoft Live</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Buisiness.com</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Omgili</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">BBC News</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Ubuntu Packages</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Creative Commons</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Ask</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Answers</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Dictionary.com</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">YouTube</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">AeroMP3</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">AOL</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Baidu</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Amazon</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Ebay</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">IMBD</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">ESPN</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">Wikipedia</col>
+ </row>
+ <row>
+ <col id="0" translatable="no">DuckDuckGo</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_tab_position">
+ <columns>
+ <!-- column-name Position -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocTop</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocLeft</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocRight</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesTabLocBottom</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_toolbar_buttontype">
+ <columns>
+ <!-- column-name Type -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeSmall</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeLarge</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeLargeText</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">preferencesButtonTypeText</col>
+ </row>
+ </data>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/password.gtk2.ui b/frontends/gtk/res/password.gtk2.ui
new file mode 100644
index 000000000..eb51e4f8f
--- /dev/null
+++ b/frontends/gtk/res/password.gtk2.ui
@@ -0,0 +1,415 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="wndPDFPassword">
+ <property name="title" translatable="yes">PDF Password</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="modal">True</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.10000000149</property>
+ <property name="xpad">12</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Owner password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFOwnerPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFOwnerPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">User password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFUserPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFUserPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkButton" id="buttonPDFSetPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image7">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Use these as passwords</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonPDFNoPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image8">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cancel</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Do not set any passwords</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/password.gtk3.ui b/frontends/gtk/res/password.gtk3.ui
new file mode 100644
index 000000000..eb51e4f8f
--- /dev/null
+++ b/frontends/gtk/res/password.gtk3.ui
@@ -0,0 +1,415 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="wndPDFPassword">
+ <property name="title" translatable="yes">PDF Password</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="modal">True</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-authentication</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.10000000149</property>
+ <property name="xpad">12</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Owner password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFOwnerPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFOwnerPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">User password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFUserPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">True</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Repeat password</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.899999976158</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">15</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entryPDFUserPassword1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="editable">True</property>
+ <property name="visibility">False</property>
+ <property name="max_length">20</property>
+ <property name="text" translatable="yes"/>
+ <property name="has_frame">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ <property name="activates_default">False</property>
+ <property name="width_chars">20</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox1">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkButton" id="buttonPDFSetPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image7">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-authentication</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Use these as passwords</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="buttonPDFNoPassword">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image8">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cancel</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Do not set any passwords</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/quirks.css b/frontends/gtk/res/quirks.css
new file mode 120000
index 000000000..88aabe48c
--- /dev/null
+++ b/frontends/gtk/res/quirks.css
@@ -0,0 +1 @@
+../../../!NetSurf/Resources/Quirks,f79 \ No newline at end of file
diff --git a/frontends/gtk/res/ssl.gtk2.ui b/frontends/gtk/res/ssl.gtk2.ui
new file mode 100644
index 000000000..90f449ddd
--- /dev/null
+++ b/frontends/gtk/res/ssl.gtk2.ui
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkDialog" id="wndSSLProblem">
+ <property name="border_width">1</property>
+ <property name="title" translatable="yes">SSL certificate problem</property>
+ <property name="modal">True</property>
+ <property name="default_width">500</property>
+ <property name="default_height">250</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkHBox" id="hbox15">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImage" id="image6">
+ <property name="visible">True</property>
+ <property name="yalign">0</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox13">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label62">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">NetSurf failed to verify the authenticity of an SSL certificate. Please verify the details presented below.</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame13">
+ <property name="visible">True</property>
+ <property name="border_width">5</property>
+ <property name="label_xalign">0</property>
+ <child>
+ <object class="GtkAlignment" id="alignment17">
+ <property name="visible">True</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkScrolledWindow" id="SSLScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <object class="GtkViewport" id="SSLViewport">
+ <property name="visible">True</property>
+ <property name="resize_mode">GTK_RESIZE_QUEUE</property>
+ <child>
+ <object class="GtkDrawingArea" id="SSLDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label63">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Certificate chain&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area3">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="sslreject">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment16">
+ <property name="visible">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox14">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkImage" id="image5">
+ <property name="visible">True</property>
+ <property name="stock">gtk-cancel</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reject</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="sslaccept">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <child>
+ <object class="GtkAlignment" id="alignment15">
+ <property name="visible">True</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <child>
+ <object class="GtkHBox" id="hbox13">
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="stock">gtk-apply</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label60">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Accept</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">sslreject</action-widget>
+ <action-widget response="-5">sslaccept</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/ssl.gtk3.ui b/frontends/gtk/res/ssl.gtk3.ui
new file mode 100644
index 000000000..dace2a49e
--- /dev/null
+++ b/frontends/gtk/res/ssl.gtk3.ui
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-apply</property>
+ </object>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-cancel</property>
+ </object>
+ <object class="GtkDialog" id="wndSSLProblem">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="default_width">440</property>
+ <property name="default_height">260</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="sslreject">
+ <property name="label" translatable="yes">_Reject</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="image">image3</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="sslaccept">
+ <property name="label" translatable="yes">_Accept</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="image">image2</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">8</property>
+ <property name="stock">gtk-dialog-warning</property>
+ <property name="icon-size">6</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">NetSurf failed to verify the authenticity of an SSL certificate. Please verify the details presented below.</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkScrolledWindow" id="SSLScrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport" id="SSLViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkDrawingArea" id="SSLDrawingArea">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">&lt;b&gt;Certificate Chain&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">sslreject</action-widget>
+ <action-widget response="-5">sslaccept</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/tabcontents.gtk2.ui b/frontends/gtk/res/tabcontents.gtk2.ui
new file mode 100644
index 000000000..63e290e8b
--- /dev/null
+++ b/frontends/gtk/res/tabcontents.gtk2.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkTable" id="tabContents">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <child>
+ <object class="GtkLayout" id="layout">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK | GDK_SCROLL_MASK</property>
+ <property name="hadjustment">layouthadjustment</property>
+ <property name="vadjustment">layoutvadjustment</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStatusbar" id="resizer">
+ <property name="height_request">1</property>
+ <property name="visible">True</property>
+ <property name="spacing">2</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHPaned" id="hpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkLabel" id="status_bar">
+ <property name="width_request">1</property>
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Status</property>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHScrollbar" id="hscrollbar">
+ <property name="visible">True</property>
+ <property name="adjustment">layouthadjustment</property>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVScrollbar" id="vscrollbar">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">layoutvadjustment</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="x_options"></property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="layouthadjustment">
+ <property name="upper">100</property>
+ <property name="step_increment">30</property>
+ <property name="page_increment">10</property>
+ <property name="page_size">10</property>
+ </object>
+ <object class="GtkAdjustment" id="layoutvadjustment">
+ <property name="upper">100</property>
+ <property name="step_increment">30</property>
+ <property name="page_increment">10</property>
+ <property name="page_size">10</property>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/tabcontents.gtk3.ui b/frontends/gtk/res/tabcontents.gtk3.ui
new file mode 100644
index 000000000..23328b3b7
--- /dev/null
+++ b/frontends/gtk/res/tabcontents.gtk3.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkAdjustment" id="layouthadjustment">
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="layoutvadjustment">
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkGrid" id="tabContents">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">2</property>
+ <property name="column_spacing">2</property>
+ <child>
+ <object class="GtkLayout" id="layout">
+ <property name="visible">True</property>
+ <property name="app_paintable">True</property>
+ <property name="can_focus">False</property>
+ <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK | GDK_SCROLL_MASK</property>
+ <property name="hadjustment">layouthadjustment</property>
+ <property name="vadjustment">layoutvadjustment</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrollbar" id="vscrollbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">layoutvadjustment</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkPaned" id="hpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <child>
+ <object class="GtkLabel" id="status_bar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">4</property>
+ <property name="label" translatable="yes">Status</property>
+ <property name="single_line_mode">True</property>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrollbar" id="hscrollbar">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="adjustment">layouthadjustment</property>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/throbber/throbber0.png b/frontends/gtk/res/throbber/throbber0.png
new file mode 100644
index 000000000..bfcb5d37f
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber0.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber1.png b/frontends/gtk/res/throbber/throbber1.png
new file mode 100644
index 000000000..a44b70d64
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber1.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber2.png b/frontends/gtk/res/throbber/throbber2.png
new file mode 100644
index 000000000..1bbdd8b4d
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber2.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber3.png b/frontends/gtk/res/throbber/throbber3.png
new file mode 100644
index 000000000..a62488ab8
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber3.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber4.png b/frontends/gtk/res/throbber/throbber4.png
new file mode 100644
index 000000000..4e685dcfb
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber4.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber5.png b/frontends/gtk/res/throbber/throbber5.png
new file mode 100644
index 000000000..72adf67d9
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber5.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber6.png b/frontends/gtk/res/throbber/throbber6.png
new file mode 100644
index 000000000..f7dcc2c0c
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber6.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber7.png b/frontends/gtk/res/throbber/throbber7.png
new file mode 100644
index 000000000..da9d8aee3
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber7.png
Binary files differ
diff --git a/frontends/gtk/res/throbber/throbber8.png b/frontends/gtk/res/throbber/throbber8.png
new file mode 100644
index 000000000..8505d1030
--- /dev/null
+++ b/frontends/gtk/res/throbber/throbber8.png
Binary files differ
diff --git a/frontends/gtk/res/toolbar.gtk2.ui b/frontends/gtk/res/toolbar.gtk2.ui
new file mode 100644
index 000000000..d84db5c8c
--- /dev/null
+++ b/frontends/gtk/res/toolbar.gtk2.ui
@@ -0,0 +1,189 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="toolbarwindow">
+ <property name="width_request">700</property>
+ <property name="height_request">450</property>
+ <property name="title" translatable="yes"/>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <child>
+ <object class="GtkVBox" id="windowvbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="toolbarlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Move items from store to toolbar Rearrange items in toolbar Move items from toolbar to store</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ <child>
+ <object class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <object class="GtkVBox" id="widgetvbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="buttonhbox">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkButton" id="resetbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkHBox" id="button1hbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-refresh</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="refreshbuttonlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reset to defaults</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="okbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="label">gtk-apply</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancelbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/toolbar.gtk3.ui b/frontends/gtk/res/toolbar.gtk3.ui
new file mode 100644
index 000000000..d84db5c8c
--- /dev/null
+++ b/frontends/gtk/res/toolbar.gtk3.ui
@@ -0,0 +1,189 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="toolbarwindow">
+ <property name="width_request">700</property>
+ <property name="height_request">450</property>
+ <property name="title" translatable="yes"/>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="focus_on_map">True</property>
+ <property name="urgency_hint">False</property>
+ <child>
+ <object class="GtkVBox" id="windowvbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkLabel" id="toolbarlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Move items from store to toolbar Rearrange items in toolbar Move items from toolbar to store</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ <child>
+ <object class="GtkViewport" id="viewport1">
+ <property name="visible">True</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <child>
+ <object class="GtkVBox" id="widgetvbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="buttonhbox">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkButton" id="resetbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <child>
+ <object class="GtkHBox" id="button1hbox">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-refresh</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="refreshbuttonlabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Reset to defaults</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+ <property name="width_chars">-1</property>
+ <property name="single_line_mode">False</property>
+ <property name="angle">0</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkButton" id="okbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="has_focus">True</property>
+ <property name="label">gtk-apply</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ <packing>
+ <property name="padding">10</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancelbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/viewdata.gtk2.ui b/frontends/gtk/res/viewdata.gtk2.ui
new file mode 100644
index 000000000..7589022ca
--- /dev/null
+++ b/frontends/gtk/res/viewdata.gtk2.ui
@@ -0,0 +1,204 @@
+<?xml version="1.0"?>
+<interface>
+ <!-- interface-requires gtk+ 2.12 -->
+ <!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkWindow" id="ViewDataWindow">
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_save_as">
+ <property name="label">gtk-save-as</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="S" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_print">
+ <property name="label">gtk-print</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="P" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+ <property name="visible">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_select_all">
+ <property name="label">gtk-select-all</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="A" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_cut">
+ <property name="label">gtk-cut</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_copy">
+ <property name="label">gtk-copy</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_paste">
+ <property name="label">gtk-paste</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_delete">
+ <property name="label">gtk-delete</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="Delete" signal="activate"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_in">
+ <property name="label">gtk-zoom-in</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="plus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_out">
+ <property name="label">gtk-zoom-out</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="minus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_normal">
+ <property name="label">gtk-zoom-100</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ <accelerator key="0" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_about">
+ <property name="label">gtk-about</property>
+ <property name="visible">True</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="sourcescrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTextView" id="viewdata_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="pixels_above_lines">1</property>
+ <property name="pixels_below_lines">1</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">word</property>
+ <property name="left_margin">3</property>
+ <property name="right_margin">3</property>
+ <property name="accepts_tab">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/viewdata.gtk3.ui b/frontends/gtk/res/viewdata.gtk3.ui
new file mode 100644
index 000000000..b742d5f6b
--- /dev/null
+++ b/frontends/gtk/res/viewdata.gtk3.ui
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkWindow" id="ViewDataWindow">
+ <property name="can_focus">False</property>
+ <property name="default_width">640</property>
+ <property name="default_height">480</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_File</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_save_as">
+ <property name="label">gtk-save-as</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_print">
+ <property name="label">gtk-print</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_close">
+ <property name="label">gtk-close</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Edit</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_select_all">
+ <property name="label">gtk-select-all</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_cut">
+ <property name="label">gtk-cut</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_copy">
+ <property name="label">gtk-copy</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_paste">
+ <property name="label">gtk-paste</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_delete">
+ <property name="label">gtk-delete</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem3">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_View</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_in">
+ <property name="label">gtk-zoom-in</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_out">
+ <property name="label">gtk-zoom-out</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_zoom_normal">
+ <property name="label">gtk-zoom-100</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkMenuItem" id="menuitem4">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Help</property>
+ <property name="use_underline">True</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="viewdata_about">
+ <property name="label">gtk-about</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="sourcescrolled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="window_placement">bottom-right</property>
+ <child>
+ <object class="GtkTextView" id="viewdata_view">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="pixels_above_lines">1</property>
+ <property name="pixels_below_lines">1</property>
+ <property name="editable">False</property>
+ <property name="wrap_mode">word</property>
+ <property name="left_margin">3</property>
+ <property name="right_margin">3</property>
+ <property name="accepts_tab">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/warning.gtk2.ui b/frontends/gtk/res/warning.gtk2.ui
new file mode 100644
index 000000000..e4fb4e662
--- /dev/null
+++ b/frontends/gtk/res/warning.gtk2.ui
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="wndWarning">
+ <property name="title" translatable="yes">Warning from NetSurf</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="urgency_hint">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox32">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <child>
+ <object class="GtkHBox" id="hbox30">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkImage" id="image519">
+ <property name="visible">True</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="labelWarning">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Help help help! I'm being held prisoner by a bunch of RISC OS zealots!</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">3</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="button14">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <signal handler="gtk_widget_hide" name="clicked" object="wndWarning"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/warning.gtk3.ui b/frontends/gtk/res/warning.gtk3.ui
new file mode 100644
index 000000000..e4fb4e662
--- /dev/null
+++ b/frontends/gtk/res/warning.gtk3.ui
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkWindow" id="wndWarning">
+ <property name="title" translatable="yes">Warning from NetSurf</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="urgency_hint">True</property>
+ <child>
+ <object class="GtkVBox" id="vbox32">
+ <property name="visible">True</property>
+ <property name="border_width">2</property>
+ <child>
+ <object class="GtkHBox" id="hbox30">
+ <property name="visible">True</property>
+ <property name="border_width">3</property>
+ <child>
+ <object class="GtkImage" id="image519">
+ <property name="visible">True</property>
+ <property name="xpad">12</property>
+ <property name="icon_size">6</property>
+ <property name="icon_name">gtk-dialog-warning</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="labelWarning">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Help help help! I'm being held prisoner by a bunch of RISC OS zealots!</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="padding">1</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkHSeparator" id="hseparator2">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="padding">3</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHButtonBox" id="hbuttonbox2">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object class="GtkButton" id="button14">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <signal handler="gtk_widget_hide" name="clicked" object="wndWarning"/>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/frontends/gtk/res/welcome.html b/frontends/gtk/res/welcome.html
new file mode 120000
index 000000000..1abdc5e8a
--- /dev/null
+++ b/frontends/gtk/res/welcome.html
@@ -0,0 +1 @@
+en/welcome.html \ No newline at end of file
diff --git a/frontends/gtk/resources.c b/frontends/gtk/resources.c
new file mode 100644
index 000000000..dfe3d3dad
--- /dev/null
+++ b/frontends/gtk/resources.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Implementation of gtk builtin resource handling.
+ *
+ * This presents a unified interface to the rest of the codebase to
+ * obtain resources. Note this is not anything to do with the resource
+ * scheme handling beyond possibly providing the underlying data.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/filepath.h"
+
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+
+/** log contents of gresource /org/netsource */
+#ifdef WITH_GRESOURCE
+#define SHOW_GRESOURCE
+#undef SHOW_GRESOURCE
+#endif
+
+#ifdef WITH_BUILTIN_PIXBUF
+#ifdef __GNUC__
+extern const guint8 menu_cursor_pixdata[] __attribute__ ((__aligned__ (4)));
+extern const guint8 favicon_pixdata[] __attribute__ ((__aligned__ (4)));
+extern const guint8 netsurf_pixdata[] __attribute__ ((__aligned__ (4)));
+#else
+extern const guint8 menu_cursor_pixdata[];
+extern const guint8 favicon_pixdata[];
+extern const guint8 netsurf_pixdata[];
+#endif
+#endif
+
+/** type of resource entry */
+enum nsgtk_resource_type_e {
+ NSGTK_RESOURCE_FILE, /**< entry is a file on disc */
+ NSGTK_RESOURCE_GLIB, /**< entry is a gresource accessed by path */
+ NSGTK_RESOURCE_DIRECT, /**< entry is a gresource accesed by gbytes */
+ NSGTK_RESOURCE_INLINE, /**< entry is compiled in accessed by pointer */
+};
+
+/** resource entry */
+struct nsgtk_resource_s {
+ const char *name;
+ unsigned int len;
+ enum nsgtk_resource_type_e type;
+ char *path;
+};
+
+#define RES_ENTRY(name) { name, sizeof((name)) - 1, NSGTK_RESOURCE_FILE, NULL }
+
+/** resources that are used for gtk builder */
+static struct nsgtk_resource_s ui_resource[] = {
+ RES_ENTRY("netsurf"),
+ RES_ENTRY("tabcontents"),
+ RES_ENTRY("password"),
+ RES_ENTRY("login"),
+ RES_ENTRY("ssl"),
+ RES_ENTRY("toolbar"),
+ RES_ENTRY("downloads"),
+ RES_ENTRY("history"),
+ RES_ENTRY("options"),
+ RES_ENTRY("hotlist"),
+ RES_ENTRY("cookies"),
+ RES_ENTRY("viewdata"),
+ RES_ENTRY("warning"),
+ { NULL, 0, NSGTK_RESOURCE_FILE, NULL },
+};
+
+/** resources that are used as pixbufs */
+static struct nsgtk_resource_s pixbuf_resource[] = {
+ RES_ENTRY("favicon.png"),
+ RES_ENTRY("netsurf.xpm"),
+ RES_ENTRY("menu_cursor.png"),
+ RES_ENTRY("arrow_down_8x32.png"),
+ RES_ENTRY("throbber/throbber0.png"),
+ RES_ENTRY("throbber/throbber1.png"),
+ RES_ENTRY("throbber/throbber2.png"),
+ RES_ENTRY("throbber/throbber3.png"),
+ RES_ENTRY("throbber/throbber4.png"),
+ RES_ENTRY("throbber/throbber5.png"),
+ RES_ENTRY("throbber/throbber6.png"),
+ RES_ENTRY("throbber/throbber7.png"),
+ RES_ENTRY("throbber/throbber8.png"),
+ { NULL, 0, NSGTK_RESOURCE_FILE, NULL },
+};
+
+/** resources that are used for direct data access */
+static struct nsgtk_resource_s direct_resource[] = {
+ RES_ENTRY("welcome.html"),
+ RES_ENTRY("credits.html"),
+ RES_ENTRY("licence.html"),
+ RES_ENTRY("maps.html"),
+ RES_ENTRY("default.css"),
+ RES_ENTRY("adblock.css"),
+ RES_ENTRY("internal.css"),
+ RES_ENTRY("quirks.css"),
+ RES_ENTRY("netsurf.png"),
+ RES_ENTRY("default.ico"),
+ RES_ENTRY("icons/arrow-l.png"),
+ RES_ENTRY("icons/content.png"),
+ RES_ENTRY("icons/directory2.png"),
+ RES_ENTRY("icons/directory.png"),
+ RES_ENTRY("icons/hotlist-add.png"),
+ RES_ENTRY("icons/hotlist-rmv.png"),
+ RES_ENTRY("icons/search.png"),
+ RES_ENTRY("languages"),
+ RES_ENTRY("Messages"),
+ { NULL, 0, NSGTK_RESOURCE_FILE, NULL },
+};
+
+
+/* exported interface documented in gtk/resources.h */
+GdkCursor *nsgtk_create_menu_cursor(void)
+{
+ GdkCursor *cursor = NULL;
+ GdkPixbuf *pixbuf;
+ nserror res;
+
+ res = nsgdk_pixbuf_new_from_resname("menu_cursor.png", &pixbuf);
+ if (res == NSERROR_OK) {
+ cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
+ pixbuf, 0, 3);
+ g_object_unref(pixbuf);
+ }
+
+ return cursor;
+}
+
+
+/**
+ * locate a resource
+ *
+ * The way GTK accesses resource files has changed greatly between
+ * releases. This initilises the interface that hides all the
+ * implementation details from the rest of the code.
+ *
+ * If the GResource is not enabled or the item cannot be found in the
+ * compiled in resources the files will be loaded directly from disc
+ * instead.
+ *
+ * \param respath A string vector containing the valid resource search paths
+ * \param resource A resource entry to initialise
+ */
+static nserror
+init_resource(char **respath, struct nsgtk_resource_s *resource)
+{
+ char *resname;
+#ifdef WITH_GRESOURCE
+ int resnamelen;
+ gboolean present;
+ const gchar * const *langv;
+ int langc = 0;
+
+ langv = g_get_language_names();
+
+ while (langv[langc] != NULL) {
+ resnamelen = snprintf(NULL, 0,
+ "/org/netsurf/%s/%s",
+ langv[langc], resource->name);
+
+ resname = malloc(resnamelen + 1);
+ if (resname == NULL) {
+ return NSERROR_NOMEM;
+ }
+ snprintf(resname, resnamelen + 1,
+ "/org/netsurf/%s/%s",
+ langv[langc], resource->name);
+
+ present = g_resources_get_info(resname,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ NULL, NULL, NULL);
+ if (present == TRUE) {
+ /* found an entry in the resources */
+ resource->path = resname;
+ resource->type = NSGTK_RESOURCE_GLIB;
+ LOG("Found gresource path %s", resource->path);
+ return NSERROR_OK;
+ }
+ /*LOG("gresource \"%s\" not found", resname);*/
+ free(resname);
+
+ langc++;
+ }
+ resnamelen = snprintf(NULL, 0, "/org/netsurf/%s", resource->name);
+
+ resname = malloc(resnamelen + 1);
+ if (resname == NULL) {
+ return NSERROR_NOMEM;
+ }
+ snprintf(resname, resnamelen + 1, "/org/netsurf/%s", resource->name);
+
+ present = g_resources_get_info(resname,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ NULL, NULL, NULL);
+ if (present == TRUE) {
+ /* found an entry in the resources */
+ resource->path = resname;
+ resource->type = NSGTK_RESOURCE_GLIB;
+ LOG("Found gresource path %s", resource->path);
+ return NSERROR_OK;
+ }
+ /*LOG("gresource \"%s\" not found", resname);*/
+ free(resname);
+
+#endif
+
+ resname = filepath_find(respath, resource->name);
+ if (resname == NULL) {
+ LOG("Unable to find resource %s on resource path",
+ resource->name);
+ return NSERROR_NOT_FOUND;
+ }
+
+ /* found an entry on the path */
+ resource->path = resname;
+ resource->type = NSGTK_RESOURCE_FILE;
+
+ LOG("Found file resource path %s", resource->path);
+ return NSERROR_OK;
+}
+
+/**
+ * locate and setup a direct resource
+ *
+ * Direct resources have general type of NSGTK_RESOURCE_GLIB but have
+ * g_resources_lookup_data() applied and the result stored so the data
+ * can be directly accessed without additional processing.
+ *
+ * \param respath A string vector containing the valid resource search paths
+ * \param resource A resource entry to initialise
+ */
+static nserror
+init_direct_resource(char **respath, struct nsgtk_resource_s *resource)
+{
+ nserror res;
+
+ res = init_resource(respath, resource);
+
+#ifdef WITH_GRESOURCE
+ if ((res == NSERROR_OK) &&
+ (resource->type == NSGTK_RESOURCE_GLIB)) {
+ /* found gresource we can convert */
+ GBytes *data;
+
+ data = g_resources_lookup_data(resource->path,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ NULL);
+ if (data != NULL) {
+ resource->type = NSGTK_RESOURCE_DIRECT;
+ resource->path = (char *)data;
+ }
+ }
+#endif
+
+ return res;
+}
+
+/**
+ * locate a pixbuf resource
+ *
+ * Pixbuf resources can be compiled inline
+ *
+ * \param respath A string vector containing the valid resource search paths
+ * \param resource A resource entry to initialise
+ */
+static nserror
+init_pixbuf_resource(char **respath, struct nsgtk_resource_s *resource)
+{
+#ifdef WITH_BUILTIN_PIXBUF
+ if (strncmp(resource->name, "menu_cursor.png", resource->len) == 0) {
+ resource->path = (char *)&menu_cursor_pixdata[0];
+ resource->type = NSGTK_RESOURCE_INLINE;
+ LOG("Found builtin for %s", resource->name);
+ return NSERROR_OK;
+ }
+
+ if (strncmp(resource->name, "netsurf.xpm", resource->len) == 0) {
+ resource->path = (char *)&netsurf_pixdata[0];
+ resource->type = NSGTK_RESOURCE_INLINE;
+ LOG("Found builtin for %s", resource->name);
+ return NSERROR_OK;
+ }
+
+ if (strncmp(resource->name, "favicon.png", resource->len) == 0) {
+ resource->path = (char *)&favicon_pixdata[0];
+ resource->type = NSGTK_RESOURCE_INLINE;
+ LOG("Found builtin for %s", resource->name);
+ return NSERROR_OK;
+ }
+#endif
+ return init_resource(respath, resource);
+}
+
+/**
+ * locate a ui resource
+ *
+ * UI resources need their resource name changing to account for gtk versions
+ *
+ * \param respath A string vector containing the valid resource search paths
+ * \param ui_res A resource entry to initialise
+ */
+static nserror init_ui_resource(char **respath, struct nsgtk_resource_s *ui_res)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ int gtkv = 3;
+#else
+ int gtkv = 2;
+#endif
+ int resnamelen;
+ char *resname;
+ struct nsgtk_resource_s resource;
+ nserror res;
+
+ resnamelen = ui_res->len + 10; /* allow for the expanded ui name */
+
+ resname = malloc(resnamelen);
+ if (resname == NULL) {
+ return NSERROR_NOMEM;
+ }
+ snprintf(resname, resnamelen, "%s.gtk%d.ui", ui_res->name, gtkv);
+ resource.name = resname;
+ resource.len = ui_res->len;
+ resource.path = NULL;
+
+ res = init_resource(respath, &resource);
+
+ ui_res->path = resource.path;
+ ui_res->type = resource.type;
+
+ free(resname);
+
+ return res;
+}
+
+/**
+ * Find a resource entry by name.
+ *
+ * \param resname The resource name to match.
+ * \param resource The list of resources entries to search.
+ */
+static struct nsgtk_resource_s *
+find_resource_from_name(const char *resname, struct nsgtk_resource_s *resource)
+{
+ /* find resource from name */
+ while ((resource->name != NULL) &&
+ ((resname[0] != resource->name[0]) ||
+ (strncmp(resource->name, resname, resource->len) != 0))) {
+ resource++;
+ }
+ return resource;
+}
+
+#ifdef SHOW_GRESOURCE
+/**
+ * Debug dump of all resources compile din via GResource.
+ */
+static void list_gresource(void)
+{
+ const char *nspath = "/org/netsurf";
+ char **reslist;
+ char **cur;
+ GError* gerror = NULL;
+ reslist = g_resources_enumerate_children(nspath,
+ G_RESOURCE_LOOKUP_FLAGS_NONE,
+ &gerror);
+ if (gerror) {
+ LOG("gerror %s", gerror->message);
+ g_error_free(gerror);
+
+ } else {
+ cur = reslist;
+ while (cur != NULL && *cur != NULL) {
+ LOG("gres %s", *cur);
+ cur++;
+ }
+ g_strfreev(reslist);
+ }
+}
+#endif
+
+/**
+ * Initialise UI resource table
+ *
+ */
+/* exported interface documented in gtk/resources.h */
+nserror nsgtk_init_resources(char **respath)
+{
+ struct nsgtk_resource_s *resource;
+ nserror res;
+
+#ifdef SHOW_GRESOURCE
+ list_gresource();
+#endif
+
+ /* iterate the ui resource table and initialise all its members */
+ resource = &ui_resource[0];
+ while (resource->name != NULL) {
+ res = init_ui_resource(respath, resource);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ resource++;
+ }
+
+ /* iterate the pixbuf resource table and initialise all its members */
+ resource = &pixbuf_resource[0];
+ while (resource->name != NULL) {
+ res = init_pixbuf_resource(respath, resource);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ resource++;
+ }
+
+ /* iterate the direct resource table and initialise all its members */
+ resource = &direct_resource[0];
+ while (resource->name != NULL) {
+ res = init_direct_resource(respath, resource);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+ resource++;
+ }
+
+ return NSERROR_OK;
+}
+
+
+/* exported interface documented in gtk/resources.h */
+nserror
+nsgdk_pixbuf_new_from_resname(const char *resname, GdkPixbuf **pixbuf_out)
+{
+ struct nsgtk_resource_s *resource;
+ GdkPixbuf *new_pixbuf = NULL;
+ GError* error = NULL;
+
+ resource = find_resource_from_name(resname, &pixbuf_resource[0]);
+ if (resource->name == NULL) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ switch (resource->type) {
+ case NSGTK_RESOURCE_FILE:
+ new_pixbuf = gdk_pixbuf_new_from_file(resource->path, &error);
+ break;
+
+ case NSGTK_RESOURCE_GLIB:
+#ifdef WITH_GRESOURCE
+ new_pixbuf = gdk_pixbuf_new_from_resource(resource->path, &error);
+#endif
+ break;
+
+ case NSGTK_RESOURCE_INLINE:
+#ifdef WITH_BUILTIN_PIXBUF
+ new_pixbuf = gdk_pixbuf_new_from_inline(-1, (const guint8 *)resource->path, FALSE, &error);
+#endif
+ break;
+
+ case NSGTK_RESOURCE_DIRECT:
+ /* pixbuf resources are not currently direct */
+ break;
+ }
+
+ if (new_pixbuf == NULL) {
+ if (error != NULL) {
+ LOG("Unable to create pixbuf from file for %s with path %s \"%s\"",
+ resource->name, resource->path, error->message);
+ g_error_free(error);
+ } else {
+ LOG("Unable to create pixbuf from file for %s with path %s",
+ resource->name, resource->path);
+ }
+ return NSERROR_INIT_FAILED;
+ }
+ *pixbuf_out = new_pixbuf;
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in gtk/resources.h */
+nserror
+nsgtk_builder_new_from_resname(const char *resname, GtkBuilder **builder_out)
+{
+ GtkBuilder *new_builder;
+ struct nsgtk_resource_s *ui_res;
+ GError* error = NULL;
+
+ ui_res = find_resource_from_name(resname, &ui_resource[0]);
+ if (ui_res->name == NULL) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ new_builder = gtk_builder_new();
+
+ if (ui_res->type == NSGTK_RESOURCE_FILE) {
+ if (!gtk_builder_add_from_file(new_builder,
+ ui_res->path,
+ &error)) {
+ LOG("Unable to add UI builder from file for %s with path %s \"%s\"",
+ ui_res->name, ui_res->path, error->message);
+ g_error_free(error);
+ g_object_unref(G_OBJECT(new_builder));
+ return NSERROR_INIT_FAILED;
+ }
+ } else {
+ if (!nsgtk_builder_add_from_resource(new_builder,
+ ui_res->path,
+ &error)) {
+ LOG("Unable to add UI builder from resource for %s with path %s \"%s\"",
+ ui_res->name, ui_res->path, error->message);
+ g_error_free(error);
+ g_object_unref(G_OBJECT(new_builder));
+ return NSERROR_INIT_FAILED;
+ }
+ }
+
+ *builder_out = new_builder;
+
+ return NSERROR_OK;
+}
+
+/* exported interface documented in gtk/resources.h */
+nserror
+nsgtk_data_from_resname(const char *resname,
+ const uint8_t ** data_out,
+ size_t *data_size_out)
+{
+#ifdef WITH_GRESOURCE
+ struct nsgtk_resource_s *resource;
+ GBytes *data;
+ const gchar *buffer;
+ gsize buffer_length;
+
+ resource = find_resource_from_name(resname, &direct_resource[0]);
+ if ((resource->name == NULL) ||
+ (resource->type != NSGTK_RESOURCE_DIRECT)) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ data = (GBytes *)resource->path;
+
+ buffer_length = 0;
+ buffer = g_bytes_get_data(data, &buffer_length);
+
+ if (buffer == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ *data_out = (const uint8_t *)buffer;
+ *data_size_out = (size_t)buffer_length;
+
+ return NSERROR_OK;
+#else
+ /** \todo consider adding compiled inline resources for things
+ * other than pixbufs.
+ */
+ return NSERROR_NOT_FOUND;
+#endif
+}
+
+/* exported interface documented in gtk/resources.h */
+nserror
+nsgtk_path_from_resname(const char *resname, const char **path_out)
+{
+ struct nsgtk_resource_s *resource;
+
+ resource = find_resource_from_name(resname, &direct_resource[0]);
+ if ((resource->name == NULL) ||
+ (resource->type != NSGTK_RESOURCE_FILE)) {
+ return NSERROR_NOT_FOUND;
+ }
+
+ *path_out = (const char *)resource->path;
+
+ return NSERROR_OK;
+}
diff --git a/frontends/gtk/resources.h b/frontends/gtk/resources.h
new file mode 100644
index 000000000..923031af4
--- /dev/null
+++ b/frontends/gtk/resources.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Interface to gtk builtin resource handling.
+ *
+ * This presents a unified interface to the rest of the codebase to
+ * obtain resources. Note this is not anything to do with the resource
+ * scheme handling beyond possibly providing the underlying data.
+ *
+ */
+
+#ifndef NETSURF_GTK_RESOURCES_H
+#define NETSURF_GTK_RESOURCES_H 1
+
+/**
+ * Initialise GTK resources handling.
+ *
+ * Must be called before attempting to retrieve any resources but
+ * after logging is initialised as it logs.
+ *
+ * \param respath A string vector of paths to search for resources.
+ * \return NSERROR_OK if all resources were located else an
+ * appropriate error code.
+ */
+nserror nsgtk_init_resources(char **respath);
+
+/**
+ * Creates a menu cursor from internal resources.
+ *
+ * \return Cursor object or NULL on error.
+ */
+GdkCursor *nsgtk_create_menu_cursor(void);
+
+/**
+ * Create gtk builder object for the named ui resource.
+ *
+ * Creating gtk builder objects from a named resource requires the
+ * source xml resource to be parsed.
+ *
+ * This creates a gtk builder instance using an identifier name which
+ * is mapped to the ui_resource table which must be initialised with
+ * nsgtk_init_resources()
+ *
+ * \param resname The resource name to construct for
+ * \param builder_out The builder result
+ * \return NSERROR_OK and builder_out updated or appropriate error code
+ */
+nserror nsgtk_builder_new_from_resname(const char *resname, GtkBuilder **builder_out);
+
+
+/**
+ * Create gdk pixbuf for the named ui resource.
+ *
+ * This creates a pixbuf using an identifier name which is mapped to
+ * the ui_resource table which must be initialised with
+ * nsgtk_init_resources()
+ *
+ * \param resname The resource name to construct for
+ * \param pixbuf_out The pixbuf result
+ * \return NSERROR_OK and pixbuf_out updated or appropriate error code
+ */
+nserror nsgdk_pixbuf_new_from_resname(const char *resname, GdkPixbuf **pixbuf_out);
+
+/**
+ * Get direct pointer to resource data.
+ *
+ * For a named resource this obtains a direct acesss pointer to the
+ * data and its length.
+ *
+ * The data is read only through this pointer and remains valid until
+ * program exit.
+ *
+ * \param resname The resource name to obtain data for.
+ * \param data_out The resulting data.
+ * \param data_size_out The resulting data size.
+ * \return NSERROR_OK and data_out updated or appropriate error code.
+ */
+nserror nsgtk_data_from_resname(const char *resname, const uint8_t **data_out, size_t *data_size_out);
+
+/**
+ * Get path to resource data.
+ *
+ * For a named resource this obtains the on-disc path to that resource.
+ *
+ * The path is read only and remains valid untill program exit.
+ * \param resname The resource name to obtain path for.
+ * \param path_out The resulting data.
+ * \return NSERROR_OK and path_out updated or appropriate error code.
+ */
+nserror nsgtk_path_from_resname(const char *resname, const char **path_out);
+
+#endif
diff --git a/frontends/gtk/scaffolding.c b/frontends/gtk/scaffolding.c
new file mode 100644
index 000000000..777256703
--- /dev/null
+++ b/frontends/gtk/scaffolding.c
@@ -0,0 +1,2811 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "utils/utils.h"
+#include "utils/dirent.h"
+#include "utils/messages.h"
+#include "utils/corestrings.h"
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/file.h"
+#include "desktop/browser_history.h"
+#include "desktop/browser.h"
+#include "desktop/hotlist.h"
+#include "desktop/plotters.h"
+#include "desktop/print.h"
+#include "desktop/save_complete.h"
+#ifdef WITH_PDF_EXPORT
+#include "desktop/font_haru.h"
+#include "desktop/save_pdf.h"
+#endif
+#include "desktop/save_text.h"
+#include "desktop/searchweb.h"
+#include "desktop/textinput.h"
+#include "content/hlcache.h"
+
+#include "gtk/compat.h"
+#include "gtk/warn.h"
+#include "gtk/cookies.h"
+#include "gtk/completion.h"
+#include "gtk/preferences.h"
+#include "gtk/about.h"
+#include "gtk/viewsource.h"
+#include "gtk/bitmap.h"
+#include "gtk/gui.h"
+#include "gtk/history.h"
+#include "gtk/hotlist.h"
+#include "gtk/download.h"
+#include "gtk/menu.h"
+#include "gtk/plotters.h"
+#include "gtk/print.h"
+#include "gtk/search.h"
+#include "gtk/throbber.h"
+#include "gtk/toolbar.h"
+#include "gtk/window.h"
+#include "gtk/gdk.h"
+#include "gtk/scaffolding.h"
+#include "gtk/tabs.h"
+#include "gtk/schedule.h"
+#include "gtk/viewdata.h"
+#include "gtk/resources.h"
+#include "gtk/layout_pango.h"
+
+/** Macro to define a handler for menu, button and activate events. */
+#define MULTIHANDLER(q)\
+static gboolean nsgtk_on_##q##_activate(struct nsgtk_scaffolding *g);\
+static gboolean nsgtk_on_##q##_activate_menu(GtkMenuItem *widget, gpointer data)\
+{\
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;\
+ return nsgtk_on_##q##_activate(g);\
+}\
+static gboolean nsgtk_on_##q##_activate_button(GtkButton *widget, gpointer data)\
+{\
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;\
+ return nsgtk_on_##q##_activate(g);\
+}\
+static gboolean nsgtk_on_##q##_activate(struct nsgtk_scaffolding *g)
+
+/** Macro to define a handler for menu events. */
+#define MENUHANDLER(q)\
+static gboolean nsgtk_on_##q##_activate_menu(GtkMenuItem *widget, gpointer data)
+
+/** Macro to define a handler for button events. */
+#define BUTTONHANDLER(q)\
+static gboolean nsgtk_on_##q##_activate(GtkButton *widget, gpointer data)
+
+/** Core scaffolding structure. */
+struct nsgtk_scaffolding {
+ /** global linked list of scaffoldings for gui interface adjustments */
+ struct nsgtk_scaffolding *next, *prev;
+
+ /** currently active gui browsing context */
+ struct gui_window *top_level;
+
+ /** local history window */
+ struct gtk_history_window *history_window;
+
+ /** Builder object scaffold was created from */
+ GtkBuilder *builder;
+
+ /** scaffold container window */
+ GtkWindow *window;
+ bool fullscreen; /**< flag for the scaffold window fullscreen status */
+
+ /** tab widget holding displayed pages */
+ GtkNotebook *notebook;
+
+ /** entry widget holding the url of the current displayed page */
+ GtkWidget *url_bar;
+ GtkEntryCompletion *url_bar_completion; /**< Completions for url_bar */
+
+ /** Activity throbber */
+ GtkImage *throbber;
+ int throb_frame; /**< Current frame of throbber animation */
+
+ struct gtk_search *search;
+ /** Web search widget */
+ GtkWidget *webSearchEntry;
+
+ /** controls toolbar */
+ GtkToolbar *tool_bar;
+ struct nsgtk_button_connect *buttons[PLACEHOLDER_BUTTON];
+ int offset;
+ int toolbarmem;
+ int toolbarbase;
+ int historybase;
+
+ /** menu bar hierarchy */
+ struct nsgtk_bar_submenu *menu_bar;
+
+ /** right click popup menu hierarchy */
+ struct nsgtk_popup_menu *menu_popup;
+
+ /** link popup menu */
+ struct nsgtk_link_menu *link_menu;
+
+};
+
+/** current scaffold for model dialogue use */
+static struct nsgtk_scaffolding *scaf_current;
+
+/** global list for interface changes */
+static struct nsgtk_scaffolding *scaf_list = NULL;
+
+/** holds the context data for what's under the pointer, when the contextual
+ * menu is opened.
+ */
+static struct browser_window_features current_menu_features;
+
+
+/**
+ * Helper to hide popup menu entries by grouping
+ */
+static void popup_menu_hide(struct nsgtk_popup_menu *menu, bool submenu,
+ bool nav, bool cnp, bool custom)
+{
+ if (submenu){
+ gtk_widget_hide(GTK_WIDGET(menu->file_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->edit_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->view_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->nav_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->help_menuitem));
+
+ gtk_widget_hide(menu->first_separator);
+ }
+
+ if (nav) {
+ gtk_widget_hide(GTK_WIDGET(menu->back_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->forward_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->stop_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->reload_menuitem));
+ }
+
+ if (cnp) {
+ gtk_widget_hide(GTK_WIDGET(menu->cut_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->copy_menuitem));
+ gtk_widget_hide(GTK_WIDGET(menu->paste_menuitem));
+ }
+
+ if (custom) {
+ gtk_widget_hide(GTK_WIDGET(menu->customize_menuitem));
+ }
+
+}
+
+/**
+ * Helper to show popup menu entries by grouping
+ */
+static void popup_menu_show(struct nsgtk_popup_menu *menu, bool submenu,
+ bool nav, bool cnp, bool custom)
+{
+ if (submenu){
+ gtk_widget_show(GTK_WIDGET(menu->file_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->edit_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->view_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->nav_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->help_menuitem));
+
+ gtk_widget_show(menu->first_separator);
+ }
+
+ if (nav) {
+ gtk_widget_show(GTK_WIDGET(menu->back_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->forward_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->stop_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->reload_menuitem));
+ }
+
+ if (cnp) {
+ gtk_widget_show(GTK_WIDGET(menu->cut_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->copy_menuitem));
+ gtk_widget_show(GTK_WIDGET(menu->paste_menuitem));
+ }
+
+ if (custom) {
+ gtk_widget_show(GTK_WIDGET(menu->customize_menuitem));
+ }
+
+}
+
+
+/* event handlers and support functions for them */
+
+/**
+ * resource cleanup function for window destruction.
+ */
+static void scaffolding_window_destroy(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *gs = data;
+
+ LOG("scaffold:%p", gs);
+
+ if ((gs->history_window) && (gs->history_window->window)) {
+ gtk_widget_destroy(GTK_WIDGET(gs->history_window->window));
+ }
+
+ if (gs->prev != NULL) {
+ gs->prev->next = gs->next;
+ } else {
+ scaf_list = gs->next;
+ }
+ if (gs->next != NULL) {
+ gs->next->prev = gs->prev;
+ }
+
+ LOG("scaffold list head: %p", scaf_list);
+
+ if (scaf_list == NULL) {
+ /* no more open windows - stop the browser */
+ nsgtk_complete = true;
+ }
+}
+
+/* signal delivered on window delete event, allowing to halt close if
+ * download is in progress
+ */
+static gboolean scaffolding_window_delete_event(GtkWidget *widget,
+ GdkEvent *event, gpointer data)
+{
+ struct nsgtk_scaffolding *g = data;
+
+ if (nsgtk_check_for_downloads(GTK_WINDOW(widget)) == false) {
+ gtk_widget_destroy(GTK_WIDGET(g->window));
+ }
+ return TRUE;
+}
+
+/**
+ * Update the scaffoling button sensitivity, url bar and local history size
+ */
+static void scaffolding_update_context(struct nsgtk_scaffolding *g)
+{
+ int width, height;
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ g->buttons[BACK_BUTTON]->sensitivity =
+ browser_window_history_back_available(bw);
+ g->buttons[FORWARD_BUTTON]->sensitivity =
+ browser_window_history_forward_available(bw);
+
+ nsgtk_scaffolding_set_sensitivity(g);
+
+ /* update the url bar, particularly necessary when tabbing */
+ browser_window_refresh_url_bar(bw);
+
+ /* update the local history window, as well as queuing a redraw
+ * for it.
+ */
+ browser_window_history_size(bw, &width, &height);
+ gtk_widget_set_size_request(GTK_WIDGET(g->history_window->drawing_area),
+ width, height);
+ gtk_widget_queue_draw(GTK_WIDGET(g->history_window->drawing_area));
+}
+
+/**
+ * Make the throbber run.
+ */
+static void nsgtk_throb(void *p)
+{
+ struct nsgtk_scaffolding *g = p;
+
+ if (g->throb_frame >= (nsgtk_throbber->nframes - 1))
+ g->throb_frame = 1;
+ else
+ g->throb_frame++;
+
+ gtk_image_set_from_pixbuf(g->throbber, nsgtk_throbber->framedata[
+ g->throb_frame]);
+
+ nsgtk_schedule(100, nsgtk_throb, p);
+}
+
+static guint nsgtk_scaffolding_update_edit_actions_sensitivity(
+ struct nsgtk_scaffolding *g)
+{
+ GtkWidget *widget = gtk_window_get_focus(g->window);
+ gboolean has_selection;
+
+ if (GTK_IS_EDITABLE(widget)) {
+ has_selection = gtk_editable_get_selection_bounds(
+ GTK_EDITABLE (widget), NULL, NULL);
+
+ g->buttons[COPY_BUTTON]->sensitivity = has_selection;
+ g->buttons[CUT_BUTTON]->sensitivity = has_selection;
+ g->buttons[PASTE_BUTTON]->sensitivity = true;
+ } else {
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+ browser_editor_flags edit_f =
+ browser_window_get_editor_flags(bw);
+
+ g->buttons[COPY_BUTTON]->sensitivity =
+ edit_f & BW_EDITOR_CAN_COPY;
+ g->buttons[CUT_BUTTON]->sensitivity =
+ edit_f & BW_EDITOR_CAN_CUT;
+ g->buttons[PASTE_BUTTON]->sensitivity =
+ edit_f & BW_EDITOR_CAN_PASTE;
+ }
+
+ nsgtk_scaffolding_set_sensitivity(g);
+ return ((g->buttons[COPY_BUTTON]->sensitivity) |
+ (g->buttons[CUT_BUTTON]->sensitivity) |
+ (g->buttons[PASTE_BUTTON]->sensitivity));
+}
+
+
+static void nsgtk_scaffolding_enable_edit_actions_sensitivity(
+ struct nsgtk_scaffolding *g)
+{
+
+ g->buttons[PASTE_BUTTON]->sensitivity = true;
+ g->buttons[COPY_BUTTON]->sensitivity = true;
+ g->buttons[CUT_BUTTON]->sensitivity = true;
+ nsgtk_scaffolding_set_sensitivity(g);
+
+ popup_menu_show(g->menu_popup, false, false, true, false);
+}
+
+/* signal handling functions for the toolbar, URL bar, and menu bar */
+static gboolean nsgtk_window_edit_menu_clicked(GtkWidget *widget,
+ struct nsgtk_scaffolding *g)
+{
+ nsgtk_scaffolding_update_edit_actions_sensitivity(g);
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_edit_menu_hidden(GtkWidget *widget,
+ struct nsgtk_scaffolding *g)
+{
+ nsgtk_scaffolding_enable_edit_actions_sensitivity(g);
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_popup_menu_hidden(GtkWidget *widget,
+ struct nsgtk_scaffolding *g)
+{
+ nsgtk_scaffolding_enable_edit_actions_sensitivity(g);
+ return TRUE;
+}
+
+gboolean nsgtk_window_url_activate_event(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = data;
+ nserror ret;
+ nsurl *url;
+
+ ret = search_web_omni(gtk_entry_get_text(GTK_ENTRY(g->url_bar)),
+ SEARCH_WEB_OMNI_NONE,
+ &url);
+ if (ret == NSERROR_OK) {
+ ret = browser_window_navigate(nsgtk_get_browser_window(g->top_level),
+ url, NULL, BW_NAVIGATE_HISTORY,
+ NULL, NULL, NULL);
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+
+ return TRUE;
+}
+
+/**
+ * update handler for URL entry widget
+ */
+gboolean
+nsgtk_window_url_changed(GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer data)
+{
+ return nsgtk_completion_update(GTK_ENTRY(widget));
+}
+
+/**
+ * Event handler for popup menu on toolbar.
+ */
+static gboolean nsgtk_window_tool_bar_clicked(GtkToolbar *toolbar,
+ gint x, gint y, gint button, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+
+ /* set visibility for right-click popup menu */
+ popup_menu_hide(g->menu_popup, true, false, true, false);
+ popup_menu_show(g->menu_popup, false, false, false, true);
+
+ gtk_menu_popup(g->menu_popup->popup_menu, NULL, NULL, NULL, NULL, 0,
+ gtk_get_current_event_time());
+
+ return TRUE;
+}
+
+/**
+ * Update the menus when the number of tabs changes.
+ */
+static void nsgtk_window_tabs_add(GtkNotebook *notebook,
+ GtkWidget *page, guint page_num, struct nsgtk_scaffolding *g)
+{
+ gboolean visible = gtk_notebook_get_show_tabs(g->notebook);
+ g_object_set(g->menu_bar->view_submenu->tabs_menuitem, "visible", visible, NULL);
+ g_object_set(g->menu_popup->view_submenu->tabs_menuitem, "visible", visible, NULL);
+ g->buttons[NEXTTAB_BUTTON]->sensitivity = visible;
+ g->buttons[PREVTAB_BUTTON]->sensitivity = visible;
+ g->buttons[CLOSETAB_BUTTON]->sensitivity = visible;
+ nsgtk_scaffolding_set_sensitivity(g);
+}
+
+/**
+ * Update the menus when the number of tabs changes.
+ */
+static void
+nsgtk_window_tabs_remove(GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ struct nsgtk_scaffolding *gs)
+{
+ /* if the scaffold is being destroyed it is not useful to
+ * update the state, futher many of the widgets may have
+ * already been destroyed.
+ */
+ if (gtk_widget_in_destruction(GTK_WIDGET(gs->window)) == TRUE) {
+ return;
+ }
+
+ /* if this is the last tab destroy the scaffold in addition */
+ if (gtk_notebook_get_n_pages(notebook) == 1) {
+ gtk_widget_destroy(GTK_WIDGET(gs->window));
+ return;
+ }
+
+ gboolean visible = gtk_notebook_get_show_tabs(gs->notebook);
+ g_object_set(gs->menu_bar->view_submenu->tabs_menuitem, "visible", visible, NULL);
+ g_object_set(gs->menu_popup->view_submenu->tabs_menuitem, "visible", visible, NULL);
+ gs->buttons[NEXTTAB_BUTTON]->sensitivity = visible;
+ gs->buttons[PREVTAB_BUTTON]->sensitivity = visible;
+ gs->buttons[CLOSETAB_BUTTON]->sensitivity = visible;
+ nsgtk_scaffolding_set_sensitivity(gs);
+}
+
+/**
+ * Handle opening a file path.
+ */
+static void nsgtk_openfile_open(const char *filename)
+{
+ struct browser_window *bw;
+ char *urltxt;
+ nsurl *url;
+ nserror error;
+
+ bw = nsgtk_get_browser_window(scaf_current->top_level);
+
+ urltxt = malloc(strlen(filename) + FILE_SCHEME_PREFIX_LEN + 1);
+
+ if (urltxt != NULL) {
+ sprintf(urltxt, FILE_SCHEME_PREFIX"%s", filename);
+
+ error = nsurl_create(urltxt, &url);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+ free(urltxt);
+ }
+}
+
+/* signal handlers for menu entries */
+
+MULTIHANDLER(newwindow)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ const char *addr;
+ nsurl *url;
+ nserror error;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ } else {
+ addr = NETSURF_HOMEPAGE;
+ }
+
+ error = nsurl_create(addr, &url);
+ if (error == NSERROR_OK) {
+ error = browser_window_create(BW_CREATE_HISTORY,
+ url,
+ NULL,
+ bw,
+ NULL);
+ nsurl_unref(url);
+ }
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ }
+
+ return TRUE;
+}
+
+nserror nsgtk_scaffolding_new_tab(struct gui_window *gw)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+ nsurl *url = NULL;
+ nserror error;
+
+ if (!nsoption_bool(new_blank)) {
+ const char *addr;
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ } else {
+ addr = NETSURF_HOMEPAGE;
+ }
+ error = nsurl_create(addr, &url);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ }
+ }
+
+ error = browser_window_create(BW_CREATE_HISTORY |
+ BW_CREATE_TAB,
+ url,
+ NULL,
+ bw,
+ NULL);
+ if (url != NULL) {
+ nsurl_unref(url);
+ }
+ return error;
+}
+
+MULTIHANDLER(newtab)
+{
+ nserror error;
+
+ error = nsgtk_scaffolding_new_tab(g->top_level);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ }
+ return TRUE;
+}
+
+MULTIHANDLER(openfile)
+{
+ GtkWidget *dlgOpen;
+ gint response;
+
+ scaf_current = g;
+ dlgOpen = gtk_file_chooser_dialog_new("Open File",
+ scaf_current->window,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_OPEN, GTK_RESPONSE_OK,
+ NULL, NULL);
+
+ response = gtk_dialog_run(GTK_DIALOG(dlgOpen));
+ if (response == GTK_RESPONSE_OK) {
+ gchar *filename;
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dlgOpen));
+
+ nsgtk_openfile_open((const char *)filename);
+
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(dlgOpen);
+ return TRUE;
+}
+
+static gboolean nsgtk_filter_directory(const GtkFileFilterInfo *info,
+ gpointer data)
+{
+ DIR *d = opendir(info->filename);
+ if (d == NULL)
+ return FALSE;
+ closedir(d);
+ return TRUE;
+}
+
+MULTIHANDLER(savepage)
+{
+ if (!browser_window_has_content(nsgtk_get_browser_window(g->top_level)))
+ return FALSE;
+
+ GtkWidget *fc = gtk_file_chooser_dialog_new(
+ messages_get("gtkcompleteSave"), g->window,
+ GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ DIR *d;
+ char *path;
+ nserror res;
+ GtkFileFilter *filter = gtk_file_filter_new();
+ gtk_file_filter_set_name(filter, "Directories");
+ gtk_file_filter_add_custom(filter, GTK_FILE_FILTER_FILENAME,
+ nsgtk_filter_directory, NULL, NULL);
+ gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fc), filter);
+ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fc), filter);
+
+ res = nsurl_nice(browser_window_get_url(
+ nsgtk_get_browser_window(g->top_level)), &path, false);
+ if (res != NSERROR_OK) {
+ path = strdup(messages_get("SaveText"));
+ if (path == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return FALSE;
+ }
+ }
+
+ if (access(path, F_OK) != 0)
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), path);
+ free(path);
+
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
+ TRUE);
+
+ if (gtk_dialog_run(GTK_DIALOG(fc)) != GTK_RESPONSE_ACCEPT) {
+ gtk_widget_destroy(fc);
+ return TRUE;
+ }
+
+ path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+ d = opendir(path);
+ if (d == NULL) {
+ LOG("Unable to open directory %s for complete save: %s", path, strerror(errno));
+ if (errno == ENOTDIR)
+ nsgtk_warning("NoDirError", path);
+ else
+ nsgtk_warning("gtkFileError", path);
+ gtk_widget_destroy(fc);
+ g_free(path);
+ return TRUE;
+ }
+ closedir(d);
+ save_complete(browser_window_get_content(nsgtk_get_browser_window(
+ g->top_level)), path, NULL);
+ g_free(path);
+
+ gtk_widget_destroy(fc);
+
+ return TRUE;
+}
+
+
+MULTIHANDLER(pdf)
+{
+#ifdef WITH_PDF_EXPORT
+
+ GtkWidget *save_dialog;
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ struct print_settings *settings;
+ char filename[PATH_MAX];
+ char dirname[PATH_MAX];
+ char *url_name;
+ nserror res;
+
+ LOG("Print preview (generating PDF) started.");
+
+ res = nsurl_nice(browser_window_get_url(bw), &url_name, true);
+ if (res != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(res), 0);
+ return TRUE;
+ }
+
+ strncpy(filename, url_name, PATH_MAX);
+ strncat(filename, ".pdf", PATH_MAX - strlen(filename));
+ filename[PATH_MAX - 1] = '\0';
+
+ free(url_name);
+
+ strncpy(dirname, option_downloads_directory, PATH_MAX);
+ strncat(dirname, "/", PATH_MAX - strlen(dirname));
+ dirname[PATH_MAX - 1] = '\0';
+
+ /* this way the scale used by PDF functions is synchronized with that
+ * used by the all-purpose print interface
+ */
+ haru_nsfont_set_scale((float)option_export_scale / 100);
+
+ save_dialog = gtk_file_chooser_dialog_new("Export to PDF", g->window,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog),
+ dirname);
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog),
+ filename);
+
+ if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) {
+ gchar *filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(save_dialog));
+
+ settings = print_make_settings(PRINT_OPTIONS,
+ (const char *) filename, &haru_nsfont);
+ g_free(filename);
+
+ if (settings == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ gtk_widget_destroy(save_dialog);
+ return TRUE;
+ }
+
+ /* This will clean up the print_settings object for us */
+ print_basic_run(browser_window_get_content(bw),
+ &pdf_printer, settings);
+ }
+
+ gtk_widget_destroy(save_dialog);
+
+#endif /* WITH_PDF_EXPORT */
+
+ return TRUE;
+}
+
+MULTIHANDLER(plaintext)
+{
+ if (!browser_window_has_content(nsgtk_get_browser_window(g->top_level)))
+ return FALSE;
+
+ GtkWidget *fc = gtk_file_chooser_dialog_new(
+ messages_get("gtkplainSave"), g->window,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ char *filename;
+ nserror res;
+
+ res = nsurl_nice(browser_window_get_url(
+ nsgtk_get_browser_window(g->top_level)),
+ &filename, false);
+ if (res != NSERROR_OK) {
+ filename = strdup(messages_get("SaveText"));
+ if (filename == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return FALSE;
+ }
+ }
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), filename);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
+ TRUE);
+
+ free(filename);
+
+ if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) {
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+ save_as_text(browser_window_get_content(
+ nsgtk_get_browser_window(
+ g->top_level)), filename);
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(fc);
+ return TRUE;
+}
+
+MULTIHANDLER(drawfile)
+{
+ return TRUE;
+}
+
+MULTIHANDLER(postscript)
+{
+ return TRUE;
+}
+
+MULTIHANDLER(printpreview)
+{
+ return TRUE;
+}
+
+
+MULTIHANDLER(print)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ GtkPrintOperation *print_op;
+ GtkPageSetup *page_setup;
+ GtkPrintSettings *print_settings;
+ GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR;
+ struct print_settings *nssettings;
+ char *settings_fname = NULL;
+
+ print_op = gtk_print_operation_new();
+ if (print_op == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return TRUE;
+ }
+
+ /* use previously saved settings if any */
+ netsurf_mkpath(&settings_fname, NULL, 2, nsgtk_config_home, "Print");
+ if (settings_fname != NULL) {
+ print_settings = gtk_print_settings_new_from_file(settings_fname, NULL);
+ if (print_settings != NULL) {
+ gtk_print_operation_set_print_settings(print_op,
+ print_settings);
+
+ /* We're not interested in the settings any more */
+ g_object_unref(print_settings);
+ }
+ }
+
+ content_to_print = browser_window_get_content(bw);
+
+ page_setup = gtk_print_run_page_setup_dialog(g->window, NULL, NULL);
+ if (page_setup == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ free(settings_fname);
+ g_object_unref(print_op);
+ return TRUE;
+ }
+ gtk_print_operation_set_default_page_setup(print_op, page_setup);
+
+ nssettings = print_make_settings(PRINT_DEFAULT, NULL, nsgtk_layout_table);
+
+ g_signal_connect(print_op, "begin_print",
+ G_CALLBACK(gtk_print_signal_begin_print), nssettings);
+ g_signal_connect(print_op, "draw_page",
+ G_CALLBACK(gtk_print_signal_draw_page), NULL);
+ g_signal_connect(print_op, "end_print",
+ G_CALLBACK(gtk_print_signal_end_print), nssettings);
+
+ if (content_get_type(browser_window_get_content(bw)) !=
+ CONTENT_TEXTPLAIN) {
+ res = gtk_print_operation_run(print_op,
+ GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+ g->window,
+ NULL);
+ }
+
+ /* if the settings were used save them for future use */
+ if (settings_fname != NULL) {
+ if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
+ /* Do not increment the settings reference */
+ print_settings =
+ gtk_print_operation_get_print_settings(print_op);
+
+ gtk_print_settings_to_file(print_settings,
+ settings_fname,
+ NULL);
+ }
+ free(settings_fname);
+ }
+
+ /* Our print_settings object is destroyed by the end print handler */
+ g_object_unref(page_setup);
+ g_object_unref(print_op);
+
+ return TRUE;
+}
+
+MULTIHANDLER(closewindow)
+{
+ gtk_widget_destroy(GTK_WIDGET(g->window));
+ return TRUE;
+}
+
+MULTIHANDLER(quit)
+{
+ struct nsgtk_scaffolding *gs;
+
+ if (nsgtk_check_for_downloads(g->window) == false) {
+ gs = scaf_list;
+ while (gs != NULL) {
+ gtk_widget_destroy(GTK_WIDGET(gs->window));
+ gs = gs->next;
+ }
+ }
+
+ return TRUE;
+}
+
+MENUHANDLER(savelink)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data;
+ struct gui_window *gui = g->top_level;
+ struct browser_window *bw = nsgtk_get_browser_window(gui);
+ nserror err;
+
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ err = browser_window_navigate(bw,
+ current_menu_features.link,
+ NULL,
+ BW_NAVIGATE_DOWNLOAD,
+ NULL,
+ NULL,
+ NULL);
+ if (err != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(err), 0);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Handler for opening new window from a link. attached to the popup menu.
+ */
+MENUHANDLER(link_openwin)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data;
+ struct gui_window *gui = g->top_level;
+ struct browser_window *bw = nsgtk_get_browser_window(gui);
+ nserror err;
+
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ err = browser_window_create(BW_CREATE_CLONE | BW_CREATE_HISTORY,
+ current_menu_features.link, NULL, bw, NULL);
+ if (err != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(err), 0);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Handler for opening new tab from a link. attached to the popup menu.
+ */
+MENUHANDLER(link_opentab)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *) data;
+ struct gui_window *gui = g->top_level;
+ struct browser_window *bw = nsgtk_get_browser_window(gui);
+ nserror err;
+
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ temp_open_background = 1;
+
+ err = browser_window_create(BW_CREATE_CLONE |
+ BW_CREATE_HISTORY |
+ BW_CREATE_TAB,
+ current_menu_features.link, NULL, bw, NULL);
+ if (err != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(err), 0);
+ }
+
+ temp_open_background = -1;
+
+ return TRUE;
+}
+
+/**
+ * Handler for bookmarking a link. attached to the popup menu.
+ */
+MENUHANDLER(link_bookmark)
+{
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ hotlist_add_url(current_menu_features.link);
+
+ return TRUE;
+}
+
+/**
+ * Handler for copying a link. attached to the popup menu.
+ */
+MENUHANDLER(link_copy)
+{
+ GtkClipboard *clipboard;
+
+ if (current_menu_features.link == NULL)
+ return FALSE;
+
+ clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text(clipboard,
+ nsurl_access(current_menu_features.link), -1);
+
+ return TRUE;
+}
+
+
+MULTIHANDLER(cut)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ GtkWidget *focused = gtk_window_get_focus(g->window);
+
+ /* If the url bar has focus, let gtk handle it */
+ if (GTK_IS_EDITABLE (focused))
+ gtk_editable_cut_clipboard (GTK_EDITABLE(g->url_bar));
+ else
+ browser_window_key_press(bw, NS_KEY_CUT_SELECTION);
+
+ return TRUE;
+}
+
+MULTIHANDLER(copy)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ GtkWidget *focused = gtk_window_get_focus(g->window);
+
+ /* If the url bar has focus, let gtk handle it */
+ if (GTK_IS_EDITABLE (focused))
+ gtk_editable_copy_clipboard(GTK_EDITABLE(g->url_bar));
+ else
+ browser_window_key_press(bw, NS_KEY_COPY_SELECTION);
+
+ return TRUE;
+}
+
+MULTIHANDLER(paste)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ GtkWidget *focused = gtk_window_get_focus(g->window);
+
+ /* If the url bar has focus, let gtk handle it */
+ if (GTK_IS_EDITABLE (focused))
+ gtk_editable_paste_clipboard (GTK_EDITABLE (focused));
+ else
+ browser_window_key_press(bw, NS_KEY_PASTE);
+
+ return TRUE;
+}
+
+MULTIHANDLER(delete)
+{
+ return TRUE;
+}
+
+MENUHANDLER(customize)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ nsgtk_toolbar_customization_init(g);
+ return TRUE;
+}
+
+MULTIHANDLER(selectall)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ if (nsgtk_widget_has_focus(GTK_WIDGET(g->url_bar))) {
+ LOG("Selecting all URL bar text");
+ gtk_editable_select_region(GTK_EDITABLE(g->url_bar), 0, -1);
+ } else {
+ LOG("Selecting all document text");
+ browser_window_key_press(bw, NS_KEY_SELECT_ALL);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(find)
+{
+ nsgtk_scaffolding_toggle_search_bar_visibility(g);
+ return TRUE;
+}
+
+MULTIHANDLER(preferences)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ GtkWidget* wndpreferences;
+
+ wndpreferences = nsgtk_preferences(bw, g->window);
+ if (wndpreferences != NULL) {
+ gtk_widget_show(GTK_WIDGET(wndpreferences));
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(zoomplus)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ float old_scale = nsgtk_get_scale_for_gui(g->top_level);
+
+ browser_window_set_scale(bw, old_scale + 0.05, true);
+
+ return TRUE;
+}
+
+MULTIHANDLER(zoomnormal)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ browser_window_set_scale(bw, 1.0, true);
+
+ return TRUE;
+}
+
+MULTIHANDLER(zoomminus)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ float old_scale = nsgtk_get_scale_for_gui(g->top_level);
+
+ browser_window_set_scale(bw, old_scale - 0.05, true);
+
+ return TRUE;
+}
+
+MULTIHANDLER(fullscreen)
+{
+ if (g->fullscreen) {
+ gtk_window_unfullscreen(g->window);
+ } else {
+ gtk_window_fullscreen(g->window);
+ }
+
+ g->fullscreen = !g->fullscreen;
+
+ return TRUE;
+}
+
+MULTIHANDLER(viewsource)
+{
+ nserror ret;
+
+ ret = nsgtk_viewsource(g->window, nsgtk_get_browser_window(g->top_level));
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+
+ return TRUE;
+}
+
+MENUHANDLER(menubar)
+{
+ GtkWidget *w;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+
+ /* if the menubar is not being shown the popup menu shows the
+ * menubar entries instead.
+ */
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
+ /* need to synchronise menus as gtk grumbles when one menu
+ * is attached to both headers */
+ w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+
+ w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+
+ gtk_widget_show(GTK_WIDGET(g->menu_bar->bar_menu));
+
+ popup_menu_show(g->menu_popup, false, true, true, true);
+ popup_menu_hide(g->menu_popup, true, false, false, false);
+ } else {
+ w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+
+ w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->menubar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+
+ gtk_widget_hide(GTK_WIDGET(g->menu_bar->bar_menu));
+
+ popup_menu_show(g->menu_popup, true, true, true, true);
+
+ }
+ return TRUE;
+}
+
+MENUHANDLER(toolbar)
+{
+ GtkWidget *w;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
+ w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+
+ w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))
+ == FALSE)
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ TRUE);
+ gtk_widget_show(GTK_WIDGET(g->tool_bar));
+ } else {
+ w = GTK_WIDGET(g->menu_popup->view_submenu->toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+ w = GTK_WIDGET(g->menu_bar->view_submenu->toolbars_submenu->toolbar_menuitem);
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),
+ FALSE);
+ gtk_widget_hide(GTK_WIDGET(g->tool_bar));
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(downloads)
+{
+ nsgtk_download_show(g->window);
+
+ return TRUE;
+}
+
+MULTIHANDLER(savewindowsize)
+{
+ int x,y,w,h;
+ char *choices = NULL;
+
+ gtk_window_get_position(g->window, &x, &y);
+ gtk_window_get_size(g->window, &w, &h);
+
+ nsoption_set_int(window_width, w);
+ nsoption_set_int(window_height, h);
+ nsoption_set_int(window_x, x);
+ nsoption_set_int(window_y, y);
+
+ netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices");
+ if (choices != NULL) {
+ nsoption_write(choices, NULL, NULL);
+ free(choices);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(toggledebugging)
+{
+ struct browser_window *bw;
+
+ bw = nsgtk_get_browser_window(g->top_level);
+
+ browser_window_debug(bw, CONTENT_DEBUG_REDRAW);
+
+ nsgtk_reflow_all_windows();
+
+ return TRUE;
+}
+
+MULTIHANDLER(debugboxtree)
+{
+ gchar *fname;
+ gint handle;
+ FILE *f;
+ struct browser_window *bw;
+
+ handle = g_file_open_tmp("nsgtkboxtreeXXXXXX", &fname, NULL);
+ if ((handle == -1) || (fname == NULL)) {
+ return TRUE;
+ }
+ close(handle); /* in case it was binary mode */
+
+ /* save data to temporary file */
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ nsgtk_warning("Error saving box tree dump.",
+ "Unable to open file for writing.");
+ unlink(fname);
+ return TRUE;
+ }
+
+ bw = nsgtk_get_browser_window(g->top_level);
+
+ browser_window_debug_dump(bw, f, CONTENT_DEBUG_RENDER);
+
+ fclose(f);
+
+ nsgtk_viewfile("Box Tree Debug", "boxtree", fname);
+
+ g_free(fname);
+
+ return TRUE;
+}
+
+MULTIHANDLER(debugdomtree)
+{
+ gchar *fname;
+ gint handle;
+ FILE *f;
+ struct browser_window *bw;
+
+ handle = g_file_open_tmp("nsgtkdomtreeXXXXXX", &fname, NULL);
+ if ((handle == -1) || (fname == NULL)) {
+ return TRUE;
+ }
+ close(handle); /* in case it was binary mode */
+
+ /* save data to temporary file */
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ nsgtk_warning("Error saving box tree dump.",
+ "Unable to open file for writing.");
+ unlink(fname);
+ return TRUE;
+ }
+
+ bw = nsgtk_get_browser_window(g->top_level);
+
+ browser_window_debug_dump(bw, f, CONTENT_DEBUG_DOM);
+
+ fclose(f);
+
+ nsgtk_viewfile("DOM Tree Debug", "domtree", fname);
+
+ g_free(fname);
+
+ return TRUE;
+}
+
+
+MULTIHANDLER(stop)
+{
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+
+ browser_window_stop(bw);
+
+ return TRUE;
+}
+
+MULTIHANDLER(reload)
+{
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+ if (bw == NULL)
+ return TRUE;
+
+ /* clear potential search effects */
+ browser_window_search_clear(bw);
+
+ browser_window_reload(bw, true);
+
+ return TRUE;
+}
+
+MULTIHANDLER(back)
+{
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+
+ if ((bw == NULL) || (!browser_window_history_back_available(bw)))
+ return TRUE;
+
+ /* clear potential search effects */
+ browser_window_search_clear(bw);
+
+ browser_window_history_back(bw, false);
+ scaffolding_update_context(g);
+
+ return TRUE;
+}
+
+MULTIHANDLER(forward)
+{
+ struct browser_window *bw =
+ nsgtk_get_browser_window(g->top_level);
+
+ if ((bw == NULL) || (!browser_window_history_forward_available(bw)))
+ return TRUE;
+
+ /* clear potential search effects */
+ browser_window_search_clear(bw);
+
+ browser_window_history_forward(bw, false);
+ scaffolding_update_context(g);
+
+ return TRUE;
+}
+
+MULTIHANDLER(home)
+{
+ static const char *addr = NETSURF_HOMEPAGE;
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ nsurl *url;
+ nserror error;
+
+ if (nsoption_charp(homepage_url) != NULL) {
+ addr = nsoption_charp(homepage_url);
+ }
+
+ error = nsurl_create(addr, &url);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(localhistory)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ int x,y, width, height, mainwidth, mainheight, margin = 20;
+ /* if entries of the same url but different frag_ids have been added
+ * the history needs redrawing (what throbber code normally does)
+ */
+
+ scaffolding_update_context(g);
+ gtk_window_get_position(g->window, &x, &y);
+ gtk_window_get_size(g->window, &mainwidth, &mainheight);
+ browser_window_history_size(bw, &width, &height);
+ width = (width + g->historybase + margin > mainwidth) ?
+ mainwidth - g->historybase : width + margin;
+ height = (height + g->toolbarbase + margin > mainheight) ?
+ mainheight - g->toolbarbase : height + margin;
+ gtk_window_set_default_size(g->history_window->window, width, height);
+ gtk_widget_set_size_request(GTK_WIDGET(g->history_window->window),
+ -1, -1);
+ gtk_window_resize(g->history_window->window, width, height);
+ gtk_window_set_transient_for(g->history_window->window, g->window);
+ nsgtk_window_set_opacity(g->history_window->window, 0.9);
+ gtk_widget_show(GTK_WIDGET(g->history_window->window));
+ gtk_window_move(g->history_window->window, x + g->historybase, y +
+ g->toolbarbase);
+ gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(g->history_window->window)));
+
+ return TRUE;
+}
+
+MULTIHANDLER(globalhistory)
+{
+ gtk_widget_show(GTK_WIDGET(wndHistory));
+ gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(wndHistory)));
+
+ return TRUE;
+}
+
+MULTIHANDLER(addbookmarks)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ if (bw == NULL || !browser_window_has_content(bw))
+ return TRUE;
+ hotlist_add_url(browser_window_get_url(bw));
+ return TRUE;
+}
+
+MULTIHANDLER(showbookmarks)
+{
+ gtk_widget_show(GTK_WIDGET(wndHotlist));
+ gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(wndHotlist)));
+ gtk_window_set_focus(wndHotlist, NULL);
+
+ return TRUE;
+}
+
+MULTIHANDLER(showcookies)
+{
+ gtk_widget_show(GTK_WIDGET(wndCookies));
+ gdk_window_raise(nsgtk_widget_get_window(GTK_WIDGET(wndCookies)));
+
+ return TRUE;
+}
+
+MULTIHANDLER(openlocation)
+{
+ gtk_widget_grab_focus(GTK_WIDGET(g->url_bar));
+ return TRUE;
+}
+
+MULTIHANDLER(nexttab)
+{
+ nsgtk_tab_next(g->notebook);
+
+ return TRUE;
+}
+
+MULTIHANDLER(prevtab)
+{
+
+ nsgtk_tab_prev(g->notebook);
+
+ return TRUE;
+}
+
+MULTIHANDLER(closetab)
+{
+ nsgtk_tab_close_current(g->notebook);
+
+ return TRUE;
+}
+
+MULTIHANDLER(contents)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ nsurl *url;
+ nserror error;
+
+ error = nsurl_create("http://www.netsurf-browser.org/documentation/", &url);
+ if (error != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(error), 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(guide)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ nsurl *url;
+
+ if (nsurl_create("http://www.netsurf-browser.org/documentation/guide", &url) != NSERROR_OK) {
+ nsgtk_warning("NoMemory", 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(info)
+{
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+ nsurl *url;
+
+ if (nsurl_create("http://www.netsurf-browser.org/documentation/info", &url) != NSERROR_OK) {
+ nsgtk_warning("NoMemory", 0);
+ } else {
+ browser_window_navigate(bw,
+ url,
+ NULL,
+ BW_NAVIGATE_HISTORY,
+ NULL,
+ NULL,
+ NULL);
+ nsurl_unref(url);
+ }
+
+ return TRUE;
+}
+
+MULTIHANDLER(about)
+{
+ nsgtk_about_dialog_init(g->window);
+ return TRUE;
+}
+
+BUTTONHANDLER(history)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ return nsgtk_on_localhistory_activate(g);
+}
+
+#undef MULTIHANDLER
+#undef CHECKHANDLER
+#undef BUTTONHANDLER
+
+#if GTK_CHECK_VERSION(3,0,0)
+
+static gboolean
+nsgtk_history_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ struct rect clip;
+ struct gtk_history_window *hw = (struct gtk_history_window *)data;
+ struct browser_window *bw =
+ nsgtk_get_browser_window(hw->g->top_level);
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+
+ current_widget = widget;
+ current_cr = cr;
+
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+
+ clip.x0 = x1;
+ clip.y0 = y1;
+ clip.x1 = x2;
+ clip.y1 = y2;
+
+ ctx.plot->clip(&clip);
+
+ browser_window_history_redraw(bw, &ctx);
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+#else
+
+/* signal handler functions for the local history window */
+static gboolean
+nsgtk_history_draw_event(GtkWidget *widget, GdkEventExpose *event, gpointer g)
+{
+ struct rect clip;
+ struct gtk_history_window *hw = (struct gtk_history_window *)g;
+ struct browser_window *bw =
+ nsgtk_get_browser_window(hw->g->top_level);
+
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+
+ current_widget = widget;
+
+ current_cr = gdk_cairo_create(nsgtk_widget_get_window(widget));
+
+ clip.x0 = event->area.x;
+ clip.y0 = event->area.y;
+ clip.x1 = event->area.x + event->area.width;
+ clip.y1 = event->area.y + event->area.height;
+ ctx.plot->clip(&clip);
+
+ browser_window_history_redraw(bw, &ctx);
+
+ cairo_destroy(current_cr);
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+
+#endif /* GTK_CHECK_VERSION(3,0,0) */
+
+static gboolean nsgtk_history_button_press_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ struct gtk_history_window *hw = (struct gtk_history_window *)g;
+ struct browser_window *bw =
+ nsgtk_get_browser_window(hw->g->top_level);
+
+ LOG("X=%g, Y=%g", event->x, event->y);
+
+ browser_window_history_click(bw, event->x, event->y, false);
+
+ return TRUE;
+}
+
+
+
+static void nsgtk_attach_menu_handlers(struct nsgtk_scaffolding *g)
+{
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if (g->buttons[i]->main != NULL) {
+ g_signal_connect(g->buttons[i]->main, "activate",
+ G_CALLBACK(g->buttons[i]->mhandler), g);
+ }
+ if (g->buttons[i]->rclick != NULL) {
+ g_signal_connect(g->buttons[i]->rclick, "activate",
+ G_CALLBACK(g->buttons[i]->mhandler), g);
+ }
+ if (g->buttons[i]->popup != NULL) {
+ g_signal_connect(g->buttons[i]->popup, "activate",
+ G_CALLBACK(g->buttons[i]->mhandler), g);
+ }
+ }
+#define CONNECT_CHECK(q)\
+ g_signal_connect(g->menu_bar->view_submenu->toolbars_submenu->q##_menuitem, "toggled", G_CALLBACK(nsgtk_on_##q##_activate_menu), g);\
+ g_signal_connect(g->menu_popup->view_submenu->toolbars_submenu->q##_menuitem, "toggled", G_CALLBACK(nsgtk_on_##q##_activate_menu), g)
+ CONNECT_CHECK(menubar);
+ CONNECT_CHECK(toolbar);
+#undef CONNECT_CHECK
+
+}
+
+/**
+ * Create and connect handlers to popup menu.
+ *
+ * \param g scaffolding to attach popup menu to.
+ * \param group The accelerator group to use for the popup.
+ * \return menu structure on success or NULL on error.
+ */
+static struct nsgtk_popup_menu *
+nsgtk_new_scaffolding_popup(struct nsgtk_scaffolding *g, GtkAccelGroup *group)
+{
+ struct nsgtk_popup_menu *nmenu;
+
+ nmenu = nsgtk_popup_menu_create(group);
+
+ if (nmenu == NULL) {
+ return NULL;
+ }
+
+ g_signal_connect(nmenu->popup_menu, "hide",
+ G_CALLBACK(nsgtk_window_popup_menu_hidden), g);
+
+ g_signal_connect(nmenu->cut_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_cut_activate_menu), g);
+
+ g_signal_connect(nmenu->copy_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_copy_activate_menu), g);
+
+ g_signal_connect(nmenu->paste_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_paste_activate_menu), g);
+
+ g_signal_connect(nmenu->customize_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_customize_activate_menu), g);
+
+ /* set initial popup menu visibility */
+ popup_menu_hide(nmenu, true, false, false, true);
+
+ return nmenu;
+}
+
+/**
+ * Create and connect handlers to link popup menu.
+ *
+ * \param g scaffolding to attach popup menu to.
+ * \param group The accelerator group to use for the popup.
+ * \return true on success or false on error.
+ */
+static struct nsgtk_link_menu *
+nsgtk_new_scaffolding_link_popup(struct nsgtk_scaffolding *g, GtkAccelGroup *group)
+{
+ struct nsgtk_link_menu *nmenu;
+
+ nmenu = nsgtk_link_menu_create(group);
+
+ if (nmenu == NULL) {
+ return NULL;
+ }
+
+ g_signal_connect(nmenu->save_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_savelink_activate_menu), g);
+
+ g_signal_connect(nmenu->opentab_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_link_opentab_activate_menu), g);
+
+ g_signal_connect(nmenu->openwin_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_link_openwin_activate_menu), g);
+
+ g_signal_connect(nmenu->bookmark_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_link_bookmark_activate_menu), g);
+
+ g_signal_connect(nmenu->copy_menuitem, "activate",
+ G_CALLBACK(nsgtk_on_link_copy_activate_menu), g);
+
+ return nmenu;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct nsgtk_scaffolding *nsgtk_current_scaffolding(void)
+{
+ if (scaf_current == NULL) {
+ scaf_current = scaf_list;
+ }
+ return scaf_current;
+}
+
+/**
+ * init the array g->buttons[]
+ */
+static void nsgtk_scaffolding_toolbar_init(struct nsgtk_scaffolding *g)
+{
+#define ITEM_MAIN(p, q, r)\
+ g->buttons[p##_BUTTON]->main = g->menu_bar->q->r##_menuitem;\
+ g->buttons[p##_BUTTON]->rclick = g->menu_popup->q->r##_menuitem;\
+ g->buttons[p##_BUTTON]->mhandler = nsgtk_on_##r##_activate_menu;\
+ g->buttons[p##_BUTTON]->bhandler = nsgtk_on_##r##_activate_button;\
+ g->buttons[p##_BUTTON]->dataplus = nsgtk_toolbar_##r##_button_data;\
+ g->buttons[p##_BUTTON]->dataminus = nsgtk_toolbar_##r##_toolbar_button_data
+
+#define ITEM_SUB(p, q, r, s)\
+ g->buttons[p##_BUTTON]->main =\
+ g->menu_bar->q->r##_submenu->s##_menuitem;\
+ g->buttons[p##_BUTTON]->rclick =\
+ g->menu_popup->q->r##_submenu->s##_menuitem;\
+ g->buttons[p##_BUTTON]->mhandler =\
+ nsgtk_on_##s##_activate_menu;\
+ g->buttons[p##_BUTTON]->bhandler =\
+ nsgtk_on_##s##_activate_button;\
+ g->buttons[p##_BUTTON]->dataplus =\
+ nsgtk_toolbar_##s##_button_data;\
+ g->buttons[p##_BUTTON]->dataminus =\
+ nsgtk_toolbar_##s##_toolbar_button_data
+
+#define ITEM_BUTTON(p, q)\
+ g->buttons[p##_BUTTON]->bhandler =\
+ nsgtk_on_##q##_activate;\
+ g->buttons[p##_BUTTON]->dataplus =\
+ nsgtk_toolbar_##q##_button_data;\
+ g->buttons[p##_BUTTON]->dataminus =\
+ nsgtk_toolbar_##q##_toolbar_button_data
+
+#define ITEM_POP(p, q) \
+ g->buttons[p##_BUTTON]->popup = g->menu_popup->q##_menuitem
+
+#define SENSITIVITY(q) \
+ g->buttons[q##_BUTTON]->sensitivity = false
+
+#define ITEM_ITEM(p, q)\
+ g->buttons[p##_ITEM]->dataplus =\
+ nsgtk_toolbar_##q##_button_data;\
+ g->buttons[p##_ITEM]->dataminus =\
+ nsgtk_toolbar_##q##_toolbar_button_data
+
+ ITEM_ITEM(WEBSEARCH, websearch);
+ ITEM_ITEM(THROBBER, throbber);
+ ITEM_MAIN(NEWWINDOW, file_submenu, newwindow);
+ ITEM_MAIN(NEWTAB, file_submenu, newtab);
+ ITEM_MAIN(OPENFILE, file_submenu, openfile);
+ ITEM_MAIN(PRINT, file_submenu, print);
+ ITEM_MAIN(CLOSEWINDOW, file_submenu, closewindow);
+ ITEM_MAIN(SAVEPAGE, file_submenu, savepage);
+ ITEM_MAIN(PRINTPREVIEW, file_submenu, printpreview);
+ ITEM_MAIN(PRINT, file_submenu, print);
+ ITEM_MAIN(QUIT, file_submenu, quit);
+ ITEM_MAIN(CUT, edit_submenu, cut);
+ ITEM_MAIN(COPY, edit_submenu, copy);
+ ITEM_MAIN(PASTE, edit_submenu, paste);
+ ITEM_MAIN(DELETE, edit_submenu, delete);
+ ITEM_MAIN(SELECTALL, edit_submenu, selectall);
+ ITEM_MAIN(FIND, edit_submenu, find);
+ ITEM_MAIN(PREFERENCES, edit_submenu, preferences);
+ ITEM_MAIN(STOP, view_submenu, stop);
+ ITEM_POP(STOP, stop);
+ ITEM_MAIN(RELOAD, view_submenu, reload);
+ ITEM_POP(RELOAD, reload);
+ ITEM_MAIN(FULLSCREEN, view_submenu, fullscreen);
+ ITEM_MAIN(DOWNLOADS, tools_submenu, downloads);
+ ITEM_MAIN(SAVEWINDOWSIZE, view_submenu, savewindowsize);
+ ITEM_MAIN(BACK, nav_submenu, back);
+ ITEM_POP(BACK, back);
+ ITEM_MAIN(FORWARD, nav_submenu, forward);
+ ITEM_POP(FORWARD, forward);
+ ITEM_MAIN(HOME, nav_submenu, home);
+ ITEM_MAIN(LOCALHISTORY, nav_submenu, localhistory);
+ ITEM_MAIN(GLOBALHISTORY, nav_submenu, globalhistory);
+ ITEM_MAIN(ADDBOOKMARKS, nav_submenu, addbookmarks);
+ ITEM_MAIN(SHOWBOOKMARKS, nav_submenu, showbookmarks);
+ ITEM_MAIN(SHOWCOOKIES, tools_submenu, showcookies);
+ ITEM_MAIN(OPENLOCATION, nav_submenu, openlocation);
+ ITEM_MAIN(CONTENTS, help_submenu, contents);
+ ITEM_MAIN(INFO, help_submenu, info);
+ ITEM_MAIN(GUIDE, help_submenu, guide);
+ ITEM_MAIN(ABOUT, help_submenu, about);
+ ITEM_SUB(PLAINTEXT, file_submenu, export, plaintext);
+ ITEM_SUB(PDF, file_submenu, export, pdf);
+ ITEM_SUB(DRAWFILE, file_submenu, export, drawfile);
+ ITEM_SUB(POSTSCRIPT, file_submenu, export, postscript);
+ ITEM_SUB(ZOOMPLUS, view_submenu, scaleview, zoomplus);
+ ITEM_SUB(ZOOMMINUS, view_submenu, scaleview, zoomminus);
+ ITEM_SUB(ZOOMNORMAL, view_submenu, scaleview, zoomnormal);
+ ITEM_SUB(NEXTTAB, view_submenu, tabs, nexttab);
+ ITEM_SUB(PREVTAB, view_submenu, tabs, prevtab);
+ ITEM_SUB(CLOSETAB, view_submenu, tabs, closetab);
+
+ /* development submenu */
+ ITEM_SUB(VIEWSOURCE, tools_submenu, developer, viewsource);
+ ITEM_SUB(TOGGLEDEBUGGING, tools_submenu, developer, toggledebugging);
+ ITEM_SUB(SAVEBOXTREE, tools_submenu, developer, debugboxtree);
+ ITEM_SUB(SAVEDOMTREE, tools_submenu, developer, debugdomtree);
+ ITEM_BUTTON(HISTORY, history);
+
+ /* disable items that make no sense initially, as well as
+ * as-yet-unimplemented items */
+ SENSITIVITY(BACK);
+ SENSITIVITY(FORWARD);
+ SENSITIVITY(STOP);
+ SENSITIVITY(PRINTPREVIEW);
+ SENSITIVITY(DELETE);
+ SENSITIVITY(DRAWFILE);
+ SENSITIVITY(POSTSCRIPT);
+ SENSITIVITY(NEXTTAB);
+ SENSITIVITY(PREVTAB);
+ SENSITIVITY(CLOSETAB);
+#ifndef WITH_PDF_EXPORT
+ SENSITIVITY(PDF);
+#endif
+
+#undef ITEM_MAIN
+#undef ITEM_SUB
+#undef ITEM_BUTTON
+#undef ITEM_POP
+#undef SENSITIVITY
+
+}
+
+static void nsgtk_scaffolding_initial_sensitivity(struct nsgtk_scaffolding *g)
+{
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if (g->buttons[i]->main != NULL)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->main),
+ g->buttons[i]->sensitivity);
+ if (g->buttons[i]->rclick != NULL)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->rclick),
+ g->buttons[i]->sensitivity);
+ if ((g->buttons[i]->location != -1) &&
+ (g->buttons[i]->button != NULL))
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->button),
+ g->buttons[i]->sensitivity);
+ if (g->buttons[i]->popup != NULL)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ g->buttons[i]->popup),
+ g->buttons[i]->sensitivity);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(g->menu_bar->view_submenu->images_menuitem), FALSE);
+}
+
+
+void nsgtk_scaffolding_toolbars(struct nsgtk_scaffolding *g, int tbi)
+{
+ switch (tbi) {
+ /* case 0 is 'unset' [from fresh install / clearing options]
+ * see above */
+
+ case 1: /* Small icons */
+ /* main toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar),
+ GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->tool_bar),
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ /* search toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar),
+ GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->search->bar),
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ break;
+
+ case 2: /* Large icons */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar),
+ GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->tool_bar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ /* search toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar),
+ GTK_TOOLBAR_ICONS);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->search->bar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ break;
+
+ case 3: /* Large icons with text */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar),
+ GTK_TOOLBAR_BOTH);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->tool_bar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ /* search toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar),
+ GTK_TOOLBAR_BOTH);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(g->search->bar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ break;
+
+ case 4: /* Text icons only */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->tool_bar),
+ GTK_TOOLBAR_TEXT);
+ /* search toolbar */
+ gtk_toolbar_set_style(GTK_TOOLBAR(g->search->bar),
+ GTK_TOOLBAR_TEXT);
+ default:
+ break;
+ }
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel)
+{
+ struct nsgtk_scaffolding *gs;
+ int i;
+ GtkAccelGroup *group;
+
+ gs = malloc(sizeof(*gs));
+ if (gs == NULL) {
+ return NULL;
+ }
+
+ LOG("Constructing a scaffold of %p for gui_window %p", gs, toplevel);
+
+ gs->top_level = toplevel;
+
+ /* Construct UI widgets */
+ if (nsgtk_builder_new_from_resname("netsurf", &gs->builder) != NSERROR_OK) {
+ free(gs);
+ return NULL;
+ }
+
+ gtk_builder_connect_signals(gs->builder, NULL);
+
+/** Obtain a GTK widget handle from UI builder object */
+#define GET_WIDGET(x) GTK_WIDGET (gtk_builder_get_object(gs->builder, (x)))
+
+ gs->window = GTK_WINDOW(GET_WIDGET("wndBrowser"));
+ gs->notebook = GTK_NOTEBOOK(GET_WIDGET("notebook"));
+ gs->tool_bar = GTK_TOOLBAR(GET_WIDGET("toolbar"));
+
+ gs->search = malloc(sizeof(struct gtk_search));
+ if (gs->search == NULL) {
+ free(gs);
+ return NULL;
+ }
+
+ gs->search->bar = GTK_TOOLBAR(GET_WIDGET("searchbar"));
+ gs->search->entry = GTK_ENTRY(GET_WIDGET("searchEntry"));
+
+ gs->search->buttons[0] = GTK_TOOL_BUTTON(GET_WIDGET("searchBackButton"));
+ gs->search->buttons[1] = GTK_TOOL_BUTTON(GET_WIDGET("searchForwardButton"));
+ gs->search->buttons[2] = GTK_TOOL_BUTTON(GET_WIDGET("closeSearchButton"));
+ gs->search->checkAll = GTK_CHECK_BUTTON(GET_WIDGET("checkAllSearch"));
+ gs->search->caseSens = GTK_CHECK_BUTTON(GET_WIDGET("caseSensButton"));
+
+#undef GET_WIDGET
+
+ /* allocate buttons */
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ gs->buttons[i] = calloc(1, sizeof(struct nsgtk_button_connect));
+ if (gs->buttons[i] == NULL) {
+ for (i-- ; i >= BACK_BUTTON; i--) {
+ free(gs->buttons[i]);
+ }
+ free(gs);
+ return NULL;
+ }
+ gs->buttons[i]->location = -1;
+ gs->buttons[i]->sensitivity = true;
+ }
+
+ /* here custom toolbutton adding code */
+ gs->offset = 0;
+ gs->toolbarmem = 0;
+ gs->toolbarbase = 0;
+ gs->historybase = 0;
+ nsgtk_toolbar_customization_load(gs);
+ nsgtk_toolbar_set_physical(gs);
+
+ group = gtk_accel_group_new();
+ gtk_window_add_accel_group(gs->window, group);
+
+ gs->menu_bar = nsgtk_menu_bar_create(GTK_MENU_SHELL(gtk_builder_get_object(gs->builder, "menubar")), group);
+
+
+ /* set this window's size and position to what's in the options, or
+ * or some sensible default if they're not set yet.
+ */
+ if (nsoption_int(window_width) > 0) {
+ gtk_window_move(gs->window,
+ nsoption_int(window_x),
+ nsoption_int(window_y));
+ gtk_window_resize(gs->window,
+ nsoption_int(window_width),
+ nsoption_int(window_height));
+ } else {
+ /* Set to 1000x700, so we're very likely to fit even on
+ * 1024x768 displays, not being able to take into account
+ * window furniture or panels.
+ */
+ gtk_window_set_default_size(gs->window, 1000, 700);
+ }
+
+ /* Default toolbar button type uses system defaults */
+ if (nsoption_int(button_type) == 0) {
+ GtkSettings *settings = gtk_settings_get_default();
+ GtkIconSize tooliconsize;
+ GtkToolbarStyle toolbarstyle;
+
+ g_object_get(settings,
+ "gtk-toolbar-icon-size", &tooliconsize,
+ "gtk-toolbar-style", &toolbarstyle, NULL);
+
+ switch (toolbarstyle) {
+ case GTK_TOOLBAR_ICONS:
+ if (tooliconsize == GTK_ICON_SIZE_SMALL_TOOLBAR) {
+ nsoption_set_int(button_type, 1);
+ } else {
+ nsoption_set_int(button_type, 2);
+ }
+ break;
+
+ case GTK_TOOLBAR_TEXT:
+ nsoption_set_int(button_type, 4);
+ break;
+
+ case GTK_TOOLBAR_BOTH:
+ case GTK_TOOLBAR_BOTH_HORIZ:
+ /* no labels in default configuration */
+ default:
+ /* No system default, so use large icons */
+ nsoption_set_int(button_type, 2);
+ break;
+ }
+ }
+
+ nsgtk_scaffolding_toolbars(gs, nsoption_int(button_type));
+
+ gtk_toolbar_set_show_arrow(gs->tool_bar, TRUE);
+ gtk_widget_show_all(GTK_WIDGET(gs->tool_bar));
+ nsgtk_tab_init(gs);
+
+ gtk_widget_set_size_request(GTK_WIDGET(
+ gs->buttons[HISTORY_BUTTON]->button), 20, -1);
+
+ /* create the local history window to be associated with this scaffold */
+ gs->history_window = malloc(sizeof(struct gtk_history_window));
+ gs->history_window->g = gs;
+ gs->history_window->window =
+ GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+ gtk_window_set_transient_for(gs->history_window->window, gs->window);
+ gtk_window_set_title(gs->history_window->window, "NetSurf History");
+ gtk_window_set_type_hint(gs->history_window->window,
+ GDK_WINDOW_TYPE_HINT_UTILITY);
+ gs->history_window->scrolled =
+ GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(0, 0));
+ gtk_container_add(GTK_CONTAINER(gs->history_window->window),
+ GTK_WIDGET(gs->history_window->scrolled));
+
+ gtk_widget_show(GTK_WIDGET(gs->history_window->scrolled));
+ gs->history_window->drawing_area =
+ GTK_DRAWING_AREA(gtk_drawing_area_new());
+
+ gtk_widget_set_events(GTK_WIDGET(gs->history_window->drawing_area),
+ GDK_EXPOSURE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK);
+ nsgtk_widget_override_background_color(GTK_WIDGET(gs->history_window->drawing_area),
+ GTK_STATE_NORMAL,
+ 0, 0xffff, 0xffff, 0xffff);
+ nsgtk_scrolled_window_add_with_viewport(gs->history_window->scrolled,
+ GTK_WIDGET(gs->history_window->drawing_area));
+ gtk_widget_show(GTK_WIDGET(gs->history_window->drawing_area));
+
+
+ /* set up URL bar completion */
+ gs->url_bar_completion = nsgtk_url_entry_completion_new(gs);
+
+ /* set up the throbber. */
+ gs->throb_frame = 0;
+
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ /* connect history window signals to their handlers */
+ nsgtk_connect_draw_event(GTK_WIDGET(gs->history_window->drawing_area),
+ G_CALLBACK(nsgtk_history_draw_event),
+ gs->history_window);
+ /*CONNECT(gs->history_window->drawing_area, "motion_notify_event",
+ nsgtk_history_motion_notify_event, gs->history_window);*/
+ CONNECT(gs->history_window->drawing_area, "button_press_event",
+ nsgtk_history_button_press_event, gs->history_window);
+ CONNECT(gs->history_window->window, "delete_event",
+ gtk_widget_hide_on_delete, NULL);
+
+ g_signal_connect_after(gs->notebook, "page-added",
+ G_CALLBACK(nsgtk_window_tabs_add), gs);
+ g_signal_connect_after(gs->notebook, "page-removed",
+ G_CALLBACK(nsgtk_window_tabs_remove), gs);
+
+ /* connect main window signals to their handlers. */
+ CONNECT(gs->window, "delete-event",
+ scaffolding_window_delete_event, gs);
+
+ CONNECT(gs->window, "destroy", scaffolding_window_destroy, gs);
+
+ /* toolbar URL bar menu bar search bar signal handlers */
+ CONNECT(gs->menu_bar->edit_submenu->edit, "show",
+ nsgtk_window_edit_menu_clicked, gs);
+ CONNECT(gs->menu_bar->edit_submenu->edit, "hide",
+ nsgtk_window_edit_menu_hidden, gs);
+
+ CONNECT(gs->search->buttons[1], "clicked",
+ nsgtk_search_forward_button_clicked, gs);
+
+ CONNECT(gs->search->buttons[0], "clicked",
+ nsgtk_search_back_button_clicked, gs);
+
+ CONNECT(gs->search->entry, "changed", nsgtk_search_entry_changed, gs);
+
+ CONNECT(gs->search->entry, "activate", nsgtk_search_entry_activate, gs);
+
+ CONNECT(gs->search->entry, "key-press-event",
+ nsgtk_search_entry_key, gs);
+
+ CONNECT(gs->search->buttons[2], "clicked",
+ nsgtk_search_close_button_clicked, gs);
+
+ CONNECT(gs->search->caseSens, "toggled",
+ nsgtk_search_entry_changed, gs);
+
+ CONNECT(gs->tool_bar, "popup-context-menu",
+ nsgtk_window_tool_bar_clicked, gs);
+
+ /* create popup menu */
+ gs->menu_popup = nsgtk_new_scaffolding_popup(gs, group);
+
+ gs->link_menu = nsgtk_new_scaffolding_link_popup(gs, group);
+
+ /* set up the menu signal handlers */
+ nsgtk_scaffolding_toolbar_init(gs);
+ nsgtk_toolbar_connect_all(gs);
+ nsgtk_attach_menu_handlers(gs);
+
+ nsgtk_scaffolding_initial_sensitivity(gs);
+
+ gs->fullscreen = false;
+
+ /* attach to the list */
+ if (scaf_list) {
+ scaf_list->prev = gs;
+ }
+ gs->next = scaf_list;
+ gs->prev = NULL;
+ scaf_list = gs;
+
+ /* set icon images */
+ nsgtk_theme_implement(gs);
+
+ /* set web search provider */
+ search_web_select_provider(nsoption_int(search_provider));
+
+ /* finally, show the window. */
+ gtk_widget_show(GTK_WIDGET(gs->window));
+
+ LOG("creation complete");
+
+ return gs;
+}
+
+/* exported function documented in gtk/scaffolding.h */
+void nsgtk_window_set_title(struct gui_window *gw, const char *title)
+{
+ struct nsgtk_scaffolding *gs = nsgtk_get_scaffold(gw);
+ int title_len;
+ char *newtitle;
+
+ if ((title == NULL) || (title[0] == '\0')) {
+ if (gs->top_level != gw) {
+ gtk_window_set_title(gs->window, "NetSurf");
+ }
+ return;
+ }
+
+ nsgtk_tab_set_title(gw, title);
+
+ if (gs->top_level != gw) {
+ /* not top level window so do not set window title */
+ return;
+ }
+
+ title_len = strlen(title) + SLEN(" - NetSurf") + 1;
+ newtitle = malloc(title_len);
+ if (newtitle == NULL) {
+ return;
+ }
+
+ snprintf(newtitle, title_len, "%s - NetSurf", title);
+
+ gtk_window_set_title(gs->window, newtitle);
+
+ free(newtitle);
+}
+
+
+nserror gui_window_set_url(struct gui_window *gw, nsurl *url)
+{
+ struct nsgtk_scaffolding *g;
+ size_t idn_url_l;
+ char *idn_url_s = NULL;
+
+ g = nsgtk_get_scaffold(gw);
+ if (g->top_level == gw) {
+ if (nsoption_bool(display_decoded_idn) == true) {
+ if (nsurl_get_utf8(url, &idn_url_s, &idn_url_l) != NSERROR_OK)
+ idn_url_s = NULL;
+ }
+
+ gtk_entry_set_text(GTK_ENTRY(g->url_bar), idn_url_s ? idn_url_s : nsurl_access(url));
+
+ if(idn_url_s)
+ free(idn_url_s);
+
+ gtk_editable_set_position(GTK_EDITABLE(g->url_bar), -1);
+ }
+ return NSERROR_OK;
+}
+
+void gui_window_start_throbber(struct gui_window* _g)
+{
+ struct nsgtk_scaffolding *g = nsgtk_get_scaffold(_g);
+ g->buttons[STOP_BUTTON]->sensitivity = true;
+ g->buttons[RELOAD_BUTTON]->sensitivity = false;
+ nsgtk_scaffolding_set_sensitivity(g);
+
+ scaffolding_update_context(g);
+
+ nsgtk_schedule(100, nsgtk_throb, g);
+}
+
+void gui_window_stop_throbber(struct gui_window* _g)
+{
+ struct nsgtk_scaffolding *g = nsgtk_get_scaffold(_g);
+ if (g == NULL)
+ return;
+ scaffolding_update_context(g);
+ nsgtk_schedule(-1, nsgtk_throb, g);
+ if (g->buttons[STOP_BUTTON] != NULL)
+ g->buttons[STOP_BUTTON]->sensitivity = false;
+ if (g->buttons[RELOAD_BUTTON] != NULL)
+ g->buttons[RELOAD_BUTTON]->sensitivity = true;
+
+ nsgtk_scaffolding_set_sensitivity(g);
+
+ if ((g->throbber == NULL) || (nsgtk_throbber == NULL) ||
+ (nsgtk_throbber->framedata == NULL) ||
+ (nsgtk_throbber->framedata[0] == NULL))
+ return;
+ gtk_image_set_from_pixbuf(g->throbber, nsgtk_throbber->framedata[0]);
+}
+
+
+/**
+ * set favicon
+ */
+void
+nsgtk_scaffolding_set_icon(struct gui_window *gw)
+{
+ struct nsgtk_scaffolding *sc = nsgtk_get_scaffold(gw);
+ GdkPixbuf *icon_pixbuf = nsgtk_get_icon(gw);
+
+ /* check icon needs to be shown */
+ if ((icon_pixbuf == NULL) ||
+ (sc->top_level != gw)) {
+ return;
+ }
+
+ nsgtk_entry_set_icon_from_pixbuf(sc->url_bar,
+ GTK_ENTRY_ICON_PRIMARY,
+ icon_pixbuf);
+
+ gtk_widget_show_all(GTK_WIDGET(sc->buttons[URL_BAR_ITEM]->button));
+}
+
+static void
+nsgtk_scaffolding_set_websearch(struct nsgtk_scaffolding *g, const char *content)
+{
+ /** \todo this code appears technically correct, though
+ * currently has no effect at all.
+ */
+ PangoLayout *lo = gtk_entry_get_layout(GTK_ENTRY(g->webSearchEntry));
+ if (lo != NULL) {
+ pango_layout_set_font_description(lo, NULL);
+ PangoFontDescription *desc = pango_font_description_new();
+ if (desc != NULL) {
+ pango_font_description_set_style(desc,
+ PANGO_STYLE_ITALIC);
+ pango_font_description_set_family(desc, "Arial");
+ pango_font_description_set_weight(desc,
+ PANGO_WEIGHT_ULTRALIGHT);
+ pango_font_description_set_size(desc,
+ 10 * PANGO_SCALE);
+ pango_layout_set_font_description(lo, desc);
+ }
+
+ PangoAttrList *list = pango_attr_list_new();
+ if (list != NULL) {
+ PangoAttribute *italic = pango_attr_style_new(
+ PANGO_STYLE_ITALIC);
+ if (italic != NULL) {
+ italic->start_index = 0;
+ italic->end_index = strlen(content);
+ }
+ PangoAttribute *grey = pango_attr_foreground_new(
+ 0x7777, 0x7777, 0x7777);
+ if (grey != NULL) {
+ grey->start_index = 0;
+ grey->end_index = strlen(content);
+ }
+ pango_attr_list_insert(list, italic);
+ pango_attr_list_insert(list, grey);
+ pango_layout_set_attributes(lo, list);
+ pango_attr_list_unref(list);
+ }
+ pango_layout_set_text(lo, content, -1);
+ }
+/* an alternative method */
+/* char *parse = malloc(strlen(content) + 1);
+ PangoAttrList *list = pango_layout_get_attributes(lo);
+ char *markup = g_strconcat("<span foreground='#777777'><i>", content,
+ "</i></span>", NULL);
+ pango_parse_markup(markup, -1, 0, &list, &parse, NULL, NULL);
+ gtk_widget_show_all(g->webSearchEntry);
+*/
+ gtk_entry_set_visibility(GTK_ENTRY(g->webSearchEntry), TRUE);
+ gtk_entry_set_text(GTK_ENTRY(g->webSearchEntry), content);
+}
+
+/**
+ * GTK UI callback when search provider details are updated.
+ *
+ * \param provider_name The providers name.
+ * \param provider_bitmap The bitmap representing the provider.
+ * \return NSERROR_OK on success else error code.
+ */
+static nserror
+gui_search_web_provider_update(const char *provider_name,
+ struct bitmap *provider_bitmap)
+{
+ struct nsgtk_scaffolding *current;
+ GdkPixbuf *srch_pixbuf = NULL;
+ char *searchcontent;
+
+ LOG("name:%s bitmap %p", provider_name, provider_bitmap);
+
+ if (provider_bitmap != NULL) {
+ srch_pixbuf = nsgdk_pixbuf_get_from_surface(provider_bitmap->surface, 16, 16);
+
+ if (srch_pixbuf == NULL) {
+ return NSERROR_NOMEM;
+ }
+ }
+
+ /* setup the search content name */
+ searchcontent = malloc(strlen(provider_name) + SLEN("Search ") + 1);
+ if (searchcontent != NULL) {
+ sprintf(searchcontent, "Search %s", provider_name);
+ }
+
+ /* set the search provider parameters up in each scaffold */
+ for (current = scaf_list; current != NULL; current = current->next) {
+ /* add ico to each window's toolbar */
+ if (srch_pixbuf != NULL) {
+ nsgtk_entry_set_icon_from_pixbuf(current->webSearchEntry,
+ GTK_ENTRY_ICON_PRIMARY,
+ srch_pixbuf);
+ } else {
+ nsgtk_entry_set_icon_from_stock(current->webSearchEntry,
+ GTK_ENTRY_ICON_PRIMARY,
+ NSGTK_STOCK_FIND);
+ }
+
+ /* set search entry text */
+ if (searchcontent != NULL) {
+ nsgtk_scaffolding_set_websearch(current, searchcontent);
+ } else {
+ nsgtk_scaffolding_set_websearch(current, provider_name);
+ }
+ }
+
+ free(searchcontent);
+
+ if (srch_pixbuf != NULL) {
+ g_object_unref(srch_pixbuf);
+ }
+
+ return NSERROR_OK;
+}
+
+static struct gui_search_web_table search_web_table = {
+ .provider_update = gui_search_web_provider_update,
+};
+
+struct gui_search_web_table *nsgtk_search_web_table = &search_web_table;
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkWindow* nsgtk_scaffolding_window(struct nsgtk_scaffolding *g)
+{
+ return g->window;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkNotebook* nsgtk_scaffolding_notebook(struct nsgtk_scaffolding *g)
+{
+ return g->notebook;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkWidget *nsgtk_scaffolding_urlbar(struct nsgtk_scaffolding *g)
+{
+ return g->url_bar;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkWidget *nsgtk_scaffolding_websearch(struct nsgtk_scaffolding *g)
+{
+ return g->webSearchEntry;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkToolbar *nsgtk_scaffolding_toolbar(struct nsgtk_scaffolding *g)
+{
+ return g->tool_bar;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct nsgtk_button_connect *
+nsgtk_scaffolding_button(struct nsgtk_scaffolding *g, int i)
+{
+ return g->buttons[i];
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct gtk_search *nsgtk_scaffolding_search(struct nsgtk_scaffolding *g)
+{
+ return g->search;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+GtkMenuBar *nsgtk_scaffolding_menu_bar(struct nsgtk_scaffolding *g)
+{
+ return g->menu_bar->bar_menu;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct gtk_history_window *
+nsgtk_scaffolding_history_window(struct nsgtk_scaffolding *g)
+{
+ return g->history_window;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct nsgtk_scaffolding *nsgtk_scaffolding_iterate(struct nsgtk_scaffolding *g)
+{
+ if (g == NULL) {
+ return scaf_list;
+ }
+ return g->next;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_reset_offset(struct nsgtk_scaffolding *g)
+{
+ g->offset = 0;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_update_url_bar_ref(struct nsgtk_scaffolding *g)
+{
+ g->url_bar = GTK_WIDGET(gtk_bin_get_child(GTK_BIN(
+ nsgtk_scaffolding_button(g, URL_BAR_ITEM)->button)));
+
+ gtk_entry_set_completion(GTK_ENTRY(g->url_bar),
+ g->url_bar_completion);
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_update_throbber_ref(struct nsgtk_scaffolding *g)
+{
+ g->throbber = GTK_IMAGE(gtk_bin_get_child(
+ GTK_BIN(g->buttons[THROBBER_ITEM]->button)));
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_update_websearch_ref(struct nsgtk_scaffolding *g)
+{
+ g->webSearchEntry = gtk_bin_get_child(GTK_BIN(
+ g->buttons[WEBSEARCH_ITEM]->button));
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_toggle_search_bar_visibility(struct nsgtk_scaffolding *g)
+{
+ gboolean vis;
+ struct browser_window *bw = nsgtk_get_browser_window(g->top_level);
+
+ g_object_get(G_OBJECT(g->search->bar), "visible", &vis, NULL);
+ if (vis) {
+ if (bw != NULL) {
+ browser_window_search_clear(bw);
+ }
+
+ gtk_widget_hide(GTK_WIDGET(g->search->bar));
+ } else {
+ gtk_widget_show(GTK_WIDGET(g->search->bar));
+ gtk_widget_grab_focus(GTK_WIDGET(g->search->entry));
+ }
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+struct gui_window *nsgtk_scaffolding_top_level(struct nsgtk_scaffolding *g)
+{
+ return g->top_level;
+}
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_set_top_level(struct gui_window *gw)
+{
+ struct browser_window *bw;
+ struct nsgtk_scaffolding *sc;
+
+ assert(gw != NULL);
+
+ bw = nsgtk_get_browser_window(gw);
+
+ assert(bw != NULL);
+
+ sc = nsgtk_get_scaffold(gw);
+ assert(sc != NULL);
+
+ sc->top_level = gw;
+
+ /* Synchronise the history (will also update the URL bar) */
+ scaffolding_update_context(sc);
+
+ /* clear effects of potential searches */
+ browser_window_search_clear(bw);
+
+ nsgtk_scaffolding_set_icon(gw);
+
+ /* Ensure the window's title bar is updated */
+ nsgtk_window_set_title(gw, browser_window_get_title(bw));
+
+}
+
+/* exported interface documented in scaffolding.h */
+void nsgtk_scaffolding_set_sensitivity(struct nsgtk_scaffolding *g)
+{
+ int i;
+#define SENSITIVITY(q)\
+ i = q##_BUTTON;\
+ if (g->buttons[i]->main != NULL)\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->main),\
+ g->buttons[i]->sensitivity);\
+ if (g->buttons[i]->rclick != NULL)\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->rclick),\
+ g->buttons[i]->sensitivity);\
+ if ((g->buttons[i]->location != -1) && \
+ (g->buttons[i]->button != NULL))\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->button),\
+ g->buttons[i]->sensitivity);\
+ if (g->buttons[i]->popup != NULL)\
+ gtk_widget_set_sensitive(GTK_WIDGET(\
+ g->buttons[i]->popup),\
+ g->buttons[i]->sensitivity);
+
+ SENSITIVITY(STOP)
+ SENSITIVITY(RELOAD)
+ SENSITIVITY(CUT)
+ SENSITIVITY(COPY)
+ SENSITIVITY(PASTE)
+ SENSITIVITY(BACK)
+ SENSITIVITY(FORWARD)
+ SENSITIVITY(NEXTTAB)
+ SENSITIVITY(PREVTAB)
+ SENSITIVITY(CLOSETAB)
+#undef SENSITIVITY
+}
+
+
+/* exported interface documented in gtk/scaffolding.h */
+void nsgtk_scaffolding_context_menu(struct nsgtk_scaffolding *g,
+ gdouble x,
+ gdouble y)
+{
+ GtkMenu *gtkmenu;
+
+ /* update the global context menu features */
+ browser_window_get_features(nsgtk_get_browser_window(g->top_level),
+ x, y, &current_menu_features);
+
+ if (current_menu_features.link != NULL) {
+ /* menu is opening over a link */
+ gtkmenu = g->link_menu->link_menu;
+ } else {
+ gtkmenu = g->menu_popup->popup_menu;
+
+ nsgtk_scaffolding_update_edit_actions_sensitivity(g);
+
+ if (!(g->buttons[COPY_BUTTON]->sensitivity)) {
+ gtk_widget_hide(GTK_WIDGET(g->menu_popup->copy_menuitem));
+ } else {
+ gtk_widget_show(GTK_WIDGET(g->menu_popup->copy_menuitem));
+ }
+
+ if (!(g->buttons[CUT_BUTTON]->sensitivity)) {
+ gtk_widget_hide(GTK_WIDGET(g->menu_popup->cut_menuitem));
+ } else {
+ gtk_widget_show(GTK_WIDGET(g->menu_popup->cut_menuitem));
+ }
+
+ if (!(g->buttons[PASTE_BUTTON]->sensitivity)) {
+ gtk_widget_hide(GTK_WIDGET(g->menu_popup->paste_menuitem));
+ } else {
+ gtk_widget_show(GTK_WIDGET(g->menu_popup->paste_menuitem));
+ }
+
+ /* hide customize */
+ popup_menu_hide(g->menu_popup, false, false, false, true);
+ }
+
+ gtk_menu_popup(gtkmenu, NULL, NULL, NULL, NULL, 0,
+ gtk_get_current_event_time());
+}
+
+/**
+ * reallocate width for history button, reallocate buttons right of history;
+ * memorise base of history button / toolbar
+ */
+void nsgtk_scaffolding_toolbar_size_allocate(GtkWidget *widget,
+ GtkAllocation *alloc, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ int i = nsgtk_toolbar_get_id_from_widget(widget, g);
+ if (i == -1)
+ return;
+ if ((g->toolbarmem == alloc->x) ||
+ (g->buttons[i]->location <
+ g->buttons[HISTORY_BUTTON]->location))
+ /* no reallocation after first adjustment, no reallocation for buttons
+ * left of history button */
+ return;
+ if (widget == GTK_WIDGET(g->buttons[HISTORY_BUTTON]->button)) {
+ if (alloc->width == 20)
+ return;
+
+ g->toolbarbase = alloc->y + alloc->height;
+ g->historybase = alloc->x + 20;
+ if (g->offset == 0)
+ g->offset = alloc->width - 20;
+ alloc->width = 20;
+ } else if (g->buttons[i]->location <=
+ g->buttons[URL_BAR_ITEM]->location) {
+ alloc->x -= g->offset;
+ if (i == URL_BAR_ITEM)
+ alloc->width += g->offset;
+ }
+ g->toolbarmem = alloc->x;
+ gtk_widget_size_allocate(widget, alloc);
+}
diff --git a/frontends/gtk/scaffolding.h b/frontends/gtk/scaffolding.h
new file mode 100644
index 000000000..e1fd9bf2a
--- /dev/null
+++ b/frontends/gtk/scaffolding.h
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NETSURF_GTK_SCAFFOLDING_H
+#define NETSURF_GTK_SCAFFOLDING_H 1
+
+#include <stdbool.h>
+#include "utils/errors.h"
+
+struct bitmap;
+struct hlcache_handle;
+struct gui_window;
+struct gui_search_web_table;
+struct nsurl;
+
+extern struct gui_search_web_table *nsgtk_search_web_table;
+
+typedef enum {
+ BACK_BUTTON = 0,
+ HISTORY_BUTTON,
+ FORWARD_BUTTON,
+ STOP_BUTTON,
+ RELOAD_BUTTON,
+ HOME_BUTTON,
+ URL_BAR_ITEM,
+ WEBSEARCH_ITEM,
+ THROBBER_ITEM,
+ NEWWINDOW_BUTTON,
+ NEWTAB_BUTTON,
+ OPENFILE_BUTTON,
+ CLOSETAB_BUTTON,
+ CLOSEWINDOW_BUTTON,
+ SAVEPAGE_BUTTON,
+ PDF_BUTTON,
+ PLAINTEXT_BUTTON,
+ DRAWFILE_BUTTON,
+ POSTSCRIPT_BUTTON,
+ PRINTPREVIEW_BUTTON,
+ PRINT_BUTTON,
+ QUIT_BUTTON,
+ CUT_BUTTON,
+ COPY_BUTTON,
+ PASTE_BUTTON,
+ DELETE_BUTTON,
+ SELECTALL_BUTTON,
+ FIND_BUTTON,
+ PREFERENCES_BUTTON,
+ ZOOMPLUS_BUTTON,
+ ZOOMMINUS_BUTTON,
+ ZOOMNORMAL_BUTTON,
+ FULLSCREEN_BUTTON,
+ VIEWSOURCE_BUTTON,
+ DOWNLOADS_BUTTON,
+ SAVEWINDOWSIZE_BUTTON,
+ TOGGLEDEBUGGING_BUTTON,
+ SAVEBOXTREE_BUTTON,
+ SAVEDOMTREE_BUTTON,
+ LOCALHISTORY_BUTTON,
+ GLOBALHISTORY_BUTTON,
+ ADDBOOKMARKS_BUTTON,
+ SHOWBOOKMARKS_BUTTON,
+ SHOWCOOKIES_BUTTON,
+ OPENLOCATION_BUTTON,
+ NEXTTAB_BUTTON,
+ PREVTAB_BUTTON,
+ CONTENTS_BUTTON,
+ GUIDE_BUTTON,
+ INFO_BUTTON,
+ ABOUT_BUTTON,
+ PLACEHOLDER_BUTTON /* size indicator; array maximum indices */
+} nsgtk_toolbar_button; /* PLACEHOLDER_BUTTON - 1 */
+
+struct gtk_history_window {
+ struct nsgtk_scaffolding *g;
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+};
+
+struct gtk_search {
+ GtkToolbar *bar;
+ GtkEntry *entry;
+ GtkToolButton *buttons[3]; /* back, forward, */
+ GtkCheckButton *checkAll; /* close */
+ GtkCheckButton *caseSens;
+};
+
+struct nsgtk_button_connect {
+ GtkToolItem *button;
+ int location; /* in toolbar */
+ bool sensitivity;
+ GtkWidget *main; /* left click menu entry */
+ GtkWidget *rclick; /* right click menu */
+ GtkWidget *popup; /* popup menu entry */
+ void *mhandler; /* menu item clicked */
+ void *bhandler; /* button clicked */
+ void *dataplus; /* customization -> toolbar */
+ void *dataminus; /* customization -> store */
+};
+
+/**
+ * create a new scaffolding for a window.
+ *
+ * \param gw The gui window to create the new scaffold around.
+ * \return The newly constructed scaffold or NULL on error.
+ */
+struct nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *gw);
+
+/**
+ * Obtain the most recently used scaffolding element.
+ *
+ * This allows tabs to be opened in the most recently used window
+ */
+struct nsgtk_scaffolding *nsgtk_current_scaffolding(void);
+
+/* acessors for gtk elements within a scaffold */
+
+/**
+ * Get the gtk window for a scaffolding.
+ */
+GtkWindow *nsgtk_scaffolding_window(struct nsgtk_scaffolding *g);
+
+/**
+ * Get the gtk notebook from a scaffold.
+ */
+GtkNotebook *nsgtk_scaffolding_notebook(struct nsgtk_scaffolding *g);
+
+/**
+ * Get the gtk url bar from a scaffold.
+ */
+GtkWidget *nsgtk_scaffolding_urlbar(struct nsgtk_scaffolding *g);
+
+/**
+ * Get the gtk web search entry from a scaffold.
+ */
+GtkWidget *nsgtk_scaffolding_websearch(struct nsgtk_scaffolding *g);
+
+/**
+ * Get the gtk toolbar from a scaffold.
+ */
+GtkToolbar *nsgtk_scaffolding_toolbar(struct nsgtk_scaffolding *g);
+
+
+struct nsgtk_button_connect *nsgtk_scaffolding_button(struct nsgtk_scaffolding *g, int i);
+
+struct gtk_search *nsgtk_scaffolding_search(struct nsgtk_scaffolding *g);
+
+GtkMenuBar *nsgtk_scaffolding_menu_bar(struct nsgtk_scaffolding *g);
+
+struct gtk_history_window *nsgtk_scaffolding_history_window(struct nsgtk_scaffolding *g);
+
+struct gui_window *nsgtk_scaffolding_top_level(struct nsgtk_scaffolding *g);
+
+/**
+ * reset the scaffold offset value to 0.
+ *
+ * \todo The value is only ever altered in
+ * nsgtk_scaffolding_toolbar_size_allocate and is something to do with
+ * the history button either clarify or remove!
+ */
+void nsgtk_scaffolding_reset_offset(struct nsgtk_scaffolding *g);
+
+/**
+ * Iterate through available scaffolding.
+ */
+struct nsgtk_scaffolding *nsgtk_scaffolding_iterate(struct nsgtk_scaffolding *g);
+
+void nsgtk_scaffolding_update_url_bar_ref(struct nsgtk_scaffolding *g);
+
+void nsgtk_scaffolding_update_throbber_ref(struct nsgtk_scaffolding *g);
+
+void nsgtk_scaffolding_update_websearch_ref(struct nsgtk_scaffolding *g);
+
+void nsgtk_scaffolding_toggle_search_bar_visibility(struct nsgtk_scaffolding *g);
+
+/**
+ * Set the current active top level gui window.
+ */
+void nsgtk_scaffolding_set_top_level(struct gui_window *g);
+
+/**
+ * update the sensitivity of context sensitive UI elements
+ *
+ * widgets altered in arrays:
+ * main
+ * right click menu
+ * location
+ * popup
+ * current arrays are:
+ * stop
+ * reload
+ * cut
+ * copy
+ * paste
+ * back
+ * forward
+ * nexttab
+ * prevtab
+ * closetab
+ */
+void nsgtk_scaffolding_set_sensitivity(struct nsgtk_scaffolding *g);
+
+/**
+ * Open a context sensitive menu.
+ *
+ * \param g the scaffolding containing the browser window.
+ * \param x The x co-ordinate.
+ * \param y The y co-ordinate.
+ */
+void nsgtk_scaffolding_context_menu(struct nsgtk_scaffolding *g, gdouble x, gdouble y);
+
+void nsgtk_scaffolding_toolbar_size_allocate(GtkWidget *widget, GtkAllocation *alloc, gpointer data);
+
+void nsgtk_scaffolding_set_icon(struct gui_window *gw);
+
+gboolean nsgtk_window_url_activate_event(GtkWidget *, gpointer);
+
+gboolean nsgtk_window_url_changed(GtkWidget *, GdkEventKey *, gpointer);
+
+nserror nsgtk_scaffolding_new_tab(struct gui_window *gw);
+
+/* core acessors */
+/**
+ * set the title in the window
+ *
+ * \param gw The gui window to set title on
+ * \param title The title to set which may be NULL
+ */
+void nsgtk_window_set_title(struct gui_window *gw, const char *title);
+
+nserror gui_window_set_url(struct gui_window *g, struct nsurl *url);
+void gui_window_start_throbber(struct gui_window *g);
+void gui_window_stop_throbber(struct gui_window *g);
+
+void nsgtk_scaffolding_toolbars(struct nsgtk_scaffolding *g, int tbi);
+
+#endif /* NETSURF_GTK_SCAFFOLDING_H */
diff --git a/frontends/gtk/schedule.c b/frontends/gtk/schedule.c
new file mode 100644
index 000000000..cf0333388
--- /dev/null
+++ b/frontends/gtk/schedule.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2006-2007 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "utils/errors.h"
+
+#include "gtk/schedule.h"
+
+#ifdef DEBUG_GTK_SCHEDULE
+#include "utils/log.h"
+#else
+#define LOG(format, args...) ((void) 0)
+#endif
+
+/** Killable callback closure embodiment. */
+typedef struct {
+ void (*callback)(void *); /**< The callback function. */
+ void *context; /**< The context for the callback. */
+ bool callback_killed; /**< Whether or not this was killed. */
+} _nsgtk_callback_t;
+
+/** List of callbacks which have occurred and are pending running. */
+static GList *pending_callbacks = NULL;
+/** List of callbacks which are queued to occur in the future. */
+static GList *queued_callbacks = NULL;
+/** List of callbacks which are about to be run in this ::schedule_run. */
+static GList *this_run = NULL;
+
+static gboolean
+nsgtk_schedule_generic_callback(gpointer data)
+{
+ _nsgtk_callback_t *cb = (_nsgtk_callback_t *)(data);
+ if (cb->callback_killed) {
+ /* This callback instance has been killed. */
+ LOG("CB at %p already dead.", cb);
+ }
+ queued_callbacks = g_list_remove(queued_callbacks, cb);
+ pending_callbacks = g_list_append(pending_callbacks, cb);
+ return FALSE;
+}
+
+static void
+nsgtk_schedule_kill_callback(void *_target, void *_match)
+{
+ _nsgtk_callback_t *target = (_nsgtk_callback_t *)_target;
+ _nsgtk_callback_t *match = (_nsgtk_callback_t *)_match;
+ if ((target->callback == match->callback) &&
+ (target->context == match->context)) {
+ LOG("Found match for %p(%p), killing.", target->callback, target->context);
+ target->callback = NULL;
+ target->context = NULL;
+ target->callback_killed = true;
+ }
+}
+
+static void
+schedule_remove(void (*callback)(void *p), void *p)
+{
+ _nsgtk_callback_t cb_match = {
+ .callback = callback,
+ .context = p,
+ };
+
+ g_list_foreach(queued_callbacks,
+ nsgtk_schedule_kill_callback, &cb_match);
+ g_list_foreach(pending_callbacks,
+ nsgtk_schedule_kill_callback, &cb_match);
+ g_list_foreach(this_run,
+ nsgtk_schedule_kill_callback, &cb_match);
+}
+
+/* exported interface documented in gtk/schedule.h */
+nserror nsgtk_schedule(int t, void (*callback)(void *p), void *p)
+{
+ _nsgtk_callback_t *cb;
+
+ /* Kill any pending schedule of this kind. */
+ schedule_remove(callback, p);
+
+ if (t < 0) {
+ return NSERROR_OK;
+ }
+
+ cb = malloc(sizeof(_nsgtk_callback_t));
+ cb->callback = callback;
+ cb->context = p;
+ cb->callback_killed = false;
+ /* Prepend is faster right now. */
+ queued_callbacks = g_list_prepend(queued_callbacks, cb);
+ g_timeout_add(t, nsgtk_schedule_generic_callback, cb);
+
+ return NSERROR_OK;
+}
+
+bool
+schedule_run(void)
+{
+ /* Capture this run of pending callbacks into the list. */
+ this_run = pending_callbacks;
+
+ if (this_run == NULL)
+ return false; /* Nothing to do */
+
+ /* Clear the pending list. */
+ pending_callbacks = NULL;
+
+ LOG("Captured a run of %d callbacks to fire.", g_list_length(this_run));
+
+ /* Run all the callbacks which made it this far. */
+ while (this_run != NULL) {
+ _nsgtk_callback_t *cb = (_nsgtk_callback_t *)(this_run->data);
+ this_run = g_list_remove(this_run, this_run->data);
+ if (!cb->callback_killed)
+ cb->callback(cb->context);
+ free(cb);
+ }
+ return true;
+}
diff --git a/frontends/gtk/schedule.h b/frontends/gtk/schedule.h
new file mode 100644
index 000000000..0a2d724d4
--- /dev/null
+++ b/frontends/gtk/schedule.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NETSURF_GTK_CALLBACK_H
+#define NETSURF_GTK_CALLBACK_H 1
+
+nserror nsgtk_schedule(int t, void (*callback)(void *p), void *p);
+
+bool schedule_run(void);
+
+#endif /* NETSURF_GTK_CALLBACK_H */
diff --git a/frontends/gtk/search.c b/frontends/gtk/search.c
new file mode 100644
index 000000000..d2adcf1b5
--- /dev/null
+++ b/frontends/gtk/search.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+ /** \file
+ * Free text search (front component)
+ */
+#include <stdint.h>
+#include <ctype.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "content/content.h"
+#include "content/hlcache.h"
+#include "desktop/browser.h"
+#include "desktop/search.h"
+#include "desktop/searchweb.h"
+#include "desktop/gui_search.h"
+
+#include "gtk/warn.h"
+#include "gtk/compat.h"
+#include "gtk/search.h"
+#include "gtk/scaffolding.h"
+#include "gtk/window.h"
+
+/**
+ * activate search forwards button in gui.
+ *
+ * \param active activate/inactivate
+ * \param gw The gui window in which to activite the search button in.
+ */
+static void nsgtk_search_set_forward_state(bool active, struct gui_window *gw)
+{
+ if (gw != NULL && nsgtk_get_browser_window(gw) != NULL) {
+ struct nsgtk_scaffolding *g = nsgtk_get_scaffold(gw);
+ gtk_widget_set_sensitive(
+ GTK_WIDGET(nsgtk_scaffolding_search(g)->buttons[1]),
+ active);
+ }
+}
+
+/**
+ * activate search back button in gui.
+ *
+ * \param active activate/inactivate
+ * \param gw The gui window in which to activite the search button in.
+ */
+static void nsgtk_search_set_back_state(bool active, struct gui_window *gw)
+{
+ if (gw != NULL && nsgtk_get_browser_window(gw) != NULL) {
+ struct nsgtk_scaffolding *g = nsgtk_get_scaffold(gw);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_search(
+ g)->buttons[0]), active);
+ }
+}
+
+/** connected to the search forward button */
+
+gboolean nsgtk_search_forward_button_clicked(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ struct gui_window *gw = nsgtk_scaffolding_top_level(g);
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+
+ assert(bw);
+
+ search_flags_t flags = SEARCH_FLAG_FORWARDS |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->caseSens)) ?
+ SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->checkAll)) ?
+ SEARCH_FLAG_SHOWALL : 0);
+
+ browser_window_search(bw, gw, flags,
+ gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry));
+ return TRUE;
+}
+
+/** connected to the search back button */
+
+gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ struct gui_window *gw = nsgtk_scaffolding_top_level(g);
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+
+ assert(bw);
+
+ search_flags_t flags = 0 |(gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->caseSens)) ?
+ SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->checkAll)) ?
+ SEARCH_FLAG_SHOWALL : 0);
+
+ browser_window_search(bw, gw, flags,
+ gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry));
+ return TRUE;
+}
+
+/** connected to the search close button */
+
+gboolean nsgtk_search_close_button_clicked(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ nsgtk_scaffolding_toggle_search_bar_visibility(g);
+ return TRUE;
+}
+
+/** connected to the search entry [typing] */
+
+gboolean nsgtk_search_entry_changed(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ struct gui_window *gw = nsgtk_scaffolding_top_level(g);
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+ search_flags_t flags;
+
+ assert(bw != NULL);
+
+ nsgtk_search_set_forward_state(true, gw);
+ nsgtk_search_set_back_state(true, gw);
+
+ flags = SEARCH_FLAG_FORWARDS |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->caseSens)) ?
+ SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->checkAll)) ?
+ SEARCH_FLAG_SHOWALL : 0);
+
+ browser_window_search(bw, gw, flags,
+ gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry));
+ return TRUE;
+}
+
+/** connected to the search entry [return key] */
+
+gboolean nsgtk_search_entry_activate(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ struct gui_window *gw = nsgtk_scaffolding_top_level(g);
+ struct browser_window *bw = nsgtk_get_browser_window(gw);
+ search_flags_t flags;
+
+ assert(bw);
+
+ flags = SEARCH_FLAG_FORWARDS |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->caseSens)) ?
+ SEARCH_FLAG_CASE_SENSITIVE : 0) |
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+ nsgtk_scaffolding_search(g)->checkAll)) ?
+ SEARCH_FLAG_SHOWALL : 0);
+
+ browser_window_search(bw, gw, flags,
+ gtk_entry_get_text(nsgtk_scaffolding_search(g)->entry));
+ return FALSE;
+}
+
+/** allows escape key to close search bar too */
+
+gboolean
+nsgtk_search_entry_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+ if (event->keyval == GDK_KEY(Escape)) {
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ nsgtk_scaffolding_toggle_search_bar_visibility(g);
+ }
+ return FALSE;
+}
+
+/** connected to the websearch entry [return key] */
+
+gboolean nsgtk_websearch_activate(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = data;
+ nserror ret;
+ nsurl *url;
+
+ ret = search_web_omni(
+ gtk_entry_get_text(GTK_ENTRY(nsgtk_scaffolding_websearch(g))),
+ SEARCH_WEB_OMNI_SEARCHONLY,
+ &url);
+ if (ret == NSERROR_OK) {
+ temp_open_background = 0;
+ ret = browser_window_create(
+ BW_CREATE_HISTORY | BW_CREATE_TAB,
+ url,
+ NULL,
+ nsgtk_get_browser_window(nsgtk_scaffolding_top_level(g)),
+ NULL);
+ temp_open_background = -1;
+ nsurl_unref(url);
+ }
+ if (ret != NSERROR_OK) {
+ nsgtk_warning(messages_get_errorcode(ret), 0);
+ }
+
+ return TRUE;
+}
+
+/**
+ * allows a click in the websearch entry field to clear the name of the
+ * provider
+ */
+
+gboolean nsgtk_websearch_clear(GtkWidget *widget, GdkEventFocus *f,
+ gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ gtk_editable_select_region(GTK_EDITABLE(
+ nsgtk_scaffolding_websearch(g)), 0, -1);
+ gtk_widget_grab_focus(GTK_WIDGET(nsgtk_scaffolding_websearch(g)));
+ return TRUE;
+}
+
+
+
+static struct gui_search_table search_table = {
+ .forward_state = (void *)nsgtk_search_set_forward_state,
+ .back_state = (void *)nsgtk_search_set_back_state,
+};
+
+struct gui_search_table *nsgtk_search_table = &search_table;
diff --git a/frontends/gtk/search.h b/frontends/gtk/search.h
new file mode 100644
index 000000000..dd8c60d0f
--- /dev/null
+++ b/frontends/gtk/search.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NETSURF_GTK_SEARCH_H_
+#define _NETSURF_GTK_SEARCH_H_
+
+struct gui_search_table *nsgtk_search_table;
+
+struct nsgtk_scaffolding;
+
+void nsgtk_search_bar_toggle_visibility(struct nsgtk_scaffolding * g);
+gboolean nsgtk_search_entry_changed(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_entry_activate(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_entry_key(GtkWidget *widget, GdkEventKey *event, gpointer data);
+gboolean nsgtk_search_forward_button_clicked(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_back_button_clicked(GtkWidget *widget, gpointer data);
+gboolean nsgtk_search_close_button_clicked(GtkWidget *widget, gpointer data);
+gboolean nsgtk_websearch_activate(GtkWidget *widget, gpointer data);
+gboolean nsgtk_websearch_clear(GtkWidget *widget, GdkEventFocus *f, gpointer data);
+
+#endif
diff --git a/frontends/gtk/selection.c b/frontends/gtk/selection.c
new file mode 100644
index 000000000..d1388ed27
--- /dev/null
+++ b/frontends/gtk/selection.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2008 Mike Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <stdlib.h>
+
+#include "utils/log.h"
+#include "desktop/browser.h"
+#include "desktop/gui_clipboard.h"
+
+#include "gtk/window.h"
+
+static GString *current_selection = NULL;
+static GtkClipboard *clipboard;
+
+
+/**
+ * Core asks front end for clipboard contents.
+ *
+ * \param buffer UTF-8 text, allocated by front end, ownership yeilded to core
+ * \param length Byte length of UTF-8 text in buffer
+ */
+static void gui_get_clipboard(char **buffer, size_t *length)
+{
+ gchar *gtext;
+
+ *buffer = NULL;
+ *length = 0;
+
+ /* get clipboard contents from gtk */
+ clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gtext = gtk_clipboard_wait_for_text(clipboard); /* conv to utf-8 */
+
+ if (gtext == NULL)
+ return;
+
+ *length = strlen(gtext);
+ *buffer = malloc(*length);
+ if (*buffer == NULL) {
+ *length = 0;
+ g_free(gtext);
+ return;
+ }
+
+ memcpy(*buffer, gtext, *length);
+
+ g_free(gtext);
+}
+
+
+/**
+ * Core tells front end to put given text in clipboard
+ *
+ * \param buffer UTF-8 text, owned by core
+ * \param length Byte length of UTF-8 text in buffer
+ * \param styles Array of styles given to text runs, owned by core, or NULL
+ * \param n_styles Number of text run styles in array
+ */
+static void gui_set_clipboard(const char *buffer, size_t length,
+ nsclipboard_styles styles[], int n_styles)
+{
+ clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+
+ if (!current_selection)
+ current_selection = g_string_new(NULL);
+ else
+ g_string_set_size(current_selection, 0);
+
+ current_selection = g_string_append_len(current_selection,
+ buffer, length);
+
+ gtk_clipboard_set_text(clipboard, current_selection->str, -1);
+}
+
+static struct gui_clipboard_table clipboard_table = {
+ .get = gui_get_clipboard,
+ .set = gui_set_clipboard,
+};
+
+struct gui_clipboard_table *nsgtk_clipboard_table = &clipboard_table;
diff --git a/frontends/gtk/selection.h b/frontends/gtk/selection.h
new file mode 100644
index 000000000..6463692cf
--- /dev/null
+++ b/frontends/gtk/selection.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2008 Mike Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTK_SELECTION_H
+#define GTK_SELECTION_H
+
+struct gui_clipboard_table *nsgtk_clipboard_table;
+
+#endif
diff --git a/frontends/gtk/sexy_icon_entry.c b/frontends/gtk/sexy_icon_entry.c
new file mode 100644
index 000000000..fff650cc4
--- /dev/null
+++ b/frontends/gtk/sexy_icon_entry.c
@@ -0,0 +1,982 @@
+/*
+ * libsexy/sexy-icon-entry.c Entry widget
+ * Copyright (C) 2004-2006 Christian Hammond.
+ * modified for NetSurf
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ * This file before modifications was originally part of LibSexy,
+ * http://www.chipx86.com/; it is redistributed under GPLv2
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>
+ * or write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "gtk/sexy_icon_entry.h"
+#include "gtk/compat.h"
+
+#define ICON_MARGIN 2
+#define MAX_ICONS 2
+
+#define IS_VALID_ICON_ENTRY_POSITION(pos) \
+ ((pos) == SEXY_ICON_ENTRY_PRIMARY || \
+ (pos) == SEXY_ICON_ENTRY_SECONDARY)
+
+typedef struct
+{
+ GtkImage *icon;
+ gboolean highlight;
+ gboolean hovered;
+ GdkWindow *window;
+
+} SexyIconInfo;
+
+struct _SexyIconEntryPriv
+{
+ SexyIconInfo icons[MAX_ICONS];
+
+ gulong icon_released_id;
+};
+
+enum
+{
+ ICON_PRESSED,
+ ICON_RELEASED,
+ LAST_SIGNAL
+};
+
+/* static void sexy_icon_entry_class_init(SexyIconEntryClass *klass); */
+static void sexy_icon_entry_editable_init(GtkEditableClass *iface);
+/* static void sexy_icon_entry_init(SexyIconEntry *entry); */
+static void sexy_icon_entry_finalize(GObject *obj);
+static void sexy_icon_entry_destroy(GtkObject *obj);
+static void sexy_icon_entry_map(GtkWidget *widget);
+static void sexy_icon_entry_unmap(GtkWidget *widget);
+static void sexy_icon_entry_realize(GtkWidget *widget);
+static void sexy_icon_entry_unrealize(GtkWidget *widget);
+static void sexy_icon_entry_size_request(GtkWidget *widget,
+ GtkRequisition *requisition);
+static void sexy_icon_entry_size_allocate(GtkWidget *widget,
+ GtkAllocation *allocation);
+static gint sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event);
+static gint sexy_icon_entry_enter_notify(GtkWidget *widget,
+ GdkEventCrossing *event);
+static gint sexy_icon_entry_leave_notify(GtkWidget *widget,
+ GdkEventCrossing *event);
+static gint sexy_icon_entry_button_press(GtkWidget *widget,
+ GdkEventButton *event);
+static gint sexy_icon_entry_button_release(GtkWidget *widget,
+ GdkEventButton *event);
+
+static GtkEntryClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE_EXTENDED(SexyIconEntry, sexy_icon_entry, GTK_TYPE_ENTRY,
+ 0,
+ G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE,
+ sexy_icon_entry_editable_init));
+
+void
+sexy_icon_entry_class_init(SexyIconEntryClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkEntryClass *entry_class;
+
+ parent_class = g_type_class_peek_parent(klass);
+
+ gobject_class = G_OBJECT_CLASS(klass);
+ object_class = GTK_OBJECT_CLASS(klass);
+ widget_class = GTK_WIDGET_CLASS(klass);
+ entry_class = GTK_ENTRY_CLASS(klass);
+
+ gobject_class->finalize = sexy_icon_entry_finalize;
+
+ object_class->destroy = sexy_icon_entry_destroy;
+
+ widget_class->map = sexy_icon_entry_map;
+ widget_class->unmap = sexy_icon_entry_unmap;
+ widget_class->realize = sexy_icon_entry_realize;
+ widget_class->unrealize = sexy_icon_entry_unrealize;
+ widget_class->size_request = sexy_icon_entry_size_request;
+ widget_class->size_allocate = sexy_icon_entry_size_allocate;
+ widget_class->expose_event = sexy_icon_entry_expose;
+ widget_class->enter_notify_event = sexy_icon_entry_enter_notify;
+ widget_class->leave_notify_event = sexy_icon_entry_leave_notify;
+ widget_class->button_press_event = sexy_icon_entry_button_press;
+ widget_class->button_release_event = sexy_icon_entry_button_release;
+
+ /*
+ * SexyIconEntry::icon-pressed:
+ * @entry: The entry on which the signal is emitted.
+ * @icon_pos: The position of the clicked icon.
+ * @button: The mouse button clicked.
+ *
+ * The ::icon-pressed signal is emitted when an icon is clicked.
+ */
+ /* signal modified to compile directly in NetSurf - param 8 of
+ * g_signal_new() changed from marshal type to NULL - so there may
+ * well be no working signal */
+ signals[ICON_PRESSED] =
+ g_signal_new("icon_pressed",
+ G_TYPE_FROM_CLASS(gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(SexyIconEntryClass, icon_pressed),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ G_TYPE_INT,
+ G_TYPE_INT);
+
+ /*
+ * SexyIconEntry::icon-released:
+ * @entry: The entry on which the signal is emitted.
+ * @icon_pos: The position of the clicked icon.
+ * @button: The mouse button clicked.
+ *
+ * The ::icon-released signal is emitted on the button release from a
+ * mouse click.
+ */
+ /* signal modified to compile directly in NetSurf - param 8 of
+ * g_signal_new() changed from marshal type to NULL - so there may
+ * well be no working signal */
+ signals[ICON_RELEASED] =
+ g_signal_new("icon_released",
+ G_TYPE_FROM_CLASS(gobject_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET(SexyIconEntryClass, icon_released),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ G_TYPE_INT,
+ G_TYPE_INT);
+}
+
+static void
+sexy_icon_entry_editable_init(GtkEditableClass *iface)
+{
+};
+
+void
+sexy_icon_entry_init(SexyIconEntry *entry)
+{
+ entry->priv = g_new0(SexyIconEntryPriv, 1);
+}
+
+static void
+sexy_icon_entry_finalize(GObject *obj)
+{
+ SexyIconEntry *entry;
+
+ g_return_if_fail(obj != NULL);
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(obj));
+
+ entry = SEXY_ICON_ENTRY(obj);
+
+ g_free(entry->priv);
+
+ if (G_OBJECT_CLASS(parent_class)->finalize)
+ G_OBJECT_CLASS(parent_class)->finalize(obj);
+}
+
+static void
+sexy_icon_entry_destroy(GtkObject *obj)
+{
+ SexyIconEntry *entry;
+
+ entry = SEXY_ICON_ENTRY(obj);
+
+ sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_PRIMARY, NULL);
+ sexy_icon_entry_set_icon(entry, SEXY_ICON_ENTRY_SECONDARY, NULL);
+
+ if (GTK_OBJECT_CLASS(parent_class)->destroy)
+ GTK_OBJECT_CLASS(parent_class)->destroy(obj);
+}
+
+static void
+sexy_icon_entry_map(GtkWidget *widget)
+{
+ if (nsgtk_widget_get_realized(widget) && !nsgtk_widget_get_mapped(widget))
+ {
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ GTK_WIDGET_CLASS(parent_class)->map(widget);
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (entry->priv->icons[i].icon != NULL)
+ gdk_window_show(entry->priv->icons[i].window);
+ }
+ }
+}
+
+static void
+sexy_icon_entry_unmap(GtkWidget *widget)
+{
+ if (nsgtk_widget_get_mapped(widget))
+ {
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (entry->priv->icons[i].icon != NULL)
+ gdk_window_hide(entry->priv->icons[i].window);
+ }
+
+ GTK_WIDGET_CLASS(parent_class)->unmap(widget);
+ }
+}
+
+static gint
+get_icon_width(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
+{
+ GtkRequisition requisition;
+ gint menu_icon_width;
+ gint width;
+ SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
+
+ if (icon_info->icon == NULL)
+ return 0;
+
+ gtk_widget_size_request(GTK_WIDGET(icon_info->icon), &requisition);
+ gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &menu_icon_width, NULL);
+
+ width = MAX(requisition.width, menu_icon_width);
+
+ return width;
+}
+
+static void
+get_borders(SexyIconEntry *entry, gint *xborder, gint *yborder)
+{
+ GtkWidget *widget = GTK_WIDGET(entry);
+ gint focus_width;
+ gboolean interior_focus;
+
+ gtk_widget_style_get(widget,
+ "interior-focus", &interior_focus,
+ "focus-line-width", &focus_width,
+ NULL);
+
+ if (gtk_entry_get_has_frame(GTK_ENTRY(entry)))
+ {
+ *xborder = widget->style->xthickness;
+ *yborder = widget->style->ythickness;
+ }
+ else
+ {
+ *xborder = 0;
+ *yborder = 0;
+ }
+
+ if (!interior_focus)
+ {
+ *xborder += focus_width;
+ *yborder += focus_width;
+ }
+}
+
+static void
+get_text_area_size(SexyIconEntry *entry, GtkAllocation *alloc)
+{
+ GtkWidget *widget = GTK_WIDGET(entry);
+ GtkRequisition requisition;
+ gint xborder, yborder;
+
+ gtk_widget_get_child_requisition(widget, &requisition);
+ get_borders(entry, &xborder, &yborder);
+
+ alloc->x = xborder;
+ alloc->y = yborder;
+ alloc->width = widget->allocation.width - xborder * 2;
+ alloc->height = requisition.height - yborder * 2;
+}
+
+static void
+get_icon_allocation(SexyIconEntry *icon_entry,
+ gboolean left,
+ GtkAllocation *widget_alloc,
+ GtkAllocation *text_area_alloc,
+ GtkAllocation *allocation,
+ SexyIconEntryPosition *icon_pos)
+{
+ gboolean rtl;
+
+ rtl = (gtk_widget_get_direction(GTK_WIDGET(icon_entry)) ==
+ GTK_TEXT_DIR_RTL);
+
+ if (left)
+ *icon_pos = (rtl ? SEXY_ICON_ENTRY_SECONDARY : SEXY_ICON_ENTRY_PRIMARY);
+ else
+ *icon_pos = (rtl ? SEXY_ICON_ENTRY_PRIMARY : SEXY_ICON_ENTRY_SECONDARY);
+
+ allocation->y = text_area_alloc->y;
+ allocation->width = get_icon_width(icon_entry, *icon_pos);
+ allocation->height = text_area_alloc->height;
+
+ if (left)
+ allocation->x = text_area_alloc->x + ICON_MARGIN;
+ else
+ {
+ allocation->x = text_area_alloc->x + text_area_alloc->width -
+ allocation->width - ICON_MARGIN;
+ }
+}
+
+static void
+sexy_icon_entry_realize(GtkWidget *widget)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ int i;
+
+ GTK_WIDGET_CLASS(parent_class)->realize(widget);
+
+ attributes.x = 0;
+ attributes.y = 0;
+ attributes.width = 1;
+ attributes.height = 1;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual(widget);
+ attributes.colormap = gtk_widget_get_colormap(widget);
+ attributes.event_mask = gtk_widget_get_events(widget);
+ attributes.event_mask |=
+ (GDK_EXPOSURE_MASK
+ | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ SexyIconInfo *icon_info;
+
+ icon_info = &entry->priv->icons[i];
+ icon_info->window = gdk_window_new(widget->window, &attributes,
+ attributes_mask);
+ gdk_window_set_user_data(icon_info->window, widget);
+
+ gdk_window_set_background(icon_info->window,
+ &widget->style->base[nsgtk_widget_get_state(widget)]);
+ }
+
+ gtk_widget_queue_resize(widget);
+}
+
+static void
+sexy_icon_entry_unrealize(GtkWidget *widget)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ SexyIconInfo *icon_info = &entry->priv->icons[i];
+
+ gdk_window_destroy(icon_info->window);
+ icon_info->window = NULL;
+ }
+}
+
+static void
+sexy_icon_entry_size_request(GtkWidget *widget, GtkRequisition *requisition)
+{
+ GtkEntry *gtkentry;
+ SexyIconEntry *entry;
+ gint icon_widths = 0;
+ int i;
+
+ gtkentry = GTK_ENTRY(widget);
+ entry = SEXY_ICON_ENTRY(widget);
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ int icon_width = get_icon_width(entry, i);
+
+ if (icon_width > 0)
+ icon_widths += icon_width + ICON_MARGIN;
+ }
+
+ GTK_WIDGET_CLASS(parent_class)->size_request(widget, requisition);
+
+ if (icon_widths > requisition->width)
+ requisition->width += icon_widths;
+}
+
+static void
+place_windows(SexyIconEntry *icon_entry, GtkAllocation *widget_alloc)
+{
+ SexyIconEntryPosition left_icon_pos;
+ SexyIconEntryPosition right_icon_pos;
+ GtkAllocation left_icon_alloc;
+ GtkAllocation right_icon_alloc;
+ GtkAllocation text_area_alloc;
+
+ get_text_area_size(icon_entry, &text_area_alloc);
+ get_icon_allocation(icon_entry, TRUE, widget_alloc, &text_area_alloc,
+ &left_icon_alloc, &left_icon_pos);
+ get_icon_allocation(icon_entry, FALSE, widget_alloc, &text_area_alloc,
+ &right_icon_alloc, &right_icon_pos);
+
+ if (left_icon_alloc.width > 0)
+ {
+ text_area_alloc.x = left_icon_alloc.x + left_icon_alloc.width +
+ ICON_MARGIN;
+ }
+
+ if (right_icon_alloc.width > 0)
+ text_area_alloc.width -= right_icon_alloc.width + ICON_MARGIN;
+
+ text_area_alloc.width -= text_area_alloc.x;
+
+ gdk_window_move_resize(icon_entry->priv->icons[left_icon_pos].window,
+ left_icon_alloc.x, left_icon_alloc.y,
+ left_icon_alloc.width, left_icon_alloc.height);
+
+ gdk_window_move_resize(icon_entry->priv->icons[right_icon_pos].window,
+ right_icon_alloc.x, right_icon_alloc.y,
+ right_icon_alloc.width, right_icon_alloc.height);
+
+ gdk_window_move_resize(GTK_ENTRY(icon_entry)->text_area,
+ text_area_alloc.x, text_area_alloc.y,
+ text_area_alloc.width, text_area_alloc.height);
+}
+
+static void
+sexy_icon_entry_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(widget));
+ g_return_if_fail(allocation != NULL);
+
+ widget->allocation = *allocation;
+
+ GTK_WIDGET_CLASS(parent_class)->size_allocate(widget, allocation);
+
+ if (nsgtk_widget_get_realized(widget))
+ place_windows(SEXY_ICON_ENTRY(widget), allocation);
+}
+
+static GdkPixbuf *
+get_pixbuf_from_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos)
+{
+ GdkPixbuf *pixbuf = NULL;
+ gchar *stock_id;
+ SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
+ GtkIconSize size;
+
+ switch (gtk_image_get_storage_type(GTK_IMAGE(icon_info->icon)))
+ {
+ case GTK_IMAGE_PIXBUF:
+ pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(icon_info->icon));
+ g_object_ref(pixbuf);
+ break;
+
+ case GTK_IMAGE_STOCK:
+ gtk_image_get_stock(GTK_IMAGE(icon_info->icon), &stock_id, &size);
+ pixbuf = gtk_widget_render_icon(GTK_WIDGET(entry),
+ stock_id, size, NULL);
+ break;
+
+ default:
+ return NULL;
+ }
+
+ return pixbuf;
+}
+
+/* Kudos to the gnome-panel guys. */
+static void
+colorshift_pixbuf(GdkPixbuf *dest, GdkPixbuf *src, int shift)
+{
+ gint i, j;
+ gint width, height, has_alpha, src_rowstride, dest_rowstride;
+ guchar *target_pixels;
+ guchar *original_pixels;
+ guchar *pix_src;
+ guchar *pix_dest;
+ int val;
+ guchar r, g, b;
+
+ has_alpha = gdk_pixbuf_get_has_alpha(src);
+ width = gdk_pixbuf_get_width(src);
+ height = gdk_pixbuf_get_height(src);
+ src_rowstride = gdk_pixbuf_get_rowstride(src);
+ dest_rowstride = gdk_pixbuf_get_rowstride(dest);
+ original_pixels = gdk_pixbuf_get_pixels(src);
+ target_pixels = gdk_pixbuf_get_pixels(dest);
+
+ for (i = 0; i < height; i++)
+ {
+ pix_dest = target_pixels + i * dest_rowstride;
+ pix_src = original_pixels + i * src_rowstride;
+
+ for (j = 0; j < width; j++)
+ {
+ r = *(pix_src++);
+ g = *(pix_src++);
+ b = *(pix_src++);
+
+ val = r + shift;
+ *(pix_dest++) = CLAMP(val, 0, 255);
+
+ val = g + shift;
+ *(pix_dest++) = CLAMP(val, 0, 255);
+
+ val = b + shift;
+ *(pix_dest++) = CLAMP(val, 0, 255);
+
+ if (has_alpha)
+ *(pix_dest++) = *(pix_src++);
+ }
+ }
+}
+
+static void
+draw_icon(GtkWidget *widget, SexyIconEntryPosition icon_pos)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ SexyIconInfo *icon_info = &entry->priv->icons[icon_pos];
+ GdkPixbuf *pixbuf;
+ gint x, y, width, height;
+
+ if (icon_info->icon == NULL || !nsgtk_widget_get_realized(widget))
+ return;
+
+ if ((pixbuf = get_pixbuf_from_icon(entry, icon_pos)) == NULL)
+ return;
+
+ gdk_drawable_get_size(icon_info->window, &width, &height);
+
+ if (width == 1 || height == 1)
+ {
+ /*
+ * size_allocate hasn't been called yet. These are the default values.
+ */
+ return;
+ }
+
+ if (gdk_pixbuf_get_height(pixbuf) > height)
+ {
+ GdkPixbuf *temp_pixbuf;
+ int scale;
+
+ scale = height - (2 * ICON_MARGIN);
+
+ temp_pixbuf = gdk_pixbuf_scale_simple(pixbuf, scale, scale,
+ GDK_INTERP_BILINEAR);
+
+ g_object_unref(pixbuf);
+
+ pixbuf = temp_pixbuf;
+ }
+
+ x = (width - gdk_pixbuf_get_width(pixbuf)) / 2;
+ y = (height - gdk_pixbuf_get_height(pixbuf)) / 2;
+
+ if (icon_info->hovered)
+ {
+ GdkPixbuf *temp_pixbuf;
+
+ temp_pixbuf = gdk_pixbuf_copy(pixbuf);
+
+ colorshift_pixbuf(temp_pixbuf, pixbuf, 30);
+
+ g_object_unref(pixbuf);
+
+ pixbuf = temp_pixbuf;
+ }
+
+ gdk_draw_pixbuf(icon_info->window, widget->style->black_gc, pixbuf,
+ 0, 0, x, y, -1, -1,
+ GDK_RGB_DITHER_NORMAL, 0, 0);
+
+ g_object_unref(pixbuf);
+}
+
+static gint
+sexy_icon_entry_expose(GtkWidget *widget, GdkEventExpose *event)
+{
+ SexyIconEntry *entry;
+
+ g_return_val_if_fail(SEXY_IS_ICON_ENTRY(widget), FALSE);
+ g_return_val_if_fail(event != NULL, FALSE);
+
+ entry = SEXY_ICON_ENTRY(widget);
+
+ if (nsgtk_widget_is_drawable(widget))
+ {
+ gboolean found = FALSE;
+ int i;
+
+ for (i = 0; i < MAX_ICONS && !found; i++)
+ {
+ SexyIconInfo *icon_info = &entry->priv->icons[i];
+
+ if (event->window == icon_info->window)
+ {
+ gint width;
+ GtkAllocation text_area_alloc;
+
+ get_text_area_size(entry, &text_area_alloc);
+ gdk_drawable_get_size(icon_info->window, &width, NULL);
+
+ gtk_paint_flat_box(widget->style, icon_info->window,
+ nsgtk_widget_get_state(widget), GTK_SHADOW_NONE,
+ NULL, widget, "entry_bg",
+ 0, 0, width, text_area_alloc.height);
+
+ draw_icon(widget, i);
+
+ found = TRUE;
+ }
+ }
+
+ if (!found)
+ GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
+ }
+
+ return FALSE;
+}
+
+static void
+update_icon(GObject *obj, GParamSpec *param, SexyIconEntry *entry)
+{
+ if (param != NULL)
+ {
+ const char *name = g_param_spec_get_name(param);
+
+ if (strcmp(name, "pixbuf") && strcmp(name, "stock") &&
+ strcmp(name, "image") && strcmp(name, "pixmap") &&
+ strcmp(name, "icon_set") && strcmp(name, "pixbuf_animation"))
+ {
+ return;
+ }
+ }
+
+ gtk_widget_queue_resize(GTK_WIDGET(entry));
+}
+
+static gint
+sexy_icon_entry_enter_notify(GtkWidget *widget, GdkEventCrossing *event)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (event->window == entry->priv->icons[i].window)
+ {
+ if (sexy_icon_entry_get_icon_highlight(entry, i))
+ {
+ entry->priv->icons[i].hovered = TRUE;
+
+ update_icon(NULL, NULL, entry);
+
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static gint
+sexy_icon_entry_leave_notify(GtkWidget *widget, GdkEventCrossing *event)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (event->window == entry->priv->icons[i].window)
+ {
+ if (sexy_icon_entry_get_icon_highlight(entry, i))
+ {
+ entry->priv->icons[i].hovered = FALSE;
+
+ update_icon(NULL, NULL, entry);
+
+ break;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static gint
+sexy_icon_entry_button_press(GtkWidget *widget, GdkEventButton *event)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ if (event->window == entry->priv->icons[i].window)
+ {
+ if (event->button == 1 &&
+ sexy_icon_entry_get_icon_highlight(entry, i))
+ {
+ entry->priv->icons[i].hovered = FALSE;
+
+ update_icon(NULL, NULL, entry);
+ }
+
+ g_signal_emit(entry, signals[ICON_PRESSED], 0, i, event->button);
+
+ return TRUE;
+ }
+ }
+
+ if (GTK_WIDGET_CLASS(parent_class)->button_press_event)
+ return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget,
+ event);
+
+ return FALSE;
+}
+
+static gint
+sexy_icon_entry_button_release(GtkWidget *widget, GdkEventButton *event)
+{
+ SexyIconEntry *entry = SEXY_ICON_ENTRY(widget);
+ int i;
+
+ for (i = 0; i < MAX_ICONS; i++)
+ {
+ GdkWindow *icon_window = entry->priv->icons[i].window;
+
+ if (event->window == icon_window)
+ {
+ int width, height;
+ gdk_drawable_get_size(icon_window, &width, &height);
+
+ if (event->button == 1 &&
+ sexy_icon_entry_get_icon_highlight(entry, i) &&
+ event->x >= 0 && event->y >= 0 &&
+ event->x <= width && event->y <= height)
+ {
+ entry->priv->icons[i].hovered = TRUE;
+
+ update_icon(NULL, NULL, entry);
+ }
+
+ g_signal_emit(entry, signals[ICON_RELEASED], 0, i, event->button);
+
+ return TRUE;
+ }
+ }
+
+ if (GTK_WIDGET_CLASS(parent_class)->button_release_event)
+ return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget,
+ event);
+
+ return FALSE;
+}
+
+/*
+ * sexy_icon_entry_new
+ *
+ * Creates a new SexyIconEntry widget.
+ *
+ * Returns a new #SexyIconEntry.
+ */
+GtkWidget *
+sexy_icon_entry_new(void)
+{
+ return GTK_WIDGET(g_object_new(SEXY_TYPE_ICON_ENTRY, NULL));
+}
+
+/*
+ * sexy_icon_entry_set_icon
+ * @param entry A #SexyIconEntry.
+ * @param position Icon position.
+ * @param icon A #GtkImage to set as the icon.
+ *
+ * Sets the icon shown in the entry
+ */
+void
+sexy_icon_entry_set_icon(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
+ GtkImage *icon)
+{
+ SexyIconInfo *icon_info;
+
+ g_return_if_fail(entry != NULL);
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
+ g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
+ g_return_if_fail(icon == NULL || GTK_IS_IMAGE(icon));
+
+ icon_info = &entry->priv->icons[icon_pos];
+
+ if (icon == icon_info->icon)
+ return;
+
+ if (icon_pos == SEXY_ICON_ENTRY_SECONDARY &&
+ entry->priv->icon_released_id != 0)
+ {
+ g_signal_handler_disconnect(entry, entry->priv->icon_released_id);
+ entry->priv->icon_released_id = 0;
+ }
+
+ if (icon == NULL)
+ {
+ if (icon_info->icon != NULL)
+ {
+ gtk_widget_destroy(GTK_WIDGET(icon_info->icon));
+ icon_info->icon = NULL;
+
+ /*
+ * Explicitly check, as the pointer may become invalidated
+ * during destruction.
+ */
+ if (icon_info->window != NULL && GDK_IS_WINDOW(icon_info->window))
+ gdk_window_hide(icon_info->window);
+ }
+ }
+ else
+ {
+ if (icon_info->window != NULL && icon_info->icon == NULL)
+ gdk_window_show(icon_info->window);
+
+ g_signal_connect(G_OBJECT(icon), "notify",
+ G_CALLBACK(update_icon), entry);
+
+ icon_info->icon = icon;
+ g_object_ref(icon);
+ }
+
+ update_icon(NULL, NULL, entry);
+}
+
+/*
+ * sexy_icon_entry_set_icon_highlight
+ * @param entry A #SexyIconEntry;
+ * @param position Icon position.
+ * @param highlight TRUE if the icon should highlight on mouse-over
+ *
+ * Determines whether the icon will highlight on mouse-over.
+ */
+void
+sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
+ SexyIconEntryPosition icon_pos,
+ gboolean highlight)
+{
+ SexyIconInfo *icon_info;
+
+ g_return_if_fail(entry != NULL);
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(entry));
+ g_return_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos));
+
+ icon_info = &entry->priv->icons[icon_pos];
+
+ if (icon_info->highlight == highlight)
+ return;
+
+ icon_info->highlight = highlight;
+}
+
+/*
+ * sexy_icon_entry_get_icon
+ * @param entry A #SexyIconEntry.
+ * @param position Icon position.
+ *
+ * Retrieves the image used for the icon
+ *
+ * Returns: A #GtkImage.
+ */
+GtkImage *
+sexy_icon_entry_get_icon(const SexyIconEntry *entry,
+ SexyIconEntryPosition icon_pos)
+{
+ g_return_val_if_fail(entry != NULL, NULL);
+ g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), NULL);
+ g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), NULL);
+
+ return entry->priv->icons[icon_pos].icon;
+}
+
+/*
+ * sexy_icon_entry_get_icon_highlight
+ * @param entry A #SexyIconEntry.
+ * @param position Icon position.
+ *
+ * Retrieves whether entry will highlight the icon on mouseover.
+ *
+ * Returns: TRUE if icon highlights.
+ */
+gboolean
+sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
+ SexyIconEntryPosition icon_pos)
+{
+ g_return_val_if_fail(entry != NULL, FALSE);
+ g_return_val_if_fail(SEXY_IS_ICON_ENTRY(entry), FALSE);
+ g_return_val_if_fail(IS_VALID_ICON_ENTRY_POSITION(icon_pos), FALSE);
+
+ return entry->priv->icons[icon_pos].highlight;
+}
+
+static void
+clear_button_clicked_cb(SexyIconEntry *icon_entry,
+ SexyIconEntryPosition icon_pos,
+ int button)
+{
+ if (icon_pos != SEXY_ICON_ENTRY_SECONDARY || button != 1)
+ return;
+
+ gtk_entry_set_text(GTK_ENTRY(icon_entry), "");
+}
+
+/*
+ * sexy_icon_entry_add_clear_button
+ * @param icon_entry A #SexyIconEntry.
+ *
+ * A convenience function to add a clear button to the end of the entry.
+ * This is useful for search boxes.
+ */
+void
+sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry)
+{
+ GtkWidget *icon;
+
+ g_return_if_fail(icon_entry != NULL);
+ g_return_if_fail(SEXY_IS_ICON_ENTRY(icon_entry));
+
+ icon = nsgtk_image_new_from_stock(NSGTK_STOCK_CLEAR,
+ GTK_ICON_SIZE_MENU);
+ gtk_widget_show(icon);
+ sexy_icon_entry_set_icon(SEXY_ICON_ENTRY(icon_entry),
+ SEXY_ICON_ENTRY_SECONDARY,
+ GTK_IMAGE(icon));
+ sexy_icon_entry_set_icon_highlight(SEXY_ICON_ENTRY(icon_entry),
+ SEXY_ICON_ENTRY_SECONDARY, TRUE);
+
+ if (icon_entry->priv->icon_released_id != 0)
+ {
+ g_signal_handler_disconnect(icon_entry,
+ icon_entry->priv->icon_released_id);
+ }
+
+ icon_entry->priv->icon_released_id =
+ g_signal_connect(G_OBJECT(icon_entry), "icon_released",
+ G_CALLBACK(clear_button_clicked_cb), NULL);
+}
diff --git a/frontends/gtk/sexy_icon_entry.h b/frontends/gtk/sexy_icon_entry.h
new file mode 100644
index 000000000..bd7fb3eb9
--- /dev/null
+++ b/frontends/gtk/sexy_icon_entry.h
@@ -0,0 +1,100 @@
+/*
+ * file libsexy/sexy-icon-entry.h Entry widget
+ *
+ * Copyright (C) 2004-2006 Christian Hammond.
+ * redistributed under GPLv2
+ *
+ * libsexy 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>
+ * or write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _SEXY_ICON_ENTRY_H_
+#define _SEXY_ICON_ENTRY_H_
+
+typedef struct _SexyIconEntry SexyIconEntry;
+typedef struct _SexyIconEntryClass SexyIconEntryClass;
+typedef struct _SexyIconEntryPriv SexyIconEntryPriv;
+
+#include <gtk/gtk.h>
+
+#define SEXY_TYPE_ICON_ENTRY (sexy_icon_entry_get_type())
+#define SEXY_ICON_ENTRY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntry))
+#define SEXY_ICON_ENTRY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass))
+#define SEXY_IS_ICON_ENTRY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), SEXY_TYPE_ICON_ENTRY))
+#define SEXY_IS_ICON_ENTRY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), SEXY_TYPE_ICON_ENTRY))
+#define SEXY_ICON_ENTRY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), SEXY_TYPE_ICON_ENTRY, SexyIconEntryClass))
+
+typedef enum
+{
+ SEXY_ICON_ENTRY_PRIMARY,
+ SEXY_ICON_ENTRY_SECONDARY
+
+} SexyIconEntryPosition;
+
+struct _SexyIconEntry
+{
+ GtkEntry parent_object;
+
+ SexyIconEntryPriv *priv;
+
+ void (*gtk_reserved1)(void);
+ void (*gtk_reserved2)(void);
+ void (*gtk_reserved3)(void);
+ void (*gtk_reserved4)(void);
+};
+
+struct _SexyIconEntryClass
+{
+ GtkEntryClass parent_class;
+
+ /* Signals */
+ void (*icon_pressed)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
+ int button);
+ void (*icon_released)(SexyIconEntry *entry, SexyIconEntryPosition icon_pos,
+ int button);
+
+ void (*gtk_reserved1)(void);
+ void (*gtk_reserved2)(void);
+ void (*gtk_reserved3)(void);
+ void (*gtk_reserved4)(void);
+};
+
+G_BEGIN_DECLS
+
+GType sexy_icon_entry_get_type(void);
+
+GtkWidget *sexy_icon_entry_new(void);
+
+void sexy_icon_entry_set_icon(SexyIconEntry *entry,
+ SexyIconEntryPosition position,
+ GtkImage *icon);
+
+void sexy_icon_entry_set_icon_highlight(SexyIconEntry *entry,
+ SexyIconEntryPosition position,
+ gboolean highlight);
+
+GtkImage *sexy_icon_entry_get_icon(const SexyIconEntry *entry,
+ SexyIconEntryPosition position);
+
+gboolean sexy_icon_entry_get_icon_highlight(const SexyIconEntry *entry,
+ SexyIconEntryPosition position);
+void sexy_icon_entry_add_clear_button(SexyIconEntry *icon_entry);
+
+G_END_DECLS
+
+#endif /* _SEXY_ICON_ENTRY_H_ */
diff --git a/frontends/gtk/ssl_cert.c b/frontends/gtk/ssl_cert.c
new file mode 100644
index 000000000..742029f83
--- /dev/null
+++ b/frontends/gtk/ssl_cert.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsurl.h"
+#include "desktop/tree.h"
+#include "desktop/sslcert_viewer.h"
+
+#include "gtk/treeview.h"
+#include "gtk/scaffolding.h"
+#include "gtk/resources.h"
+#include "gtk/ssl_cert.h"
+
+
+static void nsgtk_ssl_accept(GtkButton *w, gpointer data)
+{
+ void **session = data;
+ GtkBuilder *x = session[0];
+ struct nsgtk_treeview *wnd = session[1];
+ struct sslcert_session_data *ssl_data = session[2];
+
+ sslcert_viewer_accept(ssl_data);
+
+ nsgtk_treeview_destroy(wnd);
+ g_object_unref(G_OBJECT(x));
+ free(session);
+}
+
+static void nsgtk_ssl_reject(GtkWidget *w, gpointer data)
+{
+ void **session = data;
+ GtkBuilder *x = session[0];
+ struct nsgtk_treeview *wnd = session[1];
+ struct sslcert_session_data *ssl_data = session[2];
+
+ sslcert_viewer_reject(ssl_data);
+
+ nsgtk_treeview_destroy(wnd);
+ g_object_unref(G_OBJECT(x));
+ free(session);
+}
+
+static gboolean nsgtk_ssl_delete_event(GtkWidget *w, GdkEvent *event, gpointer data)
+{
+ nsgtk_ssl_reject(w, data);
+ return FALSE;
+}
+
+void gtk_cert_verify(nsurl *url, const struct ssl_cert_info *certs,
+ unsigned long num, nserror (*cb)(bool proceed, void *pw),
+ void *cbpw)
+{
+ static struct nsgtk_treeview *ssl_window;
+ struct sslcert_session_data *data;
+ GtkButton *accept, *reject;
+ void **session;
+ GtkDialog *dlg;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ GtkBuilder *builder;
+ GtkWindow *gtk_parent;
+ nserror res;
+
+ /* state while dlg is open */
+ session = calloc(sizeof(void *), 3);
+ if (session == NULL) {
+ return;
+ }
+
+ res = nsgtk_builder_new_from_resname("ssl", &builder);
+ if (res != NSERROR_OK) {
+ LOG("SSL UI builder init failed");
+ free(session);
+ cb(false, cbpw);
+ return;
+ }
+
+ gtk_builder_connect_signals(builder, NULL);
+
+ sslcert_viewer_create_session_data(num, url, cb, cbpw, certs, &data);
+ ssl_current_session = data;
+
+ dlg = GTK_DIALOG(gtk_builder_get_object(builder, "wndSSLProblem"));
+
+ /* set parent for transient dialog */
+ gtk_parent = nsgtk_scaffolding_window(nsgtk_current_scaffolding());
+ gtk_window_set_transient_for(GTK_WINDOW(dlg), gtk_parent);
+
+ scrolled = GTK_SCROLLED_WINDOW(gtk_builder_get_object(builder, "SSLScrolled"));
+ drawing_area = GTK_DRAWING_AREA(gtk_builder_get_object(builder, "SSLDrawingArea"));
+
+ ssl_window = nsgtk_treeview_create(TREE_SSLCERT, GTK_WINDOW(dlg), scrolled,
+ drawing_area);
+
+ if (ssl_window == NULL) {
+ free(session);
+ g_object_unref(G_OBJECT(dlg));
+ return;
+ }
+
+ accept = GTK_BUTTON(gtk_builder_get_object(builder, "sslaccept"));
+ reject = GTK_BUTTON(gtk_builder_get_object(builder, "sslreject"));
+
+ session[0] = builder;
+ session[1] = ssl_window;
+ session[2] = data;
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ CONNECT(accept, "clicked", nsgtk_ssl_accept, session);
+ CONNECT(reject, "clicked", nsgtk_ssl_reject, session);
+ CONNECT(dlg, "delete_event", G_CALLBACK(nsgtk_ssl_delete_event),
+ (gpointer)session);
+
+ gtk_widget_show(GTK_WIDGET(dlg));
+}
diff --git a/frontends/gtk/ssl_cert.h b/frontends/gtk/ssl_cert.h
new file mode 100644
index 000000000..48937d457
--- /dev/null
+++ b/frontends/gtk/ssl_cert.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NETSURF_GTK_SSL_CERT_H
+#define NETSURF_GTK_SSL_CERT_H 1
+
+struct nsurl;
+struct ssl_cert_info;
+
+/**
+ * Prompt the user to verify a certificate with issuse.
+ *
+ * \param url The URL being verified.
+ * \param certs The certificate to be verified
+ * \param num The number of certificates to be verified.
+ * \param cb Callback upon user decision.
+ * \param cbpw Context pointer passed to cb
+ */
+void gtk_cert_verify(struct nsurl *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw);
+
+#endif
diff --git a/frontends/gtk/tabs.c b/frontends/gtk/tabs.c
new file mode 100644
index 000000000..67a410d20
--- /dev/null
+++ b/frontends/gtk/tabs.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2008 Michael Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "utils/nsoption.h"
+#include "utils/messages.h"
+#include "desktop/browser.h"
+#include "content/content.h"
+#include "desktop/search.h"
+
+#include "gtk/compat.h"
+#include "gtk/window.h"
+#include "gtk/search.h"
+#include "gtk/tabs.h"
+
+#define TAB_WIDTH_N_CHARS 15
+
+/** callback to update sizes when style-set gtk signal */
+static void nsgtk_tab_update_size(GtkWidget *hbox, GtkStyle *previous_style,
+ GtkWidget *close_button)
+{
+ PangoFontMetrics *metrics;
+ PangoContext *context;
+ int char_width, h, w;
+ GtkStyleContext *style;
+ GtkStateFlags state;
+
+ state = nsgtk_widget_get_state_flags(hbox);
+ style = nsgtk_widget_get_style_context(hbox);
+
+ context = gtk_widget_get_pango_context(hbox);
+ metrics = pango_context_get_metrics(context,
+ nsgtk_style_context_get_font(style, state),
+ pango_context_get_language(context));
+
+ char_width = pango_font_metrics_get_approximate_digit_width(metrics);
+ pango_font_metrics_unref(metrics);
+
+ nsgtk_icon_size_lookup_for_settings(gtk_widget_get_settings (hbox),
+ GTK_ICON_SIZE_MENU, &w, &h);
+
+ gtk_widget_set_size_request(hbox,
+ TAB_WIDTH_N_CHARS * PANGO_PIXELS(char_width) + 2 * w,
+ -1);
+
+ gtk_widget_set_size_request(close_button, w + 4, h + 4);
+}
+
+/** Create a notebook tab label */
+static GtkWidget *nsgtk_tab_label_setup(struct gui_window *window)
+{
+ GtkWidget *hbox, *label, *button, *close;
+
+ hbox = nsgtk_hbox_new(FALSE, 2);
+
+ label = gtk_label_new(messages_get("NewTab"));
+ gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
+ gtk_label_set_single_line_mode(GTK_LABEL(label), TRUE);
+ nsgtk_widget_set_alignment(label, GTK_ALIGN_START, GTK_ALIGN_CENTER);
+ nsgtk_widget_set_margins(label, 0, 0);
+ gtk_widget_show(label);
+
+ button = gtk_button_new();
+
+ close = nsgtk_image_new_from_stock(NSGTK_STOCK_CLOSE,
+ GTK_ICON_SIZE_MENU);
+ gtk_container_add(GTK_CONTAINER(button), close);
+ gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
+ gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+ gtk_widget_set_tooltip_text(button, "Close this tab.");
+
+#ifdef FIXME
+ GtkRcStyle *rcstyle;
+ rcstyle = gtk_rc_style_new();
+ rcstyle->xthickness = rcstyle->ythickness = 0;
+ gtk_widget_modify_style(button, rcstyle);
+ g_object_unref(rcstyle);
+#endif
+
+ g_signal_connect_swapped(button, "clicked",
+ G_CALLBACK(nsgtk_window_destroy_browser), window);
+ g_signal_connect(hbox, "style-set",
+ G_CALLBACK(nsgtk_tab_update_size), button);
+
+ gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(hbox), "label", label);
+ g_object_set_data(G_OBJECT(hbox), "close-button", button);
+
+
+ gtk_widget_show_all(hbox);
+ return hbox;
+}
+#include "utils/log.h"
+
+/** callback when page is switched */
+
+static gint srcpagenum;
+
+/** The switch-page signal handler
+ *
+ * This signal is handled both before and after delivery to work round
+ * issue that setting the selected tab during the switch-page signal
+ * fails
+ */
+static void
+nsgtk_tab_switch_page(GtkNotebook *notebook,
+ GtkWidget *page,
+ guint selpagenum,
+ gpointer user_data)
+{
+ srcpagenum = gtk_notebook_get_current_page(notebook);
+}
+
+static void
+nsgtk_tab_switch_page_after(GtkNotebook *notebook,
+ GtkWidget *selpage,
+ guint selpagenum,
+ gpointer user_data)
+{
+ GtkWidget *srcpage;
+ GtkWidget *addpage;
+ struct gui_window *gw;
+ nserror error;
+
+ addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+
+ if (selpage == addpage) {
+ if ((srcpagenum != -1) &&
+ (srcpagenum != (gint)selpagenum)) {
+ /* ensure the add tab is not actually selected */
+ LOG("src %d sel %d", srcpagenum, selpagenum);
+ srcpage = gtk_notebook_get_nth_page(notebook, srcpagenum);
+ gw = g_object_get_data(G_OBJECT(srcpage), "gui_window");
+ if ((gw != NULL) && (nsgtk_get_scaffold(gw) != NULL)) {
+ error = nsgtk_scaffolding_new_tab(gw);
+ if (error != NSERROR_OK) {
+ LOG("Failed to open new tab.");
+ }
+ }
+ }
+ } else {
+ LOG("sel %d", selpagenum);
+ /* tab with page in it */
+ gw = g_object_get_data(G_OBJECT(selpage), "gui_window");
+ if (gw != NULL) {
+ nsgtk_scaffolding_set_top_level(gw);
+ }
+ }
+}
+
+static void nsgtk_tab_page_reordered(GtkNotebook *notebook,
+ GtkWidget *child,
+ guint page_num,
+ gpointer user_data)
+{
+ gint pages;
+ GtkWidget *addpage;
+
+ pages = gtk_notebook_get_n_pages(notebook);
+ addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+
+ if (((gint)page_num == (pages - 1)) &&
+ (child != addpage)) {
+ /* moved tab to end */
+ gtk_notebook_reorder_child(notebook, addpage, -1);
+ }
+}
+
+static void
+nsgtk_tab_orientation(GtkNotebook *notebook)
+{
+ switch (nsoption_int(position_tab)) {
+ case 0:
+ gtk_notebook_set_tab_pos(notebook, GTK_POS_TOP);
+ break;
+
+ case 1:
+ gtk_notebook_set_tab_pos(notebook, GTK_POS_LEFT);
+ break;
+
+ case 2:
+ gtk_notebook_set_tab_pos(notebook, GTK_POS_RIGHT);
+ break;
+
+ case 3:
+ gtk_notebook_set_tab_pos(notebook, GTK_POS_BOTTOM);
+ break;
+
+ }
+}
+
+/** adds a "new tab" tab */
+static GtkWidget *
+nsgtk_tab_add_newtab(GtkNotebook *notebook)
+{
+ GtkWidget *tablabel;
+ GtkWidget *tabcontents;
+ GtkWidget *add;
+
+ tablabel = nsgtk_hbox_new(FALSE, 1);
+ tabcontents = nsgtk_hbox_new(FALSE, 1);
+
+ add = nsgtk_image_new_from_stock(NSGTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
+
+ gtk_box_pack_start(GTK_BOX(tablabel), add, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(tablabel);
+
+ gtk_notebook_append_page(notebook, tabcontents, tablabel);
+
+ gtk_notebook_set_tab_reorderable(notebook, tabcontents, false);
+
+ gtk_widget_show_all(tabcontents);
+
+ g_object_set_data(G_OBJECT(notebook), "addtab", tabcontents);
+
+ return tablabel;
+}
+
+/** callback to alter tab visibility when pages are added or removed */
+static void
+nsgtk_tab_visibility_update(GtkNotebook *notebook, GtkWidget *child, guint page)
+{
+ gint pagec = gtk_notebook_get_n_pages(notebook);
+ GtkWidget *addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+
+ if (addpage != NULL) {
+ pagec--; /* skip the add tab */
+ if ((gint)page == pagec) {
+ /* ensure the add new tab cannot be current */
+ gtk_notebook_set_current_page(notebook, page - 1);
+ }
+ }
+
+ if ((nsoption_bool(show_single_tab) == true) || (pagec > 1)) {
+ gtk_notebook_set_show_tabs(notebook, TRUE);
+ } else {
+ gtk_notebook_set_show_tabs(notebook, FALSE);
+ }
+}
+
+/* exported interface documented in gtk/tabs.h */
+void nsgtk_tab_options_changed(GtkNotebook *notebook)
+{
+ nsgtk_tab_orientation(notebook);
+ nsgtk_tab_visibility_update(notebook, NULL, 0);
+}
+
+
+/* exported interface documented in gtk/tabs.h */
+void nsgtk_tab_init(struct nsgtk_scaffolding *gs)
+{
+ GtkNotebook *notebook;
+
+ notebook = nsgtk_scaffolding_notebook(gs);
+
+ nsgtk_tab_add_newtab(notebook);
+
+ g_signal_connect(notebook, "switch-page",
+ G_CALLBACK(nsgtk_tab_switch_page), NULL);
+ g_signal_connect_after(notebook, "switch-page",
+ G_CALLBACK(nsgtk_tab_switch_page_after), NULL);
+
+ g_signal_connect(notebook, "page-removed",
+ G_CALLBACK(nsgtk_tab_visibility_update), NULL);
+ g_signal_connect(notebook, "page-added",
+ G_CALLBACK(nsgtk_tab_visibility_update), NULL);
+ g_signal_connect(notebook, "page-reordered",
+ G_CALLBACK(nsgtk_tab_page_reordered), NULL);
+
+
+ nsgtk_tab_options_changed(notebook);
+}
+
+/* exported interface documented in gtk/tabs.h */
+void nsgtk_tab_add(struct gui_window *gw,
+ GtkWidget *tab_contents,
+ bool background)
+{
+ GtkNotebook *notebook;
+ GtkWidget *tabBox;
+ gint remember;
+ gint pages;
+ gint newpage;
+
+ g_object_set_data(G_OBJECT(tab_contents), "gui_window", gw);
+
+ notebook = nsgtk_scaffolding_notebook(nsgtk_get_scaffold(gw));
+
+ tabBox = nsgtk_tab_label_setup(gw);
+
+ nsgtk_window_set_tab(gw, tabBox);
+
+ remember = gtk_notebook_get_current_page(notebook);
+
+ pages = gtk_notebook_get_n_pages(notebook);
+
+ newpage = gtk_notebook_insert_page(notebook, tab_contents, tabBox, pages - 1);
+
+ gtk_notebook_set_tab_reorderable(notebook, tab_contents, true);
+
+ gtk_widget_show_all(tab_contents);
+
+ if (background) {
+ gtk_notebook_set_current_page(notebook, remember);
+ } else {
+ gtk_notebook_set_current_page(notebook, newpage);
+ }
+
+ gtk_widget_grab_focus(GTK_WIDGET(nsgtk_scaffolding_urlbar(
+ nsgtk_get_scaffold(gw))));
+}
+
+/* exported interface documented in gtk/tabs.h */
+void nsgtk_tab_set_title(struct gui_window *g, const char *title)
+{
+ GtkWidget *label;
+ GtkWidget *tab;
+
+ tab = nsgtk_window_get_tab(g);
+ if (tab == NULL) {
+ return;
+ }
+
+ label = g_object_get_data(G_OBJECT(tab), "label");
+ gtk_label_set_text(GTK_LABEL(label), title);
+ gtk_widget_set_tooltip_text(tab, title);
+}
+
+/* exported interface documented in gtk/tabs.h */
+nserror nsgtk_tab_close_current(GtkNotebook *notebook)
+{
+ gint pagen;
+ GtkWidget *page;
+ struct gui_window *gw;
+ GtkWidget *addpage;
+
+ pagen = gtk_notebook_get_current_page(notebook);
+ if (pagen == -1) {
+ return NSERROR_OK;
+ }
+
+ page = gtk_notebook_get_nth_page(notebook, pagen);
+ if (page == NULL) {
+ return NSERROR_OK;
+ }
+
+ addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+ if (page == addpage) {
+ /* the add new tab page is current, cannot close that */
+ return NSERROR_OK;
+ }
+
+ gw = g_object_get_data(G_OBJECT(page), "gui_window");
+ if (gw == NULL) {
+ return NSERROR_OK;
+ }
+
+ nsgtk_window_destroy_browser(gw);
+
+ return NSERROR_OK;
+}
+
+nserror nsgtk_tab_prev(GtkNotebook *notebook)
+{
+ gtk_notebook_prev_page(notebook);
+
+ return NSERROR_OK;
+
+}
+
+nserror nsgtk_tab_next(GtkNotebook *notebook)
+{
+ gint pagen;
+ GtkWidget *page;
+ GtkWidget *addpage;
+
+ pagen = gtk_notebook_get_current_page(notebook);
+ if (pagen == -1) {
+ return NSERROR_OK;
+ }
+
+ page = gtk_notebook_get_nth_page(notebook, pagen + 1);
+ if (page == NULL) {
+ return NSERROR_OK;
+ }
+
+ addpage = g_object_get_data(G_OBJECT(notebook), "addtab");
+ if (page == addpage) {
+ /* cannot make add new tab page current */
+ return NSERROR_OK;
+ }
+
+ gtk_notebook_set_current_page(notebook, pagen + 1);
+
+ return NSERROR_OK;
+}
diff --git a/frontends/gtk/tabs.h b/frontends/gtk/tabs.h
new file mode 100644
index 000000000..440d61336
--- /dev/null
+++ b/frontends/gtk/tabs.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008 Michael Lester <element3260@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NETSURF_GTK_TABS_H_
+#define _NETSURF_GTK_TABS_H_
+
+struct gui_window;
+
+void nsgtk_tab_init(struct nsgtk_scaffolding *gs);
+void nsgtk_tab_add(struct gui_window *window, GtkWidget *tab_contents, bool background);
+
+/** set the tab title
+ *
+ * The tab title will be set to the parameter
+ *
+ * \note currently only called from nsgtk_window_set_title()
+ *
+ * \param g the gui window to set tab title for.
+ * \param title The title text which may not be NULL.
+ */
+void nsgtk_tab_set_title(struct gui_window *g, const char *title);
+void nsgtk_tab_options_changed(GtkNotebook *notebook);
+nserror nsgtk_tab_close_current(GtkNotebook *notebook);
+nserror nsgtk_tab_prev(GtkNotebook *notebook);
+nserror nsgtk_tab_next(GtkNotebook *notebook);
+
+#endif
diff --git a/frontends/gtk/throbber.c b/frontends/gtk/throbber.c
new file mode 100644
index 000000000..9392c3909
--- /dev/null
+++ b/frontends/gtk/throbber.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2008 Rob Kendrick <rjek@netsurf-browser.org>
+ * Copyright 2008 Sean Fox <dyntryx@gmail.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <gtk/gtk.h>
+#include <stdint.h>
+
+#include "utils/log.h"
+
+#include "gtk/resources.h"
+#include "gtk/throbber.h"
+
+struct nsgtk_throbber *nsgtk_throbber = NULL;
+
+#define THROBBER_FRAMES 9
+#define THROBBER_FMT "throbber/throbber%d.png"
+
+/* exported interface documented in gtk/throbber.h */
+nserror nsgtk_throbber_init(void)
+{
+ struct nsgtk_throbber *throb; /**< structure we generate */
+ int frame;
+ char resname[] = THROBBER_FMT;
+ nserror res = NSERROR_OK;
+
+ throb = malloc(sizeof(*throb));
+ if (throb == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ throb->framedata = malloc(sizeof(GdkPixbuf *) * THROBBER_FRAMES);
+ if (throb->framedata == NULL) {
+ free(throb);
+ return false;
+ }
+
+ for (frame = 0; frame < THROBBER_FRAMES; frame++) {
+ snprintf(resname, sizeof(resname), THROBBER_FMT, frame);
+ res = nsgdk_pixbuf_new_from_resname(resname,
+ throb->framedata + frame);
+ if (res != NSERROR_OK) {
+ break;
+ }
+ LOG("%s",resname);
+ }
+
+ if (frame < 1) {
+ /* we need at least two frames - one for idle, one for active */
+ LOG("Insufficent number of frames (%d) in throbber animation.", frame);
+ res = NSERROR_INIT_FAILED;
+ }
+
+ throb->nframes = frame;
+ nsgtk_throbber = throb;
+ return res;
+
+
+}
+
+
+void nsgtk_throbber_finalise(void)
+{
+ int i;
+
+ for (i = 0; i < nsgtk_throbber->nframes; i++)
+ g_object_unref(nsgtk_throbber->framedata[i]);
+
+ free(nsgtk_throbber->framedata);
+ free(nsgtk_throbber);
+
+ nsgtk_throbber = NULL;
+}
+
diff --git a/frontends/gtk/throbber.h b/frontends/gtk/throbber.h
new file mode 100644
index 000000000..e0b47e15c
--- /dev/null
+++ b/frontends/gtk/throbber.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008 Rob Kendrick <rjek@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_THROBBER_H__
+#define __GTK_THROBBER_H__
+
+#include <gtk/gtk.h>
+
+struct nsgtk_throbber
+{
+ int nframes; /**< Number of frames in the throbber */
+ GdkPixbuf **framedata;
+};
+
+extern struct nsgtk_throbber *nsgtk_throbber;
+
+nserror nsgtk_throbber_init(void);
+void nsgtk_throbber_finalise(void);
+
+#endif /* __GTK_THROBBER_H__ */
diff --git a/frontends/gtk/toolbar.c b/frontends/gtk/toolbar.c
new file mode 100644
index 000000000..208b5c0b9
--- /dev/null
+++ b/frontends/gtk/toolbar.c
@@ -0,0 +1,1416 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <gtk/gtk.h>
+
+#include "desktop/browser.h"
+#include "desktop/searchweb.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+
+#include "gtk/gui.h"
+#include "gtk/warn.h"
+#include "gtk/scaffolding.h"
+#include "gtk/search.h"
+#include "gtk/throbber.h"
+#include "gtk/window.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/toolbar.h"
+
+static GtkTargetEntry entry = {(char *)"nsgtk_button_data",
+ GTK_TARGET_SAME_APP, 0};
+
+static bool edit_mode = false;
+
+struct nsgtk_toolbar_custom_store {
+ GtkWidget *window;
+ GtkWidget *store_buttons[PLACEHOLDER_BUTTON];
+ GtkWidget *widgetvbox;
+ GtkWidget *currentbar;
+ char numberh; /* current horizontal location while adding */
+ GtkBuilder *builder; /* button widgets to store */
+ int buttonlocations[PLACEHOLDER_BUTTON];
+ int currentbutton;
+ bool fromstore;
+};
+/* the number of buttons that fit in the width of the store window */
+#define NSGTK_STORE_WIDTH 6
+
+/* the 'standard' width of a button that makes sufficient of its label
+visible */
+#define NSGTK_BUTTON_WIDTH 111
+
+/* the 'standard' height of a button that fits as many toolbars as
+possible into the store */
+#define NSGTK_BUTTON_HEIGHT 70
+
+/* the 'normal' width of the websearch bar */
+#define NSGTK_WEBSEARCH_WIDTH 150
+
+static struct nsgtk_toolbar_custom_store store;
+static struct nsgtk_toolbar_custom_store *window = &store;
+
+
+enum image_sets {
+ IMAGE_SET_MAIN_MENU = 0,
+ IMAGE_SET_RCLICK_MENU,
+ IMAGE_SET_POPUP_MENU,
+ IMAGE_SET_BUTTONS,
+ IMAGE_SET_COUNT
+};
+
+typedef enum search_buttons {
+ SEARCH_BACK_BUTTON = 0,
+ SEARCH_FORWARD_BUTTON,
+ SEARCH_CLOSE_BUTTON,
+ SEARCH_BUTTONS_COUNT
+} nsgtk_search_buttons;
+
+struct nsgtk_theme {
+ GtkImage *image[PLACEHOLDER_BUTTON];
+ GtkImage *searchimage[SEARCH_BUTTONS_COUNT];
+ /* apng throbber element */
+};
+
+/**
+ * returns a string without its underscores
+ *
+ * \param s The string to change.
+ * \param replacespace true to insert a space where there was an underscore
+ * \return The altered string
+ */
+static char *remove_underscores(const char *s, bool replacespace)
+{
+ size_t i, ii, len;
+ char *ret;
+ len = strlen(s);
+ ret = malloc(len + 1);
+ if (ret == NULL) {
+ return NULL;
+ }
+ for (i = 0, ii = 0; i < len; i++) {
+ if (s[i] != '_') {
+ ret[ii++] = s[i];
+ } else if (replacespace) {
+ ret[ii++] = ' ';
+ }
+ }
+ ret[ii] = '\0';
+ return ret;
+}
+
+
+/**
+ * get default image for buttons / menu items from gtk stock items.
+ *
+ * \param tbbutton button reference
+ * \param iconsize The size of icons to select.
+ * \return default images.
+ */
+static GtkImage *
+nsgtk_theme_image_default(nsgtk_toolbar_button tbbutton, GtkIconSize iconsize)
+{
+ GtkImage *image; /* The GTK image to return */
+
+ switch(tbbutton) {
+
+#define BUTTON_IMAGE(p, q) \
+ case p##_BUTTON: \
+ image = GTK_IMAGE(nsgtk_image_new_from_stock(q, iconsize)); \
+ break
+
+ BUTTON_IMAGE(BACK, NSGTK_STOCK_GO_BACK);
+ BUTTON_IMAGE(FORWARD, NSGTK_STOCK_GO_FORWARD);
+ BUTTON_IMAGE(STOP, NSGTK_STOCK_STOP);
+ BUTTON_IMAGE(RELOAD, NSGTK_STOCK_REFRESH);
+ BUTTON_IMAGE(HOME, NSGTK_STOCK_HOME);
+ BUTTON_IMAGE(NEWWINDOW, "gtk-new");
+ BUTTON_IMAGE(NEWTAB, "gtk-new");
+ BUTTON_IMAGE(OPENFILE, NSGTK_STOCK_OPEN);
+ BUTTON_IMAGE(CLOSETAB, NSGTK_STOCK_CLOSE);
+ BUTTON_IMAGE(CLOSEWINDOW, NSGTK_STOCK_CLOSE);
+ BUTTON_IMAGE(SAVEPAGE, NSGTK_STOCK_SAVE_AS);
+ BUTTON_IMAGE(PRINTPREVIEW, "gtk-print-preview");
+ BUTTON_IMAGE(PRINT, "gtk-print");
+ BUTTON_IMAGE(QUIT, "gtk-quit");
+ BUTTON_IMAGE(CUT, "gtk-cut");
+ BUTTON_IMAGE(COPY, "gtk-copy");
+ BUTTON_IMAGE(PASTE, "gtk-paste");
+ BUTTON_IMAGE(DELETE, "gtk-delete");
+ BUTTON_IMAGE(SELECTALL, "gtk-select-all");
+ BUTTON_IMAGE(FIND, NSGTK_STOCK_FIND);
+ BUTTON_IMAGE(PREFERENCES, "gtk-preferences");
+ BUTTON_IMAGE(ZOOMPLUS, "gtk-zoom-in");
+ BUTTON_IMAGE(ZOOMMINUS, "gtk-zoom-out");
+ BUTTON_IMAGE(ZOOMNORMAL, "gtk-zoom-100");
+ BUTTON_IMAGE(FULLSCREEN, "gtk-fullscreen");
+ BUTTON_IMAGE(VIEWSOURCE, "gtk-index");
+ BUTTON_IMAGE(CONTENTS, "gtk-help");
+ BUTTON_IMAGE(ABOUT, "gtk-about");
+#undef BUTTON_IMAGE
+
+ case HISTORY_BUTTON:
+ image = GTK_IMAGE(gtk_image_new_from_pixbuf(arrow_down_pixbuf));
+ break;
+
+ default:
+ image = GTK_IMAGE(nsgtk_image_new_from_stock("gtk-missing-image",
+ iconsize));
+ break;
+
+ }
+ return image;
+
+}
+
+/**
+ * Get default image for search buttons / menu items from gtk stock items
+ *
+ * \param tbbutton search button reference
+ * \param iconsize The size of icons to select.
+ * \return default search image.
+ */
+
+static GtkImage *
+nsgtk_theme_searchimage_default(nsgtk_search_buttons tbbutton,
+ GtkIconSize iconsize)
+{
+ switch (tbbutton) {
+
+ case (SEARCH_BACK_BUTTON):
+ return GTK_IMAGE(nsgtk_image_new_from_stock(NSGTK_STOCK_GO_BACK,
+ iconsize));
+ case (SEARCH_FORWARD_BUTTON):
+ return GTK_IMAGE(nsgtk_image_new_from_stock(NSGTK_STOCK_GO_FORWARD,
+ iconsize));
+ case (SEARCH_CLOSE_BUTTON):
+ return GTK_IMAGE(nsgtk_image_new_from_stock(NSGTK_STOCK_CLOSE,
+ iconsize));
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * initialise a theme structure with gtk images
+ */
+static struct nsgtk_theme *nsgtk_theme_load(GtkIconSize iconsize)
+{
+ struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme));
+ int btnloop;
+
+ if (theme == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return NULL;
+ }
+
+ for (btnloop = BACK_BUTTON; btnloop < PLACEHOLDER_BUTTON ; btnloop++) {
+ theme->image[btnloop] = nsgtk_theme_image_default(btnloop, iconsize);
+ }
+
+ for (btnloop = SEARCH_BACK_BUTTON; btnloop < SEARCH_BUTTONS_COUNT; btnloop++) {
+ theme->searchimage[btnloop] = nsgtk_theme_searchimage_default(btnloop, iconsize);
+ }
+ return theme;
+}
+
+
+
+/* exported function documented in gtk/toolbar.h */
+void nsgtk_theme_implement(struct nsgtk_scaffolding *g)
+{
+ struct nsgtk_theme *theme[IMAGE_SET_COUNT];
+ int i;
+ struct nsgtk_button_connect *button;
+ struct gtk_search *search;
+
+ theme[IMAGE_SET_MAIN_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU);
+ theme[IMAGE_SET_RCLICK_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU);
+ theme[IMAGE_SET_POPUP_MENU] = nsgtk_theme_load(GTK_ICON_SIZE_MENU);
+ theme[IMAGE_SET_BUTTONS] = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if ((i == URL_BAR_ITEM) || (i == THROBBER_ITEM) ||
+ (i == WEBSEARCH_ITEM))
+ continue;
+
+ button = nsgtk_scaffolding_button(g, i);
+ if (button == NULL)
+ continue;
+
+ /* gtk_image_menu_item_set_image accepts NULL image */
+ if ((button->main != NULL) &&
+ (theme[IMAGE_SET_MAIN_MENU] != NULL)) {
+ nsgtk_image_menu_item_set_image(
+ GTK_WIDGET(button->main),
+ GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->image[i]));
+ gtk_widget_show_all(GTK_WIDGET(button->main));
+ }
+ if ((button->rclick != NULL) &&
+ (theme[IMAGE_SET_RCLICK_MENU] != NULL)) {
+ nsgtk_image_menu_item_set_image(GTK_WIDGET(button->rclick),
+ GTK_WIDGET(
+ theme[IMAGE_SET_RCLICK_MENU]->
+ image[i]));
+ gtk_widget_show_all(GTK_WIDGET(button->rclick));
+ }
+ if ((button->popup != NULL) &&
+ (theme[IMAGE_SET_POPUP_MENU] != NULL)) {
+ nsgtk_image_menu_item_set_image(GTK_WIDGET(button->popup),
+ GTK_WIDGET(
+ theme[IMAGE_SET_POPUP_MENU]->
+ image[i]));
+ gtk_widget_show_all(GTK_WIDGET(button->popup));
+ }
+ if ((button->location != -1) && (button->button != NULL) &&
+ (theme[IMAGE_SET_BUTTONS] != NULL)) {
+ gtk_tool_button_set_icon_widget(
+ GTK_TOOL_BUTTON(button->button),
+ GTK_WIDGET(
+ theme[IMAGE_SET_BUTTONS]->
+ image[i]));
+ gtk_widget_show_all(GTK_WIDGET(button->button));
+ }
+ }
+
+ /* set search bar images */
+ search = nsgtk_scaffolding_search(g);
+ if ((search != NULL) && (theme[IMAGE_SET_MAIN_MENU] != NULL)) {
+ /* gtk_tool_button_set_icon_widget accepts NULL image */
+ if (search->buttons[SEARCH_BACK_BUTTON] != NULL) {
+ gtk_tool_button_set_icon_widget(
+ search->buttons[SEARCH_BACK_BUTTON],
+ GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->
+ searchimage[SEARCH_BACK_BUTTON]));
+ gtk_widget_show_all(GTK_WIDGET(
+ search->buttons[SEARCH_BACK_BUTTON]));
+ }
+ if (search->buttons[SEARCH_FORWARD_BUTTON] != NULL) {
+ gtk_tool_button_set_icon_widget(
+ search->buttons[SEARCH_FORWARD_BUTTON],
+ GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->
+ searchimage[SEARCH_FORWARD_BUTTON]));
+ gtk_widget_show_all(GTK_WIDGET(
+ search->buttons[
+ SEARCH_FORWARD_BUTTON]));
+ }
+ if (search->buttons[SEARCH_CLOSE_BUTTON] != NULL) {
+ gtk_tool_button_set_icon_widget(
+ search->buttons[SEARCH_CLOSE_BUTTON],
+ GTK_WIDGET(theme[IMAGE_SET_MAIN_MENU]->
+ searchimage[SEARCH_CLOSE_BUTTON]));
+ gtk_widget_show_all(GTK_WIDGET(
+ search->buttons[SEARCH_CLOSE_BUTTON]));
+ }
+ }
+
+ for (i = 0; i < IMAGE_SET_COUNT; i++) {
+ if (theme[i] != NULL) {
+ free(theme[i]);
+ }
+ }
+}
+
+
+/**
+ * callback function to iterate toolbar's widgets
+ */
+static void nsgtk_toolbar_clear_toolbar(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ gtk_container_remove(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)),
+ widget);
+}
+
+/**
+ * connect temporary handler for toolbar edit events
+ *
+ * \param g The scaffolding
+ * \param bi The button index
+ */
+static void nsgtk_toolbar_temp_connect(struct nsgtk_scaffolding *g,
+ nsgtk_toolbar_button bi)
+{
+ struct nsgtk_button_connect *bc;
+
+ if (bi != URL_BAR_ITEM) {
+ bc = nsgtk_scaffolding_button(g, bi);
+ if ((bc->button != NULL) && (bc->dataminus != NULL)) {
+ g_signal_connect(bc->button,
+ "drag-data-get",
+ G_CALLBACK(bc->dataminus),
+ g);
+ }
+ }
+}
+
+/**
+ * get scaffolding button index of button at location
+ *
+ * \return toolbar item id from location when there is an item at that logical
+ * location; else -1
+ */
+static nsgtk_toolbar_button
+nsgtk_toolbar_get_id_at_location(struct nsgtk_scaffolding *g, int i)
+{
+ int q;
+ for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++) {
+ if (nsgtk_scaffolding_button(g, q)->location == i) {
+ return q;
+ }
+ }
+ return -1;
+}
+
+/**
+ * widget factory for creation of toolbar item widgets
+ * \param g the reference scaffolding
+ * \param i the id of the widget
+ * \param theme the theme to make the widgets from
+ */
+static GtkWidget *
+nsgtk_toolbar_make_widget(struct nsgtk_scaffolding *g,
+ nsgtk_toolbar_button i,
+ struct nsgtk_theme *theme)
+{
+ GtkWidget *w = NULL;
+
+ switch(i) {
+
+/* gtk_tool_button_new() accepts NULL args */
+#define MAKE_STOCKBUTTON(p, q) \
+ case p##_BUTTON: { \
+ GtkStockItem item; \
+ char *label = NULL; \
+ if (nsgtk_stock_lookup(q, &item) && \
+ (item.label != NULL) && \
+ ((label = remove_underscores(item.label, false)) != NULL)) { \
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \
+ theme->image[p##_BUTTON]), label)); \
+ free(label); \
+ } else { \
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \
+ theme->image[p##_BUTTON]), q)); \
+ } \
+ break; \
+ }
+
+ MAKE_STOCKBUTTON(HOME, NSGTK_STOCK_HOME)
+ MAKE_STOCKBUTTON(BACK, NSGTK_STOCK_GO_BACK)
+ MAKE_STOCKBUTTON(FORWARD, NSGTK_STOCK_GO_FORWARD)
+ MAKE_STOCKBUTTON(STOP, NSGTK_STOCK_STOP)
+ MAKE_STOCKBUTTON(RELOAD, NSGTK_STOCK_REFRESH)
+#undef MAKE_STOCKBUTTON
+
+ case HISTORY_BUTTON:
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
+ theme->image[HISTORY_BUTTON]), "H"));
+ break;
+
+ case URL_BAR_ITEM: {
+ GtkWidget *entry = nsgtk_entry_new();
+ w = GTK_WIDGET(gtk_tool_item_new());
+
+ if ((entry == NULL) || (w == NULL)) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+
+ nsgtk_entry_set_icon_from_pixbuf(entry,
+ GTK_ENTRY_ICON_PRIMARY,
+ favicon_pixbuf);
+
+ gtk_container_add(GTK_CONTAINER(w), entry);
+ gtk_tool_item_set_expand(GTK_TOOL_ITEM(w), TRUE);
+ break;
+ }
+
+ case THROBBER_ITEM: {
+ if ((nsgtk_throbber == NULL) ||
+ (nsgtk_throbber->framedata == NULL) ||
+ (nsgtk_throbber->framedata[0] == NULL)) {
+ return NULL;
+ }
+
+ if (edit_mode) {
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
+ gtk_image_new_from_pixbuf(
+ nsgtk_throbber->framedata[0])),
+ "[throbber]"));
+ } else {
+ GtkWidget *image;
+
+ w = GTK_WIDGET(gtk_tool_item_new());
+
+ image = gtk_image_new_from_pixbuf(nsgtk_throbber->framedata[0]);
+ if (image != NULL) {
+ nsgtk_widget_set_alignment(image,
+ GTK_ALIGN_CENTER,
+ GTK_ALIGN_CENTER);
+ nsgtk_widget_set_margins(image, 3, 0);
+
+ gtk_container_add(GTK_CONTAINER(w), image);
+ }
+ }
+ break;
+ }
+
+ case WEBSEARCH_ITEM: {
+ if (edit_mode)
+ return GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET(
+ nsgtk_image_new_from_stock(NSGTK_STOCK_FIND,
+ GTK_ICON_SIZE_LARGE_TOOLBAR)),
+ "[websearch]"));
+
+ GtkWidget *entry = nsgtk_entry_new();
+
+ w = GTK_WIDGET(gtk_tool_item_new());
+
+ if ((entry == NULL) || (w == NULL)) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return NULL;
+ }
+
+ gtk_widget_set_size_request(entry, NSGTK_WEBSEARCH_WIDTH, -1);
+
+ nsgtk_entry_set_icon_from_stock(entry, GTK_ENTRY_ICON_PRIMARY,
+ NSGTK_STOCK_INFO);
+
+ gtk_container_add(GTK_CONTAINER(w), entry);
+ break;
+ }
+
+/* gtk_tool_button_new accepts NULL args */
+#define MAKE_MENUBUTTON(p, q) \
+ case p##_BUTTON: { \
+ char *label = NULL; \
+ label = remove_underscores(messages_get(#q), false); \
+ w = GTK_WIDGET(gtk_tool_button_new(GTK_WIDGET( \
+ theme->image[p##_BUTTON]), label)); \
+ if (label != NULL) \
+ free(label); \
+ break; \
+ }
+
+ MAKE_MENUBUTTON(NEWWINDOW, gtkNewWindow)
+ MAKE_MENUBUTTON(NEWTAB, gtkNewTab)
+ MAKE_MENUBUTTON(OPENFILE, gtkOpenFile)
+ MAKE_MENUBUTTON(CLOSETAB, gtkCloseTab)
+ MAKE_MENUBUTTON(CLOSEWINDOW, gtkCloseWindow)
+ MAKE_MENUBUTTON(SAVEPAGE, gtkSavePage)
+ MAKE_MENUBUTTON(PRINTPREVIEW, gtkPrintPreview)
+ MAKE_MENUBUTTON(PRINT, gtkPrint)
+ MAKE_MENUBUTTON(QUIT, gtkQuitMenu)
+ MAKE_MENUBUTTON(CUT, gtkCut)
+ MAKE_MENUBUTTON(COPY, gtkCopy)
+ MAKE_MENUBUTTON(PASTE, gtkPaste)
+ MAKE_MENUBUTTON(DELETE, gtkDelete)
+ MAKE_MENUBUTTON(SELECTALL, gtkSelectAll)
+ MAKE_MENUBUTTON(PREFERENCES, gtkPreferences)
+ MAKE_MENUBUTTON(ZOOMPLUS, gtkZoomPlus)
+ MAKE_MENUBUTTON(ZOOMMINUS, gtkZoomMinus)
+ MAKE_MENUBUTTON(ZOOMNORMAL, gtkZoomNormal)
+ MAKE_MENUBUTTON(FULLSCREEN, gtkFullScreen)
+ MAKE_MENUBUTTON(VIEWSOURCE, gtkViewSource)
+ MAKE_MENUBUTTON(CONTENTS, gtkContents)
+ MAKE_MENUBUTTON(ABOUT, gtkAbout)
+ MAKE_MENUBUTTON(PDF, gtkPDF)
+ MAKE_MENUBUTTON(PLAINTEXT, gtkPlainText)
+ MAKE_MENUBUTTON(DRAWFILE, gtkDrawFile)
+ MAKE_MENUBUTTON(POSTSCRIPT, gtkPostScript)
+ MAKE_MENUBUTTON(FIND, gtkFind)
+ MAKE_MENUBUTTON(DOWNLOADS, gtkDownloads)
+ MAKE_MENUBUTTON(SAVEWINDOWSIZE, gtkSaveWindowSize)
+ MAKE_MENUBUTTON(TOGGLEDEBUGGING, gtkToggleDebugging)
+ MAKE_MENUBUTTON(SAVEBOXTREE, gtkDebugBoxTree)
+ MAKE_MENUBUTTON(SAVEDOMTREE, gtkDebugDomTree)
+ MAKE_MENUBUTTON(LOCALHISTORY, gtkLocalHistory)
+ MAKE_MENUBUTTON(GLOBALHISTORY, gtkGlobalHistory)
+ MAKE_MENUBUTTON(ADDBOOKMARKS, gtkAddBookMarks)
+ MAKE_MENUBUTTON(SHOWBOOKMARKS, gtkShowBookMarks)
+ MAKE_MENUBUTTON(SHOWCOOKIES, gtkShowCookies)
+ MAKE_MENUBUTTON(OPENLOCATION, gtkOpenLocation)
+ MAKE_MENUBUTTON(NEXTTAB, gtkNextTab)
+ MAKE_MENUBUTTON(PREVTAB, gtkPrevTab)
+ MAKE_MENUBUTTON(GUIDE, gtkGuide)
+ MAKE_MENUBUTTON(INFO, gtkUserInformation)
+#undef MAKE_MENUBUTTON
+
+ default:
+ break;
+
+ }
+ return w;
+}
+
+/**
+ * called when a widget is dropped onto the toolbar
+ */
+static gboolean
+nsgtk_toolbar_data(GtkWidget *widget,
+ GdkDragContext *gdc,
+ gint x,
+ gint y,
+ guint time,
+ gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ int ind = gtk_toolbar_get_drop_index(nsgtk_scaffolding_toolbar(g),
+ x, y);
+ int q, i;
+ if (window->currentbutton == -1)
+ return TRUE;
+ struct nsgtk_theme *theme =
+ nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ if (theme == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return TRUE;
+ }
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->location
+ != -1) {
+ /* widget was already in the toolbar; so replace */
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->
+ location < ind)
+ ind--;
+ gtk_container_remove(GTK_CONTAINER(
+ nsgtk_scaffolding_toolbar(g)), GTK_WIDGET(
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button));
+ /* 'move' all widgets further right than the original location,
+ * one place to the left in logical schema */
+ for (i = nsgtk_scaffolding_button(g, window->currentbutton)->
+ location + 1; i < PLACEHOLDER_BUTTON; i++) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ nsgtk_scaffolding_button(g, q)->location--;
+ }
+ nsgtk_scaffolding_button(g, window->currentbutton)->
+ location = -1;
+ }
+ nsgtk_scaffolding_button(g, window->currentbutton)->button =
+ GTK_TOOL_ITEM(nsgtk_toolbar_make_widget(g,
+ window->currentbutton, theme));
+ free(theme);
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->button
+ == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return TRUE;
+ }
+ /* update logical schema */
+ nsgtk_scaffolding_reset_offset(g);
+ /* 'move' all widgets further right than the new location, one place to
+ * the right in logical schema */
+ for (i = PLACEHOLDER_BUTTON - 1; i >= ind; i--) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ nsgtk_scaffolding_button(g, q)->location++;
+ }
+ nsgtk_scaffolding_button(g, window->currentbutton)->location = ind;
+
+ /* complete action */
+ GtkToolItem *current_button;
+
+ current_button = GTK_TOOL_ITEM(nsgtk_scaffolding_button(g, window->currentbutton)->button);
+
+ gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g), current_button, ind);
+
+ gtk_tool_item_set_use_drag_window(current_button, TRUE);
+ gtk_drag_source_set(GTK_WIDGET(current_button),
+ GDK_BUTTON1_MASK, &entry, 1,
+ GDK_ACTION_COPY);
+ nsgtk_toolbar_temp_connect(g, window->currentbutton);
+ gtk_widget_show_all(GTK_WIDGET(current_button));
+
+
+ window->currentbutton = -1;
+
+ return TRUE;
+}
+
+/**
+ * connected to toolbutton drop; perhaps one day it'll work properly so it may
+ * replace the global current_button
+ */
+static gboolean
+nsgtk_toolbar_move_complete(GtkWidget *widget,
+ GdkDragContext *gdc,
+ gint x,
+ gint y,
+ GtkSelectionData *selection,
+ guint info,
+ guint time,
+ gpointer data)
+{
+ return FALSE;
+}
+
+/**
+ * called when hovering an item above the toolbar
+ */
+static gboolean
+nsgtk_toolbar_action(GtkWidget *widget, GdkDragContext *gdc, gint x,
+ gint y, guint time, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ GtkToolItem *item = gtk_tool_button_new(NULL, NULL);
+ if (item != NULL)
+ gtk_toolbar_set_drop_highlight_item(
+ nsgtk_scaffolding_toolbar(g),
+ GTK_TOOL_ITEM(item),
+ gtk_toolbar_get_drop_index(
+ nsgtk_scaffolding_toolbar(g), x, y));
+ return FALSE;
+}
+
+/**
+ * called when hovering stops
+ */
+static void
+nsgtk_toolbar_clear(GtkWidget *widget, GdkDragContext *gdc, guint time,
+ gpointer data)
+{
+ gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(widget), NULL, 0);
+}
+
+/**
+ * add item to toolbar.
+ *
+ * the function should be called, when multiple items are being added,
+ * in ascending order.
+ *
+ * \param g the scaffolding whose toolbar an item is added to.
+ * \param i the location in the toolbar.
+ * \param theme The theme in use.
+ */
+static void
+nsgtk_toolbar_add_item_to_toolbar(struct nsgtk_scaffolding *g, int i,
+ struct nsgtk_theme *theme)
+{
+ int q;
+ for (q = BACK_BUTTON; q < PLACEHOLDER_BUTTON; q++)
+ if (nsgtk_scaffolding_button(g, q)->location == i) {
+ nsgtk_scaffolding_button(g, q)->button = GTK_TOOL_ITEM(
+ nsgtk_toolbar_make_widget(g, q,
+ theme));
+ gtk_toolbar_insert(nsgtk_scaffolding_toolbar(g),
+ nsgtk_scaffolding_button(g, q)->button,
+ i);
+ break;
+ }
+}
+
+/**
+ * cleanup code physical update of all toolbars; resensitize
+ * \param g the 'front' scaffolding that called customize
+ */
+static void nsgtk_toolbar_close(struct nsgtk_scaffolding *g)
+{
+ int i;
+
+ struct nsgtk_scaffolding *list;
+ struct nsgtk_theme *theme;
+
+ list = nsgtk_scaffolding_iterate(NULL);
+ while (list) {
+ theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ if (theme == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ continue;
+ }
+ /* clear toolbar */
+ gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(
+ list)), nsgtk_toolbar_clear_toolbar, list);
+ /* then add items */
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ nsgtk_toolbar_add_item_to_toolbar(list, i, theme);
+ }
+ nsgtk_toolbar_connect_all(list);
+ gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar(
+ list)));
+ nsgtk_scaffolding_set_sensitivity(list);
+ nsgtk_widget_override_background_color(GTK_WIDGET(nsgtk_window_get_layout(nsgtk_scaffolding_top_level(list))), GTK_STATE_NORMAL, 0, 0xFFFF, 0xFFFF, 0xFFFF);
+ g_signal_handler_unblock(GTK_WIDGET(
+ nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list),
+ NSGTK_WINDOW_SIGNAL_CLICK));
+ g_signal_handler_unblock(GTK_WIDGET(
+ nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list),
+ NSGTK_WINDOW_SIGNAL_REDRAW));
+ browser_window_refresh_url_bar(
+ nsgtk_get_browser_window(
+ nsgtk_scaffolding_top_level(list)));
+
+ if (list != g)
+ gtk_widget_set_sensitive(GTK_WIDGET(
+ nsgtk_scaffolding_window(list)), TRUE);
+ free(theme);
+ list = nsgtk_scaffolding_iterate(list);
+ }
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)),
+ TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)),
+ TRUE);
+ /* update favicon etc */
+ nsgtk_scaffolding_set_top_level(nsgtk_scaffolding_top_level(g));
+
+ search_web_select_provider(-1);
+}
+
+/**
+ * when cancel button is clicked
+ */
+static gboolean nsgtk_toolbar_cancel_clicked(GtkWidget *widget, gpointer data)
+{
+ edit_mode = false;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ /* reset g->buttons->location */
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ nsgtk_scaffolding_button(g, i)->location =
+ window->buttonlocations[i];
+ }
+ nsgtk_toolbar_set_physical(g);
+ nsgtk_toolbar_connect_all(g);
+ nsgtk_toolbar_close(g);
+ nsgtk_scaffolding_set_sensitivity(g);
+ gtk_widget_destroy(window->window);
+ return TRUE;
+}
+
+/**
+ * physically add widgets to store window
+ */
+static bool nsgtk_toolbar_add_store_widget(GtkWidget *widget)
+{
+ if (window->numberh >= NSGTK_STORE_WIDTH) {
+ window->currentbar = gtk_toolbar_new();
+ if (window->currentbar == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ return false;
+ }
+ gtk_toolbar_set_style(GTK_TOOLBAR(window->currentbar),
+ GTK_TOOLBAR_BOTH);
+ gtk_toolbar_set_icon_size(GTK_TOOLBAR(window->currentbar),
+ GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_box_pack_start(GTK_BOX(window->widgetvbox),
+ window->currentbar, FALSE, FALSE, 0);
+ window->numberh = 0;
+ }
+ gtk_widget_set_size_request(widget, NSGTK_BUTTON_WIDTH,
+ NSGTK_BUTTON_HEIGHT);
+ gtk_toolbar_insert(GTK_TOOLBAR(window->currentbar), GTK_TOOL_ITEM(
+ widget), window->numberh++);
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(widget), TRUE);
+ gtk_drag_source_set(widget, GDK_BUTTON1_MASK, &entry, 1,
+ GDK_ACTION_COPY);
+ gtk_widget_show_all(window->window);
+ return true;
+}
+
+/**
+ * save toolbar settings to file
+ */
+static void nsgtk_toolbar_customization_save(struct nsgtk_scaffolding *g)
+{
+ int i;
+ FILE *f = fopen(toolbar_indices_file_location, "w");
+ if (f == NULL){
+ nsgtk_warning("gtkFileError", toolbar_indices_file_location);
+ return;
+ }
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ fprintf(f, "%d;%d|", i, nsgtk_scaffolding_button(g, i)->location);
+ }
+ fclose(f);
+}
+
+/**
+ * cast toolbar settings to all scaffoldings referenced from the global linked
+ * list of gui_windows
+ */
+static void nsgtk_toolbar_cast(struct nsgtk_scaffolding *g)
+{
+ int i;
+ struct nsgtk_scaffolding *list;
+
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ window->buttonlocations[i] =
+ ((nsgtk_scaffolding_button(g, i)->location
+ >= -1) &&
+ (nsgtk_scaffolding_button(g, i)->location
+ < PLACEHOLDER_BUTTON)) ?
+ nsgtk_scaffolding_button(g, i)->location : -1;
+ }
+
+ list = nsgtk_scaffolding_iterate(NULL);
+ while (list) {
+ if (list != g)
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_scaffolding_button(list, i)->location =
+ window->buttonlocations[i];
+ list = nsgtk_scaffolding_iterate(list);
+ }
+}
+
+/**
+ * when 'save settings' button is clicked
+ */
+static gboolean nsgtk_toolbar_persist(GtkWidget *widget, gpointer data)
+{
+ edit_mode = false;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ /* save state to file, update toolbars for all windows */
+ nsgtk_toolbar_customization_save(g);
+ nsgtk_toolbar_cast(g);
+ nsgtk_toolbar_set_physical(g);
+ nsgtk_toolbar_close(g);
+ gtk_widget_destroy(window->window);
+ return TRUE;
+}
+
+/**
+ * when 'reload defaults' button is clicked
+ */
+static gboolean nsgtk_toolbar_reset(GtkWidget *widget, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ int i;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_scaffolding_button(g, i)->location =
+ (i <= THROBBER_ITEM) ? i : -1;
+ nsgtk_toolbar_set_physical(g);
+ for (i = BACK_BUTTON; i <= THROBBER_ITEM; i++) {
+ if (i == URL_BAR_ITEM)
+ continue;
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(
+ nsgtk_scaffolding_button(g, i)->button), TRUE);
+ gtk_drag_source_set(GTK_WIDGET(
+ nsgtk_scaffolding_button(g, i)->button),
+ GDK_BUTTON1_MASK, &entry, 1, GDK_ACTION_COPY);
+ nsgtk_toolbar_temp_connect(g, i);
+ }
+ return TRUE;
+}
+
+/**
+ * when titlebar / alt-F4 window close event happens
+ */
+static gboolean nsgtk_toolbar_delete(GtkWidget *widget, GdkEvent *event,
+ gpointer data)
+{
+ edit_mode = false;
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ /* reset g->buttons->location */
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ nsgtk_scaffolding_button(g, i)->location =
+ window->buttonlocations[i];
+ }
+ nsgtk_toolbar_set_physical(g);
+ nsgtk_toolbar_connect_all(g);
+ nsgtk_toolbar_close(g);
+ nsgtk_scaffolding_set_sensitivity(g);
+ gtk_widget_destroy(window->window);
+ return TRUE;
+}
+
+/**
+ * called when a widget is dropped onto the store window
+ */
+static gboolean
+nsgtk_toolbar_store_return(GtkWidget *widget, GdkDragContext *gdc,
+ gint x, gint y, guint time, gpointer data)
+{
+ struct nsgtk_scaffolding *g = (struct nsgtk_scaffolding *)data;
+ int q, i;
+
+ if ((window->fromstore) || (window->currentbutton == -1)) {
+ window->currentbutton = -1;
+ return FALSE;
+ }
+ if (nsgtk_scaffolding_button(g, window->currentbutton)->location
+ != -1) {
+ /* 'move' all widgets further right, one place to the left
+ * in logical schema */
+ for (i = nsgtk_scaffolding_button(g, window->currentbutton)->
+ location + 1; i < PLACEHOLDER_BUTTON; i++) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ nsgtk_scaffolding_button(g, q)->location--;
+ }
+ gtk_container_remove(GTK_CONTAINER(
+ nsgtk_scaffolding_toolbar(g)), GTK_WIDGET(
+ nsgtk_scaffolding_button(g,
+ window->currentbutton)->button));
+ nsgtk_scaffolding_button(g, window->currentbutton)->location
+ = -1;
+ }
+ window->currentbutton = -1;
+ gtk_drag_finish(gdc, TRUE, TRUE, time);
+ return FALSE;
+}
+
+/**
+ * called when hovering above the store
+ */
+static gboolean
+nsgtk_toolbar_store_action(GtkWidget *widget, GdkDragContext *gdc,
+ gint x, gint y, guint time, gpointer data)
+{
+ return FALSE;
+}
+
+/**
+ * create store window
+ */
+static void nsgtk_toolbar_window_open(struct nsgtk_scaffolding *g)
+{
+ int x = 0, y = 0;
+ struct nsgtk_theme *theme;
+ nserror res;
+
+ theme = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ if (theme == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ nsgtk_toolbar_cancel_clicked(NULL, g);
+ return;
+ }
+
+ res = nsgtk_builder_new_from_resname("toolbar", &window->builder);
+ if (res != NSERROR_OK) {
+ LOG("Toolbar UI builder init failed");
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ nsgtk_toolbar_cancel_clicked(NULL, g);
+ free(theme);
+ return;
+ }
+
+ gtk_builder_connect_signals(window->builder, NULL);
+
+ window->window = GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "toolbarwindow"));
+ if (window->window == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ nsgtk_toolbar_cancel_clicked(NULL, g);
+ free(theme);
+ return;
+ }
+
+ window->widgetvbox = GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "widgetvbox"));
+ if (window->widgetvbox == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ nsgtk_toolbar_cancel_clicked(NULL, g);
+ free(theme);
+ return;
+ }
+
+ window->numberh = NSGTK_STORE_WIDTH; /* preset to width [in buttons] of */
+ /* store to cause creation of a new toolbar */
+ window->currentbutton = -1;
+ /* load toolbuttons */
+ /* add toolbuttons to window */
+ /* set event handlers */
+ for (int i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if (i == URL_BAR_ITEM)
+ continue;
+ window->store_buttons[i] =
+ nsgtk_toolbar_make_widget(g, i, theme);
+ if (window->store_buttons[i] == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ continue;
+ }
+ nsgtk_toolbar_add_store_widget(window->store_buttons[i]);
+ g_signal_connect(window->store_buttons[i], "drag-data-get",
+ G_CALLBACK(
+ nsgtk_scaffolding_button(g, i)->dataplus), g);
+ }
+ free(theme);
+
+ gtk_window_set_transient_for(GTK_WINDOW(window->window),
+ nsgtk_scaffolding_window(g));
+ gtk_window_set_title(GTK_WINDOW(window->window), messages_get(
+ "gtkToolBarTitle"));
+ gtk_window_set_accept_focus(GTK_WINDOW(window->window), FALSE);
+ gtk_drag_dest_set(GTK_WIDGET(window->window), GTK_DEST_DEFAULT_MOTION |
+ GTK_DEST_DEFAULT_DROP, &entry, 1, GDK_ACTION_COPY);
+ gtk_widget_show_all(window->window);
+ gtk_window_set_position(GTK_WINDOW(window->window),
+ GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_window_get_position(nsgtk_scaffolding_window(g), &x, &y);
+ gtk_window_move(GTK_WINDOW(window->window), x, y + 100);
+
+ g_signal_connect(GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "cancelbutton")),
+ "clicked",
+ G_CALLBACK(nsgtk_toolbar_cancel_clicked),
+ g);
+
+ g_signal_connect(GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "okbutton")),
+ "clicked",
+ G_CALLBACK(nsgtk_toolbar_persist),
+ g);
+
+ g_signal_connect(GTK_WIDGET(gtk_builder_get_object(window->builder,
+ "resetbutton")),
+ "clicked",
+ G_CALLBACK(nsgtk_toolbar_reset),
+ g);
+
+ g_signal_connect(window->window, "delete-event",
+ G_CALLBACK(nsgtk_toolbar_delete), g);
+
+ g_signal_connect(window->window, "drag-drop",
+ G_CALLBACK(nsgtk_toolbar_store_return), g);
+
+ g_signal_connect(window->window, "drag-motion",
+ G_CALLBACK(nsgtk_toolbar_store_action), g);
+}
+
+/**
+ * change behaviour of scaffoldings while editing toolbar
+ *
+ * All buttons as well as window clicks are desensitized; then buttons
+ * in the front window are changed to movable buttons
+ */
+void nsgtk_toolbar_customization_init(struct nsgtk_scaffolding *g)
+{
+ int i;
+ struct nsgtk_scaffolding *list;
+ edit_mode = true;
+
+ list = nsgtk_scaffolding_iterate(NULL);
+ while (list) {
+ g_signal_handler_block(GTK_WIDGET(
+ nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list),
+ NSGTK_WINDOW_SIGNAL_CLICK));
+ g_signal_handler_block(GTK_WIDGET(
+ nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ nsgtk_window_get_signalhandler(
+ nsgtk_scaffolding_top_level(list),
+ NSGTK_WINDOW_SIGNAL_REDRAW));
+ nsgtk_widget_override_background_color(
+ GTK_WIDGET(nsgtk_window_get_layout(
+ nsgtk_scaffolding_top_level(list))),
+ GTK_STATE_NORMAL, 0, 0xEEEE, 0xEEEE, 0xEEEE);
+
+ if (list == g) {
+ list = nsgtk_scaffolding_iterate(list);
+ continue;
+ }
+ /* set sensitive for all gui_windows save g */
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_window(
+ list)), FALSE);
+ list = nsgtk_scaffolding_iterate(list);
+ }
+ /* set sensitive for all of g save toolbar */
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_menu_bar(g)),
+ FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(nsgtk_scaffolding_notebook(g)),
+ FALSE);
+
+ /* set editable aspect for toolbar */
+ gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)),
+ nsgtk_toolbar_clear_toolbar, g);
+ nsgtk_toolbar_set_physical(g);
+ /* memorize button locations, set editable */
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ window->buttonlocations[i] = nsgtk_scaffolding_button(g, i)
+ ->location;
+ if ((window->buttonlocations[i] == -1) || (i == URL_BAR_ITEM))
+ continue;
+ gtk_tool_item_set_use_drag_window(GTK_TOOL_ITEM(
+ nsgtk_scaffolding_button(g, i)->button), TRUE);
+ gtk_drag_source_set(GTK_WIDGET(nsgtk_scaffolding_button(
+ g, i)->button), GDK_BUTTON1_MASK, &entry, 1,
+ GDK_ACTION_COPY);
+ nsgtk_toolbar_temp_connect(g, i);
+ }
+
+ /* add move button listeners */
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-drop", G_CALLBACK(nsgtk_toolbar_data), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-data-received", G_CALLBACK(
+ nsgtk_toolbar_move_complete), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-motion", G_CALLBACK(nsgtk_toolbar_action), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ "drag-leave", G_CALLBACK(
+ nsgtk_toolbar_clear), g);
+
+ /* set data types */
+ gtk_drag_dest_set(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)),
+ GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+ &entry, 1, GDK_ACTION_COPY);
+
+ /* open toolbar window */
+ nsgtk_toolbar_window_open(g);
+}
+
+/**
+ * set toolbar logical -> physical; physically visible toolbar buttons are made
+ * to correspond to the logically stored schema in terms of location
+ * visibility etc
+ */
+void nsgtk_toolbar_set_physical(struct nsgtk_scaffolding *g)
+{
+ int i;
+ struct nsgtk_theme *theme =
+ nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
+ if (theme == NULL) {
+ nsgtk_warning(messages_get("NoMemory"), 0);
+ return;
+ }
+ /* simplest is to clear the toolbar then reload it from memory */
+ gtk_container_foreach(GTK_CONTAINER(nsgtk_scaffolding_toolbar(g)),
+ nsgtk_toolbar_clear_toolbar, g);
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_toolbar_add_item_to_toolbar(g, i, theme);
+ gtk_widget_show_all(GTK_WIDGET(nsgtk_scaffolding_toolbar(g)));
+ free(theme);
+}
+
+/**
+ * \return toolbar item id when a widget is an element of the scaffolding
+ * else -1
+ */
+int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget,
+ struct nsgtk_scaffolding *g)
+{
+ int i;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ if ((nsgtk_scaffolding_button(g, i)->location != -1)
+ && (widget == GTK_WIDGET(
+ nsgtk_scaffolding_button(g, i)->button))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+/**
+ * add handlers to factory widgets
+ * \param g the scaffolding to attach handlers to
+ * \param i the toolbar item id
+ */
+static void
+nsgtk_toolbar_set_handler(struct nsgtk_scaffolding *g, nsgtk_toolbar_button i)
+{
+ switch(i){
+ case URL_BAR_ITEM:
+ nsgtk_scaffolding_update_url_bar_ref(g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)),
+ "activate", G_CALLBACK(
+ nsgtk_window_url_activate_event), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_urlbar(g)),
+ "changed", G_CALLBACK(
+ nsgtk_window_url_changed), g);
+ break;
+ case THROBBER_ITEM:
+ nsgtk_scaffolding_update_throbber_ref(g);
+ break;
+ case WEBSEARCH_ITEM:
+ nsgtk_scaffolding_update_websearch_ref(g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)),
+ "activate", G_CALLBACK(
+ nsgtk_websearch_activate), g);
+ g_signal_connect(GTK_WIDGET(nsgtk_scaffolding_websearch(g)),
+ "button-press-event", G_CALLBACK(
+ nsgtk_websearch_clear), g);
+ break;
+ default:
+ if ((nsgtk_scaffolding_button(g, i)->bhandler != NULL) &&
+ (nsgtk_scaffolding_button(g, i)->button
+ != NULL))
+ g_signal_connect(nsgtk_scaffolding_button(g, i)->
+ button, "clicked",
+ G_CALLBACK(nsgtk_scaffolding_button(g,
+ i)->bhandler), g);
+ break;
+ }
+}
+
+/**
+ * connect 'normal' handlers to toolbar buttons
+ */
+void nsgtk_toolbar_connect_all(struct nsgtk_scaffolding *g)
+{
+ int q, i;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
+ q = nsgtk_toolbar_get_id_at_location(g, i);
+ if (q == -1)
+ continue;
+ if (nsgtk_scaffolding_button(g, q)->button != NULL)
+ g_signal_connect(
+ nsgtk_scaffolding_button(g, q)->button,
+ "size-allocate", G_CALLBACK(
+ nsgtk_scaffolding_toolbar_size_allocate
+ ), g);
+ nsgtk_toolbar_set_handler(g, q);
+ }
+}
+
+
+#define DATAHANDLER(p, q, r)\
+gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\
+ *cont, GtkSelectionData *selection, guint info, guint time,\
+ gpointer data)\
+{\
+ r->currentbutton = q##_BUTTON;\
+ r->fromstore = true;\
+ return TRUE;\
+}\
+gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\
+ GdkDragContext *cont, GtkSelectionData *selection, guint info,\
+ guint time, gpointer data)\
+{\
+ r->currentbutton = q##_BUTTON;\
+ r->fromstore = false;\
+ return TRUE;\
+}
+
+DATAHANDLER(home, HOME, window)
+DATAHANDLER(forward, FORWARD, window)
+DATAHANDLER(back, BACK, window)
+DATAHANDLER(stop, STOP, window)
+DATAHANDLER(reload, RELOAD, window)
+DATAHANDLER(history, HISTORY, window)
+DATAHANDLER(newwindow, NEWWINDOW, window)
+DATAHANDLER(newtab, NEWTAB, window)
+DATAHANDLER(openfile, OPENFILE, window)
+DATAHANDLER(closetab, CLOSETAB, window)
+DATAHANDLER(closewindow, CLOSEWINDOW, window)
+DATAHANDLER(savepage, SAVEPAGE, window)
+DATAHANDLER(printpreview, PRINTPREVIEW, window)
+DATAHANDLER(print, PRINT, window)
+DATAHANDLER(quit, QUIT, window)
+DATAHANDLER(cut, CUT, window)
+DATAHANDLER(copy, COPY, window)
+DATAHANDLER(paste, PASTE, window)
+DATAHANDLER(delete, DELETE, window)
+DATAHANDLER(selectall, SELECTALL, window)
+DATAHANDLER(preferences, PREFERENCES, window)
+DATAHANDLER(zoomplus, ZOOMPLUS, window)
+DATAHANDLER(zoomminus, ZOOMMINUS, window)
+DATAHANDLER(zoomnormal, ZOOMNORMAL, window)
+DATAHANDLER(fullscreen, FULLSCREEN, window)
+DATAHANDLER(viewsource, VIEWSOURCE, window)
+DATAHANDLER(contents, CONTENTS, window)
+DATAHANDLER(about, ABOUT, window)
+DATAHANDLER(pdf, PDF, window)
+DATAHANDLER(plaintext, PLAINTEXT, window)
+DATAHANDLER(drawfile, DRAWFILE, window)
+DATAHANDLER(postscript, POSTSCRIPT, window)
+DATAHANDLER(find, FIND, window)
+DATAHANDLER(downloads, DOWNLOADS, window)
+DATAHANDLER(savewindowsize, SAVEWINDOWSIZE, window)
+DATAHANDLER(toggledebugging, TOGGLEDEBUGGING, window)
+DATAHANDLER(debugboxtree, SAVEBOXTREE, window)
+DATAHANDLER(debugdomtree, SAVEDOMTREE, window)
+DATAHANDLER(localhistory, LOCALHISTORY, window)
+DATAHANDLER(globalhistory, GLOBALHISTORY, window)
+DATAHANDLER(addbookmarks, ADDBOOKMARKS, window)
+DATAHANDLER(showbookmarks, SHOWBOOKMARKS, window)
+DATAHANDLER(showcookies, SHOWCOOKIES, window)
+DATAHANDLER(openlocation, OPENLOCATION, window)
+DATAHANDLER(nexttab, NEXTTAB, window)
+DATAHANDLER(prevtab, PREVTAB, window)
+DATAHANDLER(guide, GUIDE, window)
+DATAHANDLER(info, INFO, window)
+#undef DATAHANDLER
+#define DATAHANDLER(p, q, r)\
+gboolean nsgtk_toolbar_##p##_button_data(GtkWidget *widget, GdkDragContext\
+ *cont, GtkSelectionData *selection, guint info, guint time,\
+ gpointer data)\
+{\
+ r->currentbutton = q##_ITEM;\
+ r->fromstore = true;\
+ return TRUE;\
+}\
+gboolean nsgtk_toolbar_##p##_toolbar_button_data(GtkWidget *widget,\
+ GdkDragContext *cont, GtkSelectionData *selection, guint info,\
+ guint time, gpointer data)\
+{\
+ r->currentbutton = q##_ITEM;\
+ r->fromstore = false;\
+ return TRUE;\
+}
+
+DATAHANDLER(throbber, THROBBER, window)
+DATAHANDLER(websearch, WEBSEARCH, window)
+#undef DATAHANDLER
+
+
+/**
+ * load toolbar settings from file; file is a set of fields arranged as
+ * [itemreference];[itemlocation]|[itemreference];[itemlocation]| etc
+ */
+void nsgtk_toolbar_customization_load(struct nsgtk_scaffolding *g)
+{
+ int i, ii;
+ char *val;
+ char buffer[SLEN("11;|") * 2 * PLACEHOLDER_BUTTON]; /* numbers 0-99 */
+ buffer[0] = '\0';
+ char *buffer1, *subbuffer, *ptr = NULL, *pter = NULL;
+ for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++)
+ nsgtk_scaffolding_button(g, i)->location =
+ (i <= THROBBER_ITEM) ? i : -1;
+ FILE *f = fopen(toolbar_indices_file_location, "r");
+ if (f == NULL) {
+ nsgtk_warning(messages_get("gtkFileError"),
+ toolbar_indices_file_location);
+ return;
+ }
+ val = fgets(buffer, sizeof buffer, f);
+ if (val == NULL) {
+ LOG("empty read toolbar settings");
+ }
+ fclose(f);
+ i = BACK_BUTTON;
+ ii = BACK_BUTTON;
+ buffer1 = strtok_r(buffer, "|", &ptr);
+ while (buffer1 != NULL) {
+ subbuffer = strtok_r(buffer1, ";", &pter);
+ i = atoi(subbuffer);
+ subbuffer = strtok_r(NULL, ";", &pter);
+ ii = atoi(subbuffer);
+ if ((i >= BACK_BUTTON) && (i < PLACEHOLDER_BUTTON) &&
+ (ii >= -1) && (ii < PLACEHOLDER_BUTTON)) {
+ nsgtk_scaffolding_button(g, i)->location = ii;
+ }
+ buffer1 = strtok_r(NULL, "|", &ptr);
+ }
+}
diff --git a/frontends/gtk/toolbar.h b/frontends/gtk/toolbar.h
new file mode 100644
index 000000000..0453109ca
--- /dev/null
+++ b/frontends/gtk/toolbar.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NETSURF_GTK_TOOLBAR_H_
+#define _NETSURF_GTK_TOOLBAR_H_
+
+#include <gtk/gtk.h>
+
+#include "gtk/scaffolding.h"
+
+/**
+ * sets up the images for scaffolding.
+ */
+void nsgtk_theme_implement(struct nsgtk_scaffolding *g);
+
+void nsgtk_toolbar_customization_init(struct nsgtk_scaffolding *g);
+void nsgtk_toolbar_init(struct nsgtk_scaffolding *g);
+void nsgtk_toolbar_customization_load(struct nsgtk_scaffolding *g);
+void nsgtk_toolbar_set_physical(struct nsgtk_scaffolding *g);
+void nsgtk_toolbar_connect_all(struct nsgtk_scaffolding *g);
+int nsgtk_toolbar_get_id_from_widget(GtkWidget *widget, struct nsgtk_scaffolding *g);
+
+#define TOOLPROTO(q) gboolean nsgtk_toolbar_##q##_button_data(\
+ GtkWidget *widget, GdkDragContext *cont, GtkSelectionData\
+ *selection, guint info, guint time, gpointer data);\
+gboolean nsgtk_toolbar_##q##_toolbar_button_data(GtkWidget *widget,\
+ GdkDragContext *cont, GtkSelectionData *selection, guint info,\
+ guint time, gpointer data)
+TOOLPROTO(home);
+TOOLPROTO(back);
+TOOLPROTO(forward);
+TOOLPROTO(reload);
+TOOLPROTO(stop);
+TOOLPROTO(throbber);
+TOOLPROTO(websearch);
+TOOLPROTO(history);
+TOOLPROTO(newwindow);
+TOOLPROTO(newtab);
+TOOLPROTO(openfile);
+TOOLPROTO(closetab);
+TOOLPROTO(closewindow);
+TOOLPROTO(savepage);
+TOOLPROTO(pdf);
+TOOLPROTO(plaintext);
+TOOLPROTO(drawfile);
+TOOLPROTO(postscript);
+TOOLPROTO(printpreview);
+TOOLPROTO(print);
+TOOLPROTO(quit);
+TOOLPROTO(cut);
+TOOLPROTO(copy);
+TOOLPROTO(paste);
+TOOLPROTO(delete);
+TOOLPROTO(selectall);
+TOOLPROTO(find);
+TOOLPROTO(preferences);
+TOOLPROTO(zoomplus);
+TOOLPROTO(zoomminus);
+TOOLPROTO(zoomnormal);
+TOOLPROTO(fullscreen);
+TOOLPROTO(viewsource);
+TOOLPROTO(downloads);
+TOOLPROTO(localhistory);
+TOOLPROTO(globalhistory);
+TOOLPROTO(addbookmarks);
+TOOLPROTO(showbookmarks);
+TOOLPROTO(showcookies);
+TOOLPROTO(openlocation);
+TOOLPROTO(nexttab);
+TOOLPROTO(prevtab);
+TOOLPROTO(savewindowsize);
+TOOLPROTO(toggledebugging);
+TOOLPROTO(debugboxtree);
+TOOLPROTO(debugdomtree);
+TOOLPROTO(contents);
+TOOLPROTO(guide);
+TOOLPROTO(info);
+TOOLPROTO(about);
+#undef TOOLPROTO
+
+#endif
diff --git a/frontends/gtk/treeview.c b/frontends/gtk/treeview.c
new file mode 100644
index 000000000..9baf57b62
--- /dev/null
+++ b/frontends/gtk/treeview.c
@@ -0,0 +1,586 @@
+/*
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Generic tree handling (implementation).
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <math.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "desktop/tree.h"
+#include "desktop/plotters.h"
+
+#include "gtk/warn.h"
+#include "gtk/compat.h"
+#include "gtk/gui.h"
+#include "gtk/plotters.h"
+#include "gtk/treeview.h"
+
+struct nsgtk_treeview {
+ GtkWindow *window;
+ GtkScrolledWindow *scrolled;
+ GtkDrawingArea *drawing_area;
+ GtkIMContext *input_method;
+ bool mouse_pressed;
+ int mouse_pressed_x;
+ int mouse_pressed_y;
+ int last_x, last_y;
+ browser_mouse_state mouse_state;
+ struct tree *tree;
+};
+
+void nsgtk_treeview_destroy(struct nsgtk_treeview *tv)
+{
+ tree_delete(tv->tree);
+ g_object_unref(tv->input_method);
+ gtk_widget_destroy(GTK_WIDGET(tv->window));
+ free(tv);
+}
+
+struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv)
+{
+ return tv->tree;
+}
+
+static void nsgtk_tree_redraw_request(int x, int y, int width, int height, void *data)
+{
+ struct nsgtk_treeview *tw = data;
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(tw->drawing_area),
+ x, y, width, height);
+}
+
+
+/**
+ * Updates the tree owner following a tree resize
+ *
+ * \param tree the tree to update the owner of
+ * \param width The width to resize to.
+ * \param height The height to resize to.
+ * \param data The treeview resize.
+ */
+static void nsgtk_tree_resized(struct tree *tree, int width, int height, void *data)
+{
+ struct nsgtk_treeview *tw = data;
+
+ gtk_widget_set_size_request(GTK_WIDGET(tw->drawing_area),
+ width, height);
+ return;
+}
+
+/**
+ * Scrolls the tree to make an element visible
+ *
+ * \param y Y coordinate of the element
+ * \param height height of the element
+ * \param data user data assigned to the tree on tree creation
+ */
+static void nsgtk_tree_scroll_visible(int y, int height, void *data)
+{
+ int y0, y1;
+ gdouble page;
+ struct nsgtk_treeview *tw = data;
+ GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(tw->scrolled);
+
+ assert(vadj);
+
+ g_object_get(vadj, "page-size", &page, NULL);
+
+ y0 = (int)(gtk_adjustment_get_value(vadj));
+ y1 = y0 + page;
+
+ if ((y >= y0) && (y + height <= y1))
+ return;
+ if (y + height > y1)
+ y0 = y0 + (y + height - y1);
+ if (y < y0)
+ y0 = y;
+ gtk_adjustment_set_value(vadj, y0);
+}
+
+
+/**
+ * Retrieves the dimensions of the window with the tree
+ *
+ * \param data user data assigned to the tree on tree creation
+ * \param width will be updated to window width if not NULL
+ * \param height will be updated to window height if not NULL
+ */
+static void nsgtk_tree_get_window_dimensions(int *width, int *height, void *data)
+{
+ struct nsgtk_treeview *tw = data;
+ GtkAdjustment *vadj;
+ GtkAdjustment *hadj;
+ gdouble page;
+
+ if (width != NULL) {
+ hadj = gtk_scrolled_window_get_hadjustment(tw->scrolled);
+ g_object_get(hadj, "page-size", &page, NULL);
+ *width = page;
+ }
+
+ if (height != NULL) {
+ vadj = gtk_scrolled_window_get_vadjustment(tw->scrolled);
+ g_object_get(vadj, "page-size", &page, NULL);
+ *height = page;
+ }
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+
+static gboolean
+nsgtk_tree_window_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ struct tree *tree = (struct tree *)data;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+
+ current_widget = widget;
+ current_cr = cr;
+
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+
+ tree_draw(tree, 0, 0, x1, y1, x2 - x1, y2 - y1, &ctx);
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+
+#else
+
+/* signal handler functions for a tree window */
+static gboolean
+nsgtk_tree_window_draw_event(GtkWidget *widget, GdkEventExpose *event, gpointer g)
+{
+ struct tree *tree = (struct tree *) g;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+ int x, y, width, height;
+
+ x = event->area.x;
+ y = event->area.y;
+ width = event->area.width;
+ height = event->area.height;
+
+ current_widget = widget;
+ current_cr = gdk_cairo_create(nsgtk_widget_get_window(widget));
+
+ tree_draw(tree, 0, 0, x, y, width, height, &ctx);
+
+ current_widget = NULL;
+ cairo_destroy(current_cr);
+
+ return FALSE;
+}
+
+#endif
+
+void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g)
+{
+}
+
+static gboolean
+nsgtk_tree_window_button_press_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ struct nsgtk_treeview *tw = g;
+ struct tree *tree = tw->tree;
+
+ gtk_im_context_reset(tw->input_method);
+ gtk_widget_grab_focus(GTK_WIDGET(tw->drawing_area));
+
+ tw->mouse_pressed = true;
+ tw->mouse_pressed_x = event->x;
+ tw->mouse_pressed_y = event->y;
+
+ if (event->type == GDK_2BUTTON_PRESS)
+ tw->mouse_state = BROWSER_MOUSE_DOUBLE_CLICK;
+
+ switch (event->button) {
+ case 1: tw->mouse_state |= BROWSER_MOUSE_PRESS_1; break;
+ case 2: tw->mouse_state |= BROWSER_MOUSE_PRESS_2; break;
+ }
+ /* Handle the modifiers too */
+ if (event->state & GDK_SHIFT_MASK)
+ tw->mouse_state |= BROWSER_MOUSE_MOD_1;
+ if (event->state & GDK_CONTROL_MASK)
+ tw->mouse_state |= BROWSER_MOUSE_MOD_2;
+ if (event->state & GDK_MOD1_MASK)
+ tw->mouse_state |= BROWSER_MOUSE_MOD_3;
+
+ /* Record where we pressed, for use when determining whether to start
+ * a drag in motion notify events. */
+ tw->last_x = event->x;
+ tw->last_y = event->y;
+
+ tree_mouse_action(tree, tw->mouse_state, event->x, event->y);
+
+ return TRUE;
+}
+
+static gboolean
+nsgtk_tree_window_button_release_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer g)
+{
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+ bool alt = event->state & GDK_MOD1_MASK;
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
+ struct tree *tree = tw->tree;
+
+ /* We consider only button 1 clicks as double clicks.
+ * If the mouse state is PRESS then we are waiting for a release to emit
+ * a click event, otherwise just reset the state to nothing*/
+ if (tw->mouse_state & BROWSER_MOUSE_DOUBLE_CLICK) {
+
+ if (tw->mouse_state & BROWSER_MOUSE_PRESS_1)
+ tw->mouse_state ^= BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_CLICK_1;
+ else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2)
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_CLICK_2 |
+ BROWSER_MOUSE_DOUBLE_CLICK);
+
+ } else if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) {
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_CLICK_1);
+ } else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2) {
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_CLICK_2);
+ } else if (tw->mouse_state & BROWSER_MOUSE_HOLDING_1) {
+ tw->mouse_state ^= (BROWSER_MOUSE_HOLDING_1 |
+ BROWSER_MOUSE_DRAG_ON);
+ } else if (tw->mouse_state & BROWSER_MOUSE_HOLDING_2) {
+ tw->mouse_state ^= (BROWSER_MOUSE_HOLDING_2 |
+ BROWSER_MOUSE_DRAG_ON);
+ }
+
+ /* Handle modifiers being removed */
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_1;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_2;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_3;
+
+
+ if (tw->mouse_state &
+ ~(BROWSER_MOUSE_MOD_1 |
+ BROWSER_MOUSE_MOD_2 |
+ BROWSER_MOUSE_MOD_3)) {
+ tree_mouse_action(tree, tw->mouse_state,
+ event->x, event->y);
+ } else {
+ tree_drag_end(tree, tw->mouse_state,
+ tw->mouse_pressed_x,
+ tw->mouse_pressed_y,
+ event->x, event->y);
+ }
+
+ tw->mouse_state = 0;
+ tw->mouse_pressed = false;
+
+ return TRUE;
+}
+
+static gboolean
+nsgtk_tree_window_motion_notify_event(GtkWidget *widget,
+ GdkEventMotion *event, gpointer g)
+{
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+ bool alt = event->state & GDK_MOD1_MASK;
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
+ struct tree *tree = tw->tree;
+
+ if (tw->mouse_pressed == false)
+ return TRUE;
+
+ if ((fabs(event->x - tw->last_x) < 5.0) &&
+ (fabs(event->y - tw->last_y) < 5.0)) {
+ /* Mouse hasn't moved far enough from press coordinate
+ * for this to be considered a drag.
+ */
+ return FALSE;
+ } else {
+ /* This is a drag, ensure it's always treated as such,
+ * even if we drag back over the press location.
+ */
+ tw->last_x = INT_MIN;
+ tw->last_y = INT_MIN;
+ }
+
+ if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) {
+ /* Start button 1 drag */
+ tree_mouse_action(tree, BROWSER_MOUSE_DRAG_1,
+ tw->mouse_pressed_x, tw->mouse_pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_HOLDING_1);
+ tw->mouse_state |= BROWSER_MOUSE_DRAG_ON;
+ return TRUE;
+ }
+ else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2){
+ /* Start button 2s drag */
+ tree_mouse_action(tree, BROWSER_MOUSE_DRAG_2,
+ tw->mouse_pressed_x, tw->mouse_pressed_y);
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_HOLDING_2);
+ tw->mouse_state |= BROWSER_MOUSE_DRAG_ON;
+ return TRUE;
+ }
+
+ /* Handle modifiers being removed */
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_1;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_2;
+ if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt)
+ tw->mouse_state ^= BROWSER_MOUSE_MOD_3;
+
+ if (tw->mouse_state & (BROWSER_MOUSE_HOLDING_1 |
+ BROWSER_MOUSE_HOLDING_2))
+ tree_mouse_action(tree, tw->mouse_state, event->x,
+ event->y);
+
+ return TRUE;
+}
+
+
+static gboolean
+nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event,
+ gpointer g)
+{
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
+ struct tree *tree = tw->tree;
+ uint32_t nskey;
+ double value;
+ GtkAdjustment *vscroll;
+ GtkAdjustment *hscroll;
+ GtkAdjustment *scroll = NULL;
+ gdouble hpage, vpage;
+
+ if (gtk_im_context_filter_keypress(tw->input_method, event))
+ return TRUE;
+
+ nskey = gtk_gui_gdkkey_to_nskey(event);
+
+ if (tree_keypress(tree, nskey) == true)
+ return TRUE;
+
+ vscroll = gtk_scrolled_window_get_vadjustment(tw->scrolled);
+ hscroll = gtk_scrolled_window_get_hadjustment(tw->scrolled);
+ g_object_get(vscroll, "page-size", &vpage, NULL);
+ g_object_get(hscroll, "page-size", &hpage, NULL);
+
+ switch (event->keyval) {
+ case GDK_KEY(Home):
+ case GDK_KEY(KP_Home):
+ scroll = vscroll;
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case GDK_KEY(End):
+ case GDK_KEY(KP_End):
+ scroll = vscroll;
+ value = nsgtk_adjustment_get_upper(scroll) - vpage;
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case GDK_KEY(Left):
+ case GDK_KEY(KP_Left):
+ scroll = hscroll;
+ value = gtk_adjustment_get_value(scroll) -
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case GDK_KEY(Up):
+ case GDK_KEY(KP_Up):
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) -
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case GDK_KEY(Right):
+ case GDK_KEY(KP_Right):
+ scroll = hscroll;
+ value = gtk_adjustment_get_value(scroll) +
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value > nsgtk_adjustment_get_upper(scroll) - hpage)
+ value = nsgtk_adjustment_get_upper(scroll) - hpage;
+ break;
+
+ case GDK_KEY(Down):
+ case GDK_KEY(KP_Down):
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) +
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value > nsgtk_adjustment_get_upper(scroll) - vpage)
+ value = nsgtk_adjustment_get_upper(scroll) - vpage;
+ break;
+
+ case GDK_KEY(Page_Up):
+ case GDK_KEY(KP_Page_Up):
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) -
+ nsgtk_adjustment_get_page_increment(scroll);
+
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+
+ break;
+
+ case GDK_KEY(Page_Down):
+ case GDK_KEY(KP_Page_Down):
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) +
+ nsgtk_adjustment_get_page_increment(scroll);
+
+ if (value > nsgtk_adjustment_get_upper(scroll) - vpage)
+ value = nsgtk_adjustment_get_upper(scroll) - vpage;
+ break;
+
+ default:
+ break;
+ }
+
+ if (scroll != NULL)
+ gtk_adjustment_set_value(scroll, value);
+
+ return TRUE;
+}
+
+static gboolean
+nsgtk_tree_window_keyrelease_event(GtkWidget *widget, GdkEventKey *event,
+ gpointer g)
+{
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g;
+
+ return gtk_im_context_filter_keypress(tw->input_method, event);
+}
+
+static void
+nsgtk_tree_window_input_method_commit(GtkIMContext *ctx,
+ const gchar *str, gpointer data)
+{
+ struct nsgtk_treeview *tw = (struct nsgtk_treeview *) data;
+ size_t len = strlen(str), offset = 0;
+
+ while (offset < len) {
+ uint32_t nskey = utf8_to_ucs4(str + offset, len - offset);
+
+ tree_keypress(tw->tree, nskey);
+
+ offset = utf8_next(str, len, offset);
+ }
+}
+
+
+static const struct treeview_table nsgtk_tree_callbacks = {
+ .redraw_request = nsgtk_tree_redraw_request,
+ .resized = nsgtk_tree_resized,
+ .scroll_visible = nsgtk_tree_scroll_visible,
+ .get_window_dimensions = nsgtk_tree_get_window_dimensions
+};
+
+struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags,
+ GtkWindow *window, GtkScrolledWindow *scrolled,
+ GtkDrawingArea *drawing_area)
+{
+ struct nsgtk_treeview *tv;
+
+ assert(drawing_area != NULL);
+
+ tv = malloc(sizeof(struct nsgtk_treeview));
+ if (tv == NULL) {
+ LOG("malloc failed");
+ nsgtk_warning("NoMemory", 0);
+ return NULL;
+ }
+
+ tv->window = window;
+ tv->scrolled = scrolled;
+ tv->drawing_area = drawing_area;
+ tv->input_method = gtk_im_multicontext_new();
+ tv->tree = tree_create(flags, &nsgtk_tree_callbacks, tv);
+ tv->mouse_state = 0;
+ tv->mouse_pressed = false;
+
+ nsgtk_widget_override_background_color(GTK_WIDGET(drawing_area),
+ GTK_STATE_NORMAL,
+ 0, 0xffff, 0xffff, 0xffff);
+
+ nsgtk_connect_draw_event(GTK_WIDGET(drawing_area), G_CALLBACK(nsgtk_tree_window_draw_event), tv->tree);
+
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+ CONNECT(drawing_area, "button-press-event",
+ nsgtk_tree_window_button_press_event,
+ tv);
+ CONNECT(drawing_area, "button-release-event",
+ nsgtk_tree_window_button_release_event,
+ tv);
+ CONNECT(drawing_area, "motion-notify-event",
+ nsgtk_tree_window_motion_notify_event,
+ tv);
+ CONNECT(drawing_area, "key-press-event",
+ nsgtk_tree_window_keypress_event,
+ tv);
+ CONNECT(drawing_area, "key-release-event",
+ nsgtk_tree_window_keyrelease_event,
+ tv);
+
+
+ /* input method */
+ gtk_im_context_set_client_window(tv->input_method,
+ nsgtk_widget_get_window(GTK_WIDGET(tv->window)));
+ gtk_im_context_set_use_preedit(tv->input_method, FALSE);
+ /* input method signals */
+ CONNECT(tv->input_method, "commit",
+ nsgtk_tree_window_input_method_commit,
+ tv);
+
+ return tv;
+}
diff --git a/frontends/gtk/treeview.h b/frontends/gtk/treeview.h
new file mode 100644
index 000000000..ad8180f33
--- /dev/null
+++ b/frontends/gtk/treeview.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net>
+ * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Generic tree handling.
+ */
+
+#ifndef __NSGTK_TREEVIEW_H__
+#define __NSGTK_TREEVIEW_H__
+
+struct nsgtk_treeview;
+
+struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags,
+ GtkWindow *window, GtkScrolledWindow *scrolled,
+ GtkDrawingArea *drawing_area);
+void nsgtk_treeview_destroy(struct nsgtk_treeview *tv);
+
+struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv);
+
+void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g);
+
+#endif /*__NSGTK_TREEVIEW_H__*/
diff --git a/frontends/gtk/viewdata.c b/frontends/gtk/viewdata.c
new file mode 100644
index 000000000..55b25467e
--- /dev/null
+++ b/frontends/gtk/viewdata.c
@@ -0,0 +1,990 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * generic data viewer implementation.
+ *
+ * This viewer can be used for utf-8 encoded chunk of data. Thie data
+ * might be page source or the debugging of dom or box trees. It will
+ * show the data in a tab, window or editor as per user configuration.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#define _WITH_GETLINE /* necessary for FreeBSD */
+#include <stdio.h>
+#include <unistd.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+#include "utils/file.h"
+#include "utils/filepath.h"
+
+#include "desktop/browser.h"
+#include "content/hlcache.h"
+#include "content/content.h"
+
+#include "gtk/warn.h"
+#include "gtk/about.h"
+#include "gtk/fetch.h"
+#include "gtk/compat.h"
+#include "gtk/resources.h"
+#include "gtk/viewdata.h"
+
+struct nsgtk_viewdata_ctx {
+ char *data;
+ size_t data_len;
+ char *filename;
+
+ GtkBuilder *builder; /**< The gtk builder that built the widgets. */
+ GtkWindow *window; /**< handle to gtk window (builder holds reference) */
+ GtkTextView *gv; /**< handle to gtk text view (builder holds reference) */
+
+ struct nsgtk_viewdata_ctx *next;
+ struct nsgtk_viewdata_ctx *prev;
+};
+
+struct menu_events {
+ const char *widget;
+ GCallback handler;
+};
+
+static struct nsgtk_viewdata_ctx *nsgtk_viewdata_list = NULL;
+static char viewdata_zoomlevel = 10;
+
+#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) }
+#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \
+ GtkMenuItem *widget, gpointer g)
+
+MENUPROTO(viewdata_save_as);
+MENUPROTO(viewdata_print);
+MENUPROTO(viewdata_close);
+MENUPROTO(viewdata_select_all);
+MENUPROTO(viewdata_cut);
+MENUPROTO(viewdata_copy);
+MENUPROTO(viewdata_paste);
+MENUPROTO(viewdata_delete);
+MENUPROTO(viewdata_zoom_in);
+MENUPROTO(viewdata_zoom_out);
+MENUPROTO(viewdata_zoom_normal);
+MENUPROTO(viewdata_about);
+
+static struct menu_events viewdata_menu_events[] = {
+ MENUEVENT(viewdata_save_as),
+ MENUEVENT(viewdata_print),
+ MENUEVENT(viewdata_close),
+ MENUEVENT(viewdata_select_all),
+ MENUEVENT(viewdata_cut),
+ MENUEVENT(viewdata_copy),
+ MENUEVENT(viewdata_paste),
+ MENUEVENT(viewdata_delete),
+ MENUEVENT(viewdata_zoom_in),
+ MENUEVENT(viewdata_zoom_out),
+ MENUEVENT(viewdata_zoom_normal),
+ MENUEVENT(viewdata_about),
+ {NULL, NULL}
+};
+
+static void nsgtk_attach_viewdata_menu_handlers(GtkBuilder *xml, gpointer g)
+{
+ struct menu_events *event = viewdata_menu_events;
+
+ while (event->widget != NULL)
+ {
+ GtkWidget *w = GTK_WIDGET(gtk_builder_get_object(xml, event->widget));
+ g_signal_connect(G_OBJECT(w), "activate", event->handler, g);
+ event++;
+ }
+}
+
+static gboolean nsgtk_viewdata_destroy_event(GtkBuilder *window, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *vdctx = (struct nsgtk_viewdata_ctx *)g;
+
+ if (vdctx->next != NULL) {
+ vdctx->next->prev = vdctx->prev;
+ }
+
+ if (vdctx->prev != NULL) {
+ vdctx->prev->next = vdctx->next;
+ } else {
+ nsgtk_viewdata_list = vdctx->next;
+ }
+
+ /* release the data */
+ free(vdctx->data);
+
+ /* free the builder */
+ g_object_unref(G_OBJECT(vdctx->builder));
+
+ /* free the context structure */
+ free(vdctx);
+
+ return FALSE;
+}
+
+static gboolean nsgtk_viewdata_delete_event(GtkWindow * window, gpointer g)
+{
+ return FALSE;
+}
+
+
+
+static void nsgtk_viewdata_file_save(GtkWindow *parent, const char *filename,
+ const char *data, size_t data_size)
+{
+ FILE *f;
+ GtkWidget *notif;
+ GtkWidget *label;
+
+ f = fopen(filename, "w+");
+ if (f != NULL) {
+ fwrite(data, data_size, 1, f);
+ fclose(f);
+ return;
+ }
+
+ /* inform user of faliure */
+ notif = gtk_dialog_new_with_buttons(messages_get("gtkSaveFailedTitle"),
+ parent,
+ GTK_DIALOG_MODAL,
+ NSGTK_STOCK_OK,
+ GTK_RESPONSE_NONE,
+ NULL);
+
+ g_signal_connect_swapped(notif, "response",
+ G_CALLBACK(gtk_widget_destroy), notif);
+
+ label = gtk_label_new(messages_get("gtkSaveFailed"));
+ gtk_container_add(GTK_CONTAINER(nsgtk_dialog_get_content_area(GTK_DIALOG(notif))), label);
+ gtk_widget_show_all(notif);
+
+}
+
+
+gboolean nsgtk_on_viewdata_save_as_activate(GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+ GtkWidget *fc;
+
+ fc = gtk_file_chooser_dialog_new(messages_get("gtkSaveFile"),
+ nsg->window,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ NSGTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_SAVE,
+ GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), nsg->filename);
+
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
+ TRUE);
+
+ if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) {
+ char *filename;
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+ nsgtk_viewdata_file_save(nsg->window, filename, nsg->data, nsg->data_len);
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(fc);
+
+ return TRUE;
+}
+
+
+gboolean nsgtk_on_viewdata_print_activate( GtkMenuItem *widget, gpointer g)
+{
+ /* correct printing */
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_close_activate( GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+
+ gtk_widget_destroy(GTK_WIDGET(nsg->window));
+
+ return TRUE;
+}
+
+
+
+gboolean nsgtk_on_viewdata_select_all_activate (GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(nsg->gv);
+ GtkTextIter start, end;
+
+ gtk_text_buffer_get_bounds(buf, &start, &end);
+
+ gtk_text_buffer_select_range(buf, &start, &end);
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_cut_activate(GtkMenuItem *widget, gpointer g)
+{
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_copy_activate(GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(nsg->gv));
+
+ gtk_text_buffer_copy_clipboard(buf,
+ gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_paste_activate(GtkMenuItem *widget, gpointer g)
+{
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_delete_activate(GtkMenuItem *widget, gpointer g)
+{
+ return TRUE;
+}
+
+static void nsgtk_viewdata_update_zoomlevel(gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg;
+ GtkTextBuffer *buf;
+ GtkTextTagTable *tab;
+ GtkTextTag *tag;
+
+ nsg = nsgtk_viewdata_list;
+ while (nsg) {
+ if (nsg->gv) {
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(nsg->gv));
+
+ tab = gtk_text_buffer_get_tag_table(
+ GTK_TEXT_BUFFER(buf));
+
+ tag = gtk_text_tag_table_lookup(tab, "zoomlevel");
+ if (!tag) {
+ tag = gtk_text_tag_new("zoomlevel");
+ gtk_text_tag_table_add(tab, GTK_TEXT_TAG(tag));
+ }
+
+ gdouble fscale = ((gdouble) viewdata_zoomlevel) / 10;
+
+ g_object_set(GTK_TEXT_TAG(tag), "scale", fscale, NULL);
+
+ GtkTextIter start, end;
+
+ gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buf),
+ &start, &end);
+ gtk_text_buffer_remove_all_tags(GTK_TEXT_BUFFER(buf),
+ &start, &end);
+ gtk_text_buffer_apply_tag(GTK_TEXT_BUFFER(buf),
+ GTK_TEXT_TAG(tag), &start, &end);
+ }
+ nsg = nsg->next;
+ }
+}
+
+gboolean nsgtk_on_viewdata_zoom_in_activate(GtkMenuItem *widget, gpointer g)
+{
+ viewdata_zoomlevel++;
+ nsgtk_viewdata_update_zoomlevel(g);
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_zoom_out_activate(GtkMenuItem *widget, gpointer g)
+{
+ if (viewdata_zoomlevel > 1) {
+ viewdata_zoomlevel--;
+ nsgtk_viewdata_update_zoomlevel(g);
+ }
+
+ return TRUE;
+}
+
+
+gboolean nsgtk_on_viewdata_zoom_normal_activate(GtkMenuItem *widget, gpointer g)
+{
+ viewdata_zoomlevel = 10;
+ nsgtk_viewdata_update_zoomlevel(g);
+
+ return TRUE;
+}
+
+gboolean nsgtk_on_viewdata_about_activate(GtkMenuItem *widget, gpointer g)
+{
+ struct nsgtk_viewdata_ctx *nsg = (struct nsgtk_viewdata_ctx *) g;
+
+ nsgtk_about_dialog_init(nsg->window);
+
+ return TRUE;
+}
+
+/**
+ * View the data in a gtk text window.
+ */
+static nserror
+window_init(const char *title,
+ const char *filename,
+ char *ndata,
+ size_t ndata_len)
+{
+ GtkWindow *window;
+ GtkWidget *cutbutton;
+ GtkWidget *pastebutton;
+ GtkWidget *deletebutton;
+ GtkWidget *printbutton;
+ GtkTextView *dataview;
+ PangoFontDescription *fontdesc;
+ GtkTextBuffer *tb;
+ struct nsgtk_viewdata_ctx *newctx;
+ nserror res;
+
+ newctx = malloc(sizeof(struct nsgtk_viewdata_ctx));
+ if (newctx == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ res = nsgtk_builder_new_from_resname("viewdata", &newctx->builder);
+ if (res != NSERROR_OK) {
+ LOG("Viewdata UI builder init failed");
+ free(newctx);
+ return res;
+ }
+
+ gtk_builder_connect_signals(newctx->builder, NULL);
+
+ window = GTK_WINDOW(gtk_builder_get_object(newctx->builder,
+ "ViewDataWindow"));
+ if (window == NULL) {
+ LOG("Unable to find window in builder ");
+
+ /* free the builder */
+ g_object_unref(G_OBJECT(newctx->builder));
+
+ /* free the context structure */
+ free(newctx);
+
+ return NSERROR_INIT_FAILED;
+ }
+
+ cutbutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_cut"));
+ pastebutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_paste"));
+ deletebutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_delete"));
+ printbutton = GTK_WIDGET(gtk_builder_get_object(newctx->builder, "viewdata_print"));
+ gtk_widget_set_sensitive(cutbutton, FALSE);
+ gtk_widget_set_sensitive(pastebutton, FALSE);
+ gtk_widget_set_sensitive(deletebutton, FALSE);
+ /* for now */
+ gtk_widget_set_sensitive(printbutton, FALSE);
+
+
+ newctx->filename = strdup(filename);
+
+ newctx->data = ndata;
+ newctx->data_len = ndata_len;
+
+ newctx->window = window;
+
+ newctx->next = nsgtk_viewdata_list;
+ newctx->prev = NULL;
+ if (nsgtk_viewdata_list != NULL) {
+ nsgtk_viewdata_list->prev = newctx;
+ }
+ nsgtk_viewdata_list = newctx;
+
+ nsgtk_attach_viewdata_menu_handlers(newctx->builder, newctx);
+
+ gtk_window_set_title(window, title);
+
+ g_signal_connect(G_OBJECT(window), "destroy",
+ G_CALLBACK(nsgtk_viewdata_destroy_event),
+ newctx);
+ g_signal_connect(G_OBJECT(window), "delete-event",
+ G_CALLBACK(nsgtk_viewdata_delete_event),
+ newctx);
+
+ dataview = GTK_TEXT_VIEW(gtk_builder_get_object(newctx->builder,
+ "viewdata_view"));
+
+ fontdesc = pango_font_description_from_string("Monospace 8");
+
+ newctx->gv = dataview;
+ nsgtk_widget_modify_font(GTK_WIDGET(dataview), fontdesc);
+
+ tb = gtk_text_view_get_buffer(dataview);
+ gtk_text_buffer_set_text(tb, newctx->data, -1);
+
+ gtk_widget_show(GTK_WIDGET(window));
+
+ return NSERROR_OK;
+}
+
+/**
+ * open a window to dispaly an existing file.
+ */
+static nserror
+window_init_fname(const char *title,
+ const char *leafname,
+ const char *filename)
+{
+ nserror ret;
+ FILE *f;
+ char *ndata;
+ long tell_len;
+ size_t ndata_len;
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ return NSERROR_NOT_FOUND;
+ }
+ if (fseek(f, 0, SEEK_END) != 0) {
+ fclose(f);
+ return NSERROR_BAD_SIZE;
+ }
+
+ tell_len = ftell(f);
+ if (tell_len == -1) {
+ fclose(f);
+ return NSERROR_BAD_SIZE;
+ }
+
+ if (fseek(f, 0, SEEK_SET) != 0) {
+ fclose(f);
+ return NSERROR_BAD_SIZE;
+ }
+
+ ndata = malloc(tell_len);
+
+ ndata_len = fread(ndata, 1, tell_len, f);
+
+ fclose(f);
+
+ /* window init takes ownership of the ndata if there is no error */
+ ret = window_init(title, leafname, ndata, ndata_len);
+ if (ret != NSERROR_OK) {
+ free(ndata);
+ }
+
+ return ret;
+}
+
+/**
+ * open a new tab from an existing file.
+ */
+static nserror
+tab_init_fname(const char *title,
+ const char *leafname,
+ const char *fname)
+{
+ nsurl *url;
+ nserror ret;
+
+ /* Open tab on temporary file */
+ ret = netsurf_path_to_nsurl(fname, &url);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ /* open tab on temportary file */
+ ret = browser_window_create(BW_CREATE_TAB | BW_CREATE_HISTORY, url, NULL, NULL, NULL);
+ nsurl_unref(url);
+ if (ret != NSERROR_OK) {
+ return ret;
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * create a new tab from data.
+ */
+static nserror
+tab_init(const char *title,
+ const char *leafname,
+ char *ndata,
+ size_t ndata_len)
+{
+ nserror ret;
+ gchar *fname;
+ gint handle;
+ FILE *f;
+
+ handle = g_file_open_tmp("nsgtkdataXXXXXX", &fname, NULL);
+ if ((handle == -1) || (fname == NULL)) {
+ return NSERROR_SAVE_FAILED;
+ }
+ close(handle); /* in case it was binary mode */
+
+ /* save data to temporary file */
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ nsgtk_warning(messages_get("gtkSourceTabError"), 0);
+ g_free(fname);
+ return NSERROR_SAVE_FAILED;
+ }
+ fprintf(f, "%s", ndata);
+ fclose(f);
+
+ ret = tab_init_fname(title, leafname, fname);
+ if (ret == NSERROR_OK) {
+ free(ndata);
+ }
+
+ g_free(fname);
+
+ return ret;
+}
+
+
+/**
+ * Build string vector of search path.
+ *
+ * ${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+ *
+ * $XDG_DATA_HOME if empty use $HOME/.local/share
+ *
+ * XDG_DATA_DIRS if empty use /usr/local/share/:/usr/share/
+ *
+ * \return string vector of search pathnames or NULL on error.
+ */
+static char** xdg_data_strvec(void)
+{
+ const char *xdg_data_dirs;
+ const char *xdg_data_home;
+ const char *home_dir;
+ char *xdg_data_path;
+ int xdg_data_size;
+ char **svec;
+
+ xdg_data_dirs = getenv("XDG_DATA_DIRS");
+ if ((xdg_data_dirs == NULL) ||
+ (*xdg_data_dirs == 0) ||
+ (strlen(xdg_data_dirs) > 4096)) {
+ xdg_data_dirs = "/usr/local/share/:/usr/share/";
+ }
+
+ xdg_data_home = getenv("XDG_DATA_HOME");
+ if ((xdg_data_home == NULL) ||
+ (*xdg_data_home == 0) ||
+ (strlen(xdg_data_home) > 4096)) {
+ /* $XDG_DATA_HOME is empty use $HOME/.local/share */
+
+ home_dir = getenv("HOME");
+ if ((home_dir == NULL) ||
+ (*home_dir == 0) ||
+ (strlen(home_dir) > 4096)) {
+ xdg_data_path = strdup(xdg_data_dirs);
+ } else {
+ xdg_data_size = strlen(home_dir) +
+ SLEN("/.local/share:") +
+ strlen(xdg_data_dirs) + 1;
+ xdg_data_path = malloc(xdg_data_size);
+ snprintf(xdg_data_path, xdg_data_size ,
+ "%s/.local/share/:%s",
+ home_dir, xdg_data_dirs);
+ }
+ } else {
+ xdg_data_size = strlen(xdg_data_home) +
+ strlen(xdg_data_dirs) + 2;
+ xdg_data_path = malloc(xdg_data_size);
+ snprintf(xdg_data_path, xdg_data_size , "%s:%s",
+ xdg_data_home, xdg_data_dirs);
+ }
+
+ LOG("%s", xdg_data_path);
+
+ svec = filepath_path_to_strvec(xdg_data_path);
+ free(xdg_data_path);
+
+ return svec;
+}
+
+/**
+ * Search application defaults file for matching mime type.
+ *
+ * create filename form path and applications/defaults.list
+ *
+ * look for [Default Applications]
+ * search lines looking like mime/type=Desktop
+ *
+ * \param path The base path.
+ * \param mimetype The mimetype to search for.
+ * \return The desktop file associated with the mime type or NULL if not found.
+ */
+static char *xdg_get_default_app(const char *path, const char *mimetype)
+{
+ FILE *fp;
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t rd;
+ int fname_len;
+ char *fname;
+ int mimetype_len;
+ char *ret = NULL;
+
+ fname_len = strlen(path) + SLEN("/applications/defaults.list") + 1;
+ fname = malloc(fname_len);
+ snprintf(fname, fname_len, "%s/applications/defaults.list", path);
+
+ LOG("Checking %s", fname);
+
+ fp = fopen(fname, "r");
+ free(fname);
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ mimetype_len = strlen(mimetype);
+ while ((rd = getline(&line, &len, fp)) != -1) {
+ /* line includes line endings if present, remove them */
+ while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) {
+ rd--;
+ }
+ line[rd] = 0;
+
+ /* look for mimetype */
+ if ((rd > mimetype_len) &&
+ (line[mimetype_len] == '=') &&
+ (strncmp(line, mimetype, mimetype_len) == 0)) {
+
+ ret = strdup(line + mimetype_len + 1);
+
+ LOG("Found line match for %s length %zu\n", mimetype, rd);
+ LOG("Result %s", ret);
+
+ break;
+ }
+ }
+
+ free(line);
+ fclose(fp);
+
+ return ret;
+}
+
+/**
+ * Search desktop file for an Exec line.
+ *
+ * search path is combined with applications/application.desktop to
+ * create a filename.
+ *
+ * Desktop file format http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
+ *
+ * \todo The parsing of the desktop file is badly incomplete and needs
+ * improving. For example the handling of the = delimiter is wrong and
+ * selection from the "Desktop Entry" group is completely absent.
+ *
+ */
+static char *xdg_get_exec_cmd(const char *path, const char *desktop)
+{
+ FILE *fp;
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t rd;
+ int fname_len;
+ char *fname;
+ char *ret = NULL;
+
+ fname_len = strlen(path) + SLEN("/applications/") + strlen(desktop) + 1;
+ fname = malloc(fname_len);
+ snprintf(fname, fname_len, "%s/applications/%s", path, desktop);
+
+ LOG("Checking %s", fname);
+
+ fp = fopen(fname, "r");
+ free(fname);
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ while ((rd = getline(&line, &len, fp)) != -1) {
+ /* line includes line endings if present, remove them */
+ while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) {
+ rd--;
+ }
+ line[rd] = 0;
+
+ /* look for mimetype */
+ if ((rd > (ssize_t)SLEN("Exec=")) &&
+ (strncmp(line, "Exec=", SLEN("Exec=")) == 0)) {
+
+ ret = strdup(line + SLEN("Exec="));
+
+ LOG("Found Exec length %zu", rd);
+ LOG("Result %s", ret);
+
+ break;
+ }
+ }
+
+ free(line);
+ fclose(fp);
+
+ return ret;
+}
+
+static char *exec_arg(const char *arg, int len, const char *fname)
+{
+ char *res = NULL;
+
+ if (*arg == '%') {
+ arg++;
+ if ((*arg == 'f') || (*arg == 'F') ||
+ (*arg == 'u') || (*arg == 'U')) {
+ res = strdup(fname);
+ }
+ } else {
+ res = calloc(1, len + 1);
+ if (res != NULL) {
+ memcpy(res, arg, len);
+ }
+ }
+
+ return res;
+}
+
+/**
+ * Build vector for executing app.
+ */
+static char **build_exec_argv(const char *fname, const char *exec_cmd)
+{
+ char **argv;
+ const char *start; /* current arguments start */
+ const char *cur; /* current ptr within exec cmd */
+ int aidx = 0; /* argv index */
+
+ argv = calloc(10, sizeof(char *));
+ if (argv == NULL) {
+ return NULL;
+ }
+
+ cur = exec_cmd;
+ while (*cur != 0) {
+ /* skip whitespace */
+ while ((*cur != 0) && (*cur == ' ')) {
+ cur++;
+ }
+ if (*cur == 0) {
+ break;
+ }
+ start = cur;
+
+ /* find end of element */
+ while ((*cur != 0) && (*cur != ' ')) {
+ cur++;
+ }
+
+ argv[aidx] = exec_arg(start, cur - start, fname);
+ if (argv[aidx] != NULL) {
+ LOG("adding \"%s\"", argv[aidx]);
+ aidx++;
+ }
+ }
+
+ /* if no arguments were found there was nothing to execute */
+ if (aidx == 0) {
+ free(argv);
+ return NULL;
+ }
+
+ return argv;
+}
+
+
+/**
+ * open an editor from an existing file.
+ */
+static nserror
+editor_init_fname(const char *title,
+ const char *leafname,
+ const char *fname)
+{
+ char **xdg_data_vec;
+ int veci;
+ /* desktop file of default app for mimetype */
+ char *def_app_desktop = NULL;
+ char *exec_cmd = NULL;
+ char **argv;
+
+ /* build string vector of search path */
+ xdg_data_vec = xdg_data_strvec();
+
+ /* find user configured app for opening text/plain */
+ veci = 0;
+ while (xdg_data_vec[veci] != NULL) {
+ def_app_desktop = xdg_get_default_app(xdg_data_vec[veci],
+ "text/plain");
+ if (def_app_desktop != NULL) {
+ break;
+ }
+ veci++;
+ }
+
+ if (def_app_desktop == NULL) {
+ /* no default app */
+ filepath_free_strvec(xdg_data_vec);
+ return NSERROR_NOT_FOUND;
+ }
+
+ /* find app to execute */
+ veci = 0;
+ while (xdg_data_vec[veci] != NULL) {
+ exec_cmd = xdg_get_exec_cmd(xdg_data_vec[veci], def_app_desktop);
+ if (exec_cmd != NULL) {
+ break;
+ }
+ veci++;
+ }
+ free(def_app_desktop);
+ filepath_free_strvec(xdg_data_vec);
+
+ if (exec_cmd == NULL) {
+ /* no exec entry */
+ return NSERROR_NOT_FOUND;
+ }
+
+ /* build exec vector */
+ argv = build_exec_argv(fname, exec_cmd);
+ free(exec_cmd);
+
+ /* execute target app on saved data */
+ if (g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
+ NULL, NULL) != TRUE) {
+ return NSERROR_NOT_FOUND;
+ }
+ filepath_free_strvec(argv);
+
+ return NSERROR_OK;
+}
+
+/**
+ * open an editor with data.
+ */
+static nserror
+editor_init(const char *title,
+ const char *leafname,
+ char *ndata,
+ size_t ndata_len)
+{
+
+ nserror ret;
+ gchar *fname;
+ gint handle;
+ FILE *f;
+
+ handle = g_file_open_tmp("nsgtkdataXXXXXX", &fname, NULL);
+ if ((handle == -1) || (fname == NULL)) {
+ return NSERROR_SAVE_FAILED;
+ }
+ close(handle); /* in case it was binary mode */
+
+ /* save data to temporary file */
+ f = fopen(fname, "w");
+ if (f == NULL) {
+ nsgtk_warning(messages_get("gtkSourceTabError"), 0);
+ g_free(fname);
+ return NSERROR_SAVE_FAILED;
+ }
+ fprintf(f, "%s", ndata);
+ fclose(f);
+
+ ret = editor_init_fname(title, leafname, fname);
+ if (ret == NSERROR_OK) {
+ free(ndata);
+ }
+
+ g_free(fname);
+
+ return ret;
+}
+
+/* exported interface documented in gtk/viewdata.h */
+nserror
+nsgtk_viewdata(const char *title,
+ const char *filename,
+ char *ndata,
+ size_t ndata_len)
+{
+ nserror ret;
+
+ switch (nsoption_int(developer_view)) {
+ case 0:
+ ret = window_init(title, filename, ndata, ndata_len);
+ break;
+
+ case 1:
+ ret = tab_init(title, filename, ndata, ndata_len);
+ break;
+
+ case 2:
+ ret = editor_init(title, filename, ndata, ndata_len);
+ break;
+
+ default:
+ ret = NSERROR_BAD_PARAMETER;
+ break;
+ }
+ if (ret != NSERROR_OK) {
+ /* release the data */
+ free(ndata);
+ }
+
+
+ return ret;
+}
+
+/* exported interface documented in gtk/viewdata.h */
+nserror
+nsgtk_viewfile(const char *title,
+ const char *leafname,
+ const char *filename)
+{
+ nserror ret;
+
+ switch (nsoption_int(developer_view)) {
+ case 0:
+ ret = window_init_fname(title, leafname, filename);
+ break;
+
+ case 1:
+ ret = tab_init_fname(title, leafname, filename);
+ break;
+
+ case 2:
+ ret = editor_init_fname(title, leafname, filename);
+ break;
+
+ default:
+ ret = NSERROR_BAD_PARAMETER;
+ break;
+ }
+
+ return ret;
+}
diff --git a/frontends/gtk/viewdata.h b/frontends/gtk/viewdata.h
new file mode 100644
index 000000000..1767b4821
--- /dev/null
+++ b/frontends/gtk/viewdata.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NETSURF_GTK_VIEWDATA_H_
+#define _NETSURF_GTK_VIEWDATA_H_
+
+/**
+ * Display text to a user.
+ *
+ * The data is utf-8 encoded text and will be presented in a window, a
+ * tab or an editor as per the user configuration.
+ *
+ * \param title The title of the data being displayed.
+ * \param filename The suggested filename to be used.
+ * \param data The data to be shown. This data will be freed once the
+ * display is complete, the caller no longer owns the allocation.
+ * \param data_size The size of the data in data.
+ */
+nserror nsgtk_viewdata(const char *title, const char *filename, char *data, size_t data_size);
+
+/**
+ * Display file to a user.
+ *
+ * The file is interpreted as utf-8 encoded text and will be presented
+ * in a window, a tab or an editor as per the user configuration.
+ *
+ * \param title The title of the data being displayed.
+ * \param leafname The suggested leafname to be used.
+ * \param filename The filename of the data to be viewed.
+ */
+nserror nsgtk_viewfile(const char *title, const char *leafname, const char *filename);
+
+#endif
diff --git a/frontends/gtk/viewsource.c b/frontends/gtk/viewsource.c
new file mode 100644
index 000000000..554cfbf39
--- /dev/null
+++ b/frontends/gtk/viewsource.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2009 Mark Benjamin <MarkBenjamin@dfgh.net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <gtk/gtk.h>
+
+#include "utils/utils.h"
+#include "utils/utf8.h"
+#include "utils/nsurl.h"
+#include "utils/messages.h"
+#include "desktop/browser.h"
+#include "content/content.h"
+
+#include "gtk/viewdata.h"
+#include "gtk/viewsource.h"
+
+nserror nsgtk_viewsource(GtkWindow *parent, struct browser_window *bw)
+{
+ nserror ret;
+ struct hlcache_handle *hlcontent;
+ const char *source_data;
+ unsigned long source_size;
+ char *ndata = NULL;
+ size_t ndata_len;
+ char *filename;
+ char *title;
+
+ hlcontent = browser_window_get_content(bw);
+ if (hlcontent == NULL) {
+ return NSERROR_BAD_PARAMETER;
+ }
+
+ if (content_get_type(hlcontent) != CONTENT_HTML) {
+ return NSERROR_BAD_CONTENT;
+ }
+
+ source_data = content_get_source_data(hlcontent, &source_size);
+
+ ret = nsurl_nice(browser_window_get_url(bw), &filename, false);
+ if (ret != NSERROR_OK) {
+ filename = strdup(messages_get("SaveSource"));
+ if (filename == NULL) {
+ return NSERROR_NOMEM;
+ }
+ }
+
+ title = malloc(strlen(nsurl_access(browser_window_get_url(bw))) + SLEN("Source of - NetSurf") + 1);
+ if (title == NULL) {
+ free(filename);
+ return NSERROR_NOMEM;
+ }
+ sprintf(title, "Source of %s - NetSurf", nsurl_access(browser_window_get_url(bw)));
+
+ ret = utf8_from_enc(source_data,
+ content_get_encoding(hlcontent, CONTENT_ENCODING_NORMAL),
+ source_size,
+ &ndata,
+ &ndata_len);
+ if (ret == NSERROR_OK) {
+ ret = nsgtk_viewdata(title, filename, ndata, ndata_len);
+ }
+
+ free(filename);
+ free(title);
+
+ return ret;
+}
diff --git a/frontends/gtk/viewsource.h b/frontends/gtk/viewsource.h
new file mode 100644
index 000000000..bba878874
--- /dev/null
+++ b/frontends/gtk/viewsource.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NETSURF_GTK_VIEWSOURCE_H_
+#define _NETSURF_GTK_VIEWSOURCE_H_
+
+nserror nsgtk_viewsource(GtkWindow *parent, struct browser_window *bw);
+
+#endif
+
diff --git a/frontends/gtk/warn.h b/frontends/gtk/warn.h
new file mode 100644
index 000000000..d24f55438
--- /dev/null
+++ b/frontends/gtk/warn.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTK_WARN_H
+#define GTK_WARN_H
+
+/**
+ * Warn the user of an event.
+ *
+ * \param[in] warning A warning looked up in the message translation table
+ * \param[in] detail Additional text to be displayed or NULL.
+ * \return NSERROR_OK on success or error code if there was a
+ * faliure displaying the message to the user.
+ */
+nserror nsgtk_warning(const char *warning, const char *detail);
+
+#endif
diff --git a/frontends/gtk/window.c b/frontends/gtk/window.c
new file mode 100644
index 000000000..de333d1b0
--- /dev/null
+++ b/frontends/gtk/window.c
@@ -0,0 +1,1337 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Implementation of gtk windowing.
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <limits.h>
+#include <assert.h>
+#include <math.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+
+#include "utils/log.h"
+#include "utils/utf8.h"
+#include "utils/utils.h"
+#include "utils/nsoption.h"
+#include "content/hlcache.h"
+#include "gtk/window.h"
+#include "gtk/selection.h"
+#include "desktop/browser.h"
+#include "desktop/mouse.h"
+#include "desktop/searchweb.h"
+#include "desktop/textinput.h"
+#include "desktop/gui_window.h"
+#include "desktop/plotters.h"
+#include "render/form.h"
+
+#include "gtk/warn.h"
+#include "gtk/compat.h"
+#include "gtk/gui.h"
+#include "gtk/scaffolding.h"
+#include "gtk/plotters.h"
+#include "gtk/schedule.h"
+#include "gtk/tabs.h"
+#include "gtk/bitmap.h"
+#include "gtk/gdk.h"
+#include "gtk/resources.h"
+
+static GtkWidget *select_menu;
+static struct form_control *select_menu_control;
+
+static void nsgtk_select_menu_clicked(GtkCheckMenuItem *checkmenuitem,
+ gpointer user_data);
+
+struct gui_window {
+ /**
+ * The gtk scaffold object containing menu, buttons, url bar, [tabs],
+ * drawing area, etc that may contain one or more gui_windows.
+ */
+ struct nsgtk_scaffolding *scaffold;
+
+ /** The 'content' window that is rendered in the gui_window */
+ struct browser_window *bw;
+
+ /** mouse state and events. */
+ struct {
+ struct gui_window *gui;
+
+ gdouble pressed_x;
+ gdouble pressed_y;
+ gboolean waiting;
+ browser_mouse_state state;
+ } mouse;
+
+ /** caret dimension and location for rendering */
+ int caretx, carety, careth;
+
+ /** caret shape for rendering */
+ gui_pointer_shape current_pointer;
+
+ /** previous event location */
+ int last_x, last_y;
+
+ /** The top level container (tabContents) */
+ GtkWidget *container;
+
+ /** display widget for this page or frame */
+ GtkLayout *layout;
+
+ /** handle to the the visible tab */
+ GtkWidget *tab;
+
+ /** statusbar */
+ GtkLabel *status_bar;
+
+ /** status pane */
+ GtkPaned *paned;
+
+ /** has the status pane had its first size operation yet? */
+ bool paned_sized;
+
+ /** to allow disactivation / resume of normal window behaviour */
+ gulong signalhandler[NSGTK_WINDOW_SIGNAL_COUNT];
+
+ /** The icon this window should have */
+ GdkPixbuf *icon;
+
+ /** The input method to use with this window */
+ GtkIMContext *input_method;
+
+ /** list for cleanup */
+ struct gui_window *next, *prev;
+};
+
+/**< first entry in window list */
+struct gui_window *window_list = NULL;
+
+/** flag controlling opening of tabs in teh background */
+int temp_open_background = -1;
+
+struct nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g)
+{
+ return g->scaffold;
+}
+
+GdkPixbuf *nsgtk_get_icon(struct gui_window *gw)
+{
+ return gw->icon;
+}
+
+struct browser_window *nsgtk_get_browser_window(struct gui_window *g)
+{
+ return g->bw;
+}
+
+unsigned long nsgtk_window_get_signalhandler(struct gui_window *g, int i)
+{
+ return g->signalhandler[i];
+}
+
+GtkLayout *nsgtk_window_get_layout(struct gui_window *g)
+{
+ return g->layout;
+}
+
+GtkWidget *nsgtk_window_get_tab(struct gui_window *g)
+{
+ return g->tab;
+}
+
+void nsgtk_window_set_tab(struct gui_window *g, GtkWidget *w)
+{
+ g->tab = w;
+}
+
+
+struct gui_window *nsgtk_window_iterate(struct gui_window *g)
+{
+ return g->next;
+}
+
+float nsgtk_get_scale_for_gui(struct gui_window *g)
+{
+ return browser_window_get_scale(g->bw);
+}
+
+static void nsgtk_select_menu_clicked(GtkCheckMenuItem *checkmenuitem,
+ gpointer user_data)
+{
+ form_select_process_selection(select_menu_control,
+ (intptr_t)user_data);
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+
+static gboolean
+nsgtk_window_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ struct gui_window *gw = data;
+ struct gui_window *z;
+ struct rect clip;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+
+ assert(gw);
+ assert(gw->bw);
+
+ for (z = window_list; z && z != gw; z = z->next)
+ continue;
+ assert(z);
+ assert(GTK_WIDGET(gw->layout) == widget);
+
+ current_widget = (GtkWidget *)gw->layout;
+ current_cr = cr;
+
+ GtkAdjustment *vscroll = nsgtk_layout_get_vadjustment(gw->layout);
+ GtkAdjustment *hscroll = nsgtk_layout_get_hadjustment(gw->layout);
+
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+
+ clip.x0 = x1;
+ clip.y0 = y1;
+ clip.x1 = x2;
+ clip.y1 = y2;
+
+ browser_window_redraw(gw->bw,
+ -gtk_adjustment_get_value(hscroll),
+ -gtk_adjustment_get_value(vscroll),
+ &clip,
+ &ctx);
+
+ if (gw->careth != 0) {
+ nsgtk_plot_caret(gw->caretx, gw->carety, gw->careth);
+ }
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+
+#else
+
+static gboolean
+nsgtk_window_draw_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ struct gui_window *gw = data;
+ struct gui_window *z;
+ struct rect clip;
+ struct redraw_context ctx = {
+ .interactive = true,
+ .background_images = true,
+ .plot = &nsgtk_plotters
+ };
+
+ assert(gw);
+ assert(gw->bw);
+
+ for (z = window_list; z && z != gw; z = z->next)
+ continue;
+ assert(z);
+ assert(GTK_WIDGET(gw->layout) == widget);
+
+ current_widget = (GtkWidget *)gw->layout;
+ current_cr = gdk_cairo_create(nsgtk_layout_get_bin_window(gw->layout));
+
+ clip.x0 = event->area.x;
+ clip.y0 = event->area.y;
+ clip.x1 = event->area.x + event->area.width;
+ clip.y1 = event->area.y + event->area.height;
+
+ browser_window_redraw(gw->bw, 0, 0, &clip, &ctx);
+
+ if (gw->careth != 0) {
+ nsgtk_plot_caret(gw->caretx, gw->carety, gw->careth);
+ }
+
+ cairo_destroy(current_cr);
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+
+#endif
+
+static gboolean nsgtk_window_motion_notify_event(GtkWidget *widget,
+ GdkEventMotion *event, gpointer data)
+{
+ struct gui_window *g = data;
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+
+ if ((fabs(event->x - g->last_x) < 5.0) &&
+ (fabs(event->y - g->last_y) < 5.0)) {
+ /* Mouse hasn't moved far enough from press coordinate
+ * for this to be considered a drag.
+ */
+ return FALSE;
+ } else {
+ /* This is a drag, ensure it's always treated as such,
+ * even if we drag back over the press location.
+ */
+ g->last_x = INT_MIN;
+ g->last_y = INT_MIN;
+ }
+
+ if (g->mouse.state & BROWSER_MOUSE_PRESS_1) {
+ /* Start button 1 drag */
+ browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_1,
+ g->mouse.pressed_x, g->mouse.pressed_y);
+
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ g->mouse.state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_HOLDING_1);
+ g->mouse.state |= BROWSER_MOUSE_DRAG_ON;
+ } else if (g->mouse.state & BROWSER_MOUSE_PRESS_2) {
+ /* Start button 2 drag */
+ browser_window_mouse_click(g->bw, BROWSER_MOUSE_DRAG_2,
+ g->mouse.pressed_x, g->mouse.pressed_y);
+
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ g->mouse.state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_HOLDING_2);
+ g->mouse.state |= BROWSER_MOUSE_DRAG_ON;
+ }
+
+ /* Handle modifiers being removed */
+ if (g->mouse.state & BROWSER_MOUSE_MOD_1 && !shift)
+ g->mouse.state ^= BROWSER_MOUSE_MOD_1;
+ if (g->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ g->mouse.state ^= BROWSER_MOUSE_MOD_2;
+
+ browser_window_mouse_track(g->bw, g->mouse.state,
+ event->x / browser_window_get_scale(g->bw),
+ event->y / browser_window_get_scale(g->bw));
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_button_press_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer data)
+{
+ struct gui_window *g = data;
+
+ gtk_im_context_reset(g->input_method);
+ gtk_widget_grab_focus(GTK_WIDGET(g->layout));
+ gtk_widget_hide(GTK_WIDGET(nsgtk_scaffolding_history_window(
+ g->scaffold)->window));
+
+ g->mouse.pressed_x = event->x / browser_window_get_scale(g->bw);
+ g->mouse.pressed_y = event->y / browser_window_get_scale(g->bw);
+
+ switch (event->button) {
+ case 1: /* Left button, usually. Pass to core as BUTTON 1. */
+ g->mouse.state = BROWSER_MOUSE_PRESS_1;
+ break;
+
+ case 2: /* Middle button, usually. Pass to core as BUTTON 2 */
+ g->mouse.state = BROWSER_MOUSE_PRESS_2;
+ break;
+
+ case 3: /* Right button, usually. Action button, context menu. */
+ browser_window_remove_caret(g->bw, true);
+ nsgtk_scaffolding_context_menu(g->scaffold,
+ g->mouse.pressed_x,
+ g->mouse.pressed_y);
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+
+ /* Modify for double & triple clicks */
+ if (event->type == GDK_3BUTTON_PRESS)
+ g->mouse.state |= BROWSER_MOUSE_TRIPLE_CLICK;
+ else if (event->type == GDK_2BUTTON_PRESS)
+ g->mouse.state |= BROWSER_MOUSE_DOUBLE_CLICK;
+
+ /* Handle the modifiers too */
+ if (event->state & GDK_SHIFT_MASK)
+ g->mouse.state |= BROWSER_MOUSE_MOD_1;
+ if (event->state & GDK_CONTROL_MASK)
+ g->mouse.state |= BROWSER_MOUSE_MOD_2;
+
+ /* Record where we pressed, for use when determining whether to start
+ * a drag in motion notify events. */
+ g->last_x = event->x;
+ g->last_y = event->y;
+
+ browser_window_mouse_click(g->bw, g->mouse.state, g->mouse.pressed_x,
+ g->mouse.pressed_y);
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_button_release_event(GtkWidget *widget,
+ GdkEventButton *event, gpointer data)
+{
+ struct gui_window *g = data;
+ bool shift = event->state & GDK_SHIFT_MASK;
+ bool ctrl = event->state & GDK_CONTROL_MASK;
+
+ /* If the mouse state is PRESS then we are waiting for a release to emit
+ * a click event, otherwise just reset the state to nothing */
+ if (g->mouse.state & BROWSER_MOUSE_PRESS_1)
+ g->mouse.state ^= (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1);
+ else if (g->mouse.state & BROWSER_MOUSE_PRESS_2)
+ g->mouse.state ^= (BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2);
+
+ /* Handle modifiers being removed */
+ if (g->mouse.state & BROWSER_MOUSE_MOD_1 && !shift)
+ g->mouse.state ^= BROWSER_MOUSE_MOD_1;
+ if (g->mouse.state & BROWSER_MOUSE_MOD_2 && !ctrl)
+ g->mouse.state ^= BROWSER_MOUSE_MOD_2;
+
+ if (g->mouse.state & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) {
+ browser_window_mouse_click(g->bw, g->mouse.state,
+ event->x / browser_window_get_scale(g->bw),
+ event->y / browser_window_get_scale(g->bw));
+ } else {
+ browser_window_mouse_track(g->bw, 0,
+ event->x / browser_window_get_scale(g->bw),
+ event->y / browser_window_get_scale(g->bw));
+ }
+
+ g->mouse.state = 0;
+ return TRUE;
+}
+
+static gboolean
+nsgtk_window_scroll_event(GtkWidget *widget,
+ GdkEventScroll *event,
+ gpointer data)
+{
+ struct gui_window *g = data;
+ double value;
+ double deltax = 0;
+ double deltay = 0;
+ GtkAdjustment *vscroll = nsgtk_layout_get_vadjustment(g->layout);
+ GtkAdjustment *hscroll = nsgtk_layout_get_hadjustment(g->layout);
+ GtkAllocation alloc;
+
+ switch (event->direction) {
+ case GDK_SCROLL_LEFT:
+ deltax = -1.0;
+ break;
+
+ case GDK_SCROLL_UP:
+ deltay = -1.0;
+ break;
+
+ case GDK_SCROLL_RIGHT:
+ deltax = 1.0;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ deltay = 1.0;
+ break;
+
+#if GTK_CHECK_VERSION(3,4,0)
+ case GDK_SCROLL_SMOOTH:
+ gdk_event_get_scroll_deltas((GdkEvent *)event, &deltax, &deltay);
+ break;
+#endif
+ default:
+ LOG("Unhandled mouse scroll direction");
+ return TRUE;
+ }
+
+ deltax *= nsgtk_adjustment_get_step_increment(hscroll);
+ deltay *= nsgtk_adjustment_get_step_increment(vscroll);
+
+ if (browser_window_scroll_at_point(g->bw,
+ event->x / browser_window_get_scale(g->bw),
+ event->y / browser_window_get_scale(g->bw),
+ deltax, deltay) != true) {
+
+ /* core did not handle event so change adjustments */
+
+ /* Horizontal */
+ if (deltax != 0) {
+ value = gtk_adjustment_get_value(hscroll) + deltax;
+
+ /* @todo consider gtk_widget_get_allocated_width() */
+ nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc);
+
+ if (value > nsgtk_adjustment_get_upper(hscroll) - alloc.width) {
+ value = nsgtk_adjustment_get_upper(hscroll) - alloc.width;
+ }
+ if (value < nsgtk_adjustment_get_lower(hscroll)) {
+ value = nsgtk_adjustment_get_lower(hscroll);
+ }
+
+ gtk_adjustment_set_value(hscroll, value);
+ }
+
+ /* Vertical */
+ if (deltay != 0) {
+ value = gtk_adjustment_get_value(vscroll) + deltay;
+
+ /* @todo consider gtk_widget_get_allocated_height */
+ nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc);
+
+ if (value > (nsgtk_adjustment_get_upper(vscroll) - alloc.height)) {
+ value = nsgtk_adjustment_get_upper(vscroll) - alloc.height;
+ }
+ if (value < nsgtk_adjustment_get_lower(vscroll)) {
+ value = nsgtk_adjustment_get_lower(vscroll);
+ }
+
+ gtk_adjustment_set_value(vscroll, value);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_keypress_event(GtkWidget *widget,
+ GdkEventKey *event, gpointer data)
+{
+ struct gui_window *g = data;
+ uint32_t nskey;
+
+ if (gtk_im_context_filter_keypress(g->input_method, event))
+ return TRUE;
+
+ nskey = gtk_gui_gdkkey_to_nskey(event);
+
+ if (browser_window_key_press(g->bw, nskey))
+ return TRUE;
+
+ if ((event->state & 0x7) != 0)
+ return TRUE;
+
+ double value;
+ GtkAdjustment *vscroll = nsgtk_layout_get_vadjustment(g->layout);
+ GtkAdjustment *hscroll = nsgtk_layout_get_hadjustment(g->layout);
+ GtkAllocation alloc;
+
+ /* @todo consider gtk_widget_get_allocated_width() */
+ nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc);
+
+ switch (event->keyval) {
+
+ case GDK_KEY(Home):
+ case GDK_KEY(KP_Home):
+ value = nsgtk_adjustment_get_lower(vscroll);
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(End):
+ case GDK_KEY(KP_End):
+ value = nsgtk_adjustment_get_upper(vscroll) - alloc.height;
+
+ if (value < nsgtk_adjustment_get_lower(vscroll))
+ value = nsgtk_adjustment_get_lower(vscroll);
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(Left):
+ case GDK_KEY(KP_Left):
+ value = gtk_adjustment_get_value(hscroll) -
+ nsgtk_adjustment_get_step_increment(hscroll);
+
+ if (value < nsgtk_adjustment_get_lower(hscroll))
+ value = nsgtk_adjustment_get_lower(hscroll);
+
+ gtk_adjustment_set_value(hscroll, value);
+ break;
+
+ case GDK_KEY(Up):
+ case GDK_KEY(KP_Up):
+ value = gtk_adjustment_get_value(vscroll) -
+ nsgtk_adjustment_get_step_increment(vscroll);
+
+ if (value < nsgtk_adjustment_get_lower(vscroll))
+ value = nsgtk_adjustment_get_lower(vscroll);
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(Right):
+ case GDK_KEY(KP_Right):
+ value = gtk_adjustment_get_value(hscroll) +
+ nsgtk_adjustment_get_step_increment(hscroll);
+
+ if (value > nsgtk_adjustment_get_upper(hscroll) - alloc.width)
+ value = nsgtk_adjustment_get_upper(hscroll) - alloc.width;
+
+ gtk_adjustment_set_value(hscroll, value);
+ break;
+
+ case GDK_KEY(Down):
+ case GDK_KEY(KP_Down):
+ value = gtk_adjustment_get_value(vscroll) +
+ nsgtk_adjustment_get_step_increment(vscroll);
+
+ if (value > nsgtk_adjustment_get_upper(vscroll) - alloc.height)
+ value = nsgtk_adjustment_get_upper(vscroll) - alloc.height;
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(Page_Up):
+ case GDK_KEY(KP_Page_Up):
+ value = gtk_adjustment_get_value(vscroll) -
+ nsgtk_adjustment_get_page_increment(vscroll);
+
+ if (value < nsgtk_adjustment_get_lower(vscroll))
+ value = nsgtk_adjustment_get_lower(vscroll);
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ case GDK_KEY(Page_Down):
+ case GDK_KEY(KP_Page_Down):
+ value = gtk_adjustment_get_value(vscroll) +
+ nsgtk_adjustment_get_page_increment(vscroll);
+
+ if (value > nsgtk_adjustment_get_upper(vscroll) - alloc.height)
+ value = nsgtk_adjustment_get_upper(vscroll) - alloc.height;
+
+ gtk_adjustment_set_value(vscroll, value);
+ break;
+
+ default:
+ break;
+
+ }
+
+ return TRUE;
+}
+
+static gboolean nsgtk_window_keyrelease_event(GtkWidget *widget,
+ GdkEventKey *event, gpointer data)
+{
+ struct gui_window *g = data;
+
+ return gtk_im_context_filter_keypress(g->input_method, event);
+}
+
+
+static void nsgtk_window_input_method_commit(GtkIMContext *ctx,
+ const gchar *str, gpointer data)
+{
+ struct gui_window *g = data;
+ size_t len = strlen(str), offset = 0;
+
+ while (offset < len) {
+ uint32_t nskey = utf8_to_ucs4(str + offset, len - offset);
+
+ browser_window_key_press(g->bw, nskey);
+
+ offset = utf8_next(str, len, offset);
+ }
+}
+
+
+static gboolean nsgtk_window_size_allocate_event(GtkWidget *widget,
+ GtkAllocation *allocation, gpointer data)
+{
+ struct gui_window *g = data;
+
+ browser_window_schedule_reformat(g->bw);
+
+ return TRUE;
+}
+
+
+/** when the pane position is changed update the user option
+ *
+ * The slightly awkward implementation with the first allocation flag
+ * is necessary because the initial window creation does not cause an
+ * allocate-event signal so the position value in the pane is incorrect
+ * and we cannot know what it should be until after the allocation
+ * (which did not generate a signal) is done as the user position is a
+ * percentage of pane total width not an absolute value.
+ */
+static void
+nsgtk_paned_notify__position(GObject *gobject, GParamSpec *pspec, gpointer data)
+{
+ struct gui_window *g = data;
+ GtkAllocation pane_alloc;
+
+ gtk_widget_get_allocation(GTK_WIDGET(g->paned), &pane_alloc);
+
+ if (g->paned_sized == false)
+ {
+ g->paned_sized = true;
+ gtk_paned_set_position(g->paned,
+ (nsoption_int(toolbar_status_size) * pane_alloc.width) / 10000);
+ return;
+ }
+
+ nsoption_set_int(toolbar_status_size,
+ ((gtk_paned_get_position(g->paned) * 10000) / (pane_alloc.width - 1)));
+}
+
+/** Set status bar / scroll bar proportion according to user option
+ * when pane is resized.
+ */
+static gboolean nsgtk_paned_size_allocate_event(GtkWidget *widget,
+ GtkAllocation *allocation, gpointer data)
+{
+ gtk_paned_set_position(GTK_PANED(widget),
+ (nsoption_int(toolbar_status_size) * allocation->width) / 10000);
+
+ return TRUE;
+}
+
+/* destroy the browsing context as there is nothing to display it now */
+static void window_destroy(GtkWidget *widget, gpointer data)
+{
+ struct gui_window *gw = data;
+
+ browser_window_destroy(gw->bw);
+
+ g_object_unref(gw->input_method);
+}
+
+
+/**
+ * Create and open a gtk container (window or tab) for a browsing context.
+ *
+ * \param bw The browsing context to create gui_window for.
+ * \param existing An existing gui_window, may be NULL
+ * \param flags flags to control the container creation
+ * \return gui window, or NULL on error
+ *
+ * If GW_CREATE_CLONE flag is set existing is non-NULL.
+ *
+ * Front end's gui_window must include a reference to the
+ * browser window passed in the bw param.
+ */
+static struct gui_window *
+gui_window_create(struct browser_window *bw,
+ struct gui_window *existing,
+ gui_window_create_flags flags)
+{
+ struct gui_window *g; /* what is being created to return */
+ bool tempback;
+ GtkBuilder* tab_builder;
+
+ nserror res;
+
+ res = nsgtk_builder_new_from_resname("tabcontents", &tab_builder);
+ if (res != NSERROR_OK) {
+ LOG("Tab contents UI builder init failed");
+ return NULL;
+ }
+
+ gtk_builder_connect_signals(tab_builder, NULL);
+
+ g = calloc(1, sizeof(*g));
+ if (!g) {
+ nsgtk_warning("NoMemory", 0);
+ g_object_unref(tab_builder);
+ return NULL;
+ }
+
+ LOG("Creating gui window %p for browser window %p", g, bw);
+
+ g->bw = bw;
+ g->mouse.state = 0;
+ g->current_pointer = GUI_POINTER_DEFAULT;
+
+ /* attach scaffold */
+ if (flags & GW_CREATE_TAB) {
+ /* open in new tab, attach to existing scaffold */
+ if (existing != NULL) {
+ g->scaffold = existing->scaffold;
+ } else {
+ g->scaffold = nsgtk_current_scaffolding();
+ }
+ } else {
+ /* open in new window, create and attach to scaffold */
+ g->scaffold = nsgtk_new_scaffolding(g);
+ }
+ if (g->scaffold == NULL) {
+ nsgtk_warning("NoMemory", 0);
+ free(g);
+ g_object_unref(tab_builder);
+ return NULL;
+ }
+
+ /* Construct our primary elements */
+ g->container = GTK_WIDGET(gtk_builder_get_object(tab_builder, "tabContents"));
+ g->layout = GTK_LAYOUT(gtk_builder_get_object(tab_builder, "layout"));
+ g->status_bar = GTK_LABEL(gtk_builder_get_object(tab_builder, "status_bar"));
+ g->paned = GTK_PANED(gtk_builder_get_object(tab_builder, "hpaned1"));
+ g->input_method = gtk_im_multicontext_new();
+
+ /* set a default favicon */
+ g_object_ref(favicon_pixbuf);
+ g->icon = favicon_pixbuf;
+
+ /* add new gui window to global list (push_top) */
+ if (window_list) {
+ window_list->prev = g;
+ }
+ g->next = window_list;
+ g->prev = NULL;
+ window_list = g;
+
+ /* set the events we're interested in receiving from the browser's
+ * drawing area.
+ */
+ gtk_widget_add_events(GTK_WIDGET(g->layout),
+ GDK_EXPOSURE_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_KEY_RELEASE_MASK |
+ GDK_SCROLL_MASK);
+ nsgtk_widget_set_can_focus(GTK_WIDGET(g->layout), TRUE);
+
+ /* set the default background colour of the drawing area to white. */
+ nsgtk_widget_override_background_color(GTK_WIDGET(g->layout),
+ GTK_STATE_NORMAL,
+ 0, 0xffff, 0xffff, 0xffff);
+
+ g->signalhandler[NSGTK_WINDOW_SIGNAL_REDRAW] =
+ nsgtk_connect_draw_event(GTK_WIDGET(g->layout),
+ G_CALLBACK(nsgtk_window_draw_event), g);
+
+ /* helper macro to conect signals to callbacks */
+#define CONNECT(obj, sig, callback, ptr) \
+ g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr))
+
+ /* layout signals */
+ CONNECT(g->layout, "motion-notify-event",
+ nsgtk_window_motion_notify_event, g);
+ g->signalhandler[NSGTK_WINDOW_SIGNAL_CLICK] =
+ CONNECT(g->layout, "button-press-event",
+ nsgtk_window_button_press_event, g);
+ CONNECT(g->layout, "button-release-event",
+ nsgtk_window_button_release_event, g);
+ CONNECT(g->layout, "key-press-event",
+ nsgtk_window_keypress_event, g);
+ CONNECT(g->layout, "key-release-event",
+ nsgtk_window_keyrelease_event, g);
+ CONNECT(g->layout, "size-allocate",
+ nsgtk_window_size_allocate_event, g);
+ CONNECT(g->layout, "scroll-event",
+ nsgtk_window_scroll_event, g);
+
+ /* status pane signals */
+ CONNECT(g->paned, "size-allocate",
+ nsgtk_paned_size_allocate_event, g);
+
+ CONNECT(g->paned, "notify::position",
+ nsgtk_paned_notify__position, g);
+
+ /* gtk container destructor */
+ CONNECT(g->container, "destroy", window_destroy, g);
+
+ /* input method */
+ gtk_im_context_set_client_window(g->input_method,
+ nsgtk_layout_get_bin_window(g->layout));
+ gtk_im_context_set_use_preedit(g->input_method, FALSE);
+
+ /* input method signals */
+ CONNECT(g->input_method, "commit",
+ nsgtk_window_input_method_commit, g);
+
+ /* add the tab container to the scaffold notebook */
+ switch (temp_open_background) {
+ case -1:
+ tempback = !(nsoption_bool(focus_new));
+ break;
+ case 0:
+ tempback = false;
+ break;
+ default:
+ tempback = true;
+ break;
+ }
+ nsgtk_tab_add(g, g->container, tempback);
+
+ /* safe to drop the reference to the tab_builder as the container is
+ * referenced by the notebook now.
+ */
+ g_object_unref(tab_builder);
+
+ return g;
+}
+
+
+
+void nsgtk_reflow_all_windows(void)
+{
+ for (struct gui_window *g = window_list; g; g = g->next) {
+ nsgtk_tab_options_changed(nsgtk_scaffolding_notebook(g->scaffold));
+ browser_window_schedule_reformat(g->bw);
+ }
+}
+
+
+/**
+ * callback from core to reformat a window.
+ */
+static void nsgtk_window_reformat(struct gui_window *gw)
+{
+ GtkAllocation alloc;
+
+ if (gw != NULL) {
+ /** @todo consider gtk_widget_get_allocated_width() */
+ nsgtk_widget_get_allocation(GTK_WIDGET(gw->layout), &alloc);
+
+ browser_window_reformat(gw->bw, false, alloc.width, alloc.height);
+ }
+}
+
+void nsgtk_window_destroy_browser(struct gui_window *gw)
+{
+ /* remove tab */
+ gtk_widget_destroy(gw->container);
+}
+
+static void gui_window_destroy(struct gui_window *g)
+{
+ LOG("gui_window: %p", g);
+ assert(g != NULL);
+ assert(g->bw != NULL);
+ LOG("scaffolding: %p", g->scaffold);
+
+ if (g->prev) {
+ g->prev->next = g->next;
+ } else {
+ window_list = g->next;
+ }
+
+ if (g->next) {
+ g->next->prev = g->prev;
+ }
+
+ LOG("window list head: %p", window_list);
+}
+
+/**
+ * favicon setting for gtk gui window.
+ *
+ * \param gw gtk gui window to set favicon on.
+ * \param icon A handle to the new favicon content.
+ */
+static void gui_window_set_icon(struct gui_window *gw, hlcache_handle *icon)
+{
+ struct bitmap *icon_bitmap = NULL;
+
+ /* free any existing icon */
+ if (gw->icon != NULL) {
+ g_object_unref(gw->icon);
+ gw->icon = NULL;
+ }
+
+ if (icon != NULL) {
+ icon_bitmap = content_get_bitmap(icon);
+ if (icon_bitmap != NULL) {
+ LOG("Using %p bitmap", icon_bitmap);
+ gw->icon = nsgdk_pixbuf_get_from_surface(icon_bitmap->surface, 16, 16);
+ }
+ }
+
+ if (gw->icon == NULL) {
+ LOG("Using default favicon");
+ g_object_ref(favicon_pixbuf);
+ gw->icon = favicon_pixbuf;
+ }
+
+ nsgtk_scaffolding_set_icon(gw);
+}
+
+static bool gui_window_get_scroll(struct gui_window *g, int *sx, int *sy)
+{
+ GtkAdjustment *vadj = nsgtk_layout_get_vadjustment(g->layout);
+ GtkAdjustment *hadj = nsgtk_layout_get_hadjustment(g->layout);
+
+ assert(vadj);
+ assert(hadj);
+
+ *sy = (int)(gtk_adjustment_get_value(vadj));
+ *sx = (int)(gtk_adjustment_get_value(hadj));
+
+ return true;
+}
+
+static void nsgtk_redraw_caret(struct gui_window *g)
+{
+ int sx, sy;
+
+ if (g->careth == 0)
+ return;
+
+ gui_window_get_scroll(g, &sx, &sy);
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(g->layout),
+ g->caretx - sx, g->carety - sy, 1, g->careth + 1);
+
+}
+
+static void gui_window_remove_caret(struct gui_window *g)
+{
+ int sx, sy;
+ int oh = g->careth;
+
+ if (oh == 0)
+ return;
+
+ g->careth = 0;
+
+ gui_window_get_scroll(g, &sx, &sy);
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(g->layout),
+ g->caretx - sx, g->carety - sy, 1, oh + 1);
+
+}
+
+static void gui_window_redraw_window(struct gui_window *g)
+{
+ gtk_widget_queue_draw(GTK_WIDGET(g->layout));
+}
+
+static void gui_window_update_box(struct gui_window *g, const struct rect *rect)
+{
+ int sx, sy;
+ float scale;
+
+ if (!browser_window_has_content(g->bw))
+ return;
+
+ gui_window_get_scroll(g, &sx, &sy);
+ scale = browser_window_get_scale(g->bw);
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(g->layout),
+ rect->x0 * scale - sx,
+ rect->y0 * scale - sy,
+ (rect->x1 - rect->x0) * scale,
+ (rect->y1 - rect->y0) * scale);
+}
+
+static void gui_window_set_status(struct gui_window *g, const char *text)
+{
+ assert(g);
+ assert(g->status_bar);
+ gtk_label_set_text(g->status_bar, text);
+}
+
+
+static void gui_window_set_scroll(struct gui_window *g, int sx, int sy)
+{
+ GtkAdjustment *vadj = nsgtk_layout_get_vadjustment(g->layout);
+ GtkAdjustment *hadj = nsgtk_layout_get_hadjustment(g->layout);
+ gdouble vlower, vpage, vupper, hlower, hpage, hupper, x = (double)sx, y = (double)sy;
+
+ assert(vadj);
+ assert(hadj);
+
+ g_object_get(vadj, "page-size", &vpage, "lower", &vlower, "upper", &vupper, NULL);
+ g_object_get(hadj, "page-size", &hpage, "lower", &hlower, "upper", &hupper, NULL);
+
+ if (x < hlower)
+ x = hlower;
+ if (x > (hupper - hpage))
+ x = hupper - hpage;
+ if (y < vlower)
+ y = vlower;
+ if (y > (vupper - vpage))
+ y = vupper - vpage;
+
+ gtk_adjustment_set_value(vadj, y);
+ gtk_adjustment_set_value(hadj, x);
+}
+
+static void gui_window_update_extent(struct gui_window *g)
+{
+ int w, h;
+
+ if (browser_window_get_extents(g->bw, true, &w, &h) == NSERROR_OK) {
+ gtk_layout_set_size(g->layout, w, h);
+ }
+}
+
+static void gui_window_set_pointer(struct gui_window *g,
+ gui_pointer_shape shape)
+{
+ GdkCursor *cursor = NULL;
+ GdkCursorType cursortype;
+ bool nullcursor = false;
+
+ if (g->current_pointer == shape)
+ return;
+
+ g->current_pointer = shape;
+
+ switch (shape) {
+ case GUI_POINTER_POINT:
+ cursortype = GDK_HAND2;
+ break;
+ case GUI_POINTER_CARET:
+ cursortype = GDK_XTERM;
+ break;
+ case GUI_POINTER_UP:
+ cursortype = GDK_TOP_SIDE;
+ break;
+ case GUI_POINTER_DOWN:
+ cursortype = GDK_BOTTOM_SIDE;
+ break;
+ case GUI_POINTER_LEFT:
+ cursortype = GDK_LEFT_SIDE;
+ break;
+ case GUI_POINTER_RIGHT:
+ cursortype = GDK_RIGHT_SIDE;
+ break;
+ case GUI_POINTER_LD:
+ cursortype = GDK_BOTTOM_LEFT_CORNER;
+ break;
+ case GUI_POINTER_RD:
+ cursortype = GDK_BOTTOM_RIGHT_CORNER;
+ break;
+ case GUI_POINTER_LU:
+ cursortype = GDK_TOP_LEFT_CORNER;
+ break;
+ case GUI_POINTER_RU:
+ cursortype = GDK_TOP_RIGHT_CORNER;
+ break;
+ case GUI_POINTER_CROSS:
+ cursortype = GDK_CROSS;
+ break;
+ case GUI_POINTER_MOVE:
+ cursortype = GDK_FLEUR;
+ break;
+ case GUI_POINTER_WAIT:
+ cursortype = GDK_WATCH;
+ break;
+ case GUI_POINTER_HELP:
+ cursortype = GDK_QUESTION_ARROW;
+ break;
+ case GUI_POINTER_MENU:
+ cursor = nsgtk_create_menu_cursor();
+ nullcursor = true;
+ break;
+ case GUI_POINTER_PROGRESS:
+ /* In reality, this needs to be the funky left_ptr_watch
+ * which we can't do easily yet.
+ */
+ cursortype = GDK_WATCH;
+ break;
+ /* The following we're not sure about */
+ case GUI_POINTER_NO_DROP:
+ case GUI_POINTER_NOT_ALLOWED:
+ case GUI_POINTER_DEFAULT:
+ default:
+ nullcursor = true;
+ }
+
+ if (!nullcursor)
+ cursor = gdk_cursor_new_for_display(
+ gtk_widget_get_display(
+ GTK_WIDGET(g->layout)),
+ cursortype);
+ gdk_window_set_cursor(nsgtk_widget_get_window(GTK_WIDGET(g->layout)),
+ cursor);
+
+ if (!nullcursor)
+ nsgdk_cursor_unref(cursor);
+}
+
+
+static void gui_window_place_caret(struct gui_window *g, int x, int y, int height,
+ const struct rect *clip)
+{
+ nsgtk_redraw_caret(g);
+
+ y += 1;
+ height -= 1;
+
+ if (y < clip->y0) {
+ height -= clip->y0 - y;
+ y = clip->y0;
+ }
+
+ if (y + height > clip->y1) {
+ height = clip->y1 - y + 1;
+ }
+
+ g->caretx = x;
+ g->carety = y;
+ g->careth = height;
+
+ nsgtk_redraw_caret(g);
+
+ gtk_widget_grab_focus(GTK_WIDGET(g->layout));
+}
+
+
+static void gui_window_get_dimensions(struct gui_window *g, int *width, int *height,
+ bool scaled)
+{
+ GtkAllocation alloc;
+
+ /* @todo consider gtk_widget_get_allocated_width() */
+ nsgtk_widget_get_allocation(GTK_WIDGET(g->layout), &alloc);
+
+ *width = alloc.width;
+ *height = alloc.height;
+
+ if (scaled) {
+ float scale = browser_window_get_scale(g->bw);
+ *width /= scale;
+ *height /= scale;
+ }
+ LOG("width: %i", *width);
+ LOG("height: %i", *height);
+}
+
+static void gui_window_start_selection(struct gui_window *g)
+{
+ gtk_widget_grab_focus(GTK_WIDGET(g->layout));
+}
+
+static void gui_window_create_form_select_menu(struct gui_window *g,
+ struct form_control *control)
+{
+ intptr_t item;
+ struct form_option *option;
+
+ GtkWidget *menu_item;
+
+ /* control->data.select.multiple is true if multiple selections
+ * are allowable. We ignore this, as the core handles it for us.
+ * Yay. \o/
+ */
+
+ if (select_menu != NULL) {
+ gtk_widget_destroy(select_menu);
+ }
+
+ select_menu = gtk_menu_new();
+ select_menu_control = control;
+
+ item = 0;
+ option = form_select_get_option(control, item);
+ while (option != NULL) {
+ LOG("Item %"PRIdPTR" option %p text %s",
+ item, option, option->text);
+ menu_item = gtk_check_menu_item_new_with_label(option->text);
+ if (option->selected) {
+ gtk_check_menu_item_set_active(
+ GTK_CHECK_MENU_ITEM(menu_item), TRUE);
+ }
+
+ /*
+ * This casts the item index integer into an integer
+ * the size of a pointer. This allows the callback
+ * parameter to be passed avoiding allocating memory
+ * for a context with a single integer in it.
+ */
+ g_signal_connect(menu_item, "toggled",
+ G_CALLBACK(nsgtk_select_menu_clicked), (gpointer)item);
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(select_menu), menu_item);
+
+ item++;
+ option = form_select_get_option(control, item);
+ }
+
+ gtk_widget_show_all(select_menu);
+
+ gtk_menu_popup(GTK_MENU(select_menu), NULL, NULL, NULL,
+ NULL /* data */, 0, gtk_get_current_event_time());
+
+}
+
+static void
+gui_window_file_gadget_open(struct gui_window *g,
+ hlcache_handle *hl,
+ struct form_control *gadget)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_file_chooser_dialog_new("Select File",
+ nsgtk_scaffolding_window(g->scaffold),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ NSGTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NSGTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ LOG("*** open dialog: %p", dialog);
+
+ int ret = gtk_dialog_run(GTK_DIALOG(dialog));
+ LOG("*** return value: %d", ret);
+ if (ret == GTK_RESPONSE_ACCEPT) {
+ char *filename;
+
+ filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(dialog));
+
+ browser_window_set_gadget_filename(g->bw, gadget, filename);
+
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static struct gui_window_table window_table = {
+ .create = gui_window_create,
+ .destroy = gui_window_destroy,
+ .redraw = gui_window_redraw_window,
+ .update = gui_window_update_box,
+ .get_scroll = gui_window_get_scroll,
+ .set_scroll = gui_window_set_scroll,
+ .get_dimensions = gui_window_get_dimensions,
+ .update_extent = gui_window_update_extent,
+ .reformat = nsgtk_window_reformat,
+
+ .set_icon = gui_window_set_icon,
+ .set_status = gui_window_set_status,
+ .set_pointer = gui_window_set_pointer,
+ .place_caret = gui_window_place_caret,
+ .remove_caret = gui_window_remove_caret,
+ .create_form_select_menu = gui_window_create_form_select_menu,
+ .file_gadget_open = gui_window_file_gadget_open,
+ .start_selection = gui_window_start_selection,
+
+ /* from scaffold */
+ .set_title = nsgtk_window_set_title,
+ .set_url = gui_window_set_url,
+ .start_throbber = gui_window_start_throbber,
+ .stop_throbber = gui_window_stop_throbber,
+};
+
+struct gui_window_table *nsgtk_window_table = &window_table;
diff --git a/frontends/gtk/window.h b/frontends/gtk/window.h
new file mode 100644
index 000000000..c604bf3f7
--- /dev/null
+++ b/frontends/gtk/window.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2006 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf 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; version 2 of the License.
+ *
+ * NetSurf 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NETSURF_GTK_WINDOW_H
+#define NETSURF_GTK_WINDOW_H 1
+
+#include "gtk/scaffolding.h"
+
+extern struct gui_window_table *nsgtk_window_table;
+
+typedef enum nsgtk_window_signals {
+ NSGTK_WINDOW_SIGNAL_CLICK,
+ NSGTK_WINDOW_SIGNAL_REDRAW,
+ NSGTK_WINDOW_SIGNAL_COUNT
+} nsgtk_window_signal;
+
+extern struct gui_window *window_list;
+extern int temp_open_background;
+
+struct browser_window *nsgtk_get_browser_window(struct gui_window *g);
+struct nsgtk_scaffolding *nsgtk_get_scaffold(struct gui_window *g);
+GdkPixbuf *nsgtk_get_icon(struct gui_window *gw);
+void nsgtk_reflow_all_windows(void);
+float nsgtk_get_scale_for_gui(struct gui_window *g);
+int nsgtk_gui_window_update_targets(struct gui_window *g);
+void nsgtk_window_destroy_browser(struct gui_window *g);
+unsigned long nsgtk_window_get_signalhandler(struct gui_window *g, int i);
+GtkLayout *nsgtk_window_get_layout(struct gui_window *g);
+struct gui_window *nsgtk_window_iterate(struct gui_window *g);
+GtkWidget *nsgtk_window_get_tab(struct gui_window *g);
+void nsgtk_window_set_tab(struct gui_window *g, GtkWidget *w);
+
+#endif /* NETSURF_GTK_WINDOW_H */