From 58eea873f875b58050a01478b4aaed0d75135e9a Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Fri, 1 Aug 2014 00:58:42 +0100 Subject: rationalise source view --- 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 | 204 ----------------- gtk/res/source.gtk3.ui | 179 --------------- gtk/res/viewdata.gtk2.ui | 204 +++++++++++++++++ gtk/res/viewdata.gtk3.ui | 179 +++++++++++++++ gtk/scaffolding.c | 5 +- gtk/viewdata.c | 569 +++++++++++++++++++++++++++++++++++++++++++++++ gtk/viewdata.h | 36 +++ gtk/viewsource.c | 81 +++++++ gtk/viewsource.h | 25 +++ 15 files changed, 1108 insertions(+), 952 deletions(-) delete mode 100644 gtk/dialogs/source.c delete mode 100644 gtk/dialogs/source.h delete mode 100644 gtk/res/source.gtk2.ui delete mode 100644 gtk/res/source.gtk3.ui create mode 100644 gtk/res/viewdata.gtk2.ui create mode 100644 gtk/res/viewdata.gtk3.ui create mode 100644 gtk/viewdata.c create mode 100644 gtk/viewdata.h create mode 100644 gtk/viewsource.c create mode 100644 gtk/viewsource.h diff --git a/gtk/Makefile.target b/gtk/Makefile.target index ec19d1b36..f69a73a95 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 8e4e43333..000000000 --- a/gtk/dialogs/source.c +++ /dev/null @@ -1,536 +0,0 @@ -/* - * Copyright 2009 Mark Benjamin - * - * 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 . - */ - -#include -#include -#include - -#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 fcba6b664..000000000 --- a/gtk/dialogs/source.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2009 Mark Benjamin - * - * 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 . - */ - -#ifndef netsurf_gtk_dialogs_source_h_ -#define netsurf_gtk_dialogs_source_h_ - -#include -#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 7a8c32186..4d2848366 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 a9a98c6a0..32f864f71 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 612809eac..cdedbc6a7 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 84c3e0cf5..000000000 --- a/gtk/res/source.gtk2.ui +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - - - True - vertical - - - True - - - True - _File - True - - - True - - - gtk-save-as - True - True - True - - - - - - gtk-print - True - True - True - - - - - - True - - - - - gtk-close - True - True - True - - - - - - - - - True - _Edit - True - - - True - - - gtk-select-all - True - True - True - - - - - - gtk-cut - True - True - True - - - - - gtk-copy - True - True - True - - - - - gtk-paste - True - True - True - - - - - gtk-delete - True - True - True - - - - - - - - - - True - _View - True - - - True - - - gtk-zoom-in - True - True - True - - - - - - gtk-zoom-out - True - True - True - - - - - - gtk-zoom-100 - True - True - True - - - - - - - - - - True - _Help - True - - - True - - - gtk-about - True - True - True - - - - - - - - - False - 0 - - - - - True - True - automatic - automatic - - - 600 - 400 - True - True - 1 - 1 - False - word - 3 - 3 - False - - - - - 1 - - - - - - diff --git a/gtk/res/source.gtk3.ui b/gtk/res/source.gtk3.ui deleted file mode 100644 index b972315d3..000000000 --- a/gtk/res/source.gtk3.ui +++ /dev/null @@ -1,179 +0,0 @@ - - - - - - - - - menuitem1 - _File - - - - - gtk-save-as - source_save_as - - - - - - gtk-print - source_print - - - - - - gtk-close - source_close - - - - - menuitem2 - _Edit - - - - - gtk-select-all - source_select_all - - - - - - gtk-cut - source_cut - - - - - gtk-copy - source_copy - - - - - gtk-paste - source_paste - - - - - gtk-delete - source_delete - - - - - - menuitem3 - _View - - - - - gtk-zoom-in - source_zoom_in - - - - - - gtk-zoom-out - source_zoom_out - - - - - - gtk-zoom-100 - source_zoom_normal - - - - - - menuitem4 - _Help - - - - - gtk-about - source_about - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - - - True - - - False - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - 600 - 400 - True - True - 1 - 1 - False - GTK_WRAP_WORD - 3 - 3 - False - - - - - 1 - - - - - - diff --git a/gtk/res/viewdata.gtk2.ui b/gtk/res/viewdata.gtk2.ui new file mode 100644 index 000000000..c54545415 --- /dev/null +++ b/gtk/res/viewdata.gtk2.ui @@ -0,0 +1,204 @@ + + + + + + + + True + vertical + + + True + + + True + _File + True + + + True + + + gtk-save-as + True + True + True + + + + + + gtk-print + True + True + True + + + + + + True + + + + + gtk-close + True + True + True + + + + + + + + + True + _Edit + True + + + True + + + gtk-select-all + True + True + True + + + + + + gtk-cut + True + True + True + + + + + gtk-copy + True + True + True + + + + + gtk-paste + True + True + True + + + + + gtk-delete + True + True + True + + + + + + + + + + True + _View + True + + + True + + + gtk-zoom-in + True + True + True + + + + + + gtk-zoom-out + True + True + True + + + + + + gtk-zoom-100 + True + True + True + + + + + + + + + + True + _Help + True + + + True + + + gtk-about + True + True + True + + + + + + + + + False + 0 + + + + + True + True + automatic + automatic + + + 600 + 400 + True + True + 1 + 1 + False + word + 3 + 3 + False + + + + + 1 + + + + + + diff --git a/gtk/res/viewdata.gtk3.ui b/gtk/res/viewdata.gtk3.ui new file mode 100644 index 000000000..e06aaf373 --- /dev/null +++ b/gtk/res/viewdata.gtk3.ui @@ -0,0 +1,179 @@ + + + + + + + + + menuitem1 + _File + + + + + gtk-save-as + source_save_as + + + + + + gtk-print + source_print + + + + + + gtk-close + source_close + + + + + menuitem2 + _Edit + + + + + gtk-select-all + viewdata_select_all + + + + + + gtk-cut + viewdata_cut + + + + + gtk-copy + viewdata_copy + + + + + gtk-paste + viewdata_paste + + + + + gtk-delete + viewdata_delete + + + + + + menuitem3 + _View + + + + + gtk-zoom-in + viewdata_zoom_in + + + + + + gtk-zoom-out + viewdata_zoom_out + + + + + + gtk-zoom-100 + viewdata_zoom_normal + + + + + + menuitem4 + _Help + + + + + gtk-about + viewdata_about + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + + + True + + + False + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + 600 + 400 + True + True + 1 + 1 + False + GTK_WRAP_WORD + 3 + 3 + False + + + + + 1 + + + + + + diff --git a/gtk/scaffolding.c b/gtk/scaffolding.c index 77c8ddc39..0e6e8bccd 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 000000000..cb6e7c8de --- /dev/null +++ b/gtk/viewdata.c @@ -0,0 +1,569 @@ +/* + * Copyright 2014 Vincent Sanders + * + * 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 . + */ + +/** + * \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 +#include +#include + +#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 000000000..020603d6c --- /dev/null +++ b/gtk/viewdata.h @@ -0,0 +1,36 @@ +/* + * Copyright 2014 Vincent Sanders + * + * 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 . + */ + +#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 000000000..cbcbad712 --- /dev/null +++ b/gtk/viewsource.c @@ -0,0 +1,81 @@ +/* + * Copyright 2009 Mark Benjamin + * + * 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 . + */ + +#include +#include +#include +#include + +#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 000000000..fe85b30b5 --- /dev/null +++ b/gtk/viewsource.h @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Vincent Sanders + * + * 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 . + */ + +#ifndef _NETSURF_GTK_VIEWSOURCE_H_ +#define _NETSURF_GTK_VIEWSOURCE_H_ + +void nsgtk_viewsource(GtkWindow *parent, struct browser_window *bw); + +#endif + -- cgit v1.2.3