netsurf: branch master updated. release/3.1-363-g58eea87

NetSurf Browser Project (Commit Mailer) no-reply at netsurf-browser.org
Fri Aug 1 01:01:03 BST 2014


Gitweb links:

...log http://git.netsurf-browser.org/netsurf.git/shortlog/58eea873f875b58050a01478b4aaed0d75135e9a
...commit http://git.netsurf-browser.org/netsurf.git/commit/58eea873f875b58050a01478b4aaed0d75135e9a
...tree http://git.netsurf-browser.org/netsurf.git/tree/58eea873f875b58050a01478b4aaed0d75135e9a

The branch, master has been updated
       via  58eea873f875b58050a01478b4aaed0d75135e9a (commit)
      from  43fea75b7290cdcbfafa8e159dc3832581352755 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commitdiff http://git.netsurf-browser.org/netsurf.git/commit/?id=58eea873f875b58050a01478b4aaed0d75135e9a
commit 58eea873f875b58050a01478b4aaed0d75135e9a
Author: Vincent Sanders <vince at kyllikki.org>
Commit: Vincent Sanders <vince at kyllikki.org>

    rationalise source view

diff --git a/gtk/Makefile.target b/gtk/Makefile.target
index ec19d1b..f69a73a 100644
--- a/gtk/Makefile.target
+++ b/gtk/Makefile.target
@@ -110,8 +110,8 @@ S_GTK := font_pango.c bitmap.c gui.c schedule.c thumbnail.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 theme.c toolbar.c gettext.c		\
-	compat.c cookies.c hotlist.c 					\
-	$(addprefix dialogs/,preferences.c about.c source.c)
+	compat.c cookies.c hotlist.c viewdata.c viewsource.c		\
+	$(addprefix dialogs/,preferences.c about.c)
 
 S_GTK := $(addprefix gtk/,$(S_GTK)) $(addprefix utils/,container.c)
 # code in utils/container.ch is non-universal it seems
diff --git a/gtk/dialogs/source.c b/gtk/dialogs/source.c
deleted file mode 100644
index 8e4e433..0000000
--- a/gtk/dialogs/source.c
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * Copyright 2009 Mark Benjamin <MarkBenjamin at 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 <gtk/gtk.h>
-
-#include "utils/log.h"
-#include "utils/nsoption.h"
-#include "utils/utf8.h"
-#include "utils/messages.h"
-#include "utils/url.h"
-#include "utils/utils.h"
-#include "utils/file.h"
-#include "desktop/netsurf.h"
-#include "desktop/browser.h"
-#include "render/html.h"
-#include "content/hlcache.h"
-#include "content/content.h"
-
-#include "gtk/dialogs/about.h"
-#include "gtk/fetch.h"
-#include "gtk/compat.h"
-#include "gtk/gui.h"
-#include "gtk/dialogs/source.h"
-
-struct nsgtk_source_window {
-	gchar *url;
-	char *data;
-	size_t data_len;
-	GtkWindow *sourcewindow;
-	GtkTextView *gv;
-	struct browser_window *bw;
-	struct nsgtk_source_window *next;
-	struct nsgtk_source_window *prev;
-};
-
-struct menu_events {
-	const char *widget;
-	GCallback handler;
-};
-
-static GtkBuilder *glade_File;
-static struct nsgtk_source_window *nsgtk_source_list = 0;
-static char source_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(source_save_as);
-MENUPROTO(source_print);
-MENUPROTO(source_close);
-MENUPROTO(source_select_all);
-MENUPROTO(source_cut);
-MENUPROTO(source_copy);
-MENUPROTO(source_paste);
-MENUPROTO(source_delete);
-MENUPROTO(source_zoom_in);
-MENUPROTO(source_zoom_out);
-MENUPROTO(source_zoom_normal);
-MENUPROTO(source_about);
-
-static struct menu_events source_menu_events[] = {
-MENUEVENT(source_save_as),
-MENUEVENT(source_print),
-MENUEVENT(source_close),
-MENUEVENT(source_select_all),
-MENUEVENT(source_cut),
-MENUEVENT(source_copy),
-MENUEVENT(source_paste),
-MENUEVENT(source_delete),
-MENUEVENT(source_zoom_in),
-MENUEVENT(source_zoom_out),
-MENUEVENT(source_zoom_normal),
-MENUEVENT(source_about),
-{NULL, NULL}
-};
-
-static void nsgtk_attach_source_menu_handlers(GtkBuilder *xml, gpointer g)
-{
-	struct menu_events *event = source_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_source_destroy_event(GtkBuilder *window, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) g;
-
-	if (nsg->next != NULL)
-		nsg->next->prev = nsg->prev;
-
-	if (nsg->prev != NULL)
-		nsg->prev->next = nsg->next;
-	else
-		nsgtk_source_list = nsg->next;
-
-	free(nsg->data);
-	free(nsg->url);
-	free(g);
-
-	return FALSE;
-}
-
-static gboolean nsgtk_source_delete_event(GtkWindow * window, gpointer g)
-{
-	return FALSE;
-}
-
-
-static nserror
-nsgtk_source_win_init(GtkWindow *parent, struct browser_window *bw)
-{
-	char glade_Location[strlen(res_dir_location) + SLEN("source.gtk2.ui") + 1];
-
-	struct hlcache_handle *hlcontent;
-	GError* error = NULL;
-	const char *source_data;
-	unsigned long source_size;
-	char *data = NULL;
-	size_t data_len;
-	nserror ret;
-	GtkWindow *wndSource;
-	GtkWidget *cutbutton;
-	GtkWidget *pastebutton;
-	GtkWidget *deletebutton;
-	GtkWidget *printbutton;
-	GtkTextView *sourceview;
-	PangoFontDescription *fontdesc;
-	GtkTextBuffer *tb;
-	struct nsgtk_source_window *thiswindow;
-
-	hlcontent = browser_window_get_content(bw);
-	if (hlcontent == NULL) {
-		return NSERROR_OK;
-	}
-
-	if (content_get_type(hlcontent) != CONTENT_HTML) {
-		return NSERROR_OK;
-	}
-
-	sprintf(glade_Location, "%ssource.gtk2.ui", res_dir_location);
-
-	glade_File = gtk_builder_new();
-	if (!gtk_builder_add_from_file(glade_File, glade_Location, &error)) {
-		g_warning ("Couldn't load builder file: %s", error->message);
-		g_error_free (error);
-		LOG(("error loading glade tree"));
-		return NSERROR_OK;
-	}
-
-	source_data = content_get_source_data(hlcontent, &source_size);
-
-	ret = utf8_from_enc(source_data,
-			    html_get_encoding(hlcontent),
-			    source_size,
-			    &data,
-			    &data_len);
-	if (ret != NSERROR_OK) {
-		return ret;
-	}
-
-	wndSource = GTK_WINDOW(gtk_builder_get_object(glade_File, "wndSource"));
-	cutbutton = GTK_WIDGET(gtk_builder_get_object(glade_File, "source_cut"));
-	pastebutton = GTK_WIDGET(gtk_builder_get_object(glade_File, "source_paste"));
-	deletebutton = GTK_WIDGET(gtk_builder_get_object(glade_File, "source_delete"));
-	printbutton = GTK_WIDGET(gtk_builder_get_object(glade_File, "source_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);
-
-	thiswindow = malloc(sizeof(struct nsgtk_source_window));
-	if (thiswindow == NULL) {
-		free(data);
-		return NSERROR_NOMEM;
-	}
-
-	thiswindow->url = strdup(nsurl_access(browser_window_get_url(bw)));
-	if (thiswindow->url == NULL) {
-		free(thiswindow);
-		free(data);
-		return NSERROR_NOMEM;
-	}
-
-	thiswindow->data = data;
-	thiswindow->data_len = data_len;
-
-	thiswindow->sourcewindow = wndSource;
-	thiswindow->bw = bw;
-
-	char title[strlen(thiswindow->url) + SLEN("Source of  - NetSurf") + 1];
-	sprintf(title, "Source of %s - NetSurf", thiswindow->url);
-
-	thiswindow->next = nsgtk_source_list;
-	thiswindow->prev = NULL;
-	if (nsgtk_source_list != NULL) {
-		nsgtk_source_list->prev = thiswindow;
-	}
-	nsgtk_source_list = thiswindow;
-
-	nsgtk_attach_source_menu_handlers(glade_File, thiswindow);
-
-	gtk_window_set_title(wndSource, title);
-
-	g_signal_connect(G_OBJECT(wndSource), "destroy",
-			G_CALLBACK(nsgtk_source_destroy_event),
-			thiswindow);
-	g_signal_connect(G_OBJECT(wndSource), "delete-event",
-			G_CALLBACK(nsgtk_source_delete_event),
-			thiswindow);
-
-	sourceview = GTK_TEXT_VIEW(
-			gtk_builder_get_object(glade_File,
-			"source_view"));
-
-	fontdesc = pango_font_description_from_string("Monospace 8");
-
-	thiswindow->gv = sourceview;
-	nsgtk_widget_modify_font(GTK_WIDGET(sourceview), fontdesc);
-
-	tb = gtk_text_view_get_buffer(sourceview);
-	gtk_text_buffer_set_text(tb, thiswindow->data, -1);
-
-	gtk_widget_show(GTK_WIDGET(wndSource));
-
-	return NSERROR_OK;
-}
-
-/**
- * create a new tab with page source
- */
-static nserror
-nsgtk_source_tab_init(GtkWindow *parent, struct browser_window *bw)
-{
-	const char *source_data;
-	unsigned long source_size;
-	char *ndata = NULL;
-	size_t ndata_len;
-	nsurl *url;
-	nserror ret;
-	gchar *filename;
-	gint handle;
-	struct hlcache_handle *hlcontent;
-	FILE *f;
-
-	hlcontent = browser_window_get_content(bw);
-	if (hlcontent == NULL) {
-		return NSERROR_OK;
-	}
-
-	if (content_get_type(hlcontent) != CONTENT_HTML) {
-		return NSERROR_OK;
-	}
-
-	source_data = content_get_source_data(hlcontent, &source_size);
-
-	ret = utf8_from_enc(source_data,
-			  html_get_encoding(hlcontent),
-			  source_size,
-			  &ndata,
-			  &ndata_len);
-	if (ret != NSERROR_OK) {
-		return ret;
-	}
-
-	handle = g_file_open_tmp("nsgtksourceXXXXXX", &filename, NULL);
-	if ((handle == -1) || (filename == NULL)) {
-		warn_user(messages_get("gtkSourceTabError"), 0);
-		free(ndata);
-		return NSERROR_SAVE_FAILED;
-	}
-	close(handle); /* in case it was binary mode */
-
-	f = fopen(filename, "w");
-	if (f == NULL) {
-		warn_user(messages_get("gtkSourceTabError"), 0);
-		g_free(filename);
-		free(ndata);
-		return NSERROR_SAVE_FAILED;
-	}
-	fprintf(f, "%s", ndata);
-	fclose(f);
-	free(ndata);
-
-	/* Open tab */
-	ret = netsurf_path_to_nsurl(filename, &url);
-	g_free(filename);
-	if (ret == NSERROR_OK) {
-		ret = browser_window_create(BW_CREATE_TAB,
-					    url, NULL, NULL, NULL);
-		nsurl_unref(url);
-	}
-
-	return ret;
-}
-
-void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw)
-{
-	nserror ret;
-
-	if (nsoption_bool(source_tab)) {
-		ret = nsgtk_source_tab_init(parent, bw);
-	} else {
-		ret = nsgtk_source_win_init(parent, bw);
-	}
-	if (ret != NSERROR_OK) {
-		warn_user(messages_get_errorcode(ret), 0);
-	}
-}
-
-static void nsgtk_source_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, GTK_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_source_save_as_activate(GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) g;
-	GtkWidget *fc = gtk_file_chooser_dialog_new(
-			messages_get("gtkSourceSave"),
-			nsg->sourcewindow,
-			GTK_FILE_CHOOSER_ACTION_SAVE,
-			GTK_STOCK_CANCEL,
-			GTK_RESPONSE_CANCEL,
-			GTK_STOCK_SAVE,
-			GTK_RESPONSE_ACCEPT,
-			NULL);
-	char *filename;
-	nserror res;
-
-	res = url_nice(nsg->url, &filename, false);
-	if (res != NSERROR_OK) {
-		filename = strdup(messages_get("SaveSource"));
-		if (filename == NULL) {
-			warn_user("NoMemory", 0);
-			return FALSE;
-		}
-	}
-
-	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), filename);
-
-	free(filename);
-
-	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
-						       TRUE);
-
-	if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) {
-		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
-		nsgtk_source_file_save(nsg->sourcewindow, filename, nsg->data, nsg->data_len);
-		g_free(filename);
-	}
-
-	gtk_widget_destroy(fc);
-
-	return TRUE;
-}
-
-
-gboolean nsgtk_on_source_print_activate( GtkMenuItem *widget, gpointer g)
-{
-	/* correct printing */
-
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_close_activate( GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) g;
-
-	gtk_widget_destroy(GTK_WIDGET(nsg->sourcewindow));
-
-	return TRUE;
-}
-
-
-
-gboolean nsgtk_on_source_select_all_activate (GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) 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_source_cut_activate(GtkMenuItem *widget, gpointer g)
-{
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_copy_activate(GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) 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_source_paste_activate(GtkMenuItem *widget, gpointer g)
-{
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_delete_activate(GtkMenuItem *widget, gpointer g)
-{
-	return TRUE;
-}
-
-static void nsgtk_source_update_zoomlevel(gpointer g)
-{
-	struct nsgtk_source_window *nsg;
-	GtkTextBuffer *buf;
-	GtkTextTagTable *tab;
-	GtkTextTag *tag;
-
-	nsg = nsgtk_source_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) source_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_source_zoom_in_activate(GtkMenuItem *widget, gpointer g)
-{
-	source_zoomlevel++;
-	nsgtk_source_update_zoomlevel(g);
-
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_zoom_out_activate(GtkMenuItem *widget, gpointer g)
-{
-	if (source_zoomlevel > 1) {
-		source_zoomlevel--;
-		nsgtk_source_update_zoomlevel(g);
-	}
-
-	return TRUE;
-}
-
-
-gboolean nsgtk_on_source_zoom_normal_activate(GtkMenuItem *widget, gpointer g)
-{
-	source_zoomlevel = 10;
-	nsgtk_source_update_zoomlevel(g);
-
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_about_activate(GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) g;
-
-	nsgtk_about_dialog_init(nsg->sourcewindow, netsurf_version);
-
-	return TRUE;
-}
diff --git a/gtk/dialogs/source.h b/gtk/dialogs/source.h
deleted file mode 100644
index fcba6b6..0000000
--- a/gtk/dialogs/source.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2009 Mark Benjamin <netsurfbrowser.org.MarkBenjamin at 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_dialogs_source_h_
-#define netsurf_gtk_dialogs_source_h_
-
-#include <gtk/gtk.h>
-#include "desktop/browser.h"
-
-void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw);
-
-#endif
-
diff --git a/gtk/gui.c b/gtk/gui.c
index 7a8c321..4d28483 100644
--- a/gtk/gui.c
+++ b/gtk/gui.c
@@ -208,6 +208,11 @@ nsgtk_new_ui(char **respath, const char *name, GtkBuilder **pglade)
 
 	if (pglade != NULL) {
 		*pglade = builder;
+	} else {
+		/* release our reference to the builder if it is not
+		 * being used.
+		 */
+		g_object_unref(G_OBJECT(builder));
 	}
 
 	return filepath;
@@ -237,6 +242,7 @@ nsgtk_init_glade(char **respath)
 	glade_file_location->options = nsgtk_new_ui(respath, "options", NULL);
 	glade_file_location->hotlist = nsgtk_new_ui(respath, "hotlist", NULL);
 	glade_file_location->cookies = nsgtk_new_ui(respath, "cookies", NULL);
+	glade_file_location->viewdata = nsgtk_new_ui(respath, "viewdata", NULL);
 
 	glade_file_location->warning = nsgtk_new_ui(respath, "warning", &gladeWarning);
 	nsgtk_warning_window = GTK_WINDOW(gtk_builder_get_object(gladeWarning, "wndWarning"));
diff --git a/gtk/gui.h b/gtk/gui.h
index a9a98c6..32f864f 100644
--- a/gtk/gui.h
+++ b/gtk/gui.h
@@ -45,6 +45,7 @@ struct glade_file_location_s {
 	char *history;
 	char *hotlist;
 	char *cookies;
+	char *viewdata;
 };
 
 /** location of all glade files. */
diff --git a/gtk/options.h b/gtk/options.h
index 612809e..cdedbc6 100644
--- a/gtk/options.h
+++ b/gtk/options.h
@@ -68,6 +68,9 @@ NSOPTION_STRING(hotlist_path, NULL)
 /* open source views in a tab */
 NSOPTION_BOOL(source_tab, false)
 
+/* Developer information viewer display method */
+NSOPTION_INTEGER(developer_view, 0)
+
 /* currently selected theme */
 NSOPTION_INTEGER(current_theme, 0)
 
diff --git a/gtk/res/source.gtk2.ui b/gtk/res/source.gtk2.ui
deleted file mode 100644
index 84c3e0c..0000000
--- a/gtk/res/source.gtk2.ui
+++ /dev/null
@@ -1,204 +0,0 @@
-<?xml version="1.0"?>
-<interface>
-  <!-- interface-requires gtk+ 2.12 -->
-  <!-- interface-naming-policy toplevel-contextual -->
-  <object class="GtkWindow" id="wndSource">
-    <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="source_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="source_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="source_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="source_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="source_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="source_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="source_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="source_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="source_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="source_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="source_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="source_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="source_view">
-                <property name="width_request">600</property>
-                <property name="height_request">400</property>
-                <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/gtk/res/source.gtk3.ui b/gtk/res/source.gtk3.ui
deleted file mode 100644
index b972315..0000000
--- a/gtk/res/source.gtk3.ui
+++ /dev/null
@@ -1,179 +0,0 @@
-<?xml version="1.0"?>
-<!--Generated with glade3 3.4.5 on Mon Apr 20 21:28:45 2009 -->
-<interface>
-  <object class="GtkUIManager" id="uimanager1">
-    <child>
-      <object class="GtkActionGroup" id="actiongroup1">
-        <child>
-          <object class="GtkAction" id="menuitem1">
-            <property name="name">menuitem1</property>
-            <property name="label" translatable="yes">_File</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_save_as">
-            <property name="stock_id" translatable="yes">gtk-save-as</property>
-            <property name="name">source_save_as</property>
-          </object>
-          <accelerator key="S" modifiers="GDK_CONTROL_MASK"/>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_print">
-            <property name="stock_id" translatable="yes">gtk-print</property>
-            <property name="name">source_print</property>
-          </object>
-          <accelerator key="P" modifiers="GDK_CONTROL_MASK"/>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_close">
-            <property name="stock_id" translatable="yes">gtk-close</property>
-            <property name="name">source_close</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkAction" id="menuitem2">
-            <property name="name">menuitem2</property>
-            <property name="label" translatable="yes">_Edit</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_select_all">
-            <property name="stock_id" translatable="yes">gtk-select-all</property>
-            <property name="name">source_select_all</property>
-          </object>
-          <accelerator key="A" modifiers="GDK_CONTROL_MASK"/>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_cut">
-            <property name="stock_id" translatable="yes">gtk-cut</property>
-            <property name="name">source_cut</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_copy">
-            <property name="stock_id" translatable="yes">gtk-copy</property>
-            <property name="name">source_copy</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_paste">
-            <property name="stock_id" translatable="yes">gtk-paste</property>
-            <property name="name">source_paste</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_delete">
-            <property name="stock_id" translatable="yes">gtk-delete</property>
-            <property name="name">source_delete</property>
-          </object>
-          <accelerator key="Delete" modifiers=""/>
-        </child>
-        <child>
-          <object class="GtkAction" id="menuitem3">
-            <property name="name">menuitem3</property>
-            <property name="label" translatable="yes">_View</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_zoom_in">
-            <property name="stock_id" translatable="yes">gtk-zoom-in</property>
-            <property name="name">source_zoom_in</property>
-          </object>
-          <accelerator key="plus" modifiers="GDK_CONTROL_MASK"/>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_zoom_out">
-            <property name="stock_id" translatable="yes">gtk-zoom-out</property>
-            <property name="name">source_zoom_out</property>
-          </object>
-          <accelerator key="minus" modifiers="GDK_CONTROL_MASK"/>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_zoom_normal">
-            <property name="stock_id" translatable="yes">gtk-zoom-100</property>
-            <property name="name">source_zoom_normal</property>
-          </object>
-          <accelerator key="0" modifiers="GDK_CONTROL_MASK"/>
-        </child>
-        <child>
-          <object class="GtkAction" id="menuitem4">
-            <property name="name">menuitem4</property>
-            <property name="label" translatable="yes">_Help</property>
-          </object>
-        </child>
-        <child>
-          <object class="GtkAction" id="source_about">
-            <property name="stock_id" translatable="yes">gtk-about</property>
-            <property name="name">source_about</property>
-          </object>
-        </child>
-      </object>
-    </child>
-    <ui>
-      <menubar name="menubar1">
-        <menu action="menuitem1">
-          <menuitem action="source_save_as"/>
-          <menuitem action="source_print"/>
-          <separator/>
-          <menuitem action="source_close"/>
-        </menu>
-        <menu action="menuitem2">
-          <menuitem action="source_select_all"/>
-          <menuitem action="source_cut"/>
-          <menuitem action="source_copy"/>
-          <menuitem action="source_paste"/>
-          <menuitem action="source_delete"/>
-        </menu>
-        <menu action="menuitem3">
-          <menuitem action="source_zoom_in"/>
-          <menuitem action="source_zoom_out"/>
-          <menuitem action="source_zoom_normal"/>
-        </menu>
-        <menu action="menuitem4">
-          <menuitem action="source_about"/>
-        </menu>
-      </menubar>
-    </ui>
-  </object>
-  <object class="GtkWindow" id="wndSource">
-    <child>
-      <object class="GtkVBox" id="vbox1">
-        <property name="visible">True</property>
-        <child>
-          <object class="GtkMenuBar" constructor="uimanager1" id="menubar1">
-            <property name="visible">True</property>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkScrolledWindow" id="sourcescrolled">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-            <child>
-              <object class="GtkTextView" id="source_view">
-                <property name="width_request">600</property>
-                <property name="height_request">400</property>
-                <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">GTK_WRAP_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/gtk/res/viewdata.gtk2.ui b/gtk/res/viewdata.gtk2.ui
new file mode 100644
index 0000000..c545454
--- /dev/null
+++ b/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">
+    <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="width_request">600</property>
+                <property name="height_request">400</property>
+                <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/gtk/res/viewdata.gtk3.ui b/gtk/res/viewdata.gtk3.ui
new file mode 100644
index 0000000..e06aaf3
--- /dev/null
+++ b/gtk/res/viewdata.gtk3.ui
@@ -0,0 +1,179 @@
+<?xml version="1.0"?>
+<!--Generated with glade3 3.4.5 on Mon Apr 20 21:28:45 2009 -->
+<interface>
+  <object class="GtkUIManager" id="uimanager1">
+    <child>
+      <object class="GtkActionGroup" id="actiongroup1">
+        <child>
+          <object class="GtkAction" id="menuitem1">
+            <property name="name">menuitem1</property>
+            <property name="label" translatable="yes">_File</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkAction" id="source_save_as">
+            <property name="stock_id" translatable="yes">gtk-save-as</property>
+            <property name="name">source_save_as</property>
+          </object>
+          <accelerator key="S" modifiers="GDK_CONTROL_MASK"/>
+        </child>
+        <child>
+          <object class="GtkAction" id="source_print">
+            <property name="stock_id" translatable="yes">gtk-print</property>
+            <property name="name">source_print</property>
+          </object>
+          <accelerator key="P" modifiers="GDK_CONTROL_MASK"/>
+        </child>
+        <child>
+          <object class="GtkAction" id="source_close">
+            <property name="stock_id" translatable="yes">gtk-close</property>
+            <property name="name">source_close</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkAction" id="menuitem2">
+            <property name="name">menuitem2</property>
+            <property name="label" translatable="yes">_Edit</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkAction" id="viewdata_select_all">
+            <property name="stock_id" translatable="yes">gtk-select-all</property>
+            <property name="name">viewdata_select_all</property>
+          </object>
+          <accelerator key="A" modifiers="GDK_CONTROL_MASK"/>
+        </child>
+        <child>
+          <object class="GtkAction" id="viewdata_cut">
+            <property name="stock_id" translatable="yes">gtk-cut</property>
+            <property name="name">viewdata_cut</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkAction" id="viewdata_copy">
+            <property name="stock_id" translatable="yes">gtk-copy</property>
+            <property name="name">viewdata_copy</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkAction" id="viewdata_paste">
+            <property name="stock_id" translatable="yes">gtk-paste</property>
+            <property name="name">viewdata_paste</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkAction" id="viewdata_delete">
+            <property name="stock_id" translatable="yes">gtk-delete</property>
+            <property name="name">viewdata_delete</property>
+          </object>
+          <accelerator key="Delete" modifiers=""/>
+        </child>
+        <child>
+          <object class="GtkAction" id="menuitem3">
+            <property name="name">menuitem3</property>
+            <property name="label" translatable="yes">_View</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkAction" id="viewdata_zoom_in">
+            <property name="stock_id" translatable="yes">gtk-zoom-in</property>
+            <property name="name">viewdata_zoom_in</property>
+          </object>
+          <accelerator key="plus" modifiers="GDK_CONTROL_MASK"/>
+        </child>
+        <child>
+          <object class="GtkAction" id="viewdata_zoom_out">
+            <property name="stock_id" translatable="yes">gtk-zoom-out</property>
+            <property name="name">viewdata_zoom_out</property>
+          </object>
+          <accelerator key="minus" modifiers="GDK_CONTROL_MASK"/>
+        </child>
+        <child>
+          <object class="GtkAction" id="viewdata_zoom_normal">
+            <property name="stock_id" translatable="yes">gtk-zoom-100</property>
+            <property name="name">viewdata_zoom_normal</property>
+          </object>
+          <accelerator key="0" modifiers="GDK_CONTROL_MASK"/>
+        </child>
+        <child>
+          <object class="GtkAction" id="menuitem4">
+            <property name="name">menuitem4</property>
+            <property name="label" translatable="yes">_Help</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkAction" id="viewdata_about">
+            <property name="stock_id" translatable="yes">gtk-about</property>
+            <property name="name">viewdata_about</property>
+          </object>
+        </child>
+      </object>
+    </child>
+    <ui>
+      <menubar name="menubar1">
+        <menu action="menuitem1">
+          <menuitem action="viewdata_save_as"/>
+          <menuitem action="viewdata_print"/>
+          <separator/>
+          <menuitem action="viewdata_close"/>
+        </menu>
+        <menu action="menuitem2">
+          <menuitem action="viewdata_select_all"/>
+          <menuitem action="viewdata_cut"/>
+          <menuitem action="viewdata_copy"/>
+          <menuitem action="viewdata_paste"/>
+          <menuitem action="viewdata_delete"/>
+        </menu>
+        <menu action="menuitem3">
+          <menuitem action="viewdata_zoom_in"/>
+          <menuitem action="viewdata_zoom_out"/>
+          <menuitem action="viewdata_zoom_normal"/>
+        </menu>
+        <menu action="menuitem4">
+          <menuitem action="viewdata_about"/>
+        </menu>
+      </menubar>
+    </ui>
+  </object>
+  <object class="GtkWindow" id="ViewDataWindow">
+    <child>
+      <object class="GtkVBox" id="vbox1">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkMenuBar" constructor="uimanager1" id="menubar1">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="sourcescrolled">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+            <child>
+              <object class="GtkTextView" id="viewdata_view">
+                <property name="width_request">600</property>
+                <property name="height_request">400</property>
+                <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">GTK_WRAP_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/gtk/scaffolding.c b/gtk/scaffolding.c
index 77c8ddc..0e6e8bc 100644
--- a/gtk/scaffolding.c
+++ b/gtk/scaffolding.c
@@ -54,7 +54,7 @@
 #include "gtk/completion.h"
 #include "gtk/dialogs/preferences.h"
 #include "gtk/dialogs/about.h"
-#include "gtk/dialogs/source.h"
+#include "gtk/viewsource.h"
 #include "gtk/bitmap.h"
 #include "gtk/gui.h"
 #include "gtk/history.h"
@@ -1174,8 +1174,7 @@ MULTIHANDLER(fullscreen)
 
 MULTIHANDLER(viewsource)
 {
-	nsgtk_source_dialog_init(g->window,
-			nsgtk_get_browser_window(g->top_level));
+	nsgtk_viewsource(g->window, nsgtk_get_browser_window(g->top_level));
 	return TRUE;
 }
 
diff --git a/gtk/viewdata.c b/gtk/viewdata.c
new file mode 100644
index 0000000..cb6e7c8
--- /dev/null
+++ b/gtk/viewdata.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince at 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 <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "utils/messages.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+#include "utils/file.h"
+#include "desktop/netsurf.h"
+#include "desktop/browser.h"
+#include "render/html.h"
+#include "content/hlcache.h"
+#include "content/content.h"
+
+#include "gtk/dialogs/about.h"
+#include "gtk/fetch.h"
+#include "gtk/compat.h"
+#include "gtk/gui.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, GTK_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,
+					 GTK_STOCK_CANCEL,
+					 GTK_RESPONSE_CANCEL,
+					 GTK_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, netsurf_version);
+
+	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)
+{
+	GError* error = NULL;
+	GtkWindow *window;
+	GtkWidget *cutbutton;
+	GtkWidget *pastebutton;
+	GtkWidget *deletebutton;
+	GtkWidget *printbutton;
+	GtkTextView *dataview;
+	PangoFontDescription *fontdesc;
+	GtkTextBuffer *tb;
+	struct nsgtk_viewdata_ctx *newctx;
+
+	newctx = malloc(sizeof(struct nsgtk_viewdata_ctx));
+	if (newctx == NULL) {
+		return NSERROR_NOMEM;
+	}
+
+	newctx->builder = gtk_builder_new();
+	if (newctx->builder == NULL) {
+		free(newctx);
+		return NSERROR_INIT_FAILED;
+	}
+
+	if (!gtk_builder_add_from_file(newctx->builder,
+				       glade_file_location->viewdata,
+				       &error)) {
+		LOG(("Couldn't load builder file: %s", error->message));
+		g_error_free(error);
+		free(newctx);
+		return NSERROR_INIT_FAILED;
+	}
+
+
+	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;
+}
+
+/**
+ * create a new tab with page source
+ */
+static nserror
+tab_init(const char *title,
+	 const char *filename,
+	 char *ndata,
+	 size_t ndata_len)
+{
+	nsurl *url;
+	nserror ret;
+	gchar *fname;
+	gint handle;
+	FILE *f;
+
+	handle = g_file_open_tmp("nsgtksourceXXXXXX", &fname, NULL);
+	if ((handle == -1) || (fname == NULL)) {
+		return NSERROR_SAVE_FAILED;
+	}
+	close(handle); /* in case it was binary mode */
+
+	/* save data to temportary file */
+	f = fopen(fname, "w");
+	if (f == NULL) {
+		warn_user(messages_get("gtkSourceTabError"), 0);
+		g_free(fname);
+		return NSERROR_SAVE_FAILED;
+	}
+	fprintf(f, "%s", ndata);
+	fclose(f);
+
+	/* Open tab on temporary file */
+	ret = netsurf_path_to_nsurl(fname, &url);
+	g_free(fname);
+	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;
+	}
+
+	free(ndata);
+
+	return NSERROR_OK;
+}
+
+/**
+ * create a new tab with page source
+ */
+static nserror
+editor_init(const char *title,
+	    const char *filename,
+	    char *ndata,
+	    size_t ndata_len)
+{
+/* find user configured app for opening text/plain */
+
+/*
+ * serach path is ${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/
+ *
+ * search path looking for applications/defaults.list
+ *
+ * look for [Default Applications]
+ * search lines looking like mime/type=Desktop
+ *
+ * if mimetype is found
+ * use search path with applications/application.desktop
+ *
+ * search desktop file for:
+ * Exec=gedit %U
+ *
+ * execute target app on saved data
+ */
+
+	free(ndata);
+
+	return NSERROR_OK;
+}
+
+/* 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;
+}
diff --git a/gtk/viewdata.h b/gtk/viewdata.h
new file mode 100644
index 0000000..020603d
--- /dev/null
+++ b/gtk/viewdata.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince at 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);
+
+#endif
diff --git a/gtk/viewsource.c b/gtk/viewsource.c
new file mode 100644
index 0000000..cbcbad7
--- /dev/null
+++ b/gtk/viewsource.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2009 Mark Benjamin <MarkBenjamin at 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 <stdbool.h>
+#include <gtk/gtk.h>
+
+#include "utils/nsurl.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+#include "utils/utf8.h"
+#include "utils/messages.h"
+#include "desktop/browser.h"
+#include "content/content.h"
+#include "render/html.h"
+
+#include "gtk/viewdata.h"
+#include "gtk/viewsource.h"
+
+void 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;
+	}
+
+	if (content_get_type(hlcontent) != CONTENT_HTML) {
+		return;
+	}
+
+	source_data = content_get_source_data(hlcontent, &source_size);
+
+	ret = url_nice(nsurl_access(browser_window_get_url(bw)), &filename, false);
+	if (ret != NSERROR_OK) {
+		filename = strdup(messages_get("SaveSource"));
+		if (filename == NULL) {
+			return;
+		}
+	}
+
+	title = malloc(strlen(nsurl_access(browser_window_get_url(bw))) + SLEN("Source of  - NetSurf") + 1);
+	sprintf(title, "Source of %s - NetSurf", nsurl_access(browser_window_get_url(bw)));
+
+	ret = utf8_from_enc(source_data,
+			  html_get_encoding(hlcontent),
+			  source_size,
+			  &ndata,
+			  &ndata_len);
+	if (ret != NSERROR_OK) {
+	free(filename);
+		return;
+	}
+
+	ret = nsgtk_viewdata(title, filename, ndata, ndata_len);
+	free(filename);
+}
diff --git a/gtk/viewsource.h b/gtk/viewsource.h
new file mode 100644
index 0000000..fe85b30
--- /dev/null
+++ b/gtk/viewsource.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince at 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_
+
+void nsgtk_viewsource(GtkWindow *parent, struct browser_window *bw);
+
+#endif
+


-----------------------------------------------------------------------

Summary of changes:
 gtk/Makefile.target                          |    4 +-
 gtk/dialogs/source.c                         |  536 ------------------------
 gtk/dialogs/source.h                         |   28 --
 gtk/gui.c                                    |    6 +
 gtk/gui.h                                    |    1 +
 gtk/options.h                                |    3 +
 gtk/res/{source.gtk2.ui => viewdata.gtk2.ui} |   28 +-
 gtk/res/{source.gtk3.ui => viewdata.gtk3.ui} |   64 ++--
 gtk/scaffolding.c                            |    5 +-
 gtk/viewdata.c                               |  569 ++++++++++++++++++++++++++
 framebuffer/fetch.h => gtk/viewdata.h        |   19 +-
 gtk/viewsource.c                             |   81 ++++
 framebuffer/fetch.h => gtk/viewsource.h      |    8 +-
 13 files changed, 729 insertions(+), 623 deletions(-)
 delete mode 100644 gtk/dialogs/source.c
 delete mode 100644 gtk/dialogs/source.h
 rename gtk/res/{source.gtk2.ui => viewdata.gtk2.ui} (89%)
 rename gtk/res/{source.gtk3.ui => viewdata.gtk3.ui} (75%)
 create mode 100644 gtk/viewdata.c
 copy framebuffer/fetch.h => gtk/viewdata.h (53%)
 create mode 100644 gtk/viewsource.c
 copy framebuffer/fetch.h => gtk/viewsource.h (84%)

diff --git a/gtk/Makefile.target b/gtk/Makefile.target
index ec19d1b..f69a73a 100644
--- a/gtk/Makefile.target
+++ b/gtk/Makefile.target
@@ -110,8 +110,8 @@ S_GTK := font_pango.c bitmap.c gui.c schedule.c thumbnail.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 theme.c toolbar.c gettext.c		\
-	compat.c cookies.c hotlist.c 					\
-	$(addprefix dialogs/,preferences.c about.c source.c)
+	compat.c cookies.c hotlist.c viewdata.c viewsource.c		\
+	$(addprefix dialogs/,preferences.c about.c)
 
 S_GTK := $(addprefix gtk/,$(S_GTK)) $(addprefix utils/,container.c)
 # code in utils/container.ch is non-universal it seems
diff --git a/gtk/dialogs/source.c b/gtk/dialogs/source.c
deleted file mode 100644
index 8e4e433..0000000
--- a/gtk/dialogs/source.c
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * Copyright 2009 Mark Benjamin <MarkBenjamin at 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 <gtk/gtk.h>
-
-#include "utils/log.h"
-#include "utils/nsoption.h"
-#include "utils/utf8.h"
-#include "utils/messages.h"
-#include "utils/url.h"
-#include "utils/utils.h"
-#include "utils/file.h"
-#include "desktop/netsurf.h"
-#include "desktop/browser.h"
-#include "render/html.h"
-#include "content/hlcache.h"
-#include "content/content.h"
-
-#include "gtk/dialogs/about.h"
-#include "gtk/fetch.h"
-#include "gtk/compat.h"
-#include "gtk/gui.h"
-#include "gtk/dialogs/source.h"
-
-struct nsgtk_source_window {
-	gchar *url;
-	char *data;
-	size_t data_len;
-	GtkWindow *sourcewindow;
-	GtkTextView *gv;
-	struct browser_window *bw;
-	struct nsgtk_source_window *next;
-	struct nsgtk_source_window *prev;
-};
-
-struct menu_events {
-	const char *widget;
-	GCallback handler;
-};
-
-static GtkBuilder *glade_File;
-static struct nsgtk_source_window *nsgtk_source_list = 0;
-static char source_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(source_save_as);
-MENUPROTO(source_print);
-MENUPROTO(source_close);
-MENUPROTO(source_select_all);
-MENUPROTO(source_cut);
-MENUPROTO(source_copy);
-MENUPROTO(source_paste);
-MENUPROTO(source_delete);
-MENUPROTO(source_zoom_in);
-MENUPROTO(source_zoom_out);
-MENUPROTO(source_zoom_normal);
-MENUPROTO(source_about);
-
-static struct menu_events source_menu_events[] = {
-MENUEVENT(source_save_as),
-MENUEVENT(source_print),
-MENUEVENT(source_close),
-MENUEVENT(source_select_all),
-MENUEVENT(source_cut),
-MENUEVENT(source_copy),
-MENUEVENT(source_paste),
-MENUEVENT(source_delete),
-MENUEVENT(source_zoom_in),
-MENUEVENT(source_zoom_out),
-MENUEVENT(source_zoom_normal),
-MENUEVENT(source_about),
-{NULL, NULL}
-};
-
-static void nsgtk_attach_source_menu_handlers(GtkBuilder *xml, gpointer g)
-{
-	struct menu_events *event = source_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_source_destroy_event(GtkBuilder *window, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) g;
-
-	if (nsg->next != NULL)
-		nsg->next->prev = nsg->prev;
-
-	if (nsg->prev != NULL)
-		nsg->prev->next = nsg->next;
-	else
-		nsgtk_source_list = nsg->next;
-
-	free(nsg->data);
-	free(nsg->url);
-	free(g);
-
-	return FALSE;
-}
-
-static gboolean nsgtk_source_delete_event(GtkWindow * window, gpointer g)
-{
-	return FALSE;
-}
-
-
-static nserror
-nsgtk_source_win_init(GtkWindow *parent, struct browser_window *bw)
-{
-	char glade_Location[strlen(res_dir_location) + SLEN("source.gtk2.ui") + 1];
-
-	struct hlcache_handle *hlcontent;
-	GError* error = NULL;
-	const char *source_data;
-	unsigned long source_size;
-	char *data = NULL;
-	size_t data_len;
-	nserror ret;
-	GtkWindow *wndSource;
-	GtkWidget *cutbutton;
-	GtkWidget *pastebutton;
-	GtkWidget *deletebutton;
-	GtkWidget *printbutton;
-	GtkTextView *sourceview;
-	PangoFontDescription *fontdesc;
-	GtkTextBuffer *tb;
-	struct nsgtk_source_window *thiswindow;
-
-	hlcontent = browser_window_get_content(bw);
-	if (hlcontent == NULL) {
-		return NSERROR_OK;
-	}
-
-	if (content_get_type(hlcontent) != CONTENT_HTML) {
-		return NSERROR_OK;
-	}
-
-	sprintf(glade_Location, "%ssource.gtk2.ui", res_dir_location);
-
-	glade_File = gtk_builder_new();
-	if (!gtk_builder_add_from_file(glade_File, glade_Location, &error)) {
-		g_warning ("Couldn't load builder file: %s", error->message);
-		g_error_free (error);
-		LOG(("error loading glade tree"));
-		return NSERROR_OK;
-	}
-
-	source_data = content_get_source_data(hlcontent, &source_size);
-
-	ret = utf8_from_enc(source_data,
-			    html_get_encoding(hlcontent),
-			    source_size,
-			    &data,
-			    &data_len);
-	if (ret != NSERROR_OK) {
-		return ret;
-	}
-
-	wndSource = GTK_WINDOW(gtk_builder_get_object(glade_File, "wndSource"));
-	cutbutton = GTK_WIDGET(gtk_builder_get_object(glade_File, "source_cut"));
-	pastebutton = GTK_WIDGET(gtk_builder_get_object(glade_File, "source_paste"));
-	deletebutton = GTK_WIDGET(gtk_builder_get_object(glade_File, "source_delete"));
-	printbutton = GTK_WIDGET(gtk_builder_get_object(glade_File, "source_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);
-
-	thiswindow = malloc(sizeof(struct nsgtk_source_window));
-	if (thiswindow == NULL) {
-		free(data);
-		return NSERROR_NOMEM;
-	}
-
-	thiswindow->url = strdup(nsurl_access(browser_window_get_url(bw)));
-	if (thiswindow->url == NULL) {
-		free(thiswindow);
-		free(data);
-		return NSERROR_NOMEM;
-	}
-
-	thiswindow->data = data;
-	thiswindow->data_len = data_len;
-
-	thiswindow->sourcewindow = wndSource;
-	thiswindow->bw = bw;
-
-	char title[strlen(thiswindow->url) + SLEN("Source of  - NetSurf") + 1];
-	sprintf(title, "Source of %s - NetSurf", thiswindow->url);
-
-	thiswindow->next = nsgtk_source_list;
-	thiswindow->prev = NULL;
-	if (nsgtk_source_list != NULL) {
-		nsgtk_source_list->prev = thiswindow;
-	}
-	nsgtk_source_list = thiswindow;
-
-	nsgtk_attach_source_menu_handlers(glade_File, thiswindow);
-
-	gtk_window_set_title(wndSource, title);
-
-	g_signal_connect(G_OBJECT(wndSource), "destroy",
-			G_CALLBACK(nsgtk_source_destroy_event),
-			thiswindow);
-	g_signal_connect(G_OBJECT(wndSource), "delete-event",
-			G_CALLBACK(nsgtk_source_delete_event),
-			thiswindow);
-
-	sourceview = GTK_TEXT_VIEW(
-			gtk_builder_get_object(glade_File,
-			"source_view"));
-
-	fontdesc = pango_font_description_from_string("Monospace 8");
-
-	thiswindow->gv = sourceview;
-	nsgtk_widget_modify_font(GTK_WIDGET(sourceview), fontdesc);
-
-	tb = gtk_text_view_get_buffer(sourceview);
-	gtk_text_buffer_set_text(tb, thiswindow->data, -1);
-
-	gtk_widget_show(GTK_WIDGET(wndSource));
-
-	return NSERROR_OK;
-}
-
-/**
- * create a new tab with page source
- */
-static nserror
-nsgtk_source_tab_init(GtkWindow *parent, struct browser_window *bw)
-{
-	const char *source_data;
-	unsigned long source_size;
-	char *ndata = NULL;
-	size_t ndata_len;
-	nsurl *url;
-	nserror ret;
-	gchar *filename;
-	gint handle;
-	struct hlcache_handle *hlcontent;
-	FILE *f;
-
-	hlcontent = browser_window_get_content(bw);
-	if (hlcontent == NULL) {
-		return NSERROR_OK;
-	}
-
-	if (content_get_type(hlcontent) != CONTENT_HTML) {
-		return NSERROR_OK;
-	}
-
-	source_data = content_get_source_data(hlcontent, &source_size);
-
-	ret = utf8_from_enc(source_data,
-			  html_get_encoding(hlcontent),
-			  source_size,
-			  &ndata,
-			  &ndata_len);
-	if (ret != NSERROR_OK) {
-		return ret;
-	}
-
-	handle = g_file_open_tmp("nsgtksourceXXXXXX", &filename, NULL);
-	if ((handle == -1) || (filename == NULL)) {
-		warn_user(messages_get("gtkSourceTabError"), 0);
-		free(ndata);
-		return NSERROR_SAVE_FAILED;
-	}
-	close(handle); /* in case it was binary mode */
-
-	f = fopen(filename, "w");
-	if (f == NULL) {
-		warn_user(messages_get("gtkSourceTabError"), 0);
-		g_free(filename);
-		free(ndata);
-		return NSERROR_SAVE_FAILED;
-	}
-	fprintf(f, "%s", ndata);
-	fclose(f);
-	free(ndata);
-
-	/* Open tab */
-	ret = netsurf_path_to_nsurl(filename, &url);
-	g_free(filename);
-	if (ret == NSERROR_OK) {
-		ret = browser_window_create(BW_CREATE_TAB,
-					    url, NULL, NULL, NULL);
-		nsurl_unref(url);
-	}
-
-	return ret;
-}
-
-void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw)
-{
-	nserror ret;
-
-	if (nsoption_bool(source_tab)) {
-		ret = nsgtk_source_tab_init(parent, bw);
-	} else {
-		ret = nsgtk_source_win_init(parent, bw);
-	}
-	if (ret != NSERROR_OK) {
-		warn_user(messages_get_errorcode(ret), 0);
-	}
-}
-
-static void nsgtk_source_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, GTK_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_source_save_as_activate(GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) g;
-	GtkWidget *fc = gtk_file_chooser_dialog_new(
-			messages_get("gtkSourceSave"),
-			nsg->sourcewindow,
-			GTK_FILE_CHOOSER_ACTION_SAVE,
-			GTK_STOCK_CANCEL,
-			GTK_RESPONSE_CANCEL,
-			GTK_STOCK_SAVE,
-			GTK_RESPONSE_ACCEPT,
-			NULL);
-	char *filename;
-	nserror res;
-
-	res = url_nice(nsg->url, &filename, false);
-	if (res != NSERROR_OK) {
-		filename = strdup(messages_get("SaveSource"));
-		if (filename == NULL) {
-			warn_user("NoMemory", 0);
-			return FALSE;
-		}
-	}
-
-	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), filename);
-
-	free(filename);
-
-	gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(fc),
-						       TRUE);
-
-	if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT) {
-		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
-		nsgtk_source_file_save(nsg->sourcewindow, filename, nsg->data, nsg->data_len);
-		g_free(filename);
-	}
-
-	gtk_widget_destroy(fc);
-
-	return TRUE;
-}
-
-
-gboolean nsgtk_on_source_print_activate( GtkMenuItem *widget, gpointer g)
-{
-	/* correct printing */
-
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_close_activate( GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) g;
-
-	gtk_widget_destroy(GTK_WIDGET(nsg->sourcewindow));
-
-	return TRUE;
-}
-
-
-
-gboolean nsgtk_on_source_select_all_activate (GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) 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_source_cut_activate(GtkMenuItem *widget, gpointer g)
-{
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_copy_activate(GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) 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_source_paste_activate(GtkMenuItem *widget, gpointer g)
-{
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_delete_activate(GtkMenuItem *widget, gpointer g)
-{
-	return TRUE;
-}
-
-static void nsgtk_source_update_zoomlevel(gpointer g)
-{
-	struct nsgtk_source_window *nsg;
-	GtkTextBuffer *buf;
-	GtkTextTagTable *tab;
-	GtkTextTag *tag;
-
-	nsg = nsgtk_source_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) source_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_source_zoom_in_activate(GtkMenuItem *widget, gpointer g)
-{
-	source_zoomlevel++;
-	nsgtk_source_update_zoomlevel(g);
-
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_zoom_out_activate(GtkMenuItem *widget, gpointer g)
-{
-	if (source_zoomlevel > 1) {
-		source_zoomlevel--;
-		nsgtk_source_update_zoomlevel(g);
-	}
-
-	return TRUE;
-}
-
-
-gboolean nsgtk_on_source_zoom_normal_activate(GtkMenuItem *widget, gpointer g)
-{
-	source_zoomlevel = 10;
-	nsgtk_source_update_zoomlevel(g);
-
-	return TRUE;
-}
-
-gboolean nsgtk_on_source_about_activate(GtkMenuItem *widget, gpointer g)
-{
-	struct nsgtk_source_window *nsg = (struct nsgtk_source_window *) g;
-
-	nsgtk_about_dialog_init(nsg->sourcewindow, netsurf_version);
-
-	return TRUE;
-}
diff --git a/gtk/dialogs/source.h b/gtk/dialogs/source.h
deleted file mode 100644
index fcba6b6..0000000
--- a/gtk/dialogs/source.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2009 Mark Benjamin <netsurfbrowser.org.MarkBenjamin at 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_dialogs_source_h_
-#define netsurf_gtk_dialogs_source_h_
-
-#include <gtk/gtk.h>
-#include "desktop/browser.h"
-
-void nsgtk_source_dialog_init(GtkWindow *parent, struct browser_window *bw);
-
-#endif
-
diff --git a/gtk/gui.c b/gtk/gui.c
index 7a8c321..4d28483 100644
--- a/gtk/gui.c
+++ b/gtk/gui.c
@@ -208,6 +208,11 @@ nsgtk_new_ui(char **respath, const char *name, GtkBuilder **pglade)
 
 	if (pglade != NULL) {
 		*pglade = builder;
+	} else {
+		/* release our reference to the builder if it is not
+		 * being used.
+		 */
+		g_object_unref(G_OBJECT(builder));
 	}
 
 	return filepath;
@@ -237,6 +242,7 @@ nsgtk_init_glade(char **respath)
 	glade_file_location->options = nsgtk_new_ui(respath, "options", NULL);
 	glade_file_location->hotlist = nsgtk_new_ui(respath, "hotlist", NULL);
 	glade_file_location->cookies = nsgtk_new_ui(respath, "cookies", NULL);
+	glade_file_location->viewdata = nsgtk_new_ui(respath, "viewdata", NULL);
 
 	glade_file_location->warning = nsgtk_new_ui(respath, "warning", &gladeWarning);
 	nsgtk_warning_window = GTK_WINDOW(gtk_builder_get_object(gladeWarning, "wndWarning"));
diff --git a/gtk/gui.h b/gtk/gui.h
index a9a98c6..32f864f 100644
--- a/gtk/gui.h
+++ b/gtk/gui.h
@@ -45,6 +45,7 @@ struct glade_file_location_s {
 	char *history;
 	char *hotlist;
 	char *cookies;
+	char *viewdata;
 };
 
 /** location of all glade files. */
diff --git a/gtk/options.h b/gtk/options.h
index 612809e..cdedbc6 100644
--- a/gtk/options.h
+++ b/gtk/options.h
@@ -68,6 +68,9 @@ NSOPTION_STRING(hotlist_path, NULL)
 /* open source views in a tab */
 NSOPTION_BOOL(source_tab, false)
 
+/* Developer information viewer display method */
+NSOPTION_INTEGER(developer_view, 0)
+
 /* currently selected theme */
 NSOPTION_INTEGER(current_theme, 0)
 
diff --git a/gtk/res/source.gtk2.ui b/gtk/res/viewdata.gtk2.ui
similarity index 89%
rename from gtk/res/source.gtk2.ui
rename to gtk/res/viewdata.gtk2.ui
index 84c3e0c..c545454 100644
--- a/gtk/res/source.gtk2.ui
+++ b/gtk/res/viewdata.gtk2.ui
@@ -2,7 +2,7 @@
 <interface>
   <!-- interface-requires gtk+ 2.12 -->
   <!-- interface-naming-policy toplevel-contextual -->
-  <object class="GtkWindow" id="wndSource">
+  <object class="GtkWindow" id="ViewDataWindow">
     <child>
       <object class="GtkVBox" id="vbox1">
         <property name="visible">True</property>
@@ -19,7 +19,7 @@
                   <object class="GtkMenu" id="menu1">
                     <property name="visible">True</property>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_save_as">
+                      <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>
@@ -28,7 +28,7 @@
                       </object>
                     </child>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_print">
+                      <object class="GtkImageMenuItem" id="viewdata_print">
                         <property name="label">gtk-print</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -42,7 +42,7 @@
                       </object>
                     </child>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_close">
+                      <object class="GtkImageMenuItem" id="viewdata_close">
                         <property name="label">gtk-close</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -62,7 +62,7 @@
                   <object class="GtkMenu" id="menu2">
                     <property name="visible">True</property>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_select_all">
+                      <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>
@@ -71,7 +71,7 @@
                       </object>
                     </child>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_cut">
+                      <object class="GtkImageMenuItem" id="viewdata_cut">
                         <property name="label">gtk-cut</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -79,7 +79,7 @@
                       </object>
                     </child>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_copy">
+                      <object class="GtkImageMenuItem" id="viewdata_copy">
                         <property name="label">gtk-copy</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -87,7 +87,7 @@
                       </object>
                     </child>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_paste">
+                      <object class="GtkImageMenuItem" id="viewdata_paste">
                         <property name="label">gtk-paste</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -95,7 +95,7 @@
                       </object>
                     </child>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_delete">
+                      <object class="GtkImageMenuItem" id="viewdata_delete">
                         <property name="label">gtk-delete</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -116,7 +116,7 @@
                   <object class="GtkMenu" id="menu4">
                     <property name="visible">True</property>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_zoom_in">
+                      <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>
@@ -125,7 +125,7 @@
                       </object>
                     </child>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_zoom_out">
+                      <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>
@@ -134,7 +134,7 @@
                       </object>
                     </child>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_zoom_normal">
+                      <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>
@@ -155,7 +155,7 @@
                   <object class="GtkMenu" id="menu3">
                     <property name="visible">True</property>
                     <child>
-                      <object class="GtkImageMenuItem" id="source_about">
+                      <object class="GtkImageMenuItem" id="viewdata_about">
                         <property name="label">gtk-about</property>
                         <property name="visible">True</property>
                         <property name="use_underline">True</property>
@@ -179,7 +179,7 @@
             <property name="hscrollbar_policy">automatic</property>
             <property name="vscrollbar_policy">automatic</property>
             <child>
-              <object class="GtkTextView" id="source_view">
+              <object class="GtkTextView" id="viewdata_view">
                 <property name="width_request">600</property>
                 <property name="height_request">400</property>
                 <property name="visible">True</property>
diff --git a/gtk/res/source.gtk3.ui b/gtk/res/viewdata.gtk3.ui
similarity index 75%
rename from gtk/res/source.gtk3.ui
rename to gtk/res/viewdata.gtk3.ui
index b972315..e06aaf3 100644
--- a/gtk/res/source.gtk3.ui
+++ b/gtk/res/viewdata.gtk3.ui
@@ -37,34 +37,34 @@
           </object>
         </child>
         <child>
-          <object class="GtkAction" id="source_select_all">
+          <object class="GtkAction" id="viewdata_select_all">
             <property name="stock_id" translatable="yes">gtk-select-all</property>
-            <property name="name">source_select_all</property>
+            <property name="name">viewdata_select_all</property>
           </object>
           <accelerator key="A" modifiers="GDK_CONTROL_MASK"/>
         </child>
         <child>
-          <object class="GtkAction" id="source_cut">
+          <object class="GtkAction" id="viewdata_cut">
             <property name="stock_id" translatable="yes">gtk-cut</property>
-            <property name="name">source_cut</property>
+            <property name="name">viewdata_cut</property>
           </object>
         </child>
         <child>
-          <object class="GtkAction" id="source_copy">
+          <object class="GtkAction" id="viewdata_copy">
             <property name="stock_id" translatable="yes">gtk-copy</property>
-            <property name="name">source_copy</property>
+            <property name="name">viewdata_copy</property>
           </object>
         </child>
         <child>
-          <object class="GtkAction" id="source_paste">
+          <object class="GtkAction" id="viewdata_paste">
             <property name="stock_id" translatable="yes">gtk-paste</property>
-            <property name="name">source_paste</property>
+            <property name="name">viewdata_paste</property>
           </object>
         </child>
         <child>
-          <object class="GtkAction" id="source_delete">
+          <object class="GtkAction" id="viewdata_delete">
             <property name="stock_id" translatable="yes">gtk-delete</property>
-            <property name="name">source_delete</property>
+            <property name="name">viewdata_delete</property>
           </object>
           <accelerator key="Delete" modifiers=""/>
         </child>
@@ -75,23 +75,23 @@
           </object>
         </child>
         <child>
-          <object class="GtkAction" id="source_zoom_in">
+          <object class="GtkAction" id="viewdata_zoom_in">
             <property name="stock_id" translatable="yes">gtk-zoom-in</property>
-            <property name="name">source_zoom_in</property>
+            <property name="name">viewdata_zoom_in</property>
           </object>
           <accelerator key="plus" modifiers="GDK_CONTROL_MASK"/>
         </child>
         <child>
-          <object class="GtkAction" id="source_zoom_out">
+          <object class="GtkAction" id="viewdata_zoom_out">
             <property name="stock_id" translatable="yes">gtk-zoom-out</property>
-            <property name="name">source_zoom_out</property>
+            <property name="name">viewdata_zoom_out</property>
           </object>
           <accelerator key="minus" modifiers="GDK_CONTROL_MASK"/>
         </child>
         <child>
-          <object class="GtkAction" id="source_zoom_normal">
+          <object class="GtkAction" id="viewdata_zoom_normal">
             <property name="stock_id" translatable="yes">gtk-zoom-100</property>
-            <property name="name">source_zoom_normal</property>
+            <property name="name">viewdata_zoom_normal</property>
           </object>
           <accelerator key="0" modifiers="GDK_CONTROL_MASK"/>
         </child>
@@ -102,9 +102,9 @@
           </object>
         </child>
         <child>
-          <object class="GtkAction" id="source_about">
+          <object class="GtkAction" id="viewdata_about">
             <property name="stock_id" translatable="yes">gtk-about</property>
-            <property name="name">source_about</property>
+            <property name="name">viewdata_about</property>
           </object>
         </child>
       </object>
@@ -112,30 +112,30 @@
     <ui>
       <menubar name="menubar1">
         <menu action="menuitem1">
-          <menuitem action="source_save_as"/>
-          <menuitem action="source_print"/>
+          <menuitem action="viewdata_save_as"/>
+          <menuitem action="viewdata_print"/>
           <separator/>
-          <menuitem action="source_close"/>
+          <menuitem action="viewdata_close"/>
         </menu>
         <menu action="menuitem2">
-          <menuitem action="source_select_all"/>
-          <menuitem action="source_cut"/>
-          <menuitem action="source_copy"/>
-          <menuitem action="source_paste"/>
-          <menuitem action="source_delete"/>
+          <menuitem action="viewdata_select_all"/>
+          <menuitem action="viewdata_cut"/>
+          <menuitem action="viewdata_copy"/>
+          <menuitem action="viewdata_paste"/>
+          <menuitem action="viewdata_delete"/>
         </menu>
         <menu action="menuitem3">
-          <menuitem action="source_zoom_in"/>
-          <menuitem action="source_zoom_out"/>
-          <menuitem action="source_zoom_normal"/>
+          <menuitem action="viewdata_zoom_in"/>
+          <menuitem action="viewdata_zoom_out"/>
+          <menuitem action="viewdata_zoom_normal"/>
         </menu>
         <menu action="menuitem4">
-          <menuitem action="source_about"/>
+          <menuitem action="viewdata_about"/>
         </menu>
       </menubar>
     </ui>
   </object>
-  <object class="GtkWindow" id="wndSource">
+  <object class="GtkWindow" id="ViewDataWindow">
     <child>
       <object class="GtkVBox" id="vbox1">
         <property name="visible">True</property>
@@ -154,7 +154,7 @@
             <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
             <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
             <child>
-              <object class="GtkTextView" id="source_view">
+              <object class="GtkTextView" id="viewdata_view">
                 <property name="width_request">600</property>
                 <property name="height_request">400</property>
                 <property name="visible">True</property>
diff --git a/gtk/scaffolding.c b/gtk/scaffolding.c
index 77c8ddc..0e6e8bc 100644
--- a/gtk/scaffolding.c
+++ b/gtk/scaffolding.c
@@ -54,7 +54,7 @@
 #include "gtk/completion.h"
 #include "gtk/dialogs/preferences.h"
 #include "gtk/dialogs/about.h"
-#include "gtk/dialogs/source.h"
+#include "gtk/viewsource.h"
 #include "gtk/bitmap.h"
 #include "gtk/gui.h"
 #include "gtk/history.h"
@@ -1174,8 +1174,7 @@ MULTIHANDLER(fullscreen)
 
 MULTIHANDLER(viewsource)
 {
-	nsgtk_source_dialog_init(g->window,
-			nsgtk_get_browser_window(g->top_level));
+	nsgtk_viewsource(g->window, nsgtk_get_browser_window(g->top_level));
 	return TRUE;
 }
 
diff --git a/gtk/viewdata.c b/gtk/viewdata.c
new file mode 100644
index 0000000..cb6e7c8
--- /dev/null
+++ b/gtk/viewdata.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright 2014 Vincent Sanders <vince at 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 <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/nsoption.h"
+#include "utils/utf8.h"
+#include "utils/messages.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+#include "utils/file.h"
+#include "desktop/netsurf.h"
+#include "desktop/browser.h"
+#include "render/html.h"
+#include "content/hlcache.h"
+#include "content/content.h"
+
+#include "gtk/dialogs/about.h"
+#include "gtk/fetch.h"
+#include "gtk/compat.h"
+#include "gtk/gui.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, GTK_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,
+					 GTK_STOCK_CANCEL,
+					 GTK_RESPONSE_CANCEL,
+					 GTK_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, netsurf_version);
+
+	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)
+{
+	GError* error = NULL;
+	GtkWindow *window;
+	GtkWidget *cutbutton;
+	GtkWidget *pastebutton;
+	GtkWidget *deletebutton;
+	GtkWidget *printbutton;
+	GtkTextView *dataview;
+	PangoFontDescription *fontdesc;
+	GtkTextBuffer *tb;
+	struct nsgtk_viewdata_ctx *newctx;
+
+	newctx = malloc(sizeof(struct nsgtk_viewdata_ctx));
+	if (newctx == NULL) {
+		return NSERROR_NOMEM;
+	}
+
+	newctx->builder = gtk_builder_new();
+	if (newctx->builder == NULL) {
+		free(newctx);
+		return NSERROR_INIT_FAILED;
+	}
+
+	if (!gtk_builder_add_from_file(newctx->builder,
+				       glade_file_location->viewdata,
+				       &error)) {
+		LOG(("Couldn't load builder file: %s", error->message));
+		g_error_free(error);
+		free(newctx);
+		return NSERROR_INIT_FAILED;
+	}
+
+
+	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;
+}
+
+/**
+ * create a new tab with page source
+ */
+static nserror
+tab_init(const char *title,
+	 const char *filename,
+	 char *ndata,
+	 size_t ndata_len)
+{
+	nsurl *url;
+	nserror ret;
+	gchar *fname;
+	gint handle;
+	FILE *f;
+
+	handle = g_file_open_tmp("nsgtksourceXXXXXX", &fname, NULL);
+	if ((handle == -1) || (fname == NULL)) {
+		return NSERROR_SAVE_FAILED;
+	}
+	close(handle); /* in case it was binary mode */
+
+	/* save data to temportary file */
+	f = fopen(fname, "w");
+	if (f == NULL) {
+		warn_user(messages_get("gtkSourceTabError"), 0);
+		g_free(fname);
+		return NSERROR_SAVE_FAILED;
+	}
+	fprintf(f, "%s", ndata);
+	fclose(f);
+
+	/* Open tab on temporary file */
+	ret = netsurf_path_to_nsurl(fname, &url);
+	g_free(fname);
+	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;
+	}
+
+	free(ndata);
+
+	return NSERROR_OK;
+}
+
+/**
+ * create a new tab with page source
+ */
+static nserror
+editor_init(const char *title,
+	    const char *filename,
+	    char *ndata,
+	    size_t ndata_len)
+{
+/* find user configured app for opening text/plain */
+
+/*
+ * serach path is ${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/
+ *
+ * search path looking for applications/defaults.list
+ *
+ * look for [Default Applications]
+ * search lines looking like mime/type=Desktop
+ *
+ * if mimetype is found
+ * use search path with applications/application.desktop
+ *
+ * search desktop file for:
+ * Exec=gedit %U
+ *
+ * execute target app on saved data
+ */
+
+	free(ndata);
+
+	return NSERROR_OK;
+}
+
+/* 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;
+}
diff --git a/framebuffer/fetch.h b/gtk/viewdata.h
similarity index 53%
copy from framebuffer/fetch.h
copy to gtk/viewdata.h
index 718b083..020603d 100644
--- a/framebuffer/fetch.h
+++ b/gtk/viewdata.h
@@ -16,10 +16,21 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#ifndef _NETSURF_GTK_VIEWDATA_H_
+#define _NETSURF_GTK_VIEWDATA_H_
 
-#ifndef NETSURF_FB_FETCH_H
-#define NETSURF_FB_FETCH_H
-
-struct gui_fetch_table *framebuffer_fetch_table;
+/**
+ * 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);
 
 #endif
diff --git a/gtk/viewsource.c b/gtk/viewsource.c
new file mode 100644
index 0000000..cbcbad7
--- /dev/null
+++ b/gtk/viewsource.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2009 Mark Benjamin <MarkBenjamin at 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 <stdbool.h>
+#include <gtk/gtk.h>
+
+#include "utils/nsurl.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+#include "utils/utf8.h"
+#include "utils/messages.h"
+#include "desktop/browser.h"
+#include "content/content.h"
+#include "render/html.h"
+
+#include "gtk/viewdata.h"
+#include "gtk/viewsource.h"
+
+void 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;
+	}
+
+	if (content_get_type(hlcontent) != CONTENT_HTML) {
+		return;
+	}
+
+	source_data = content_get_source_data(hlcontent, &source_size);
+
+	ret = url_nice(nsurl_access(browser_window_get_url(bw)), &filename, false);
+	if (ret != NSERROR_OK) {
+		filename = strdup(messages_get("SaveSource"));
+		if (filename == NULL) {
+			return;
+		}
+	}
+
+	title = malloc(strlen(nsurl_access(browser_window_get_url(bw))) + SLEN("Source of  - NetSurf") + 1);
+	sprintf(title, "Source of %s - NetSurf", nsurl_access(browser_window_get_url(bw)));
+
+	ret = utf8_from_enc(source_data,
+			  html_get_encoding(hlcontent),
+			  source_size,
+			  &ndata,
+			  &ndata_len);
+	if (ret != NSERROR_OK) {
+	free(filename);
+		return;
+	}
+
+	ret = nsgtk_viewdata(title, filename, ndata, ndata_len);
+	free(filename);
+}
diff --git a/framebuffer/fetch.h b/gtk/viewsource.h
similarity index 84%
copy from framebuffer/fetch.h
copy to gtk/viewsource.h
index 718b083..fe85b30 100644
--- a/framebuffer/fetch.h
+++ b/gtk/viewsource.h
@@ -16,10 +16,10 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#ifndef _NETSURF_GTK_VIEWSOURCE_H_
+#define _NETSURF_GTK_VIEWSOURCE_H_
 
-#ifndef NETSURF_FB_FETCH_H
-#define NETSURF_FB_FETCH_H
-
-struct gui_fetch_table *framebuffer_fetch_table;
+void nsgtk_viewsource(GtkWindow *parent, struct browser_window *bw);
 
 #endif
+


-- 
NetSurf Browser



More information about the netsurf-commits mailing list