diff --git a/configure.in b/configure.in index ecd4ac259c..e0f4d3e8cc 100644 --- a/configure.in +++ b/configure.in @@ -514,6 +514,26 @@ AC_SUBST(GCONFTOOL) AC_SUBST(GCONF_CFLAGS) AC_SUBST(GCONF_LIBS) +### -------------------------------------------------------------------------- +### look for webkit + +AC_ARG_ENABLE(webkit, + [AS_HELP_STRING([--enable-webkit],[build with the webkit HTML engine])], + [case "${enableval}" in + yes) want_webkit=true ;; + no) want_webkit=false ;; + *) want_webkit=false ;; + esac], + [want_webkit=false]) +if test x${want_webkit} = xtrue +then + PKG_CHECK_MODULES(WEBKIT, webkit-1.0 >= "1.0") + AC_DEFINE(WANT_WEBKIT,1,[Use webkit instead of gtkhtml]) +fi +AM_CONDITIONAL(HTML_USING_WEBKIT, test x${want_webkit} = xtrue) +AC_SUBST(WEBKIT_CFLAGS) +AC_SUBST(WEBKIT_LIBS) + ### -------------------------------------------------------------------------- ### LIBXML -- GNOME_XML_LIB is defined by GNOME_XML_CHECK @@ -1511,6 +1531,7 @@ AC_CONFIG_FILES(po/Makefile.in src/gnome-utils/test/Makefile src/gnome-utils/ui/Makefile src/gnome-search/Makefile + src/html/Makefile src/import-export/Makefile src/import-export/test/Makefile src/import-export/qif-import/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 3e6c142f2d..1f0d1cd666 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,6 +15,7 @@ GUI_SUBDIRS_1 = \ tax \ app-utils \ gnome-utils \ + html \ gnome-search GUI_SUBDIRS_2 = \ diff --git a/src/business/business-gnome/Makefile.am b/src/business/business-gnome/Makefile.am index 79cda8f5b1..0cf0152f30 100644 --- a/src/business/business-gnome/Makefile.am +++ b/src/business/business-gnome/Makefile.am @@ -12,6 +12,7 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/gnome \ -I${top_srcdir}/src/app-utils \ -I${top_srcdir}/src/gnome-search \ + -I${top_srcdir}/src/html \ -I${top_srcdir}/src/report/report-gnome \ -I${top_srcdir}/src/business/business-core \ -I${top_srcdir}/src/business/business-ledger \ @@ -22,8 +23,6 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/libqof/qof \ ${GNOME_CFLAGS} \ ${GLADE_CFLAGS} \ - ${GTKHTML_CFLAGS} \ - ${GDK_PIXBUF_CFLAGS} \ ${GLIB_CFLAGS} \ ${GUILE_INCS} @@ -76,6 +75,7 @@ libgncmod_business_gnome_la_LIBADD = \ ${top_builddir}/src/gnome-search/libgncmod-gnome-search.la \ ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \ ${top_builddir}/src/app-utils/libgncmod-app-utils.la \ + ${top_builddir}/src/html/libgncmod-html.la \ ${top_builddir}/src/engine/libgncmod-engine.la \ ${top_builddir}/src/core-utils/libgnc-core-utils.la \ ${top_builddir}/src/gnc-module/libgnc-module.la \ diff --git a/src/business/dialog-tax-table/Makefile.am b/src/business/dialog-tax-table/Makefile.am index 1c621e6f6c..f97062615f 100644 --- a/src/business/dialog-tax-table/Makefile.am +++ b/src/business/dialog-tax-table/Makefile.am @@ -11,8 +11,6 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/libqof/qof \ ${GNOME_CFLAGS} \ ${GLADE_CFLAGS} \ - ${GTKHTML_CFLAGS} \ - ${GDK_PIXBUF_CFLAGS} \ ${GLIB_CFLAGS} \ ${GUILE_INCS} diff --git a/src/gnome-utils/Makefile.am b/src/gnome-utils/Makefile.am index 0431cade9f..aa0922d75a 100644 --- a/src/gnome-utils/Makefile.am +++ b/src/gnome-utils/Makefile.am @@ -18,9 +18,9 @@ AM_CPPFLAGS = \ ${GLADE_CFLAGS} \ ${GTK_CFLAGS} \ ${GNOME_CFLAGS} \ - ${GTKHTML_CFLAGS} \ + ${GNOME_PRINT_CFLAGS} \ ${GUILE_INCS} \ - ${GOFFICE_CFLAGS} + ${QOF_CFLAGS} libgncmod_gnome_utils_la_SOURCES = \ QuickFill.c \ @@ -63,9 +63,6 @@ libgncmod_gnome_utils_la_SOURCES = \ gnc-general-select.c \ gnc-gnome-utils.c \ gnc-gui-query.c \ - gnc-html-graph-gog.c \ - gnc-html-history.c \ - gnc-html.c \ gnc-icons.c \ gnc-main-window.c \ gnc-menu-extensions.c \ @@ -134,9 +131,6 @@ gncinclude_HEADERS = \ gnc-general-select.h \ gnc-gnome-utils.h \ gnc-gui-query.h \ - gnc-html-graph-gog.h \ - gnc-html-history.h \ - gnc-html.h \ gnc-icons.h \ gnc-main-window.h \ gnc-menu-extensions.h \ @@ -186,14 +180,11 @@ libgncmod_gnome_utils_la_LIBADD = \ $(top_builddir)/lib/libc/libc-missing.la \ ${top_builddir}/src/libqof/qof/libgnc-qof.la \ ${GNOME_LIBS} \ - ${GTKHTML_LIBS} \ - ${GUILE_LIBS} \ - ${GDK_PIXBUF_LIBS} \ + ${GNOME_PRINT_LIBS} \ ${GLADE_LIBS} \ ${GUILE_LIBS} \ ${GLIB_LIBS} \ ${DB_LIBS} \ - ${GOFFICE_LIBS} \ ${REGEX_LIBS} \ ${LIBXML2_LIBS} @@ -202,7 +193,7 @@ if HAVE_X11_XLIB_H endif if BUILDING_FROM_SVN -swig-gnome-utils.c: gnome-utils.i gnc-html.h \ +swig-gnome-utils.c: gnome-utils.i \ ${top_srcdir}/src/base-typemaps.i $(SWIG) -guile $(SWIG_ARGS) -Linkage module \ -I${top_srcdir}/src -o $@ $< @@ -220,11 +211,6 @@ EXTRA_DIST = \ ${gncmod_DATA} \ ${gncscm_DATA} -if !GTKHTML_USES_GTKPRINT - AM_CPPFLAGS += ${GNOME_PRINT_CFLAGS} - libgncmod_gnome_utils_la_LIBADD += ${GNOME_PRINT_LIBS} -endif - ## We borrow guile's convention and use @-...-@ as the substitution ## brackets here, instead of the usual @...@. This prevents autoconf ## from substituting the values directly into the left-hand sides of diff --git a/src/gnome-utils/gnc-gnome-utils.c b/src/gnome-utils/gnc-gnome-utils.c index 067217585d..7cb61bd179 100644 --- a/src/gnome-utils/gnc-gnome-utils.c +++ b/src/gnome-utils/gnc-gnome-utils.c @@ -32,12 +32,12 @@ #endif #include -#include "gnc-html-graph-gog.h" +//#include "gnc-html-graph-gog.h" #include "druid-gconf-setup.h" #include "gnc-gconf-utils.h" #include "gnc-gnome-utils.h" -#include "gnc-html.h" +//#include "gnc-html.h" #include "gnc-engine.h" #include "gnc-path.h" #include "gnc-ui.h" diff --git a/src/gnome-utils/gnc-html-graph-gog.c b/src/gnome-utils/gnc-html-graph-gog.c deleted file mode 100644 index c0b175775c..0000000000 --- a/src/gnome-utils/gnc-html-graph-gog.c +++ /dev/null @@ -1,835 +0,0 @@ -/******************************************************************** - * gnc-html-graph-gog.c -- GNC/HTML Graphing support via GOG * - * * - * Copyright (C) 2005 Joshua Sled * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License as * - * published by the Free Software Foundation; either version 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * - ********************************************************************/ - -#include "config.h" - -#include -#include -#include -#include - -#include "gnc-ui-util.h" -#include "gnc-html-graph-gog.h" -#include "gnc-html.h" -#include "gnc-engine.h" -#include -#include -#include -#include -#if defined(HAVE_GOFFICE_0_5) -# include -#elif defined(GOFFICE_WITH_CAIRO) -# include -#else -# include -#endif -#ifndef GTKHTML_USES_GTKPRINT -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * TODO: - * - scatter-plot marker selection - * - series-color, piecharts (hard, not really supported by GOG) - * and scatters (or drop feature) - * - title-string freeing (fixmes) - * - general graph cleanup - **/ - -#undef G_LOG_DOMAIN -#define G_LOG_DOMAIN "gnc.gui.html.graph.gog" - -static int handle_piechart(gnc_html * html, GtkHTMLEmbedded * eb, gpointer d); -static int handle_barchart(gnc_html * html, GtkHTMLEmbedded * eb, gpointer d); -static int handle_linechart(gnc_html * html, GtkHTMLEmbedded * eb, gpointer d); -static int handle_scatter(gnc_html * html, GtkHTMLEmbedded * eb, gpointer d); - -#ifdef GTKHTML_USES_GTKPRINT -static void draw_print_cb(GtkHTMLEmbedded *eb, cairo_t *cr, gpointer graph); -#else -static void draw_print_cb(GtkHTMLEmbedded *eb, GnomePrintContext *context, gpointer graph); -#endif - -static gboolean create_basic_plot_elements(const char *plot_type, GogObject **out_graph, GogObject **out_chart, GogPlot **out_plot); - -static double * read_doubles(const char * string, int nvalues); - -static void set_chart_titles_from_hash(GogObject *chart, GtkHTMLEmbedded * eb); -static void set_chart_titles(GogObject *chart, const char *title, const char* sub_title); -static void set_chart_axis_labels_from_hash(GogObject *chart, GtkHTMLEmbedded * eb); -static void set_chart_axis_labels(GogObject *chart, const char *x_axis_label, const char* y_axis_label); -static void gtkhtml_pre_3_10_1_bug_workaround(GtkHTMLEmbedded *eb); - -void -gnc_html_graph_gog_init(void) -{ - - g_debug( "init gog graphing" ); - - libgoffice_init(); - - /* Initialize plugins manager */ - go_plugins_init (NULL, NULL, NULL, NULL, TRUE, GO_PLUGIN_LOADER_MODULE_TYPE); - - gnc_html_register_object_handler( "gnc-guppi-pie", handle_piechart ); - gnc_html_register_object_handler( "gnc-guppi-bar", handle_barchart ); - gnc_html_register_object_handler( "gnc-guppi-scatter", handle_scatter ); - gnc_html_register_object_handler( "gnc-guppi-line", handle_linechart ); -} - -static double * -read_doubles(const char * string, int nvalues) -{ - int n; - gchar *next; - double * retval = g_new0(double, nvalues); - - // guile is going to (puts ...) the elements of the double array - // together. In non-POSIX locales, that will be in a format that - // the locale-specific sscanf will not be able to parse. - gnc_push_locale("C"); - { - for (n=0; nwidth, eb->height); - buf = gog_renderer_get_pixbuf (renderer); -#elif defined(GOFFICE_WITH_CAIRO) - cairo_renderer = GOG_RENDERER_CAIRO (g_object_new (GOG_RENDERER_CAIRO_TYPE, - "model", graph, - NULL)); - update_status = gog_renderer_cairo_update (cairo_renderer, - eb->width, eb->height, 1.0); - buf = gog_renderer_cairo_get_pixbuf (cairo_renderer); -#else - pixbuf_renderer = GOG_RENDERER_PIXBUF (g_object_new (GOG_RENDERER_PIXBUF_TYPE, - "model", graph, - NULL)); - update_status = gog_renderer_pixbuf_update (pixbuf_renderer, - eb->width, eb->height, 1.0); - buf = gog_renderer_pixbuf_get (pixbuf_renderer); -#endif - - widget = gtk_image_new_from_pixbuf (buf); - gtk_widget_set_size_request (widget, eb->width, eb->height); - gtk_widget_show_all (widget); - gtk_container_add (GTK_CONTAINER (eb), widget); - - // blindly copied from gnc-html-guppi.c.. - gtk_widget_set_size_request (GTK_WIDGET (eb), eb->width, eb->height); - - g_object_set_data_full (G_OBJECT (eb), "graph", graph, g_object_unref); - g_signal_connect (G_OBJECT (eb), "draw_print", - G_CALLBACK (draw_print_cb), NULL); -} - -static gboolean -create_basic_plot_elements(const char *plot_type_name, - GogObject **out_graph, - GogObject **out_chart, - GogPlot **out_plot) -{ - *out_graph = g_object_new(GOG_GRAPH_TYPE, NULL); - *out_chart = gog_object_add_by_name(*out_graph, "Chart", NULL); - *out_plot = gog_plot_new_by_name(plot_type_name); - if (!*out_plot) - { - // FIXME - log betterer; should probably use GError? - g_warning("gog: unable to load %s plugin", plot_type_name); - return FALSE; - } - gog_object_add_by_name(*out_chart, "Plot", GOG_OBJECT(*out_plot) ); - return TRUE; -} - -static void -set_chart_titles_from_hash(GogObject *chart, GtkHTMLEmbedded * eb) -{ - set_chart_titles(chart, - (const char *)g_hash_table_lookup(eb->params, "title"), - (const char *)g_hash_table_lookup(eb->params, "subtitle")); -} - -static void -set_chart_titles(GogObject *chart, const char *title, const char* sub_title) -{ - gchar *my_sub_title, *total_title; - GOData *title_scalar; - GogObject *tmp; - - if (sub_title) - my_sub_title = g_strdup_printf("%s(%s)", title ? " " : "", sub_title); - else - my_sub_title = g_strdup(""); - - total_title = g_strdup_printf("%s%s", title ? title : "", my_sub_title); - - tmp = gog_object_add_by_name(chart, "Title", NULL); - title_scalar = go_data_scalar_str_new(total_title, TRUE); - gog_dataset_set_dim(GOG_DATASET(tmp), 0, title_scalar, NULL); - - g_free(my_sub_title); -} - -static void -set_chart_axis_labels_from_hash(GogObject *chart, GtkHTMLEmbedded * eb) -{ - set_chart_axis_labels(chart, - (const char *)g_hash_table_lookup(eb->params, "x_axis_label"), - (const char *)g_hash_table_lookup(eb->params, "y_axis_label")); -} - -static void -set_chart_axis_labels(GogObject *chart, const char *x_axis_label, const char* y_axis_label) -{ - if (x_axis_label != NULL) - { - GogObject *xaxis, *label; - GOData *data; - xaxis = gog_object_get_child_by_role(chart, gog_object_find_role_by_name(chart, "X-Axis")); - label = gog_object_add_by_name(xaxis, "Label", NULL); - data = go_data_scalar_str_new(x_axis_label, FALSE); - gog_dataset_set_dim(GOG_DATASET(label), 0, data, NULL); - } - - if (y_axis_label != NULL) - { - GogObject *yaxis, *label; - GOData *data; - yaxis = gog_object_get_child_by_role(chart, gog_object_find_role_by_name(chart, "Y-Axis")); - label = gog_object_add_by_name(yaxis, "Label", NULL); - data = go_data_scalar_str_new(y_axis_label, FALSE); - gog_dataset_set_dim(GOG_DATASET(label), 0, data, NULL); - } -} - -static void -gtkhtml_pre_3_10_1_bug_workaround(GtkHTMLEmbedded *eb) -{ - /* HACK ALERT! Compensate for bug in gtkhtml < 3.10.1 - Gtkhtml set the width parameter twice (=width, =height), so both, - width (==height) and height (<1) were incorrect. */ - if (eb->height < 1) - { - eb->height = eb->width; /* only squares here :( */ - } -} - -/* - * Handle the following parameters: - * title: text - * subtitle: text - * datasize: (length data), sscanf( .., %d, (int)&datasize ) - * data: (foreach (lambda (datum) (push datum) (push " ")) data) - * colors: string; space-seperated? - * labels: string; space-seperated? - * slice_urls_[123]: ? - * legend_urls_[123]: ? - */ -static gboolean -handle_piechart(gnc_html * html, GtkHTMLEmbedded * eb, gpointer unused) -{ - GogObject *graph, *chart; - GogPlot *plot; - GogSeries *series; - GOData *labelData, *sliceData; - int datasize; - double *data = NULL; - char **labels = NULL, **colors = NULL; - - gtkhtml_pre_3_10_1_bug_workaround(eb); - - // parse data from the text-ized params. - { - char *datasizeStr, *dataStr, *labelsStr, *colorStr; - - datasizeStr = g_hash_table_lookup(eb->params, "datasize"); - dataStr = g_hash_table_lookup(eb->params, "data" ); - labelsStr = g_hash_table_lookup(eb->params, "labels"); - colorStr = g_hash_table_lookup(eb->params, "colors"); - g_return_val_if_fail( datasizeStr != NULL - && dataStr != NULL - && labelsStr != NULL - && colorStr != NULL, FALSE ); - datasize = atoi( datasizeStr ); - data = read_doubles( dataStr, datasize ); - labels = read_strings( labelsStr, datasize ); - colors = read_strings( colorStr, datasize ); - } - - if (!create_basic_plot_elements("GogPiePlot", &graph, &chart, &plot)) - { - return FALSE; - } - gog_object_add_by_name(chart, "Legend", NULL); - - GOG_STYLED_OBJECT(graph)->style->outline.width = 5; - GOG_STYLED_OBJECT(graph)->style->outline.color = RGBA_BLACK; - - series = gog_plot_new_series(plot); - labelData = go_data_vector_str_new((char const * const *)labels, datasize, NULL); - gog_series_set_dim(series, 0, labelData, NULL); - go_data_emit_changed(GO_DATA(labelData)); - - sliceData = go_data_vector_val_new(data, datasize, NULL); - gog_series_set_dim(series, 1, sliceData, NULL); - go_data_emit_changed(GO_DATA(sliceData)); - - // fixme: colors - set_chart_titles_from_hash(chart, eb); - - add_pixbuf_graph_widget (eb, graph); - - return TRUE; -} - -/** - * data_rows:int - * data_cols:int - * data:doubles[], data_rows*data_cols - * x_axis_label:string - * y_axis_label:string - * row_labels:string[] - * col_labels:string[] - * col_colors:string[] - * rotate_row_labels:boolean - * stacked:boolean - **/ -static gboolean -handle_barchart(gnc_html * html, GtkHTMLEmbedded * eb, gpointer unused) -{ - GogObject *graph, *chart; - GogPlot *plot; - GogSeries *series; - GogStyle *style; - GOData *label_data, *slice_data; - int data_rows, data_cols; - double *data = NULL; - char **col_labels = NULL, **row_labels = NULL, **col_colors = NULL; - gboolean rotate_row_labels = FALSE; - gboolean stacked = FALSE; - char *bar_type = "normal"; - int bar_overlap = 0 /*percent*/; // seperate bars; no overlap. - - gtkhtml_pre_3_10_1_bug_workaround (eb); - - // parse data from the text-ized params - // series => bars [gnc:cols] - // series-elements => segments [gnc:rows] - { - char *data_rows_str, *data_cols_str, *data_str, *col_labels_str, *row_labels_str; - char *col_colors_str, *rotate_row_labels_str = NULL, *stacked_str = NULL; - - data_rows_str = g_hash_table_lookup (eb->params, "data_rows"); - data_cols_str = g_hash_table_lookup (eb->params, "data_cols"); - data_str = g_hash_table_lookup (eb->params, "data" ); - row_labels_str = g_hash_table_lookup (eb->params, "row_labels"); - col_labels_str = g_hash_table_lookup (eb->params, "col_labels"); - col_colors_str = g_hash_table_lookup (eb->params, "col_colors"); - rotate_row_labels_str = g_hash_table_lookup (eb->params, "rotate_row_labels"); - stacked_str = g_hash_table_lookup (eb->params, "stacked"); - - rotate_row_labels = (gboolean) atoi (rotate_row_labels_str); - stacked = (gboolean) atoi (stacked_str); - -#if 0 // too strong at the moment. - g_return_val_if_fail (data_rows_str != NULL - && data_cols_str != NULL - && data_str != NULL - && col_labels_str != NULL - && row_labels_str != NULL - && col_colors_str != NULL, FALSE ); -#endif // 0 - data_rows = atoi (data_rows_str); - data_cols = atoi (data_cols_str); - data = read_doubles (data_str, data_rows*data_cols); - row_labels = read_strings (row_labels_str, data_rows); - col_labels = read_strings (col_labels_str, data_cols); - col_colors = read_strings (col_colors_str, data_cols); - } - - if (!create_basic_plot_elements("GogBarColPlot", &graph, &chart, &plot)) { - return FALSE; - } - gog_object_add_by_name(chart, "Legend", NULL); - - if ( stacked ) { - // when stacked, we want the bars on _top_ of eachother. - bar_type = "stacked"; - bar_overlap = 100 /*percent*/; - } - - g_object_set (G_OBJECT (plot), - //"vary_style_by_element", TRUE, - "type", bar_type, - "overlap_percentage", bar_overlap, - NULL); - label_data = go_data_vector_str_new ((char const * const *)row_labels, data_rows, NULL); - { - // foreach row: - // series = row - GdkColor color; - int i; - for (i = 0; i < data_cols; i++) { - GError *err = NULL; - - series = gog_plot_new_series (plot); - gog_object_set_name (GOG_OBJECT (series), col_labels[i], &err); - if (err != NULL) - { - g_warning("error setting name [%s] on series [%d]: [%s]", - col_labels[i], i, err->message); - } - - g_object_ref (label_data); - gog_series_set_dim (series, 0, label_data, NULL); - go_data_emit_changed (GO_DATA (label_data)); - - slice_data = go_data_vector_val_new (data + (i*data_rows), data_rows, NULL); - gog_series_set_dim (series, 1, slice_data, NULL); - go_data_emit_changed (GO_DATA (slice_data)); - - style = gog_styled_object_get_style (GOG_STYLED_OBJECT (series)); - style->fill.type = GOG_FILL_STYLE_PATTERN; - if (gdk_color_parse (col_colors[i], &color)) { - style->fill.auto_back = FALSE; - go_pattern_set_solid (&style->fill.pattern, GDK_TO_UINT (color)); - } else { - g_warning("cannot parse color [%s]", col_colors[i]); - } - } - } - - if (rotate_row_labels) { - GogObject *object = gog_object_get_child_by_role ( - chart, gog_object_find_role_by_name (chart, "X-Axis")); - style = gog_styled_object_get_style (GOG_STYLED_OBJECT (object)); - gog_style_set_text_angle (style, 90.0); - } - - set_chart_titles_from_hash (chart, eb); - set_chart_axis_labels_from_hash (chart, eb); - - // we need to do this twice for the barchart... :p - gog_object_update (GOG_OBJECT (graph)); - - add_pixbuf_graph_widget (eb, graph); - - g_debug("barchart rendered."); - return TRUE; -} - - -/** - * data_rows:int - * data_cols:int - * data:doubles[], data_rows*data_cols - * x_axis_label:string - * y_axis_label:string - * row_labels:string[] - * col_labels:string[] - * col_colors:string[] - * rotate_row_labels:boolean - * stacked:boolean - * markers:boolean - * major_grid:boolean - * minor_grid:boolean - **/ -static gboolean -handle_linechart(gnc_html * html, GtkHTMLEmbedded * eb, gpointer unused) -{ - GogObject *graph, *chart; - GogPlot *plot; - GogSeries *series; - GogStyle *style; - GOData *label_data, *slice_data; - int data_rows, data_cols; - double *data = NULL; - char **col_labels = NULL, **row_labels = NULL, **col_colors = NULL; - gboolean rotate_row_labels = FALSE; - gboolean stacked = FALSE; - gboolean markers = FALSE; - gboolean major_grid = FALSE; - gboolean minor_grid = FALSE; - char *line_type = "normal"; - - gtkhtml_pre_3_10_1_bug_workaround (eb); - - // parse data from the text-ized params - // series => lines [gnc:cols] - // series-elements => segments [gnc:rows] - { - char *data_rows_str, *data_cols_str, *data_str, *col_labels_str, *row_labels_str; - char *col_colors_str, *rotate_row_labels_str = NULL, *stacked_str = NULL, *markers_str = NULL; - char *major_grid_str = NULL, *minor_grid_str = NULL; - - data_rows_str = g_hash_table_lookup (eb->params, "data_rows"); - data_cols_str = g_hash_table_lookup (eb->params, "data_cols"); - data_str = g_hash_table_lookup (eb->params, "data" ); - row_labels_str = g_hash_table_lookup (eb->params, "row_labels"); - col_labels_str = g_hash_table_lookup (eb->params, "col_labels"); - col_colors_str = g_hash_table_lookup (eb->params, "col_colors"); - rotate_row_labels_str = g_hash_table_lookup (eb->params, "rotate_row_labels"); - stacked_str = g_hash_table_lookup (eb->params, "stacked"); - markers_str = g_hash_table_lookup (eb->params, "markers"); - major_grid_str = g_hash_table_lookup (eb->params, "major_grid"); - minor_grid_str = g_hash_table_lookup (eb->params, "minor_grid"); - - rotate_row_labels = (gboolean) atoi (rotate_row_labels_str); - stacked = (gboolean) atoi (stacked_str); - markers = (gboolean) atoi (markers_str); - major_grid = (gboolean) atoi (major_grid_str); - minor_grid = (gboolean) atoi (minor_grid_str); - -#if 0 // too strong at the moment. - g_return_val_if_fail (data_rows_str != NULL - && data_cols_str != NULL - && data_str != NULL - && col_labels_str != NULL - && row_labels_str != NULL - && col_colors_str != NULL, FALSE ); -#endif // 0 - - data_rows = atoi (data_rows_str); - data_cols = atoi (data_cols_str); - data = read_doubles (data_str, data_rows*data_cols); - row_labels = read_strings (row_labels_str, data_rows); - col_labels = read_strings (col_labels_str, data_cols); - col_colors = read_strings (col_colors_str, data_cols); - } - - if (!create_basic_plot_elements("GogLinePlot", &graph, &chart, &plot)) { - return FALSE; - } - gog_object_add_by_name(chart, "Legend", NULL); - - if ( stacked ) { - // when stacked, we want the lines on _top_ of eachother. - line_type = "stacked"; - } - - g_object_set (G_OBJECT (plot), - //"vary_style_by_element", TRUE, - "type", line_type, - "default-style-has-markers", markers, - NULL); - label_data = go_data_vector_str_new ((char const * const *)row_labels, data_rows, NULL); - { - // foreach row: - // series = row - GdkColor color; - int i; - for (i = 0; i < data_cols; i++) { - GError *err = NULL; - - series = gog_plot_new_series (plot); - gog_object_set_name (GOG_OBJECT (series), col_labels[i], &err); - if (err != NULL) - { - g_warning("error setting name [%s] on series [%d]: [%s]", - col_labels[i], i, err->message); - } - - g_object_ref (label_data); - gog_series_set_dim (series, 0, label_data, NULL); - go_data_emit_changed (GO_DATA (label_data)); - - slice_data = go_data_vector_val_new (data + (i*data_rows), data_rows, NULL); - gog_series_set_dim (series, 1, slice_data, NULL); - go_data_emit_changed (GO_DATA (slice_data)); - - style = gog_styled_object_get_style (GOG_STYLED_OBJECT (series)); - style->fill.type = GOG_FILL_STYLE_PATTERN; - if (gdk_color_parse (col_colors[i], &color)) { - style->fill.auto_back = FALSE; - go_pattern_set_solid (&style->fill.pattern, GDK_TO_UINT (color)); - } else { - g_warning("cannot parse color [%s]", col_colors[i]); - } - } - } - - if (rotate_row_labels) { - GogObject *object = gog_object_get_child_by_role ( - chart, gog_object_find_role_by_name (chart, "X-Axis")); - style = gog_styled_object_get_style (GOG_STYLED_OBJECT (object)); - gog_style_set_text_angle (style, 90.0); - } - - if (major_grid || minor_grid) { - GogObject *object; - gog_object_add_by_name(chart,"Grid", NULL); - object = gog_object_get_child_by_role (chart, gog_object_find_role_by_name (chart, "Y-Axis")); - if (major_grid) - gog_object_add_by_name (GOG_OBJECT (object),"MajorGrid", NULL); - if (minor_grid) - gog_object_add_by_name (GOG_OBJECT (object),"MinorGrid", NULL); - } - - set_chart_titles_from_hash (chart, eb); - set_chart_axis_labels_from_hash (chart, eb); - - // we need to do this twice for the linechart... :p - gog_object_update (GOG_OBJECT (graph)); - - add_pixbuf_graph_widget (eb, graph); - - g_debug("linechart rendered."); - return TRUE; -} - - -static gboolean -handle_scatter(gnc_html * html, GtkHTMLEmbedded * eb, gpointer unused) -{ - GogObject *graph, *chart; - GogPlot *plot; - GogSeries *series; - GOData *sliceData; - GogStyle *style; - int datasize; - double *xData, *yData; - gchar *marker_str, *color_str; - gboolean fill = FALSE; - - gtkhtml_pre_3_10_1_bug_workaround(eb); - - { - char *datasizeStr, *xDataStr, *yDataStr; - - datasizeStr = g_hash_table_lookup( eb->params, "datasize" ); - datasize = atoi( datasizeStr ); - - xDataStr = g_hash_table_lookup( eb->params, "x_data" ); - xData = read_doubles( xDataStr, datasize ); - - yDataStr = g_hash_table_lookup( eb->params, "y_data" ); - yData = read_doubles( yDataStr, datasize ); - - marker_str = g_hash_table_lookup(eb->params, "marker"); - color_str = g_hash_table_lookup(eb->params, "color"); - } - - if (!create_basic_plot_elements("GogXYPlot", &graph, &chart, &plot)) - { - return FALSE; - } - - series = gog_plot_new_series( plot ); - style = gog_styled_object_get_style(GOG_STYLED_OBJECT(series)); - - sliceData = go_data_vector_val_new( xData, datasize, NULL ); - gog_series_set_dim( series, 0, sliceData, NULL ); - go_data_emit_changed (GO_DATA (sliceData)); - - sliceData = go_data_vector_val_new( yData, datasize, NULL ); - gog_series_set_dim( series, 1, sliceData, NULL ); - go_data_emit_changed (GO_DATA (sliceData)); - - /* set marker shape */ - if (marker_str) { - GOMarkerShape shape; - - if (g_str_has_prefix(marker_str, "filled ")) { - fill = TRUE; - marker_str += 7; - } - shape = go_marker_shape_from_str(marker_str); - if (shape != GO_MARKER_NONE) { - style->marker.auto_shape = FALSE; - go_marker_set_shape(style->marker.mark, shape); - } else { - g_warning("cannot parse marker shape [%s]", marker_str); - } - } - - /* set marker and line colors */ - if (color_str) { - GdkColor color; - if (gdk_color_parse(color_str, &color)) { - style->marker.auto_outline_color = FALSE; - go_marker_set_outline_color(style->marker.mark, GDK_TO_UINT(color)); - style->line.auto_color = FALSE; - style->line.color = GDK_TO_UINT(color); - } else { - g_warning("cannot parse color [%s]", color_str); - } - } - - /* set marker fill colors */ - if (fill) { - style->marker.auto_fill_color = style->marker.auto_outline_color; - go_marker_set_fill_color(style->marker.mark, - go_marker_get_outline_color(style->marker.mark)); - } else { - GogStyle *chart_style = - gog_styled_object_get_style(GOG_STYLED_OBJECT(chart)); - - if (chart_style->fill.type == GOG_FILL_STYLE_PATTERN - && chart_style->fill.pattern.pattern == GO_PATTERN_SOLID) { - style->marker.auto_fill_color = FALSE; - go_marker_set_fill_color(style->marker.mark, - chart_style->fill.pattern.back); - } else if (chart_style->fill.type == GOG_FILL_STYLE_PATTERN - && chart_style->fill.pattern.pattern - == GO_PATTERN_FOREGROUND_SOLID) { - style->marker.auto_fill_color = FALSE; - go_marker_set_fill_color(style->marker.mark, - chart_style->fill.pattern.fore); - } else { - g_warning("fill color of markers can only be set like a solid fill " - "pattern of the chart"); - } - } - - set_chart_titles_from_hash(chart, eb); - set_chart_axis_labels_from_hash(chart, eb); - - // And twice for the scatter, too... :p - gog_object_update(GOG_OBJECT(graph)); - - add_pixbuf_graph_widget (eb, graph); - - return TRUE; -} - -#ifdef GTKHTML_USES_GTKPRINT -static void -draw_print_cb(GtkHTMLEmbedded *eb, cairo_t *cr, gpointer unused) -{ - GogGraph *graph = GOG_GRAPH(g_object_get_data(G_OBJECT(eb), "graph")); -# ifdef HAVE_GOFFICE_0_5 - GogRenderer *rend = g_object_new(GOG_RENDERER_TYPE, "model", graph, NULL); -# else - GogRendererCairo *rend = g_object_new(GOG_RENDERER_CAIRO_TYPE, "model", graph, - "cairo", cr, "is-vector", TRUE, NULL); -# endif - - /* assuming pixel size is 0.5, cf. gtkhtml/src/htmlprinter.c */ - cairo_scale(cr, 0.5, 0.5); - - cairo_translate(cr, 0, -eb->height); - -# ifdef HAVE_GOFFICE_0_5 - gog_renderer_render_to_cairo(rend, cr, eb->width, eb->height); -# else - gog_renderer_cairo_update(rend, eb->width, eb->height, 1.0); -# endif - g_object_unref(rend); -} - -#else /* !GTKHTML_USES_GTKPRINT */ -static void -draw_print_cb(GtkHTMLEmbedded *eb, GnomePrintContext *context, gpointer unused) -{ - GogGraph *graph = GOG_GRAPH (g_object_get_data (G_OBJECT (eb), "graph")); - - /* assuming pixel size is 0.5, cf. gtkhtml/src/htmlprinter.c */ - gnome_print_scale (context, 0.5, 0.5); - - gnome_print_translate (context, 0, eb->height); - gog_graph_print_to_gnome_print (graph, context, eb->width, eb->height); -} -#endif /* GTKHTML_USES_GTKPRINT */ diff --git a/src/gnome-utils/gnc-html-graph-gog.h b/src/gnome-utils/gnc-html-graph-gog.h deleted file mode 100644 index 55f22a2bb6..0000000000 --- a/src/gnome-utils/gnc-html-graph-gog.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef GNC_HTML_GRAPH_GOG_H -#define GNC_HTML_GRAPH_GOG_H 1 - -void gnc_html_graph_gog_init(void); - -#endif /* GNC_HTML_GRAPH_GOG_H */ diff --git a/src/gnome-utils/gnc-html.c b/src/gnome-utils/gnc-html.c deleted file mode 100644 index 09171d4e93..0000000000 --- a/src/gnome-utils/gnc-html.c +++ /dev/null @@ -1,1576 +0,0 @@ -/******************************************************************** - * gnc-html.c -- display HTML with some special gnucash tags. * - * * - * Copyright (C) 2000 Bill Gribble * - * Copyright (C) 2001 Linas Vepstas * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License as * - * published by the Free Software Foundation; either version 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * - ********************************************************************/ - -// libgtkhtml docs: -// http://www.fifi.org/doc/libgtkhtml-dev/html/ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "Account.h" -#include "print-session.h" -#include "gnc-engine.h" -#include "gnc-gui-query.h" -#include "gnc-html.h" -#include "gnc-html-history.h" -#include "gnc-html-graph-gog.h" -#include "gnc-ui.h" -#include "gnc-ui-util.h" - -struct gnc_html_struct { - GtkWidget * window; /* window this html goes into */ - GtkWidget * container; /* parent of the gtkhtml widget */ - GtkWidget * html; /* gtkhtml widget itself */ - gchar * current_link; /* link under mouse pointer */ - - URLType base_type; /* base of URL (path - filename) */ - gchar * base_location; - - //gnc_http * http; /* handles HTTP requests */ - GHashTable * request_info; /* hash uri to GList of GtkHTMLStream * */ - - /* callbacks */ - GncHTMLUrltypeCB urltype_cb; /* is this type OK for this instance? */ - GncHTMLLoadCB load_cb; - GncHTMLFlyoverCB flyover_cb; - GncHTMLButtonCB button_cb; - - gpointer flyover_cb_data; - gpointer load_cb_data; - gpointer button_cb_data; - - gnc_html_history * history; -}; - - -/* indicates the debugging module that this .o belongs to. */ -static QofLogModule log_module = GNC_MOD_HTML; - -/* hashes for URLType -> protocol and protocol -> URLType */ -static GHashTable * gnc_html_type_to_proto_hash = NULL; -static GHashTable * gnc_html_proto_to_type_hash = NULL; - -/* hashes an HTML classid to a handler function */ -static GHashTable * gnc_html_object_handlers = NULL; - -/* hashes an action name from a FORM definition to a handler function. - *
- * action-args is what gets passed to the handler. */ -static GHashTable * gnc_html_action_handlers = NULL; - -/* hashes handlers for loading different URLType data */ -static GHashTable * gnc_html_stream_handlers = NULL; - -/* hashes handlers for handling different URLType data */ -static GHashTable * gnc_html_url_handlers = NULL; - -static char error_404_format[] = -"

%s

%s"; -static char error_404_title[] = N_("Not found"); -static char error_404_body[] = -N_("The specified URL could not be loaded."); - - -static char * -extract_machine_name(const gchar * path) -{ - char machine_rexp[] = "^(//[^/]*)/*(.*)?$"; - regex_t compiled_m; - regmatch_t match[4]; - char * machine=NULL; - - if(!path) return NULL; - - regcomp(&compiled_m, machine_rexp, REG_EXTENDED); - - /* step 1: split the machine name away from the path - * components */ - if(!regexec(&compiled_m, path, 4, match, 0)) { - /* $1 is the machine name */ - if(match[1].rm_so != -1) { - machine = g_strndup(path+match[1].rm_so, - match[1].rm_eo - match[1].rm_so); - } - } - return machine; -} - - -/* Register the URLType if it doesn't already exist. - * Returns TRUE if successful, FALSE if the type already exists. - */ -gboolean -gnc_html_register_urltype (URLType type, const char *protocol) -{ - if (!gnc_html_type_to_proto_hash) { - gnc_html_type_to_proto_hash = g_hash_table_new (g_str_hash, g_str_equal); - gnc_html_proto_to_type_hash = g_hash_table_new (g_str_hash, g_str_equal); - } - if (!protocol) return FALSE; - if (g_hash_table_lookup (gnc_html_type_to_proto_hash, type)) - return FALSE; - - g_hash_table_insert (gnc_html_type_to_proto_hash, type, (gpointer)protocol); - if (*protocol) - g_hash_table_insert (gnc_html_proto_to_type_hash, (gpointer)protocol, type); - - return TRUE; -} - -/******************************************************************** - * gnc_html_parse_url - * this takes a URL and determines the protocol type, location, and - * possible anchor name from the URL. - ********************************************************************/ - -URLType -gnc_html_parse_url(gnc_html * html, const gchar * url, - char ** url_location, char ** url_label) -{ - char uri_rexp[] = "^(([^:][^:]+):)?([^#]+)?(#(.*))?$"; - regex_t compiled; - regmatch_t match[6]; - char * protocol=NULL, * path=NULL, * label=NULL; - int found_protocol=0, found_path=0, found_label=0; - URLType retval; - - DEBUG("parsing %s, base_location %s", - url ? url : "(null)", - html ? (html->base_location ? html->base_location - : "(null base_location)") - : "(null html)"); - regcomp(&compiled, uri_rexp, REG_EXTENDED); - - if(!regexec(&compiled, url, 6, match, 0)) { - if(match[2].rm_so != -1) { - protocol = g_new0(char, match[2].rm_eo - match[2].rm_so + 1); - strncpy(protocol, url + match[2].rm_so, - match[2].rm_eo - match[2].rm_so); - protocol[match[2].rm_eo - match[2].rm_so] = 0; - found_protocol = 1; - } - if(match[3].rm_so != -1) { - path = g_new0(char, match[3].rm_eo - match[3].rm_so + 1); - strncpy(path, url+match[3].rm_so, - match[3].rm_eo - match[3].rm_so); - path[match[3].rm_eo - match[3].rm_so] = 0; - found_path = 1; - } - if(match[5].rm_so != -1) { - label = g_new0(char, match[5].rm_eo - match[5].rm_so + 1); - strncpy(label, url+match[5].rm_so, - match[5].rm_eo - match[5].rm_so); - label[match[5].rm_eo - match[5].rm_so] = 0; - found_label = 1; - } - } - - regfree(&compiled); - - if(found_protocol) { - retval = g_hash_table_lookup (gnc_html_proto_to_type_hash, protocol); - if (!retval) { - PWARN("unhandled URL type for '%s'", url ? url : "(null)"); - retval = URL_TYPE_OTHER; - } - } - else if(found_label && !found_path) { - retval = URL_TYPE_JUMP; - } - else { - if(html) { - retval = html->base_type; - } - else { - retval = URL_TYPE_FILE; - } - } - - g_free(protocol); - - if (!safe_strcmp (retval, URL_TYPE_FILE)) { - if(!found_protocol && path && html && html->base_location) { - if (g_path_is_absolute(path)) { - *url_location = g_strdup(path); - } - else { - *url_location = - g_build_filename(html->base_location, path, (gchar*)NULL); - } - g_free(path); - } - else { - *url_location = g_strdup(path); - g_free(path); - } - - } else if (!safe_strcmp (retval, URL_TYPE_JUMP)) { - *url_location = NULL; - g_free(path); - - } else { - /* case URL_TYPE_OTHER: */ - - if(!found_protocol && path && html && html->base_location) { - if (g_path_is_absolute(path)) { - *url_location = - g_build_filename(extract_machine_name(html->base_location), - path, (gchar*)NULL); - } - else { - *url_location = - g_build_filename(html->base_location, path, (gchar*)NULL); - } - g_free(path); - } - else { - *url_location = g_strdup(path); - g_free(path); - } - } - - *url_label = label; - return retval; -} - - -static char * -extract_base_name(URLType type, const gchar * path) -{ - char machine_rexp[] = "^(//[^/]*)/*(/.*)?$"; - char path_rexp[] = "^/*(.*)/+([^/]*)$"; - regex_t compiled_m, compiled_p; - regmatch_t match[4]; - char * machine=NULL, * location = NULL, * base=NULL; - char * basename=NULL; - - DEBUG(" "); - if(!path) return NULL; - - regcomp(&compiled_m, machine_rexp, REG_EXTENDED); - regcomp(&compiled_p, path_rexp, REG_EXTENDED); - - if (!safe_strcmp (type, URL_TYPE_HTTP) || - !safe_strcmp (type, URL_TYPE_SECURE) || - !safe_strcmp (type, URL_TYPE_FTP)) { - - /* step 1: split the machine name away from the path - * components */ - if(!regexec(&compiled_m, path, 4, match, 0)) { - /* $1 is the machine name */ - if(match[1].rm_so != -1) { - machine = g_strndup(path+match[1].rm_so, - match[1].rm_eo - match[1].rm_so); - } - /* $2 is the path */ - if(match[2].rm_so != -1) { - location = g_strndup(path+match[2].rm_so, - match[2].rm_eo - match[2].rm_so); - } - } - - } else { - location = g_strdup(path); - } - /* step 2: split up the path into prefix and file components */ - if(location) { - if(!regexec(&compiled_p, location, 4, match, 0)) { - if(match[1].rm_so != -1) { - base = g_strndup(location+match[1].rm_so, - match[1].rm_eo - match[1].rm_so); - } - else { - base = NULL; - } - } - } - - regfree(&compiled_m); - regfree(&compiled_p); - - if(machine) { - if(base && (strlen(base) > 0)) { - basename = g_strconcat(machine, "/", base, "/", NULL); - } - else { - basename = g_strconcat(machine, "/", NULL); - } - } - else { - if(base && (strlen(base) > 0)) { - basename = g_strdup(base); - } - else { - basename = NULL; - } - } - - g_free(machine); - g_free(base); - g_free(location); - return basename; -} - -void -gnc_html_initialize (void) -{ - int i; - static struct { - URLType type; - char * protocol; - } types[] = { - { URL_TYPE_FILE, "file" }, - { URL_TYPE_JUMP, "" }, - { URL_TYPE_HTTP, "http" }, - { URL_TYPE_FTP, "ftp" }, - { URL_TYPE_SECURE, "https" }, - { URL_TYPE_REGISTER, "gnc-register" }, - { URL_TYPE_ACCTTREE, "gnc-acct-tree" }, - { URL_TYPE_REPORT, "gnc-report" }, - { URL_TYPE_OPTIONS, "gnc-options" }, - { URL_TYPE_SCHEME, "gnc-scm" }, - { URL_TYPE_HELP, "gnc-help" }, - { URL_TYPE_XMLDATA, "gnc-xml" }, - { URL_TYPE_PRICE, "gnc-price" }, - { URL_TYPE_BUDGET, "gnc-budget" }, - { URL_TYPE_OTHER, "" }, - { NULL, NULL }}; - - for (i = 0; types[i].type; i++) - gnc_html_register_urltype (types[i].type, types[i].protocol); - - // initialize graphing support - gnc_html_graph_gog_init(); -} - - -char * -gnc_build_url (URLType type, const gchar * location, const gchar * label) -{ - char * type_name; - - DEBUG(" "); - type_name = g_hash_table_lookup (gnc_html_type_to_proto_hash, type); - if (!type_name) - type_name = ""; - - if(label) { - return g_strdup_printf("%s%s%s#%s", type_name, (*type_name ? ":" : ""), - (location ? location : ""), - label ? label : ""); - } - else { - return g_strdup_printf("%s%s%s", type_name, (*type_name ? ":" : ""), - (location ? location : "")); - } -} - -static gboolean -http_allowed() -{ - return TRUE; -} - -static gboolean -https_allowed() -{ - return TRUE; -} - -/************************************************************ - * gnc_html_start_request: starts the gnc-http object working on an - * http/https request. - ************************************************************/ -static void -gnc_html_start_request(gnc_html * html, gchar * uri, GtkHTMLStream * handle) -{ - GList * handles = NULL; - gint need_request = FALSE; - - /* we want to make a list of handles to fill with this URI. - * multiple handles with the same URI will all get filled when the - * request comes in. */ - DEBUG("requesting %s", uri); - handles = g_hash_table_lookup(html->request_info, uri); - if(!handles) { - need_request = TRUE; - } - - handles = g_list_append(handles, handle); - g_hash_table_insert(html->request_info, uri, handles); - - if(need_request) { - g_critical("we've not supported network requests for years"); - } -} - - -/******************************************************************** - * gnc_html_load_to_stream : actually do the work of loading the HTML - * or binary data referenced by a URL and feeding it into the GtkHTML - * widget. - ********************************************************************/ - -static void -gnc_html_load_to_stream(gnc_html * html, GtkHTMLStream * handle, - URLType type, const gchar * location, - const gchar * label) -{ - char * fdata = NULL; - int fdata_len = 0; - - DEBUG("type %s, location %s, label %s", type ? type : "(null)", - location ? location : "(null)", label ? label : "(null)"); - if(!html) { - return; - } - - if (gnc_html_stream_handlers) { - GncHTMLStreamCB stream_handler; - - stream_handler = g_hash_table_lookup (gnc_html_stream_handlers, type); - if (stream_handler) { - gboolean ok = stream_handler (location, &fdata, &fdata_len); - - if(ok) { - fdata = fdata ? fdata : g_strdup (""); - gtk_html_write(GTK_HTML(html->html), handle, fdata, fdata_len); - gtk_html_end(GTK_HTML(html->html), handle, GTK_HTML_STREAM_OK); - } - else { - fdata = fdata ? fdata : - g_strdup_printf (error_404_format, - _(error_404_title), _(error_404_body)); - gtk_html_write(GTK_HTML(html->html), handle, fdata, strlen (fdata)); - gtk_html_end(GTK_HTML(html->html), handle, GTK_HTML_STREAM_ERROR); - } - - g_free(fdata); - - if(label) { - while (gtk_events_pending ()) - gtk_main_iteration (); - gtk_html_jump_to_anchor(GTK_HTML(html->html), label); - } - - return; - } - } - - do { - if (!safe_strcmp (type, URL_TYPE_SECURE) || - !safe_strcmp (type, URL_TYPE_HTTP)) { - - if (!safe_strcmp (type, URL_TYPE_SECURE)) { - if(!https_allowed()) { - gnc_error_dialog( html->window, - _("Secure HTTP access is disabled. " - "You can enable it in the Network section of " - "the Preferences dialog.")); - break; - } - } - - if(!http_allowed()) { - gnc_error_dialog( html->window, - _("Network HTTP access is disabled. " - "You can enable it in the Network section of " - "the Preferences dialog.")); - } else { - char *fullurl; - - fullurl = gnc_build_url(type, location, label); - gnc_html_start_request(html, fullurl, handle); - } - - } else { - PWARN("load_to_stream for inappropriate type\n" - "\turl = '%s#%s'\n", - location ? location : "(null)", - label ? label : "(null)"); - fdata = g_strdup_printf (error_404_format, - _(error_404_title), _(error_404_body)); - gtk_html_write(GTK_HTML(html->html), handle, fdata, strlen (fdata)); - gtk_html_end(GTK_HTML(html->html), handle, GTK_HTML_STREAM_ERROR); - g_free (fdata); - } - - } while (FALSE); - -} - - -/******************************************************************** - * gnc_html_link_clicked_cb - called when user left-clicks on html - * anchor. - ********************************************************************/ - -static void -gnc_html_link_clicked_cb(GtkHTML * html, const gchar * url, gpointer data) -{ - URLType type; - char * location = NULL; - char * label = NULL; - gnc_html * gnchtml = (gnc_html *)data; - - DEBUG("Clicked %s", url); - type = gnc_html_parse_url(gnchtml, url, &location, &label); - gnc_html_show_url(gnchtml, type, location, label, 0); - g_free(location); - g_free(label); -} - - -/******************************************************************** - * gnc_html_url_requested_cb - called when a URL needs to be - * loaded within the loading of a page (embedded image). - ********************************************************************/ - -static void -gnc_html_url_requested_cb(GtkHTML * html, char * url, - GtkHTMLStream * handle, gpointer data) -{ - URLType type; - char * location=NULL; - char * label=NULL; - gnc_html * gnchtml = (gnc_html *)data; - - DEBUG("requesting %s", url); - type = gnc_html_parse_url(gnchtml, url, &location, &label); - gnc_html_load_to_stream(gnchtml, handle, type, location, label); - g_free(location); - g_free(label); -} - - -/******************************************************************** - * gnc_html_object_requested_cb - called when an applet needs to be - * loaded. - ********************************************************************/ - -static gboolean -gnc_html_object_requested_cb(GtkHTML * html, GtkHTMLEmbedded * eb, - gpointer data) -{ - gnc_html * gnchtml = data; - GncHTMLObjectCB h; - - DEBUG(" "); - if(!eb || !(eb->classid) || !gnc_html_object_handlers) return FALSE; - - h = g_hash_table_lookup(gnc_html_object_handlers, eb->classid); - if(h) { - return h(gnchtml, eb, data); - } - else { - return FALSE; - } -} - - -/******************************************************************** - * gnc_html_on_url_cb - called when user rolls over html anchor - ********************************************************************/ - -static void -gnc_html_on_url_cb(GtkHTML * html, const gchar * url, gpointer data) -{ - gnc_html * gnchtml = (gnc_html *) data; - - DEBUG("Rollover %s", url ? url : "(null)"); - g_free(gnchtml->current_link); - gnchtml->current_link = g_strdup(url); - if(gnchtml->flyover_cb) { - (gnchtml->flyover_cb)(gnchtml, url, gnchtml->flyover_cb_data); - } -} - - -/******************************************************************** - * gnc_html_set_base_cb - ********************************************************************/ - -static void -gnc_html_set_base_cb(GtkHTML * gtkhtml, const gchar * base, - gpointer data) -{ - gnc_html * html = (gnc_html *)data; - URLType type; - char * location = NULL; - char * label = NULL; - - DEBUG("Setting base location to %s", base); - type = gnc_html_parse_url(html, base, &location, &label); - - g_free(html->base_location); - g_free(label); - - html->base_type = type; - html->base_location = location; -} - - -/******************************************************************** - * gnc_html_button_press_cb - * mouse button callback (if any) - ********************************************************************/ - -static int -gnc_html_button_press_cb(GtkWidget * widg, GdkEventButton * event, - gpointer user_data) -{ - gnc_html * html = user_data; - - DEBUG("Button Press"); - if(html->button_cb) { - (html->button_cb)(html, event, html->button_cb_data); - return TRUE; - } - else { - return FALSE; - } -} - - -/******************************************************************** - * gnc_html_pack/unpack_form_data - * convert an encoded arg string to/from a name-value hash table - ********************************************************************/ - -GHashTable * -gnc_html_unpack_form_data(const char * encoding) -{ - GHashTable * rv; - - DEBUG(" "); - rv = g_hash_table_new(g_str_hash, g_str_equal); - gnc_html_merge_form_data(rv, encoding); - return rv; -} - -void -gnc_html_merge_form_data(GHashTable * rv, const char * encoding) -{ - char * next_pair = NULL; - char * name = NULL; - char * value = NULL; - char * extr_name = NULL; - char * extr_value = NULL; - - DEBUG(" "); - if(!encoding) { - return; - } - next_pair = g_strdup(encoding); - - while(next_pair) { - name = next_pair; - if((value = strchr(name, '=')) != NULL) { - extr_name = g_strndup(name, value-name); - next_pair = strchr(value, '&'); - if(next_pair) { - extr_value = g_strndup(value+1, next_pair-value-1); - next_pair++; - } - else { - extr_value = g_strdup(value+1); - } - - g_hash_table_insert(rv, - gnc_html_decode_string(extr_name), - gnc_html_decode_string(extr_value)); - g_free(extr_name); - g_free(extr_value); - } - else { - next_pair = NULL; - } - } -} - -static gboolean -free_form_data_helper(gpointer k, gpointer v, gpointer user) -{ - DEBUG(" "); - g_free(k); - g_free(v); - return TRUE; -} - -void -gnc_html_free_form_data(GHashTable * d) -{ - DEBUG(" "); - g_hash_table_foreach_remove(d, free_form_data_helper, NULL); - g_hash_table_destroy(d); -} - -static void -pack_form_data_helper(gpointer key, gpointer val, - gpointer user_data) -{ - char * old_str = *(char **)user_data; - char * enc_key = gnc_html_encode_string((char *)key); - char * enc_val = gnc_html_encode_string((char *)val); - char * new_str = NULL; - - DEBUG(" "); - if(old_str) { - new_str = g_strconcat(old_str, "&", enc_key, "=", enc_val, NULL); - } - else { - new_str = g_strconcat(enc_key, "=", enc_val, NULL); - } - *(char **)user_data = new_str; - g_free(old_str); -} - -char * -gnc_html_pack_form_data(GHashTable * form_data) -{ - char * encoded = NULL; - DEBUG(" "); - g_hash_table_foreach(form_data, pack_form_data_helper, &encoded); - return encoded; -} - - -/******************************************************************** - * gnc_html_button_submit_cb - * form submission callback - ********************************************************************/ - -static int -gnc_html_submit_cb(GtkHTML * html, const gchar * method, - const gchar * action, const gchar * encoded_form_data, - gpointer user_data) -{ - gnc_html * gnchtml = user_data; - char * location = NULL; - char * new_loc = NULL; - char * label = NULL; - GHashTable * form_data; - URLType type; - - DEBUG(" "); - form_data = gnc_html_unpack_form_data(encoded_form_data); - type = gnc_html_parse_url(gnchtml, action, &location, &label); - - g_critical("form submission hasn't been supported in years."); - - g_free(location); - g_free(label); - g_free(new_loc); - gnc_html_free_form_data(form_data); - return TRUE; -} - - -/******************************************************************** - * gnc_html_open_scm - * insert some scheme-generated HTML - ********************************************************************/ - -static void -gnc_html_open_scm(gnc_html * html, const gchar * location, - const gchar * label, int newwin) -{ - PINFO("location='%s'", location ? location : "(null)"); -} - - -/******************************************************************** - * gnc_html_show_data - * display some HTML that the creator of the gnc-html got from - * somewhere. - ********************************************************************/ - -void -gnc_html_show_data(gnc_html * html, const char * data, - int datalen) -{ - GtkHTMLStream * handle; - - DEBUG("datalen %d, data %20.20s", datalen, data); - handle = gtk_html_begin(GTK_HTML(html->html)); - gtk_html_write(GTK_HTML(html->html), handle, data, datalen); - gtk_html_end(GTK_HTML(html->html), handle, GTK_HTML_STREAM_OK); -} - - -/******************************************************************** - * gnc_html_show_url - * - * open a URL. This is called when the user clicks a link or - * for the creator of the gnc_html window to explicitly request - * a URL. - ********************************************************************/ - -void -gnc_html_show_url(gnc_html * html, URLType type, - const gchar * location, const gchar * label, - gboolean new_window_hint) -{ - GncHTMLUrlCB url_handler; - GtkHTMLStream * handle; - gboolean new_window; - - DEBUG(" "); - if (!html) return; - if (!location) return; - - /* make sure it's OK to show this URL type in this window */ - if(new_window_hint == 0) { - if (html->urltype_cb) - new_window = !((html->urltype_cb)(type)); - else - new_window = FALSE; - } - else { - new_window = TRUE; - } - - if(!new_window) { - gnc_html_cancel(html); - } - - if (gnc_html_url_handlers) - url_handler = g_hash_table_lookup (gnc_html_url_handlers, type); - else - url_handler = NULL; - - if (url_handler) - { - GNCURLResult result; - gboolean ok; - - result.load_to_stream = FALSE; - result.url_type = type; - result.location = NULL; - result.label = NULL; - result.base_type = URL_TYPE_FILE; - result.base_location = NULL; - result.error_message = NULL; - - ok = url_handler (location, label, new_window, &result); - if (!ok) - { - if (result.error_message) - gnc_error_dialog( html->window, "%s", result.error_message); - else - /* %s is a URL (some location somewhere). */ - gnc_error_dialog( html->window, _("There was an error accessing %s."), location); - - if (html->load_cb) - html->load_cb (html, result.url_type, - location, label, - html->load_cb_data); - } - else if (result.load_to_stream) - { - gnc_html_history_node *hnode; - const char *new_location; - const char *new_label; - GtkHTMLStream * stream; - - new_location = result.location ? result.location : location; - new_label = result.label ? result.label : label; - hnode = gnc_html_history_node_new (result.url_type, - new_location, new_label); - - gnc_html_history_append (html->history, hnode); - - g_free (html->base_location); - html->base_type = result.base_type; - html->base_location = - g_strdup (extract_base_name(result.base_type, new_location)); - DEBUG("resetting base location to %s", - html->base_location ? html->base_location : "(null)"); - - stream = gtk_html_begin (GTK_HTML(html->html)); - gnc_html_load_to_stream (html, stream, result.url_type, - new_location, new_label); - - if (html->load_cb) - html->load_cb (html, result.url_type, - new_location, new_label, - html->load_cb_data); - } - - g_free (result.location); - g_free (result.label); - g_free (result.base_location); - g_free (result.error_message); - - return; - } - - if (!safe_strcmp (type, URL_TYPE_SCHEME)) { - gnc_html_open_scm(html, location, label, new_window); - - } else if (!safe_strcmp (type, URL_TYPE_JUMP)) { - gtk_html_jump_to_anchor(GTK_HTML(html->html), label); - - } else if (!safe_strcmp (type, URL_TYPE_SECURE) || - !safe_strcmp (type, URL_TYPE_HTTP) || - !safe_strcmp (type, URL_TYPE_FILE)) { - - do { - if (!safe_strcmp (type, URL_TYPE_SECURE)) { - if(!https_allowed()) { - gnc_error_dialog( html->window, - _("Secure HTTP access is disabled. " - "You can enable it in the Network section of " - "the Preferences dialog.")); - break; - } - } - - if (safe_strcmp (type, URL_TYPE_FILE)) { - if(!http_allowed()) { - gnc_error_dialog( html->window, - _("Network HTTP access is disabled. " - "You can enable it in the Network section of " - "the Preferences dialog.")); - break; - } - } - - html->base_type = type; - - if(html->base_location) g_free(html->base_location); - html->base_location = extract_base_name(type, location); - - /* FIXME : handle new_window = 1 */ - gnc_html_history_append(html->history, - gnc_html_history_node_new(type, location, label)); - handle = gtk_html_begin(GTK_HTML(html->html)); - gnc_html_load_to_stream(html, handle, type, location, label); - - } while (FALSE); - - } else { - PERR ("URLType %s not supported.", type); - } - - if(html->load_cb) { - (html->load_cb)(html, type, location, label, html->load_cb_data); - } -} - - -/******************************************************************** - * gnc_html_reload - * reload the current page - ********************************************************************/ - -void -gnc_html_reload(gnc_html * html) -{ - gnc_html_history_node * n; - - DEBUG(" "); - n = gnc_html_history_get_current(html->history); - if(n) { - gnc_html_show_url(html, n->type, n->location, n->label, 0); - } -} - - -/******************************************************************** - * gnc_html_new - * create and set up a new gtkhtml widget. - ********************************************************************/ - -gnc_html * -gnc_html_new( GtkWindow *parent ) -{ - gnc_html * retval = g_new0(gnc_html, 1); - - ENTER("parent %p", parent); - - retval->window = GTK_WIDGET(parent); - retval->container = gtk_scrolled_window_new(NULL, NULL); - retval->html = gtk_html_new(); - - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(retval->container), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - - gtk_container_add(GTK_CONTAINER(retval->container), - GTK_WIDGET(retval->html)); - - retval->request_info = g_hash_table_new(g_str_hash, g_str_equal); - //retval->http = gnc_http_new(); - retval->history = gnc_html_history_new(); - -#ifdef HAVE_GTK_2_10 - g_object_ref_sink(retval->container); -#else - g_object_ref (retval->container); - gtk_object_sink (GTK_OBJECT (retval->container)); -#endif - - /* signals */ - g_signal_connect(retval->html, "url_requested", - G_CALLBACK(gnc_html_url_requested_cb), - retval); - - g_signal_connect(retval->html, "on_url", - G_CALLBACK(gnc_html_on_url_cb), - retval); - - g_signal_connect(retval->html, "set_base", - G_CALLBACK(gnc_html_set_base_cb), - retval); - - g_signal_connect(retval->html, "link_clicked", - G_CALLBACK(gnc_html_link_clicked_cb), - retval); - - g_signal_connect (retval->html, "object_requested", - G_CALLBACK (gnc_html_object_requested_cb), - retval); - - g_signal_connect (retval->html, "button_press_event", - G_CALLBACK (gnc_html_button_press_cb), - retval); - - g_signal_connect (retval->html, "submit", - G_CALLBACK(gnc_html_submit_cb), - retval); - - gtk_html_load_empty(GTK_HTML(retval->html)); - - LEAVE("retval %p", retval); - - return retval; -} - - -/******************************************************************** - * gnc_html_cancel - * cancel any outstanding HTML fetch requests. - ********************************************************************/ - -static gboolean -html_cancel_helper(gpointer key, gpointer value, gpointer user_data) -{ - g_free(key); - g_list_free((GList *)value); - return TRUE; -} - -void -gnc_html_cancel(gnc_html * html) -{ - /* remove our own references to requests */ - //gnc_http_cancel_requests(html->http); - - g_hash_table_foreach_remove(html->request_info, html_cancel_helper, NULL); -} - - -/******************************************************************** - * gnc_html_destroy - * destroy the struct - ********************************************************************/ - -void -gnc_html_destroy(gnc_html * html) -{ - - if(!html) return; - - /* cancel any outstanding HTTP requests */ - gnc_html_cancel(html); - - gnc_html_history_destroy(html->history); - - gtk_widget_destroy(html->container); - g_object_unref(html->container); - - g_free(html->current_link); - g_free(html->base_location); - - html->window = NULL; - html->container = NULL; - html->html = NULL; - html->history = NULL; - html->current_link = NULL; - html->base_location = NULL; - - g_free(html); -} - -void -gnc_html_set_urltype_cb(gnc_html * html, GncHTMLUrltypeCB urltype_cb) -{ - html->urltype_cb = urltype_cb; -} - -void -gnc_html_set_load_cb(gnc_html * html, GncHTMLLoadCB load_cb, - gpointer data) -{ - html->load_cb = load_cb; - html->load_cb_data = data; -} - -void -gnc_html_set_flyover_cb(gnc_html * html, GncHTMLFlyoverCB flyover_cb, - gpointer data) -{ - html->flyover_cb = flyover_cb; - html->flyover_cb_data = data; -} - -void -gnc_html_set_button_cb(gnc_html * html, GncHTMLButtonCB button_cb, - gpointer data) -{ - html->button_cb = button_cb; - html->button_cb_data = data; -} - -void -gnc_html_copy(gnc_html *html) -{ - g_return_if_fail(html); - - gtk_html_copy(GTK_HTML(html->html)); -} - -/************************************************************** - * gnc_html_export : wrapper around the builtin function in gtkhtml - **************************************************************/ - -static gboolean -raw_html_receiver (gpointer engine, - const gchar *data, - size_t len, - gpointer user_data) -{ - FILE *fh = (FILE *) user_data; - size_t written; - - do { - written = fwrite (data, 1, len, fh); - len -= written; - } while (len > 0); - return TRUE; -} - -gboolean -gnc_html_export(gnc_html * html, const char *filepath) -{ - FILE *fh; - - g_return_val_if_fail (html != NULL, FALSE); - g_return_val_if_fail (filepath != NULL, FALSE); - - fh = g_fopen (filepath, "w"); - if (!fh) - return FALSE; - - gtk_html_save (GTK_HTML(html->html), GINT_TO_POINTER(raw_html_receiver), fh); - - fclose (fh); - - return TRUE; -} - -static GtkHTML * -gnc_html_get_top_html (GtkHTML *html) -{ - while (html->iframe_parent) - html = GTK_HTML (html->iframe_parent); - - return html; -} - -#ifdef GTKHTML_USES_GTKPRINT -#ifndef HAVE_GTKHTML_3_16 -static void -draw_page_cb(GtkPrintOperation *operation, GtkPrintContext *context, - gint page_nr, gpointer user_data) -{ - gnc_html *html = user_data; - - gtk_html_print_page((GtkHTML*) html->html, context); -} -#endif - -void -gnc_html_print(gnc_html *html) -{ - GtkPrintOperation *print; - GtkPrintOperationResult res; - - print = gtk_print_operation_new(); - - gnc_print_operation_init(print); - gtk_print_operation_set_use_full_page(print, FALSE); - gtk_print_operation_set_unit(print, GTK_UNIT_POINTS); - -#ifdef HAVE_GTKHTML_3_16 - res = gtk_html_print_operation_run((GtkHTML*) html->html, print, - GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, - GTK_WINDOW(html->window), NULL, NULL, - NULL, NULL, NULL, NULL); -#else - gtk_print_operation_set_n_pages(print, 1); - g_signal_connect(print, "draw_page", G_CALLBACK(draw_page_cb), html); - res = gtk_print_operation_run(print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, - GTK_WINDOW(html->window), NULL); -#endif - - if (res == GTK_PRINT_OPERATION_RESULT_APPLY) - gnc_print_operation_save_print_settings(print); - - g_object_unref(print); -} - -#else /* !GTKHTML_USES_GTKPRINT */ -void -gnc_html_print(gnc_html * html) -{ - PrintSession *ps; - GtkWidget *top_level; - PangoFontDescription *fontdesc; - - ps = gnc_print_session_create(FALSE); - if (ps == NULL) { - /* user cancelled */ - return; - } - - top_level = GTK_WIDGET (gnc_html_get_top_html (GTK_HTML(html->html))); - - if (ps->pango_font_string != NULL) { - fontdesc = pango_font_description_from_string(ps->pango_font_string); - gtk_widget_modify_font(top_level, fontdesc); - pango_font_description_free(fontdesc); - } - - gtk_html_print_set_master(GTK_HTML(html->html), ps->job); - gtk_html_print(GTK_HTML(html->html), ps->context); - - if (ps->pango_font_string != NULL) { - gtk_widget_modify_font(top_level, NULL); - } - - gnc_print_session_done(ps); -} -#endif /* GTKHTML_USES_GTKPRINT */ - -gnc_html_history * -gnc_html_get_history(gnc_html * html) -{ - if (!html) return NULL; - return html->history; -} - - -GtkWidget * -gnc_html_get_widget(gnc_html * html) -{ - if (!html) return NULL; - return html->container; -} - -void -gnc_html_register_object_handler(const char * classid, - GncHTMLObjectCB hand) -{ - g_return_if_fail (classid != NULL); - - if(!gnc_html_object_handlers) { - gnc_html_object_handlers = g_hash_table_new(g_str_hash, g_str_equal); - } - - gnc_html_unregister_object_handler (classid); - if (!hand) - return; - - g_hash_table_insert(gnc_html_object_handlers, g_strdup(classid), hand); -} - -void -gnc_html_unregister_object_handler(const char * classid) -{ - gchar * keyptr=NULL; - gchar * valptr=NULL; - gchar ** p_keyptr = &keyptr; - gchar ** p_valptr = &valptr; - - if (!g_hash_table_lookup_extended(gnc_html_object_handlers, - classid, - (gpointer *)p_keyptr, - (gpointer *)p_valptr)) - return; - - g_hash_table_remove(gnc_html_object_handlers, classid); - g_free(keyptr); -} - -void -gnc_html_register_action_handler(const char * actionid, - GncHTMLActionCB hand) -{ - g_return_if_fail (actionid != NULL); - - if(!gnc_html_action_handlers) { - gnc_html_action_handlers = g_hash_table_new(g_str_hash, g_str_equal); - } - - gnc_html_unregister_action_handler (actionid); - if (!hand) - return; - - g_hash_table_insert(gnc_html_action_handlers, g_strdup(actionid), hand); -} - -void -gnc_html_unregister_action_handler(const char * actionid) -{ - gchar * keyptr=NULL; - gchar * valptr=NULL; - gchar ** p_keyptr = &keyptr; - gchar ** p_valptr = &valptr; - - g_return_if_fail (actionid != NULL); - - if (!g_hash_table_lookup_extended(gnc_html_action_handlers, - actionid, - (gpointer *)p_keyptr, - (gpointer *)p_valptr)) - return; - - g_hash_table_remove(gnc_html_action_handlers, actionid); - g_free(keyptr); -} - -void -gnc_html_register_stream_handler(URLType url_type, GncHTMLStreamCB hand) -{ - g_return_if_fail (url_type != NULL && *url_type != '\0'); - - if(!gnc_html_stream_handlers) { - gnc_html_stream_handlers = g_hash_table_new(g_str_hash, g_str_equal); - } - - gnc_html_unregister_stream_handler (url_type); - if (!hand) - return; - - g_hash_table_insert (gnc_html_stream_handlers, url_type, hand); -} - -void -gnc_html_unregister_stream_handler(URLType url_type) -{ - g_hash_table_remove (gnc_html_stream_handlers, url_type); -} - -void -gnc_html_register_url_handler (URLType url_type, GncHTMLUrlCB hand) -{ - g_return_if_fail (url_type != NULL && *url_type != '\0'); - - if(!gnc_html_url_handlers) -{ - gnc_html_url_handlers = g_hash_table_new (g_str_hash, g_str_equal); - } - - gnc_html_unregister_url_handler (url_type); - if (!hand) - return; - - g_hash_table_insert (gnc_html_url_handlers, url_type, hand); -} - -void -gnc_html_unregister_url_handler (URLType url_type) -{ - g_hash_table_remove (gnc_html_url_handlers, url_type); -} - -/******************************************************************** - * gnc_html_encode_string - * RFC 1738 encoding of string for submission with an HTML form. - * GPL code lifted from gtkhtml. copyright notice: - * - * Copyright (C) 1997 Martin Jones (mjones@kde.org) - * Copyright (C) 1997 Torben Weis (weis@kde.org) - * Copyright (C) 1999 Helix Code, Inc. - ********************************************************************/ - -char * -gnc_html_encode_string(const char * str) -{ - static gchar *safe = "$-._!*(),"; /* RFC 1738 */ - unsigned pos = 0; - GString *encoded = g_string_new (""); - gchar buffer[5], *ptr; - guchar c; - - if(!str) return NULL; - - while(pos < strlen(str)) { - c = (unsigned char) str[pos]; - - if ((( c >= 'A') && ( c <= 'Z')) || - (( c >= 'a') && ( c <= 'z')) || - (( c >= '0') && ( c <= '9')) || - (strchr(safe, c))) { - encoded = g_string_append_c (encoded, c); - } - else if ( c == ' ' ) { - encoded = g_string_append_c (encoded, '+'); - } - else if ( c == '\n' ) { - encoded = g_string_append (encoded, "%0D%0A"); - } - else if ( c != '\r' ) { - sprintf( buffer, "%%%02X", (int)c ); - encoded = g_string_append (encoded, buffer); - } - pos++; - } - - ptr = encoded->str; - - g_string_free (encoded, FALSE); - - return (char *)ptr; -} - - -char * -gnc_html_decode_string(const char * str) -{ - static gchar * safe = "$-._!*(),"; /* RFC 1738 */ - GString * decoded = g_string_new (""); - const gchar * ptr; - guchar c; - guint hexval; - ptr = str; - - if(!str) return NULL; - - while(*ptr) { - c = (unsigned char) *ptr; - if ((( c >= 'A') && ( c <= 'Z')) || - (( c >= 'a') && ( c <= 'z')) || - (( c >= '0') && ( c <= '9')) || - (strchr(safe, c))) { - decoded = g_string_append_c (decoded, c); - } - else if ( c == '+' ) { - decoded = g_string_append_c (decoded, ' '); - } - else if (!strncmp(ptr, "%0D0A", 5)) { - decoded = g_string_append (decoded, "\n"); - ptr += 4; - } - else if(c == '%') { - ptr++; - if (1 == sscanf(ptr, "%02X", &hexval)) - decoded = g_string_append_c(decoded, (char)hexval); - else - decoded = g_string_append_c(decoded, ' '); - ptr++; - } - ptr++; - } - ptr = decoded->str; - g_string_free (decoded, FALSE); - - return (char *)ptr; -} - -/******************************************************************** - * escape/unescape_newlines : very simple string encoding for GPG - * ASCII-armored text. - ********************************************************************/ - -char * -gnc_html_unescape_newlines(const gchar * in) -{ - const char * ip = in; - char * cstr = NULL; - GString * rv = g_string_new(""); - - for(ip=in; *ip; ip++) { - if((*ip == '\\') && (*(ip+1)=='n')) { - g_string_append(rv, "\n"); - ip++; - } - else { - g_string_append_c(rv, *ip); - } - } - - g_string_append_c(rv, 0); - cstr = rv->str; - g_string_free(rv, FALSE); - return cstr; -} - -char * -gnc_html_escape_newlines(const gchar * in) -{ - char *out; - const char * ip = in; - GString * escaped = g_string_new(""); - - for(ip=in; *ip; ip++) { - if(*ip == '\012') { - g_string_append(escaped, "\\n"); - } - else { - g_string_append_c(escaped, *ip); - } - } - g_string_append_c(escaped, 0); - out = escaped->str; - g_string_free(escaped, FALSE); - return out; -} diff --git a/src/gnome-utils/gnc-html.h b/src/gnome-utils/gnc-html.h deleted file mode 100644 index d88925e9a2..0000000000 --- a/src/gnome-utils/gnc-html.h +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************** - * gnc-html.h -- display html with gnc special tags * - * Copyright (C) 2000 Bill Gribble * - * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License as * - * published by the Free Software Foundation; either version 2 of * - * the License, or (at your option) any later version. * - * * - * This program 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, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * -\********************************************************************/ - -#ifndef GNC_HTML_H -#define GNC_HTML_H - -#include - -typedef char * URLType; - -#define URL_TYPE_FILE "file" -#define URL_TYPE_JUMP "jump" -#define URL_TYPE_HTTP "http" -#define URL_TYPE_FTP "ftp" -#define URL_TYPE_SECURE "secure" -#define URL_TYPE_REGISTER "register" /* for gnucash register popups */ -#define URL_TYPE_ACCTTREE "accttree" /* for account tree windows */ -#define URL_TYPE_REPORT "report" /* for gnucash report popups */ -#define URL_TYPE_OPTIONS "options" /* for editing report options */ -#define URL_TYPE_SCHEME "scheme" /* for scheme code evaluation */ -#define URL_TYPE_HELP "help" /* for a gnucash help window */ -#define URL_TYPE_XMLDATA "xmldata" /* links to gnucash XML data files */ -#define URL_TYPE_PRICE "price" /* for price editor popups */ -#define URL_TYPE_OTHER "other" -#define URL_TYPE_BUDGET "budget" - -#include "gnc-html-history.h" - -typedef struct gnc_html_struct gnc_html; - -/* The result structure of url handlers. Strings should be g_malloc'd - * by the handler and will be freed by gnc_html. */ -typedef struct -{ - /* The following members are used if the handler succeeds (returns TRUE). */ - - gboolean load_to_stream; /* If TRUE, the url should be loaded from - * a stream using the rest of the data in - * the struct into the original gnc_html - * object. If FALSE, the handler will - * perform all needed actions itself. */ - - URLType url_type; /* Defaults to original */ - char * location; /* If NULL, use original (NULL is default) */ - char * label; /* If NULL, use original (NULL is default) */ - - URLType base_type; - char * base_location; - - /* The following members are used if the handler fails (returns FALSE). */ - char * error_message; -} GNCURLResult; - -typedef int (* GncHTMLUrltypeCB)(URLType ut); -typedef void (* GncHTMLFlyoverCB)(gnc_html * html, const char * url, - gpointer data); -typedef void (* GncHTMLLoadCB)(gnc_html * html, URLType type, - const char * location, const char * label, - gpointer data); -typedef int (* GncHTMLButtonCB)(gnc_html * html, GdkEventButton * event, - gpointer data); - -typedef gboolean (* GncHTMLObjectCB)(gnc_html * html, GtkHTMLEmbedded * eb, - gpointer data); -typedef int (* GncHTMLActionCB)(gnc_html * html, const char * method, - const char * action, GHashTable * form_data); -typedef gboolean (* GncHTMLStreamCB)(const char *location, char **data, int *datalen); -typedef gboolean (* GncHTMLUrlCB)(const char *location, const char *label, - gboolean new_window, GNCURLResult * result); - -gnc_html * gnc_html_new(GtkWindow *parent); -void gnc_html_destroy(gnc_html * html); -void gnc_html_show_url(gnc_html * html, - URLType type, - const char * location, - const char * label, - gboolean new_window_hint); -void gnc_html_show_data(gnc_html * html, - const char * data, int datalen); -void gnc_html_reload(gnc_html * html); -void gnc_html_copy(gnc_html *html); -gboolean gnc_html_export(gnc_html * html, const char *file); -void gnc_html_print(gnc_html * html); -void gnc_html_cancel(gnc_html * html); - -char * gnc_build_url (URLType type, const gchar * location, - const gchar * label); - -/* Register a new URLType. - * returns TRUE if succesful, FALSE if type already exists. - * - * protocol should be an empty string if there is no corresponding protocol. - * if protocol is NULL, this function returns FALSE. - */ -gboolean gnc_html_register_urltype (URLType type, const char *protocol); - -/* object handlers deal with objects in HTML. - * the handlers are looked up at object load time. */ -//#if 0 -void gnc_html_register_object_handler(const char * classid, - GncHTMLObjectCB hand); -//#endif -void gnc_html_unregister_object_handler(const char * classid); - -/* action handlers deal with submitting forms of the type - * . Normal get/post http: - * forms are handled as would be expected, with no callback. */ -void gnc_html_register_action_handler(const char * action, - GncHTMLActionCB hand); -void gnc_html_unregister_action_handler(const char * action); - -/* stream handlers load data for particular URLTypes. */ -void gnc_html_register_stream_handler(URLType url_type, - GncHTMLStreamCB hand); -void gnc_html_unregister_stream_handler(URLType url_type); - -/* handlers for particular URLTypes. */ -void gnc_html_register_url_handler(URLType url_type, - GncHTMLUrlCB hand); -void gnc_html_unregister_url_handler(URLType url_type); - -URLType gnc_html_parse_url(gnc_html * html, const gchar * url, - char ** url_location, char ** url_label); - -/* some string coding/decoding routines */ -char * gnc_html_encode_string(const char * in); -char * gnc_html_decode_string(const char * in); -char * gnc_html_escape_newlines(const char * in); -char * gnc_html_unescape_newlines(const char * in); - -/* utilities for dealing with encoded argument strings for forms */ -char * gnc_html_pack_form_data(GHashTable * form_data); -GHashTable * gnc_html_unpack_form_data(const char * encoding); -void gnc_html_merge_form_data(GHashTable * fdata, const char * enc); -void gnc_html_free_form_data(GHashTable * fdata); - -gnc_html_history * gnc_html_get_history(gnc_html * html); -GtkWidget * gnc_html_get_widget(gnc_html * html); - -/* setting callbacks */ -void gnc_html_set_urltype_cb(gnc_html * html, GncHTMLUrltypeCB urltype_cb); -void gnc_html_set_load_cb(gnc_html * html, GncHTMLLoadCB load_cb, - gpointer data); -void gnc_html_set_flyover_cb(gnc_html * html, GncHTMLFlyoverCB newwin_cb, - gpointer data); -void gnc_html_set_button_cb(gnc_html * html, GncHTMLButtonCB button_cb, - gpointer data); - -/* Initialize the html subsystem */ -void gnc_html_initialize (void); - -#endif diff --git a/src/gnome-utils/gnc-main-window.c b/src/gnome-utils/gnc-main-window.c index 518ae5d2b1..5b9b2817f2 100644 --- a/src/gnome-utils/gnc-main-window.c +++ b/src/gnome-utils/gnc-main-window.c @@ -65,7 +65,7 @@ #include "gnc-main.h" #include "gnc-gconf-utils.h" // +JSLED -#include "gnc-html.h" +//#include "gnc-html.h" #include "gnc-autosave.h" #ifdef HAVE_GTK_2_10 # include "print-session.h" diff --git a/src/gnome-utils/gncmod-gnome-utils.c b/src/gnome-utils/gncmod-gnome-utils.c index b78dd9be13..fe2bc64dff 100644 --- a/src/gnome-utils/gncmod-gnome-utils.c +++ b/src/gnome-utils/gncmod-gnome-utils.c @@ -14,7 +14,6 @@ #include "gnc-module-api.h" #include "dialog-options.h" -#include "gnc-html.h" #include "qof.h" #include "gnc-gui-query.h" @@ -77,7 +76,6 @@ libgncmod_gnome_utils_gnc_module_init(int refcount) { /* Initialize the options-ui database */ if (refcount == 0) { gnc_options_ui_initialize (); - gnc_html_initialize (); /* register the druid pieces */ gnc_druid_gnome_register(); diff --git a/src/gnome-utils/gnome-utils.i b/src/gnome-utils/gnome-utils.i index 70b5111107..0e9c501388 100644 --- a/src/gnome-utils/gnome-utils.i +++ b/src/gnome-utils/gnome-utils.i @@ -3,6 +3,7 @@ /* Includes the header in the wrapper code */ #include #include +#include #include #include #include @@ -11,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -24,10 +24,6 @@ SCM scm_init_sw_gnome_utils_module (void); %import "base-typemaps.i" -/* Parse the header file to generate wrappers */ -%include "gnc-html.h" - - GNCOptionWin * gnc_options_dialog_new(gchar *title); void gnc_options_dialog_destroy(GNCOptionWin * win); void gnc_options_dialog_build_contents(GNCOptionWin *propertybox, @@ -56,30 +52,3 @@ void gnc_unset_busy_cursor (GtkWidget *w); void gnc_window_show_progress (const char *message, double percentage); gboolean gnucash_ui_is_running(void); - -%init { - { - char tmp[100]; - -#define SET_ENUM(e) snprintf(tmp, 100, "(set! %s (%s))", (e), (e)); \ - scm_c_eval_string(tmp); - - SET_ENUM("URL-TYPE-FILE"); - SET_ENUM("URL-TYPE-JUMP"); - SET_ENUM("URL-TYPE-HTTP"); - SET_ENUM("URL-TYPE-FTP"); - SET_ENUM("URL-TYPE-SECURE"); - SET_ENUM("URL-TYPE-REGISTER"); - SET_ENUM("URL-TYPE-ACCTTREE"); - SET_ENUM("URL-TYPE-REPORT"); - SET_ENUM("URL-TYPE-OPTIONS"); - SET_ENUM("URL-TYPE-SCHEME"); - SET_ENUM("URL-TYPE-HELP"); - SET_ENUM("URL-TYPE-XMLDATA"); - SET_ENUM("URL-TYPE-PRICE"); - SET_ENUM("URL-TYPE-OTHER"); - -#undefine SET_ENUM - } - -} diff --git a/src/gnome/Makefile.am b/src/gnome/Makefile.am index a955d697fe..71aaef58ac 100644 --- a/src/gnome/Makefile.am +++ b/src/gnome/Makefile.am @@ -12,13 +12,15 @@ libgnc_gnome_la_LIBADD = \ ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \ ${top_builddir}/src/backend/xml/libgnc-backend-xml-utils.la \ ${top_builddir}/src/app-utils/libgncmod-app-utils.la \ + ${top_builddir}/src/html/libgncmod-html.la \ ${top_builddir}/src/engine/libgncmod-engine.la \ ${top_builddir}/src/core-utils/libgnc-core-utils.la \ ${top_builddir}/src/libqof/qof/libgnc-qof.la \ ${GLADE_LIBS} \ ${GUILE_LIBS} \ ${GNOME_LIBS} \ - ${GLIB_LIBS} + ${GLIB_LIBS} \ + ${QOF_LIBS} libgnc_gnome_la_SOURCES = \ swig-gnome.c \ @@ -112,6 +114,7 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/backend/xml \ -I${top_srcdir}/src/gnome-utils \ -I${top_srcdir}/src/gnome-search \ + -I${top_srcdir}/src/html \ -I${top_srcdir}/src/register/ledger-core \ -I${top_srcdir}/src/register/register-core \ -I${top_srcdir}/src/register/register-gnome \ @@ -121,8 +124,7 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/libqof/qof \ ${GUILE_INCS} \ ${GNOME_CFLAGS} \ - ${GDK_PIXBUF_CFLAGS} \ - ${GTKHTML_CFLAGS} \ + ${GNOME_PRINT_CFLAGS} \ ${GLADE_CFLAGS} \ $(GLIB_CFLAGS) diff --git a/src/gnome/gnc-plugin-page-account-tree.c b/src/gnome/gnc-plugin-page-account-tree.c index 457c1f09ce..922cdd876b 100644 --- a/src/gnome/gnc-plugin-page-account-tree.c +++ b/src/gnome/gnc-plugin-page-account-tree.c @@ -52,7 +52,6 @@ #include "gnc-gconf-utils.h" #include "gnc-gnome-utils.h" #include "gnc-gobject-utils.h" -#include "gnc-html.h" #include "gnc-icons.h" #include "gnc-plugin-account-tree.h" #include "gnc-session.h" diff --git a/src/gnome/gnc-plugin-page-budget.c b/src/gnome/gnc-plugin-page-budget.c index b1daa82001..fbc8a70809 100644 --- a/src/gnome/gnc-plugin-page-budget.c +++ b/src/gnome/gnc-plugin-page-budget.c @@ -45,7 +45,6 @@ #include "dialog-options.h" #include "dialog-utils.h" #include "gnc-gnome-utils.h" -#include "gnc-html.h" #include "gnc-icons.h" #include "gnc-plugin-page-budget.h" #include "gnc-plugin-budget.h" diff --git a/src/html/Makefile.am b/src/html/Makefile.am new file mode 100644 index 0000000000..e59ec462b3 --- /dev/null +++ b/src/html/Makefile.am @@ -0,0 +1,110 @@ +#SUBDIRS = . test +SUBDIRS = . + +pkglib_LTLIBRARIES = libgncmod-html.la + +AM_CPPFLAGS = \ + -I${top_srcdir}/src/core-utils \ + -I${top_srcdir}/src/gnc-module \ + -I${top_srcdir}/src/engine \ + -I${top_srcdir}/src/gnome-utils \ + -I${top_srcdir}/src/app-utils \ + -I${top_srcdir}/src \ + -I${top_builddir}/src \ + -I${top_srcdir}/lib/libc \ + ${QOF_CFLAGS} \ + ${GLIB_CFLAGS} \ + ${GTK_CFLAGS} \ + ${GNOME_CFLAGS} \ + ${GOFFICE_CFLAGS} + +libgncmod_html_la_SOURCES = \ + gncmod-html.c \ + gnc-html.c \ + gnc-html-history.c \ + gnc-html-graph-gog.c \ + gnc-html-factory.c \ + swig-gnc-html.c + +gncincludedir = ${GNC_INCLUDE_DIR} +gncinclude_HEADERS = \ + gnc-html-graph-gog.h \ + gnc-html-history.h \ + gnc-html.h \ + gnc-html-factory.h + +libgncmod_html_la_LDFLAGS = -avoid-version + +libgncmod_html_la_LIBADD = \ + ${top_builddir}/src/core-utils/libgnc-core-utils.la \ + ${top_builddir}/src/gnc-module/libgnc-module.la \ + ${top_builddir}/src/engine/libgncmod-engine.la \ + ${top_builddir}/src/calculation/libgncmod-calculation.la \ + ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \ + ${top_builddir}/src/app-utils/libgncmod-app-utils.la \ + $(top_builddir)/lib/libc/libc-missing.la \ + ${GNOME_LIBS} \ + ${GDK_PIXBUF_LIBS} \ + ${GLIB_LIBS} \ + ${DB_LIBS} \ + ${QOF_LIBS} \ + ${GOFFICE_LIBS} \ + ${GUILE_LIBS} \ + ${REGEX_LIBS} + +if HTML_USING_WEBKIT +AM_CPPFLAGS += ${WEBKIT_CFLAGS} +libgncmod_html_la_SOURCES += \ + gnc-html-webkit.c \ + gnc-html-graph-gog-webkit.c +libgncmod_html_la_LIBADD += \ + ${WEBKIT_LIBS} +else +AM_CPPFLAGS += ${GTKHTML_CFLAGS} +libgncmod_html_la_SOURCES += \ + gnc-html-gtkhtml.c \ + gnc-html-graph-gog-gtkhtml.c +libgncmod_html_la_LIBADD += \ + ${GTKHTML_LIBS} + +if !GTKHTML_USES_GTKPRINT + AM_CPPFLAGS += ${GNOME_PRINT_CFLAGS} + libgncmod_html_la_LIBADD += ${GNOME_PRINT_LIBS} +endif + +endif + +if BUILDING_FROM_SVN +swig-gnc-html.c: gnc-html.i gnc-html.h \ + ${top_srcdir}/src/base-typemaps.i + $(SWIG) -guile $(SWIG_ARGS) -Linkage module \ + -I${top_srcdir}/src -o $@ $< +endif + +EXTRA_DIST = \ + gnc-svninfo.h \ + gnc-html.i + +if !GTKHTML_USES_GTKPRINT + AM_CPPFLAGS += ${GNOME_PRINT_CFLAGS} + libgncmod_html_la_LIBADD += ${GNOME_PRINT_LIBS} +endif + +CLEANFILES = $(BUILT_SOURCES) gnucash +MAINTAINERCLEANFILES = swig-html.c + +# FIXME: Symlinking directories only works on non-win32. +if !PLATFORM_WIN32 +# +# I hate inconsistent standards. Autotools puts help files into +# ${datadir}/gnome/help/${program} while the gnome2 libraries expect +# them in ${pkgdatadir}/gnome/help/${program}. +# +install-data-hook: + $(LN_S) -f ../gnome ${DESTDIR}${pkgdatadir} + +uninstall-hook: + rm -f ${DESTDIR}${pkgdatadir}/gnome +endif + +INCLUDES = -DG_LOG_DOMAIN=\"gnc.html\" diff --git a/src/html/gnc-html-extras.h b/src/html/gnc-html-extras.h new file mode 100644 index 0000000000..8d7be73a71 --- /dev/null +++ b/src/html/gnc-html-extras.h @@ -0,0 +1,52 @@ +/******************************************************************** + * gnc-html-extras.h -- display html with gnc special tags * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_EXTRAS_H +#define GNC_HTML_EXTRAS_H + +// This file is needed so that these definitions can be separately included in the +// gnc-html.i file. The full gnc-html.h file can't be parsed because of the new +// use of GObject + +typedef gchar* URLType; + +#define URL_TYPE_FILE "file" +#define URL_TYPE_JUMP "jump" +#define URL_TYPE_HTTP "http" +#define URL_TYPE_FTP "ftp" +#define URL_TYPE_SECURE "secure" +#define URL_TYPE_REGISTER "register" /* for gnucash register popups */ +#define URL_TYPE_ACCTTREE "accttree" /* for account tree windows */ +#define URL_TYPE_REPORT "report" /* for gnucash report popups */ +#define URL_TYPE_OPTIONS "options" /* for editing report options */ +#define URL_TYPE_SCHEME "scheme" /* for scheme code evaluation */ +#define URL_TYPE_HELP "help" /* for a gnucash help window */ +#define URL_TYPE_XMLDATA "xmldata" /* links to gnucash XML data files */ +#define URL_TYPE_PRICE "price" /* for price editor popups */ +#define URL_TYPE_OTHER "other" +#define URL_TYPE_BUDGET "budget" + +gchar* gnc_build_url( URLType type, const gchar* location, const gchar* label ); + +gboolean gnc_html_engine_supports_css( void ); + +#endif diff --git a/src/html/gnc-html-factory.c b/src/html/gnc-html-factory.c new file mode 100644 index 0000000000..9b60e92975 --- /dev/null +++ b/src/html/gnc-html-factory.c @@ -0,0 +1,56 @@ +/******************************************************************** + * gnc-html_factory.c -- Factory to create HTML component * + * * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + ********************************************************************/ + +#include "config.h" + +#include + +#include "gnc-html.h" +#include "gnc-html-gtkhtml.h" +#include "gnc-html-webkit.h" +#include "qoflog.h" +#include "gnc-engine.h" + +#include "gnc-html-factory.h" + +/* indicates the debugging module that this .o belongs to. */ +static QofLogModule log_module = GNC_MOD_HTML; + +GncHtml* gnc_html_factory_create_html( void ) +{ +#ifdef WANT_WEBKIT + return gnc_html_webkit_new(); +#else + return gnc_html_gtkhtml_new(); +#endif +} + +gboolean +gnc_html_engine_supports_css( void ) +{ +#ifdef WANT_WEBKIT + return TRUE; +#else + return FALSE; +#endif +} diff --git a/src/html/gnc-html-factory.h b/src/html/gnc-html-factory.h new file mode 100644 index 0000000000..947e6ed6ce --- /dev/null +++ b/src/html/gnc-html-factory.h @@ -0,0 +1,30 @@ +/******************************************************************** + * gnc-html-factory.h -- display html with gnc special tags * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_FACTORY_H +#define GNC_HTML_FACTORY_H + +#include "gnc-html.h" + +GncHtml* gnc_html_factory_create_html( void ); + +#endif diff --git a/src/html/gnc-html-graph-gog-gtkhtml.c b/src/html/gnc-html-graph-gog-gtkhtml.c new file mode 100644 index 0000000000..3b934d54c8 --- /dev/null +++ b/src/html/gnc-html-graph-gog-gtkhtml.c @@ -0,0 +1,515 @@ +/******************************************************************** + * gnc-html-graph-gog.c -- GNC/HTML Graphing support via GOG * + * * + * Copyright (C) 2005 Joshua Sled * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + ********************************************************************/ + +#include "config.h" + +#include +#include +#include +#include + +#include "gnc-ui-util.h" +#include "gnc-html-graph-gog.h" +#include "gnc-html-graph-gog-gtkhtml.h" +#include "gnc-html.h" +#include "gnc-engine.h" +#include +#include +#include +#include +#if defined(HAVE_GOFFICE_0_5) +# include +#elif defined(GOFFICE_WITH_CAIRO) +# include +#else +# include +#endif +#ifndef GTKHTML_USES_GTKPRINT +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * TODO: + * - scatter-plot marker selection + * - series-color, piecharts (hard, not really supported by GOG) + * and scatters (or drop feature) + * - title-string freeing (fixmes) + * - general graph cleanup + **/ + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "gnc.html.graph.gog.gtkhtml" + +static int handle_piechart( GncHtml* html, gpointer eb, gpointer d ); +static int handle_barchart( GncHtml* html, gpointer eb, gpointer d ); +static int handle_linechart( GncHtml* html, gpointer eb, gpointer d ); +static int handle_scatter( GncHtml* html, gpointer eb, gpointer d ); + +#ifdef GTKHTML_USES_GTKPRINT +static void draw_print_cb(GtkHTMLEmbedded *eb, cairo_t *cr, gpointer graph); +#else +static void draw_print_cb(GtkHTMLEmbedded *eb, GnomePrintContext *context, gpointer graph); +#endif + +static gboolean create_basic_plot_elements(const char *plot_type, GogObject **out_graph, GogObject **out_chart, GogPlot **out_plot); + +static double * read_doubles(const char * string, int nvalues); + +static void set_chart_titles_from_hash(GogObject *chart, gpointer eb); +static void set_chart_titles(GogObject *chart, const char *title, const char* sub_title); +static void set_chart_axis_labels_from_hash(GogObject *chart, gpointer eb); +static void set_chart_axis_labels(GogObject *chart, const char *x_axis_label, const char* y_axis_label); + +void +gnc_html_graph_gog_gtkhtml_init( void ) +{ + gnc_html_graph_gog_init(); + + gnc_html_register_object_handler( "gnc-guppi-pie", handle_piechart ); + gnc_html_register_object_handler( "gnc-guppi-bar", handle_barchart ); + gnc_html_register_object_handler( "gnc-guppi-scatter", handle_scatter ); + gnc_html_register_object_handler( "gnc-guppi-line", handle_linechart ); +} + +static double * +read_doubles(const char * string, int nvalues) +{ + int n; + gchar *next; + double * retval = g_new0(double, nvalues); + + // guile is going to (puts ...) the elements of the double array + // together. In non-POSIX locales, that will be in a format that + // the locale-specific sscanf will not be able to parse. + gnc_push_locale("C"); + { + for (n=0; nwidth, eb->height); + gtk_widget_show_all (widget); + gtk_container_add (GTK_CONTAINER (eb), widget); + + // blindly copied from gnc-html-guppi.c.. + gtk_widget_set_size_request (GTK_WIDGET (eb), eb->width, eb->height); + + g_object_set_data_full (G_OBJECT (eb), "graph", graph, g_object_unref); + g_signal_connect (G_OBJECT (eb), "draw_print", + G_CALLBACK (draw_print_cb), NULL); +} + +static gboolean +create_basic_plot_elements(const char *plot_type_name, + GogObject **out_graph, + GogObject **out_chart, + GogPlot **out_plot) +{ + *out_graph = g_object_new(GOG_GRAPH_TYPE, NULL); + *out_chart = gog_object_add_by_name(*out_graph, "Chart", NULL); + *out_plot = gog_plot_new_by_name(plot_type_name); + if (!*out_plot) + { + // FIXME - log betterer; should probably use GError? + g_warning("gog: unable to load %s plugin", plot_type_name); + return FALSE; + } + gog_object_add_by_name(*out_chart, "Plot", GOG_OBJECT(*out_plot) ); + return TRUE; +} + +static void +set_chart_titles_from_hash(GogObject *chart, gpointer eb) +{ + set_chart_titles(chart, + (const char *)gnc_html_get_embedded_param(eb, "title"), + (const char *)gnc_html_get_embedded_param(eb, "subtitle")); +} + +static void +set_chart_titles(GogObject *chart, const char *title, const char* sub_title) +{ + gchar *my_sub_title, *total_title; + GOData *title_scalar; + GogObject *tmp; + + if (sub_title) + my_sub_title = g_strdup_printf("%s(%s)", title ? " " : "", sub_title); + else + my_sub_title = g_strdup(""); + + total_title = g_strdup_printf("%s%s", title ? title : "", my_sub_title); + + tmp = gog_object_add_by_name(chart, "Title", NULL); + title_scalar = go_data_scalar_str_new(total_title, TRUE); + gog_dataset_set_dim(GOG_DATASET(tmp), 0, title_scalar, NULL); + + g_free(my_sub_title); +} + +static void +set_chart_axis_labels_from_hash(GogObject *chart, gpointer eb) +{ + set_chart_axis_labels(chart, + gnc_html_get_embedded_param(eb, "x_axis_label"), + gnc_html_get_embedded_param(eb, "y_axis_label")); +} + +static void +set_chart_axis_labels(GogObject *chart, const char *x_axis_label, const char* y_axis_label) +{ + if (x_axis_label != NULL) + { + GogObject *xaxis, *label; + GOData *data; + xaxis = gog_object_get_child_by_role(chart, gog_object_find_role_by_name(chart, "X-Axis")); + label = gog_object_add_by_name(xaxis, "Label", NULL); + data = go_data_scalar_str_new(x_axis_label, FALSE); + gog_dataset_set_dim(GOG_DATASET(label), 0, data, NULL); + } + + if (y_axis_label != NULL) + { + GogObject *yaxis, *label; + GOData *data; + yaxis = gog_object_get_child_by_role(chart, gog_object_find_role_by_name(chart, "Y-Axis")); + label = gog_object_add_by_name(yaxis, "Label", NULL); + data = go_data_scalar_str_new(y_axis_label, FALSE); + gog_dataset_set_dim(GOG_DATASET(label), 0, data, NULL); + } +} + +/* + * Handle the following parameters: + * title: text + * subtitle: text + * datasize: (length data), sscanf( .., %d, (int)&datasize ) + * data: (foreach (lambda (datum) (push datum) (push " ")) data) + * colors: string; space-seperated? + * labels: string; space-seperated? + * slice_urls_[123]: ? + * legend_urls_[123]: ? + */ +static gboolean +handle_piechart( GncHtml* html, gpointer eb, gpointer unused ) +{ + GncHtmlPieChartInfo pieChartInfo; + + // parse data from the text-ized params. + { + const char *datasizeStr, *dataStr, *labelsStr, *colorStr; + + datasizeStr = gnc_html_get_embedded_param(eb, "datasize"); + dataStr = gnc_html_get_embedded_param(eb, "data" ); + labelsStr = gnc_html_get_embedded_param(eb, "labels"); + colorStr = gnc_html_get_embedded_param(eb, "colors"); + g_return_val_if_fail( datasizeStr != NULL + && dataStr != NULL + && labelsStr != NULL + && colorStr != NULL, FALSE ); + pieChartInfo.datasize = atoi( datasizeStr ); + pieChartInfo.data = read_doubles( dataStr, pieChartInfo.datasize ); + pieChartInfo.labels = read_strings( labelsStr, pieChartInfo.datasize ); + pieChartInfo.colors = read_strings( colorStr, pieChartInfo.datasize ); + } + + pieChartInfo.title = (const char *)gnc_html_get_embedded_param(eb, "title"); + pieChartInfo.subtitle = (const char *)gnc_html_get_embedded_param(eb, "subtitle"); + pieChartInfo.width = ((GtkHTMLEmbedded*)eb)->width; + pieChartInfo.height = ((GtkHTMLEmbedded*)eb)->height; + + add_pixbuf_graph_widget( eb, gnc_html_graph_gog_create_piechart( &pieChartInfo ) ); + + return TRUE; +} + +/** + * data_rows:int + * data_cols:int + * data:doubles[], data_rows*data_cols + * x_axis_label:string + * y_axis_label:string + * row_labels:string[] + * col_labels:string[] + * col_colors:string[] + * rotate_row_labels:boolean + * stacked:boolean + **/ +static gboolean +handle_barchart( GncHtml* html, gpointer eb, gpointer unused ) +{ + GncHtmlBarChartInfo barChartInfo; + + // parse data from the text-ized params + // series => bars [gnc:cols] + // series-elements => segments [gnc:rows] + { + const char *data_rows_str, *data_cols_str, *data_str, *col_labels_str, *row_labels_str; + const char *col_colors_str, *rotate_row_labels_str = NULL, *stacked_str = NULL; + + data_rows_str = gnc_html_get_embedded_param (eb, "data_rows"); + data_cols_str = gnc_html_get_embedded_param (eb, "data_cols"); + data_str = gnc_html_get_embedded_param (eb, "data" ); + row_labels_str = gnc_html_get_embedded_param (eb, "row_labels"); + col_labels_str = gnc_html_get_embedded_param (eb, "col_labels"); + col_colors_str = gnc_html_get_embedded_param (eb, "col_colors"); + rotate_row_labels_str = gnc_html_get_embedded_param (eb, "rotate_row_labels"); + stacked_str = gnc_html_get_embedded_param (eb, "stacked"); + + barChartInfo.rotate_row_labels = (gboolean) atoi (rotate_row_labels_str); + barChartInfo.stacked = (gboolean) atoi (stacked_str); + +#if 0 // too strong at the moment. + g_return_val_if_fail (data_rows_str != NULL + && data_cols_str != NULL + && data_str != NULL + && col_labels_str != NULL + && row_labels_str != NULL + && col_colors_str != NULL, FALSE ); +#endif // 0 + barChartInfo.data_rows = atoi (data_rows_str); + barChartInfo.data_cols = atoi (data_cols_str); + barChartInfo.data = read_doubles (data_str, barChartInfo.data_rows*barChartInfo.data_cols); + barChartInfo.row_labels = read_strings (row_labels_str, barChartInfo.data_rows); + barChartInfo.col_labels = read_strings (col_labels_str, barChartInfo.data_cols); + barChartInfo.col_colors = read_strings (col_colors_str, barChartInfo.data_cols); + } + + barChartInfo.title = (const char *)gnc_html_get_embedded_param(eb, "title"); + barChartInfo.subtitle = (const char *)gnc_html_get_embedded_param(eb, "subtitle"); + barChartInfo.width = ((GtkHTMLEmbedded*)eb)->width; + barChartInfo.height = ((GtkHTMLEmbedded*)eb)->height; + barChartInfo.x_axis_label = gnc_html_get_embedded_param(eb, "x_axis_label"), + barChartInfo.y_axis_label = gnc_html_get_embedded_param(eb, "y_axis_label"); + + add_pixbuf_graph_widget( eb, gnc_html_graph_gog_create_barchart( &barChartInfo ) ); + + g_debug("barchart rendered."); + return TRUE; +} + + +/** + * data_rows:int + * data_cols:int + * data:doubles[], data_rows*data_cols + * x_axis_label:string + * y_axis_label:string + * row_labels:string[] + * col_labels:string[] + * col_colors:string[] + * rotate_row_labels:boolean + * stacked:boolean + * markers:boolean + * major_grid:boolean + * minor_grid:boolean + **/ +static gboolean +handle_linechart( GncHtml* html, gpointer eb, gpointer unused ) +{ + GncHtmlLineChartInfo lineChartInfo; + + // parse data from the text-ized params + // series => lines [gnc:cols] + // series-elements => segments [gnc:rows] + { + const char *data_rows_str, *data_cols_str, *data_str, *col_labels_str, *row_labels_str; + const char *col_colors_str, *rotate_row_labels_str = NULL, *stacked_str = NULL, *markers_str = NULL; + const char *major_grid_str = NULL, *minor_grid_str = NULL; + + data_rows_str = gnc_html_get_embedded_param (eb, "data_rows"); + data_cols_str = gnc_html_get_embedded_param (eb, "data_cols"); + data_str = gnc_html_get_embedded_param (eb, "data" ); + row_labels_str = gnc_html_get_embedded_param (eb, "row_labels"); + col_labels_str = gnc_html_get_embedded_param (eb, "col_labels"); + col_colors_str = gnc_html_get_embedded_param (eb, "col_colors"); + rotate_row_labels_str = gnc_html_get_embedded_param (eb, "rotate_row_labels"); + stacked_str = gnc_html_get_embedded_param (eb, "stacked"); + markers_str = gnc_html_get_embedded_param (eb, "markers"); + major_grid_str = gnc_html_get_embedded_param (eb, "major_grid"); + minor_grid_str = gnc_html_get_embedded_param (eb, "minor_grid"); + + lineChartInfo.rotate_row_labels = (gboolean) atoi (rotate_row_labels_str); + lineChartInfo.stacked = (gboolean) atoi (stacked_str); + lineChartInfo.markers = (gboolean) atoi (markers_str); + lineChartInfo.major_grid = (gboolean) atoi (major_grid_str); + lineChartInfo.minor_grid = (gboolean) atoi (minor_grid_str); + + lineChartInfo.data_rows = atoi (data_rows_str); + lineChartInfo.data_cols = atoi (data_cols_str); + lineChartInfo.data = read_doubles (data_str, lineChartInfo.data_rows*lineChartInfo.data_cols); + lineChartInfo.row_labels = read_strings (row_labels_str, lineChartInfo.data_rows); + lineChartInfo.col_labels = read_strings (col_labels_str, lineChartInfo.data_cols); + lineChartInfo.col_colors = read_strings (col_colors_str, lineChartInfo.data_cols); + } + + lineChartInfo.title = (const char *)gnc_html_get_embedded_param(eb, "title"); + lineChartInfo.subtitle = (const char *)gnc_html_get_embedded_param(eb, "subtitle"); + lineChartInfo.width = ((GtkHTMLEmbedded*)eb)->width; + lineChartInfo.height = ((GtkHTMLEmbedded*)eb)->height; + lineChartInfo.x_axis_label = gnc_html_get_embedded_param(eb, "x_axis_label"), + lineChartInfo.y_axis_label = gnc_html_get_embedded_param(eb, "y_axis_label"); + + add_pixbuf_graph_widget( eb, gnc_html_graph_gog_create_linechart( &lineChartInfo ) ); + + g_debug("linechart rendered."); + return TRUE; +} + + +static gboolean +handle_scatter( GncHtml* html, gpointer eb, gpointer unused ) +{ + GncHtmlScatterPlotInfo scatterPlotInfo; + + { + const char *datasizeStr, *xDataStr, *yDataStr; + + datasizeStr = gnc_html_get_embedded_param( eb, "datasize" ); + scatterPlotInfo.datasize = atoi( datasizeStr ); + + xDataStr = gnc_html_get_embedded_param( eb, "x_data" ); + scatterPlotInfo.xData = read_doubles( xDataStr, scatterPlotInfo.datasize ); + + yDataStr = gnc_html_get_embedded_param( eb, "y_data" ); + scatterPlotInfo.yData = read_doubles( yDataStr, scatterPlotInfo.datasize ); + + scatterPlotInfo.marker_str = gnc_html_get_embedded_param(eb, "marker"); + scatterPlotInfo.color_str = gnc_html_get_embedded_param(eb, "color"); + } + + scatterPlotInfo.title = (const char *)gnc_html_get_embedded_param(eb, "title"); + scatterPlotInfo.subtitle = (const char *)gnc_html_get_embedded_param(eb, "subtitle"); + scatterPlotInfo.width = ((GtkHTMLEmbedded*)eb)->width; + scatterPlotInfo.height = ((GtkHTMLEmbedded*)eb)->height; + scatterPlotInfo.x_axis_label = gnc_html_get_embedded_param(eb, "x_axis_label"), + scatterPlotInfo.y_axis_label = gnc_html_get_embedded_param(eb, "y_axis_label"); + + add_pixbuf_graph_widget( eb, gnc_html_graph_gog_create_scatterplot( &scatterPlotInfo ) ); + + return TRUE; +} + +#ifdef GTKHTML_USES_GTKPRINT +static void +draw_print_cb(GtkHTMLEmbedded *eb, cairo_t *cr, gpointer unused) +{ + GogGraph *graph = GOG_GRAPH(g_object_get_data(G_OBJECT(eb), "graph")); +# ifdef HAVE_GOFFICE_0_5 + GogRenderer *rend = g_object_new(GOG_RENDERER_TYPE, "model", graph, NULL); +# else + GogRendererCairo *rend = g_object_new(GOG_RENDERER_CAIRO_TYPE, "model", graph, + "cairo", cr, "is-vector", TRUE, NULL); +# endif + + /* assuming pixel size is 0.5, cf. gtkhtml/src/htmlprinter.c */ + cairo_scale(cr, 0.5, 0.5); + + cairo_translate(cr, 0, -eb->height); + +# ifdef HAVE_GOFFICE_0_5 + gog_renderer_render_to_cairo(rend, cr, eb->width, eb->height); +# else + gog_renderer_cairo_update(rend, eb->width, eb->height, 1.0); +# endif + g_object_unref(rend); +} + +#else /* !GTKHTML_USES_GTKPRINT */ +static void +draw_print_cb(GtkHTMLEmbedded *eb, GnomePrintContext *context, gpointer unused) +{ + GogGraph *graph = GOG_GRAPH (g_object_get_data (G_OBJECT (eb), "graph")); + + /* assuming pixel size is 0.5, cf. gtkhtml/src/htmlprinter.c */ + gnome_print_scale (context, 0.5, 0.5); + + gnome_print_translate (context, 0, eb->height); + gog_graph_print_to_gnome_print (graph, context, eb->width, eb->height); +} +#endif /* GTKHTML_USES_GTKPRINT */ diff --git a/src/html/gnc-html-graph-gog-gtkhtml.h b/src/html/gnc-html-graph-gog-gtkhtml.h new file mode 100644 index 0000000000..bfc3c0e012 --- /dev/null +++ b/src/html/gnc-html-graph-gog-gtkhtml.h @@ -0,0 +1,30 @@ +/******************************************************************** + * gnc-html_graph_gog_gtkhtml.h -- display html with gnc special * + * tags * + * Copyright (C) 2000 Bill Gribble * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_GRAPH_GOG_GTKHTML_H +#define GNC_HTML_GRAPH_GOG_GTKHTML_H 1 + +void gnc_html_graph_gog_gtkhtml_init( void ); + +#endif /* GNC_HTML_GRAPH_GOG_GTKHTML_H */ diff --git a/src/html/gnc-html-graph-gog-webkit.c b/src/html/gnc-html-graph-gog-webkit.c new file mode 100644 index 0000000000..77eeb78d99 --- /dev/null +++ b/src/html/gnc-html-graph-gog-webkit.c @@ -0,0 +1,489 @@ +/******************************************************************** + * gnc-html-graph-gog_webkit.c -- GNC/HTML Graphing support via GOG * + * * + * Copyright (C) 2005 Joshua Sled * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + ********************************************************************/ + +#include "config.h" + +#include +#include +#include + +#include "gnc-ui-util.h" +#include "gnc-html-graph-gog.h" +#include "gnc-html-graph-gog-webkit.h" +#include "gnc-html.h" +#include "gnc-engine.h" +#include +#include +#include +#include +#if defined(HAVE_GOFFICE_0_5) +# include +#elif defined(GOFFICE_WITH_CAIRO) +# include +#else +# include +#endif +#ifndef GTKHTML_USES_GTKPRINT +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * TODO: + * - scatter-plot marker selection + * - series-color, piecharts (hard, not really supported by GOG) + * and scatters (or drop feature) + * - title-string freeing (fixmes) + * - general graph cleanup + **/ + +/* indicates the debugging module that this .o belongs to. */ +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "gnc.html.graph.gog.webkit" +static QofLogModule log_module = GNC_MOD_HTML; + +static int handle_piechart( GncHtml* html, gpointer eb, gpointer d ); +static int handle_barchart( GncHtml* html, gpointer eb, gpointer d ); +static int handle_linechart( GncHtml* html, gpointer eb, gpointer d ); +static int handle_scatter( GncHtml* html, gpointer eb, gpointer d ); + +static double * read_doubles(const char * string, int nvalues); + +void +gnc_html_graph_gog_webkit_init( void ) +{ + g_debug( "init gog graphing" ); + + libgoffice_init(); + + /* Initialize plugins manager */ + go_plugins_init( NULL, NULL, NULL, NULL, TRUE, GO_PLUGIN_LOADER_MODULE_TYPE ); + + gnc_html_register_object_handler( "gnc-guppi-pie", handle_piechart ); + gnc_html_register_object_handler( "gnc-guppi-bar", handle_barchart ); + gnc_html_register_object_handler( "gnc-guppi-scatter", handle_scatter ); + gnc_html_register_object_handler( "gnc-guppi-line", handle_linechart ); +} + +static double * +read_doubles(const char * string, int nvalues) +{ + int n; + gchar *next; + double * retval = g_new0(double, nvalues); + + // guile is going to (puts ...) the elements of the double array + // together. In non-POSIX locales, that will be in a format that + // the locale-specific sscanf will not be able to parse. + gnc_push_locale("C"); + { + for (n=0; n\n" ); + val = g_strndup( p, (p_end-p) ); + *str = p_end+strlen( "\">\n" ); + } + g_free( tag_string ); + + return val; +} + +static gchar* +convert_pixbuf_to_base64_string( GdkPixbuf* pixbuf ) +{ + gchar* pixel_buffer; + gsize pixel_buffer_size; + GError* error = NULL; + gchar* base64_buf; + + if( !gdk_pixbuf_save_to_buffer( pixbuf, &pixel_buffer, &pixel_buffer_size, "png", + &error, NULL ) ) { + PERR( "Unable to save pixbuf to buffer: %s\n", error->message ); + return NULL; + } + + base64_buf = g_base64_encode( pixel_buffer, pixel_buffer_size ); + g_free( pixel_buffer ); + return base64_buf; +} + +/* + * Handle the following parameters: + * title: text + * subtitle: text + * datasize: (length data), sscanf( .., %d, (int)&datasize ) + * data: (foreach (lambda (datum) (push datum) (push " ")) data) + * colors: string; space-seperated? + * labels: string; space-seperated? + * slice_urls_[123]: ? + * legend_urls_[123]: ? + */ +static gboolean +handle_piechart( GncHtml* html, gpointer eb, gpointer d ) +{ + gchar* object_info = (gchar*)eb; + gchar** pResult = (gchar**)d; + GncHtmlPieChartInfo pieChartInfo; + GdkPixbuf* pixbuf; + gchar* p; + gchar* p_end; + gchar* temp_str; + gchar* base64_buf; + + pieChartInfo.width = get_int_value( &object_info, "width" ); + pieChartInfo.height = get_int_value( &object_info, "height" ); + pieChartInfo.title = get_string_param( &object_info, "title" ); + pieChartInfo.subtitle = get_string_param( &object_info, "subtitle" ); + pieChartInfo.datasize = get_int_param( &object_info, "datasize" ); + temp_str = get_string_param( &object_info, "data" ); + if( temp_str != NULL ) { + pieChartInfo.data = read_doubles( temp_str, pieChartInfo.datasize ); + } + temp_str = get_string_param( &object_info, "colors" ); + if( temp_str != NULL ) { + pieChartInfo.colors = read_strings( temp_str, pieChartInfo.datasize ); + g_free( temp_str ); + } + temp_str = get_string_param( &object_info, "labels" ); + if( temp_str != NULL ) { + pieChartInfo.labels = read_strings( temp_str, pieChartInfo.datasize ); + g_free( temp_str ); + } + + pixbuf = gnc_html_graph_gog_create_piechart( &pieChartInfo ); + if( pieChartInfo.title != NULL ) g_free( (gchar*)pieChartInfo.title ); + if( pieChartInfo.subtitle != NULL ) g_free( (gchar*)pieChartInfo.subtitle ); + + base64_buf = convert_pixbuf_to_base64_string( pixbuf ); + if( base64_buf == NULL ) { + return FALSE; + } + + *pResult = g_strdup_printf( "\"Cannot", base64_buf ); + g_free( base64_buf ); + + g_debug("piechart rendered."); + return TRUE; +} + +/** + * data_rows:int + * data_cols:int + * data:doubles[], data_rows*data_cols + * x_axis_label:string + * y_axis_label:string + * row_labels:string[] + * col_labels:string[] + * col_colors:string[] + * rotate_row_labels:boolean + * stacked:boolean + **/ +static gboolean +handle_barchart( GncHtml* html, gpointer eb, gpointer d ) +{ + gchar* object_info = (gchar*)eb; + gchar** pResult = (gchar**)d; + GncHtmlBarChartInfo barChartInfo; + GdkPixbuf* pixbuf; + gchar* p; + gchar* p_end; + gchar* temp_str; + gchar* base64_buf; + + barChartInfo.width = get_int_value( &object_info, "width" ); + barChartInfo.height = get_int_value( &object_info, "height" ); + barChartInfo.title = get_string_param( &object_info, "title" ); + barChartInfo.subtitle = get_string_param( &object_info, "subtitle" ); + barChartInfo.data_rows = get_int_param( &object_info, "data_rows" ); + barChartInfo.data_cols = get_int_param( &object_info, "data_cols" ); + temp_str = get_string_param( &object_info, "data" ); + if( temp_str != NULL ) { + barChartInfo.data = read_doubles( temp_str, barChartInfo.data_rows*barChartInfo.data_cols ); + } + barChartInfo.x_axis_label = get_string_param( &object_info, "x_axis_label" ); + barChartInfo.y_axis_label = get_string_param( &object_info, "y_axis_label" ); + temp_str = get_string_param( &object_info, "col_colors" ); + if( temp_str != NULL ) { + barChartInfo.col_colors = read_strings( temp_str, barChartInfo.data_cols ); + g_free( temp_str ); + } + temp_str = get_string_param( &object_info, "row_labels" ); + if( temp_str != NULL ) { + barChartInfo.row_labels = read_strings( temp_str, barChartInfo.data_rows ); + g_free( temp_str ); + } + temp_str = get_string_param( &object_info, "col_labels" ); + if( temp_str != NULL ) { + barChartInfo.col_labels = read_strings( temp_str, barChartInfo.data_cols ); + g_free( temp_str ); + } + barChartInfo.rotate_row_labels = get_int_param( &object_info, "rotate_row_labels" ); + barChartInfo.stacked = get_int_param( &object_info, "stacked" ); + + pixbuf = gnc_html_graph_gog_create_barchart( &barChartInfo ); + if( barChartInfo.title != NULL ) g_free( (gchar*)barChartInfo.title ); + if( barChartInfo.subtitle != NULL ) g_free( (gchar*)barChartInfo.subtitle ); + if( barChartInfo.x_axis_label != NULL ) g_free( (gchar*)barChartInfo.x_axis_label ); + if( barChartInfo.y_axis_label != NULL ) g_free( (gchar*)barChartInfo.y_axis_label ); + + base64_buf = convert_pixbuf_to_base64_string( pixbuf ); + if( base64_buf == NULL ) { + return FALSE; + } + + *pResult = g_strdup_printf( "\"Cannot", base64_buf ); + + g_debug("barchart rendered."); + return TRUE; +} + + +/** + * data_rows:int + * data_cols:int + * data:doubles[], data_rows*data_cols + * x_axis_label:string + * y_axis_label:string + * row_labels:string[] + * col_labels:string[] + * col_colors:string[] + * rotate_row_labels:boolean + * stacked:boolean + * markers:boolean + * major_grid:boolean + * minor_grid:boolean + **/ +static gboolean +handle_linechart( GncHtml* html, gpointer eb, gpointer d ) +{ + gchar* object_info = (gchar*)eb; + gchar** pResult = (gchar**)d; + GncHtmlLineChartInfo lineChartInfo; + GdkPixbuf* pixbuf; + gchar* p; + gchar* p_end; + gchar* temp_str; + gchar* base64_buf; + + lineChartInfo.width = get_int_value( &object_info, "width" ); + lineChartInfo.height = get_int_value( &object_info, "height" ); + lineChartInfo.title = get_string_param( &object_info, "title" ); + lineChartInfo.subtitle = get_string_param( &object_info, "subtitle" ); + lineChartInfo.data_rows = get_int_param( &object_info, "data_rows" ); + lineChartInfo.data_cols = get_int_param( &object_info, "data_cols" ); + temp_str = get_string_param( &object_info, "data" ); + if( temp_str != NULL ) { + lineChartInfo.data = read_doubles( temp_str, lineChartInfo.data_rows*lineChartInfo.data_cols ); + } + lineChartInfo.x_axis_label = get_string_param( &object_info, "x_axis_label" ); + lineChartInfo.y_axis_label = get_string_param( &object_info, "y_axis_label" ); + temp_str = get_string_param( &object_info, "col_colors" ); + if( temp_str != NULL ) { + lineChartInfo.col_colors = read_strings( temp_str, lineChartInfo.data_cols ); + g_free( temp_str ); + } + temp_str = get_string_param( &object_info, "row_labels" ); + if( temp_str != NULL ) { + lineChartInfo.row_labels = read_strings( temp_str, lineChartInfo.data_rows ); + g_free( temp_str ); + } + temp_str = get_string_param( &object_info, "col_labels" ); + if( temp_str != NULL ) { + lineChartInfo.col_labels = read_strings( temp_str, lineChartInfo.data_cols ); + g_free( temp_str ); + } + lineChartInfo.rotate_row_labels = get_int_param( &object_info, "rotate_row_labels" ); + lineChartInfo.stacked = get_int_param( &object_info, "stacked" ); + lineChartInfo.markers = get_int_param( &object_info, "markers" ); + lineChartInfo.major_grid = get_int_param( &object_info, "major_grid" ); + lineChartInfo.minor_grid = get_int_param( &object_info, "minor_grid" ); + + pixbuf = gnc_html_graph_gog_create_linechart( &lineChartInfo ); + if( lineChartInfo.title != NULL ) g_free( (gchar*)lineChartInfo.title ); + if( lineChartInfo.subtitle != NULL ) g_free( (gchar*)lineChartInfo.subtitle ); + if( lineChartInfo.x_axis_label != NULL ) g_free( (gchar*)lineChartInfo.x_axis_label ); + if( lineChartInfo.y_axis_label != NULL ) g_free( (gchar*)lineChartInfo.y_axis_label ); + + base64_buf = convert_pixbuf_to_base64_string( pixbuf ); + if( base64_buf == NULL ) { + return FALSE; + } + + *pResult = g_strdup_printf( "\"Cannot", base64_buf ); + + g_debug("linechart rendered."); + return TRUE; +} + + +static gboolean +handle_scatter( GncHtml* html, gpointer eb, gpointer d ) +{ + gchar* object_info = (gchar*)eb; + gchar** pResult = (gchar**)d; + GncHtmlScatterPlotInfo scatterPlotInfo; + GdkPixbuf* pixbuf; + gchar* p; + gchar* p_end; + gchar* temp_str; + gchar* base64_buf; + + scatterPlotInfo.width = get_int_value( &object_info, "width" ); + scatterPlotInfo.height = get_int_value( &object_info, "height" ); + scatterPlotInfo.title = get_string_param( &object_info, "title" ); + scatterPlotInfo.subtitle = get_string_param( &object_info, "subtitle" ); + scatterPlotInfo.x_axis_label = get_string_param( &object_info, "x_axis_label" ); + scatterPlotInfo.y_axis_label = get_string_param( &object_info, "y_axis_label" ); + scatterPlotInfo.marker_str = get_string_param( &object_info, "marker" ); + scatterPlotInfo.color_str = get_string_param( &object_info, "color" ); + scatterPlotInfo.datasize = get_int_param( &object_info, "datasize" ); + temp_str = get_string_param( &object_info, "x_data" ); + if( temp_str != NULL ) { + scatterPlotInfo.xData = read_doubles( temp_str, scatterPlotInfo.datasize ); + } + temp_str = get_string_param( &object_info, "y_data" ); + if( temp_str != NULL ) { + scatterPlotInfo.yData = read_doubles( temp_str, scatterPlotInfo.datasize ); + } + + pixbuf = gnc_html_graph_gog_create_scatterplot( &scatterPlotInfo ); + if( scatterPlotInfo.title != NULL ) g_free( (gchar*)scatterPlotInfo.title ); + if( scatterPlotInfo.subtitle != NULL ) g_free( (gchar*)scatterPlotInfo.subtitle ); + + base64_buf = convert_pixbuf_to_base64_string( pixbuf ); + if( base64_buf == NULL ) { + return FALSE; + } + + *pResult = g_strdup_printf( "\"Cannot", base64_buf ); + + g_debug("scatterplot rendered."); + return TRUE; +} diff --git a/src/html/gnc-html-graph-gog-webkit.h b/src/html/gnc-html-graph-gog-webkit.h new file mode 100644 index 0000000000..a52f9e91e3 --- /dev/null +++ b/src/html/gnc-html-graph-gog-webkit.h @@ -0,0 +1,30 @@ +/******************************************************************** + * gnc-html_graph_gog_webkit.h -- display html with gnc special * + * tags * + * Copyright (C) 2000 Bill Gribble * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_GRAPH_GOG_WEBKIT_H +#define GNC_HTML_GRAPH_GOG_WEBKIT_H 1 + +void gnc_html_graph_gog_webkit_init( void ); + +#endif /* GNC_HTML_GRAPH_GOG_WEBKIT_H */ diff --git a/src/html/gnc-html-graph-gog.c b/src/html/gnc-html-graph-gog.c new file mode 100644 index 0000000000..3bd83b49c3 --- /dev/null +++ b/src/html/gnc-html-graph-gog.c @@ -0,0 +1,528 @@ +/******************************************************************** + * gnc-html-graph-gog.c -- GNC/HTML Graphing support via GOG * + * * + * Copyright (C) 2005 Joshua Sled * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + ********************************************************************/ + +#include "config.h" + +#include +#include + +#include "gnc-html-graph-gog.h" +#include "gnc-html.h" +#include "gnc-engine.h" +#include +#include +#include +#include +#if defined(HAVE_GOFFICE_0_5) +# include +#elif defined(GOFFICE_WITH_CAIRO) +# include +#else +# include +#endif +#ifndef GTKHTML_USES_GTKPRINT +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * TODO: + * - scatter-plot marker selection + * - series-color, piecharts (hard, not really supported by GOG) + * and scatters (or drop feature) + * - title-string freeing (fixmes) + * - general graph cleanup + **/ + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "gnc.html.graph.gog" + +static gboolean create_basic_plot_elements(const char *plot_type, GogObject **out_graph, GogObject **out_chart, GogPlot **out_plot); + +static void set_chart_titles(GogObject *chart, const char *title, const char* sub_title); +static void set_chart_axis_labels(GogObject *chart, const char *x_axis_label, const char* y_axis_label); + +void +gnc_html_graph_gog_init( void ) +{ + static gboolean initialized = FALSE; + + if( !initialized ) { + g_debug( "init gog graphing" ); + + libgoffice_init(); + + /* Initialize plugins manager */ + go_plugins_init( NULL, NULL, NULL, NULL, TRUE, GO_PLUGIN_LOADER_MODULE_TYPE ); + + initialized = TRUE; + } +} + +static GdkPixbuf* +create_graph_pixbuf( GogObject *graph, int width, int height ) +{ +#if defined(HAVE_GOFFICE_0_5) + GogRenderer *renderer; +#elif defined(GOFFICE_WITH_CAIRO) + GogRendererCairo *cairo_renderer; +#else + GogRendererPixbuf *pixbuf_renderer; +#endif + GdkPixbuf *buf; + gboolean update_status; + + // Note that this shouldn't be necessary as per discussion with Jody... + // ... but it is because we don't embed in a control which passes the + // update requests back to the graph widget, a-la the foo-canvas that + // gnumeric uses. We probably _should_ do something like that, though. + gog_object_update (GOG_OBJECT (graph)); + +#if defined(HAVE_GOFFICE_0_5) + renderer = GOG_RENDERER(g_object_new( GOG_RENDERER_TYPE, "model", graph, NULL )); + update_status = gog_renderer_update( renderer, width, height ); + buf = gog_renderer_get_pixbuf( renderer ); +#elif defined(GOFFICE_WITH_CAIRO) + cairo_renderer = GOG_RENDERER_CAIRO(g_object_new( GOG_RENDERER_CAIRO_TYPE, + "model", graph, + NULL )); + update_status = gog_renderer_cairo_update( cairo_renderer, width, height, 1.0 ); + buf = gog_renderer_cairo_get_pixbuf( cairo_renderer ); +#else + pixbuf_renderer = GOG_RENDERER_PIXBUF(g_object_new( GOG_RENDERER_PIXBUF_TYPE, + "model", graph, + NULL)); + update_status = gog_renderer_pixbuf_update( pixbuf_renderer, width, height, 1.0 ); + buf = gog_renderer_pixbuf_get( pixbuf_renderer ); +#endif + + g_object_set_data_full( G_OBJECT(buf), "graph", graph, g_object_unref ); + return buf; +} + +static gboolean +create_basic_plot_elements(const char *plot_type_name, + GogObject **out_graph, + GogObject **out_chart, + GogPlot **out_plot) +{ + *out_graph = g_object_new(GOG_GRAPH_TYPE, NULL); + *out_chart = gog_object_add_by_name(*out_graph, "Chart", NULL); + *out_plot = gog_plot_new_by_name(plot_type_name); + if (!*out_plot) + { + // FIXME - log betterer; should probably use GError? + g_warning("gog: unable to load %s plugin", plot_type_name); + return FALSE; + } + gog_object_add_by_name(*out_chart, "Plot", GOG_OBJECT(*out_plot) ); + return TRUE; +} + +static void +set_chart_titles(GogObject *chart, const char *title, const char* sub_title) +{ + gchar *my_sub_title, *total_title; + GOData *title_scalar; + GogObject *tmp; + + if (sub_title) + my_sub_title = g_strdup_printf("%s(%s)", title ? " " : "", sub_title); + else + my_sub_title = g_strdup(""); + + total_title = g_strdup_printf("%s%s", title ? title : "", my_sub_title); + + tmp = gog_object_add_by_name(chart, "Title", NULL); + title_scalar = go_data_scalar_str_new(total_title, TRUE); + gog_dataset_set_dim(GOG_DATASET(tmp), 0, title_scalar, NULL); + + g_free(my_sub_title); +} + +static void +set_chart_axis_labels(GogObject *chart, const char *x_axis_label, const char* y_axis_label) +{ + if (x_axis_label != NULL) + { + GogObject *xaxis, *label; + GOData *data; + xaxis = gog_object_get_child_by_role(chart, gog_object_find_role_by_name(chart, "X-Axis")); + label = gog_object_add_by_name(xaxis, "Label", NULL); + data = go_data_scalar_str_new(x_axis_label, FALSE); + gog_dataset_set_dim(GOG_DATASET(label), 0, data, NULL); + } + + if (y_axis_label != NULL) + { + GogObject *yaxis, *label; + GOData *data; + yaxis = gog_object_get_child_by_role(chart, gog_object_find_role_by_name(chart, "Y-Axis")); + label = gog_object_add_by_name(yaxis, "Label", NULL); + data = go_data_scalar_str_new(y_axis_label, FALSE); + gog_dataset_set_dim(GOG_DATASET(label), 0, data, NULL); + } +} + +/* + * Handle the following parameters: + * title: text + * subtitle: text + * datasize: (length data), sscanf( .., %d, (int)&datasize ) + * data: (foreach (lambda (datum) (push datum) (push " ")) data) + * colors: string; space-seperated? + * labels: string; space-seperated? + * slice_urls_[123]: ? + * legend_urls_[123]: ? + */ +GdkPixbuf* +gnc_html_graph_gog_create_piechart( GncHtmlPieChartInfo* info ) +{ + GogObject *graph, *chart; + GogPlot *plot; + GogSeries *series; + GOData *labelData, *sliceData; + GdkPixbuf* pixbuf; + + if( !create_basic_plot_elements( "GogPiePlot", &graph, &chart, &plot ) ) { + return NULL; + } + gog_object_add_by_name( chart, "Legend", NULL ); + + GOG_STYLED_OBJECT(graph)->style->outline.width = 5; + GOG_STYLED_OBJECT(graph)->style->outline.color = RGBA_BLACK; + + series = gog_plot_new_series( plot ); + labelData = go_data_vector_str_new( (gchar const * const *)info->labels, info->datasize, NULL ); + gog_series_set_dim( series, 0, labelData, NULL ); + go_data_emit_changed( GO_DATA(labelData) ); + + sliceData = go_data_vector_val_new( info->data, info->datasize, NULL ); + gog_series_set_dim( series, 1, sliceData, NULL ); + go_data_emit_changed( GO_DATA(sliceData) ); + + // fixme: colors + set_chart_titles( chart, info->title, info->subtitle ); + + pixbuf = create_graph_pixbuf( graph, info->width, info->height ); + + return pixbuf; +} + +/** + * data_rows:int + * data_cols:int + * data:doubles[], data_rows*data_cols + * x_axis_label:string + * y_axis_label:string + * row_labels:string[] + * col_labels:string[] + * col_colors:string[] + * rotate_row_labels:boolean + * stacked:boolean + **/ +GdkPixbuf* +gnc_html_graph_gog_create_barchart( GncHtmlBarChartInfo* info ) +{ + GogObject *graph, *chart; + GogPlot *plot; + GogSeries *series; + GogStyle *style; + GOData *label_data, *slice_data; + char *bar_type = "normal"; + int bar_overlap = 0 /*percent*/; // seperate bars; no overlap. + GdkPixbuf* pixbuf; + + if( !create_basic_plot_elements( "GogBarColPlot", &graph, &chart, &plot ) ) { + return FALSE; + } + gog_object_add_by_name( chart, "Legend", NULL ); + + if( info->stacked ) { + // when stacked, we want the bars on _top_ of eachother. + bar_type = "stacked"; + bar_overlap = 100 /*percent*/; + } + + g_object_set( G_OBJECT(plot), + //"vary_style_by_element", TRUE, + "type", bar_type, + "overlap_percentage", bar_overlap, + NULL); + label_data = go_data_vector_str_new( (gchar const * const *)info->row_labels, info->data_rows, NULL ); + { + // foreach row: + // series = row + GdkColor color; + int i; + for( i = 0; i < info->data_cols; i++ ) { + GError *err = NULL; + + series = gog_plot_new_series( plot ); + gog_object_set_name( GOG_OBJECT(series), info->col_labels[i], &err ); + if( err != NULL ) { + g_warning( "error setting name [%s] on series [%d]: [%s]", + info->col_labels[i], i, err->message); + } + + g_object_ref( label_data ); + gog_series_set_dim( series, 0, label_data, NULL ); + go_data_emit_changed( GO_DATA(label_data) ); + + slice_data = go_data_vector_val_new( info->data + (i*info->data_rows), info->data_rows, NULL ); + gog_series_set_dim( series, 1, slice_data, NULL ); + go_data_emit_changed( GO_DATA(slice_data) ); + + style = gog_styled_object_get_style( GOG_STYLED_OBJECT(series) ); + style->fill.type = GOG_FILL_STYLE_PATTERN; + if( gdk_color_parse( info->col_colors[i], &color ) ) { + style->fill.auto_back = FALSE; + go_pattern_set_solid( &style->fill.pattern, GDK_TO_UINT(color) ); + } else { + g_warning( "cannot parse color [%s]", info->col_colors[i] ); + } + } + } + + if( info->rotate_row_labels ) { + GogObject *object = gog_object_get_child_by_role( + chart, gog_object_find_role_by_name( chart, "X-Axis" ) ); + style = gog_styled_object_get_style( GOG_STYLED_OBJECT(object) ); + gog_style_set_text_angle( style, 90.0 ); + } + + set_chart_titles( chart, info->title, info->subtitle ); + set_chart_axis_labels( chart, info->x_axis_label, info->y_axis_label ); + + // we need to do this twice for the barchart... :p + gog_object_update( GOG_OBJECT(graph) ); + + pixbuf = create_graph_pixbuf( graph, info->width, info->height ); + g_debug( "barchart rendered." ); + + return pixbuf; +} + + +/** + * data_rows:int + * data_cols:int + * data:doubles[], data_rows*data_cols + * x_axis_label:string + * y_axis_label:string + * row_labels:string[] + * col_labels:string[] + * col_colors:string[] + * rotate_row_labels:boolean + * stacked:boolean + * markers:boolean + * major_grid:boolean + * minor_grid:boolean + **/ +GdkPixbuf* +gnc_html_graph_gog_create_linechart( GncHtmlLineChartInfo* info ) +{ + GogObject *graph, *chart; + GogPlot *plot; + GogSeries *series; + GogStyle *style; + GOData *label_data, *slice_data; + gchar* line_type = "normal"; + GdkPixbuf* pixbuf; + + if( !create_basic_plot_elements( "GogLinePlot", &graph, &chart, &plot ) ) { + return NULL; + } + gog_object_add_by_name( chart, "Legend", NULL ); + + if( info->stacked ) { + // when stacked, we want the lines on _top_ of eachother. + line_type = "stacked"; + } + + g_object_set( G_OBJECT(plot), + //"vary_style_by_element", TRUE, + "type", line_type, + "default-style-has-markers", info->markers, + NULL); + label_data = go_data_vector_str_new( (gchar const * const *)info->row_labels, info->data_rows, NULL ); + { + // foreach row: + // series = row + GdkColor color; + int i; + for( i = 0; i < info->data_cols; i++ ) { + GError *err = NULL; + + series = gog_plot_new_series( plot ); + gog_object_set_name( GOG_OBJECT(series), info->col_labels[i], &err ); + if( err != NULL ) { + g_warning( "error setting name [%s] on series [%d]: [%s]", + info->col_labels[i], i, err->message ); + } + + g_object_ref( label_data ); + gog_series_set_dim( series, 0, label_data, NULL ); + go_data_emit_changed( GO_DATA(label_data) ); + + slice_data = go_data_vector_val_new( info->data + (i* info->data_rows), info->data_rows, NULL ); + gog_series_set_dim( series, 1, slice_data, NULL ); + go_data_emit_changed( GO_DATA(slice_data) ); + + style = gog_styled_object_get_style( GOG_STYLED_OBJECT(series) ); + style->fill.type = GOG_FILL_STYLE_PATTERN; + if( gdk_color_parse( info->col_colors[i], &color ) ) { + style->fill.auto_back = FALSE; + go_pattern_set_solid( &style->fill.pattern, GDK_TO_UINT(color) ); + } else { + g_warning( "cannot parse color [%s]", info->col_colors[i] ); + } + } + } + + if( info->rotate_row_labels ) { + GogObject *object = gog_object_get_child_by_role( + chart, gog_object_find_role_by_name( chart, "X-Axis" ) ); + style = gog_styled_object_get_style( GOG_STYLED_OBJECT(object) ); + gog_style_set_text_angle( style, 90.0 ); + } + + if( info->major_grid || info->minor_grid ) { + GogObject *object; + gog_object_add_by_name( chart,"Grid", NULL ); + object = gog_object_get_child_by_role( chart, + gog_object_find_role_by_name( chart, "Y-Axis" ) ); + if( info->major_grid ) { + gog_object_add_by_name( GOG_OBJECT(object), "MajorGrid", NULL ); + } + if( info->minor_grid ) { + gog_object_add_by_name( GOG_OBJECT (object), "MinorGrid", NULL ); + } + } + + set_chart_titles( chart, info->title, info->subtitle ); + set_chart_axis_labels( chart, info->x_axis_label, info->y_axis_label ); + + // we need to do this twice for the linechart... :p + gog_object_update( GOG_OBJECT(graph) ); + + pixbuf = create_graph_pixbuf( graph, info->width, info->height ); + g_debug( "linechart rendered." ); + + return pixbuf; +} + +GdkPixbuf* +gnc_html_graph_gog_create_scatterplot( GncHtmlScatterPlotInfo* info ) +{ + GogObject *graph, *chart; + GogPlot *plot; + GogSeries *series; + GOData *sliceData; + GogStyle *style; + gboolean fill = FALSE; + + if( !create_basic_plot_elements( "GogXYPlot", &graph, &chart, &plot ) ) { + return NULL; + } + + series = gog_plot_new_series( plot ); + style = gog_styled_object_get_style( GOG_STYLED_OBJECT(series) ); + + sliceData = go_data_vector_val_new( info->xData, info->datasize, NULL ); + gog_series_set_dim( series, 0, sliceData, NULL ); + go_data_emit_changed( GO_DATA(sliceData) ); + + sliceData = go_data_vector_val_new( info->yData, info->datasize, NULL ); + gog_series_set_dim( series, 1, sliceData, NULL ); + go_data_emit_changed( GO_DATA(sliceData) ); + + /* set marker shape */ + if( info->marker_str != NULL ) { + GOMarkerShape shape; + + if( g_str_has_prefix( info->marker_str, "filled ") ) { + fill = TRUE; + info->marker_str += 7; + } + shape = go_marker_shape_from_str( info->marker_str ); + if( shape != GO_MARKER_NONE ) { + style->marker.auto_shape = FALSE; + go_marker_set_shape( style->marker.mark, shape ); + } else { + g_warning( "cannot parse marker shape [%s]", info->marker_str ); + } + } + + /* set marker and line colors */ + if( info->color_str != NULL ) { + GdkColor color; + if( gdk_color_parse( info->color_str, &color ) ) { + style->marker.auto_outline_color = FALSE; + go_marker_set_outline_color( style->marker.mark, GDK_TO_UINT(color) ); + style->line.auto_color = FALSE; + style->line.color = GDK_TO_UINT(color); + } else { + g_warning( "cannot parse color [%s]", info->color_str ); + } + } + + /* set marker fill colors */ + if( fill ) { + style->marker.auto_fill_color = style->marker.auto_outline_color; + go_marker_set_fill_color( style->marker.mark, + go_marker_get_outline_color( style->marker.mark ) ); + } else { + GogStyle *chart_style = gog_styled_object_get_style( GOG_STYLED_OBJECT(chart) ); + + if( chart_style->fill.type == GOG_FILL_STYLE_PATTERN + && chart_style->fill.pattern.pattern == GO_PATTERN_SOLID ) { + style->marker.auto_fill_color = FALSE; + go_marker_set_fill_color( style->marker.mark, chart_style->fill.pattern.back ); + } else if( chart_style->fill.type == GOG_FILL_STYLE_PATTERN + && chart_style->fill.pattern.pattern == GO_PATTERN_FOREGROUND_SOLID ) { + style->marker.auto_fill_color = FALSE; + go_marker_set_fill_color( style->marker.mark, chart_style->fill.pattern.fore ); + } else { + g_warning( "fill color of markers can only be set like a solid fill " + "pattern of the chart" ); + } + } + + set_chart_titles( chart, info->title, info->subtitle ); + set_chart_axis_labels( chart, info->x_axis_label, info->y_axis_label ); + + // And twice for the scatter, too... :p + gog_object_update( GOG_OBJECT(graph) ); + + return create_graph_pixbuf( graph, info->width, info->height ); +} diff --git a/src/html/gnc-html-graph-gog.h b/src/html/gnc-html-graph-gog.h new file mode 100644 index 0000000000..79babe2549 --- /dev/null +++ b/src/html/gnc-html-graph-gog.h @@ -0,0 +1,96 @@ +/******************************************************************** + * gnc-html_graph_gog.h -- display html with gnc special * + * tags * + * Copyright (C) 2000 Bill Gribble * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_GRAPH_GOG_H +#define GNC_HTML_GRAPH_GOG_H 1 + +typedef struct { + gint width; + gint height; + const gchar* title; + const gchar* subtitle; + gint datasize; + gdouble* data; + gchar** labels; + gchar** colors; +} GncHtmlPieChartInfo; + +typedef struct { + gint width; + gint height; + const gchar* title; + const gchar* subtitle; + gint data_rows; + gint data_cols; + gdouble* data; + gchar** col_labels; + gchar** row_labels; + gchar** col_colors; + const gchar* x_axis_label; + const gchar* y_axis_label; + gboolean rotate_row_labels; + gboolean stacked; +} GncHtmlBarChartInfo; + +typedef struct { + gint width; + gint height; + const gchar* title; + const gchar* subtitle; + gint data_rows; + gint data_cols; + gdouble* data; + gchar** col_labels; + gchar** row_labels; + gchar** col_colors; + gboolean rotate_row_labels; + gboolean stacked; + gboolean markers; + gboolean major_grid; + gboolean minor_grid; + const gchar* x_axis_label; + const gchar* y_axis_label; +} GncHtmlLineChartInfo; + +typedef struct { + gint width; + gint height; + const gchar* title; + const gchar* subtitle; + const gchar* x_axis_label; + const gchar* y_axis_label; + gint datasize; + gdouble* xData; + gdouble* yData; + const gchar* marker_str; + const gchar* color_str; +} GncHtmlScatterPlotInfo; + +void gnc_html_graph_gog_init( void ); +GdkPixbuf* gnc_html_graph_gog_create_piechart( GncHtmlPieChartInfo* info ); +GdkPixbuf* gnc_html_graph_gog_create_barchart( GncHtmlBarChartInfo* info ); +GdkPixbuf* gnc_html_graph_gog_create_linechart( GncHtmlLineChartInfo* info ); +GdkPixbuf* gnc_html_graph_gog_create_scatterplot( GncHtmlScatterPlotInfo* info ); + +#endif /* GNC_HTML_GRAPH_GOG_H */ diff --git a/src/html/gnc-html-gtkhtml-p.h b/src/html/gnc-html-gtkhtml-p.h new file mode 100644 index 0000000000..58f5826302 --- /dev/null +++ b/src/html/gnc-html-gtkhtml-p.h @@ -0,0 +1,34 @@ +/******************************************************************** + * gnc-html-gtkhtml-p.h -- display html with gnc special tags * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_GTKHTML_P_H +#define GNC_HTML_GTKHTML_P_H + +#include "gnc-html-p.h" + +struct _GncHtmlGtkhtmlPrivate { + struct _GncHtmlPrivate base; + + GtkWidget* html; /* gtkhtml widget itself */ +}; + +#endif diff --git a/src/html/gnc-html-gtkhtml.c b/src/html/gnc-html-gtkhtml.c new file mode 100644 index 0000000000..8c517b16b9 --- /dev/null +++ b/src/html/gnc-html-gtkhtml.c @@ -0,0 +1,932 @@ +/******************************************************************** + * gnc-html-gtkhtml.c -- display HTML with some special gnucash * + * tags. * + * * + * Copyright (C) 2000 Bill Gribble * + * Copyright (C) 2001 Linas Vepstas * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + ********************************************************************/ + +// libgtkhtml docs: +// http://www.fifi.org/doc/libgtkhtml-dev/html/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "Account.h" +#include "print-session.h" +#include "gnc-engine.h" +#include "gnc-gui-query.h" +#include "gnc-html.h" +#include "gnc-html-gtkhtml.h" +#include "gnc-html-history.h" +#include "gnc-html-graph-gog-gtkhtml.h" +#include "gnc-ui.h" +#include "gnc-ui-util.h" + +G_DEFINE_TYPE(GncHtmlGtkhtml, gnc_html_gtkhtml, GNC_TYPE_HTML ) + +static void gnc_html_gtkhtml_dispose( GObject* obj ); +static void gnc_html_gtkhtml_finalize( GObject* obj ); +static void gnc_html_gtkhtml_class_init( GncHtmlGtkhtmlClass* klass ); +static void gnc_html_gtkhtml_init( GncHtmlGtkhtml* gs ); + +//#define GNC_HTML_GTKHTML_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), GNC_TYPE_HTML_GTKHTML, GncHtmlGtkhtmlPrivate)) +#define GNC_HTML_GTKHTML_GET_PRIVATE(o) (GNC_HTML_GTKHTML(o)->priv) + +#include "gnc-html-gtkhtml-p.h" + +/* indicates the debugging module that this .o belongs to. */ +static QofLogModule log_module = GNC_MOD_HTML; + +/* hashes for URLType -> protocol and protocol -> URLType */ +//extern GHashTable* gnc_html_type_to_proto_hash; +extern GHashTable* gnc_html_proto_to_type_hash; + +/* hashes an HTML classid to a handler function */ +extern GHashTable* gnc_html_object_handlers; + +/* hashes handlers for loading different URLType data */ +extern GHashTable* gnc_html_stream_handlers; + +/* hashes handlers for handling different URLType data */ +extern GHashTable* gnc_html_url_handlers; + +static char error_404_format[] = "

%s

%s"; +static char error_404_title[] = N_("Not found"); +static char error_404_body[] = N_("The specified URL could not be loaded."); + +static void gtkhtml_pre_3_10_1_bug_workaround( GtkHTMLEmbedded* eb ); +static void gnc_html_url_requested_cb( GtkHTML* html, gchar* url, + GtkHTMLStream* handle, gpointer data ); +static void gnc_html_on_url_cb( GtkHTML* html, const gchar* url, gpointer data ); +static void gnc_html_set_base_cb( GtkHTML* gtkhtml, const gchar* base, gpointer data ); +static void gnc_html_link_clicked_cb( GtkHTML* html, const gchar* url, gpointer data ); +static gboolean gnc_html_object_requested_cb( GtkHTML* html, GtkHTMLEmbedded* eb, + gpointer data ); +static int gnc_html_button_press_cb( GtkWidget* widg, GdkEventButton* event, + gpointer user_data ); +static void impl_gtkhtml_show_url( GncHtml* self, URLType type, + const gchar* location, const gchar* label, + gboolean new_window_hint ); +static void impl_gtkhtml_show_data( GncHtml* self, const gchar* data, int datalen ); +static void impl_gtkhtml_reload( GncHtml* self ); +static void impl_gtkhtml_copy_to_clipboard( GncHtml* self ); +static gboolean impl_gtkhtml_export_to_file( GncHtml* self, const gchar* filepath ); +static void impl_gtkhtml_print( GncHtml* self ); +static void impl_gtkhtml_cancel( GncHtml* self ); +static void impl_gtkhtml_set_parent( GncHtml* self, GtkWindow* parent ); + +static void +gnc_html_gtkhtml_init( GncHtmlGtkhtml* self ) +{ + GncHtmlGtkhtmlPrivate* priv; + GncHtmlGtkhtmlPrivate* new_priv; + + new_priv = g_realloc( GNC_HTML(self)->priv, sizeof(GncHtmlGtkhtmlPrivate) ); + priv = self->priv = new_priv; + + priv->html = gtk_html_new(); + gtk_container_add( GTK_CONTAINER(priv->base.container), + GTK_WIDGET(priv->html) ); + +#ifdef HAVE_GTK_2_10 + g_object_ref_sink( priv->base.container ); +#else + g_object_ref( priv->base.container ); + gtk_object_sink( GTK_OBJECT(priv->base.container) ); +#endif + + /* signals */ + g_signal_connect( priv->html, "url_requested", + G_CALLBACK(gnc_html_url_requested_cb), + self); + + g_signal_connect( priv->html, "on_url", + G_CALLBACK(gnc_html_on_url_cb), + self ); + + g_signal_connect( priv->html, "set_base", + G_CALLBACK(gnc_html_set_base_cb), + self); + + g_signal_connect(priv->html, "link_clicked", + G_CALLBACK(gnc_html_link_clicked_cb), + self); + + g_signal_connect (priv->html, "object_requested", + G_CALLBACK (gnc_html_object_requested_cb), + self); + + g_signal_connect (priv->html, "button_press_event", + G_CALLBACK (gnc_html_button_press_cb), + self); + + gtk_html_load_empty(GTK_HTML(priv->html)); + + LEAVE("retval %p", self); +} + +static void +gnc_html_gtkhtml_class_init( GncHtmlGtkhtmlClass* klass ) +{ + GObjectClass* gobject_class = G_OBJECT_CLASS(klass); + GncHtmlClass* html_class = GNC_HTML_CLASS(klass); + + gobject_class->dispose = gnc_html_gtkhtml_dispose; + gobject_class->finalize = gnc_html_gtkhtml_finalize; + + html_class->show_url = impl_gtkhtml_show_url; + html_class->show_data = impl_gtkhtml_show_data; + html_class->reload = impl_gtkhtml_reload; + html_class->copy_to_clipboard = impl_gtkhtml_copy_to_clipboard; + html_class->export_to_file = impl_gtkhtml_export_to_file; + html_class->print = impl_gtkhtml_print; + html_class->cancel = impl_gtkhtml_cancel; + html_class->set_parent = impl_gtkhtml_set_parent; + + // Initialize graphing support + gnc_html_graph_gog_gtkhtml_init(); +} + +static void +gnc_html_gtkhtml_dispose( GObject* obj ) +{ + GncHtmlGtkhtml* self = GNC_HTML_GTKHTML(obj); + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + + if( priv->html != NULL ) { + g_object_unref( G_OBJECT(priv->html) ); + priv->html = NULL; + } + + G_OBJECT_CLASS(gnc_html_gtkhtml_parent_class)->dispose( obj ); +} + +static void +gnc_html_gtkhtml_finalize( GObject* obj ) +{ + GncHtmlGtkhtml* self = GNC_HTML_GTKHTML(obj); + +// if( self->priv != NULL ) { +// g_free( self->priv ); + self->priv = NULL; +// } + + G_OBJECT_CLASS(gnc_html_gtkhtml_parent_class)->finalize( obj ); +} + +/*****************************************************************************/ + +static char* +extract_base_name(URLType type, const gchar* path) +{ + gchar machine_rexp[] = "^(//[^/]*)/*(/.*)?$"; + gchar path_rexp[] = "^/*(.*)/+([^/]*)$"; + regex_t compiled_m, compiled_p; + regmatch_t match[4]; + gchar * machine=NULL, * location = NULL, * base=NULL; + gchar * basename=NULL; + + DEBUG(" "); + if(!path) return NULL; + + regcomp(&compiled_m, machine_rexp, REG_EXTENDED); + regcomp(&compiled_p, path_rexp, REG_EXTENDED); + + if (!safe_strcmp (type, URL_TYPE_HTTP) || + !safe_strcmp (type, URL_TYPE_SECURE) || + !safe_strcmp (type, URL_TYPE_FTP)) { + + /* step 1: split the machine name away from the path + * components */ + if(!regexec(&compiled_m, path, 4, match, 0)) { + /* $1 is the machine name */ + if(match[1].rm_so != -1) { + machine = g_strndup(path+match[1].rm_so, + match[1].rm_eo - match[1].rm_so); + } + /* $2 is the path */ + if(match[2].rm_so != -1) { + location = g_strndup(path+match[2].rm_so, + match[2].rm_eo - match[2].rm_so); + } + } + + } else { + location = g_strdup(path); + } + /* step 2: split up the path into prefix and file components */ + if(location) { + if(!regexec(&compiled_p, location, 4, match, 0)) { + if(match[1].rm_so != -1) { + base = g_strndup(location+match[1].rm_so, + match[1].rm_eo - match[1].rm_so); + } else { + base = NULL; + } + } + } + + regfree(&compiled_m); + regfree(&compiled_p); + + if(machine) { + if(base && (strlen(base) > 0)) { + basename = g_strconcat(machine, "/", base, "/", NULL); + } else { + basename = g_strconcat(machine, "/", NULL); + } + } else { + if(base && (strlen(base) > 0)) { + basename = g_strdup(base); + } else { + basename = NULL; + } + } + + g_free(machine); + g_free(base); + g_free(location); + return basename; +} + +static gboolean +http_allowed() +{ + return TRUE; +} + +static gboolean +https_allowed() +{ + return TRUE; +} + +/************************************************************ + * gnc_html_start_request: starts the gnc-http object working on an + * http/https request. + ************************************************************/ +static void +gnc_html_start_request( GncHtmlGtkhtml* self, gchar * uri, GtkHTMLStream * handle ) +{ + GList * handles = NULL; + gint need_request = FALSE; + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + + /* we want to make a list of handles to fill with this URI. + * multiple handles with the same URI will all get filled when the + * request comes in. */ + DEBUG("requesting %s", uri); + handles = g_hash_table_lookup( priv->base.request_info, uri ); + if( handles == NULL ) { + need_request = TRUE; + } + + handles = g_list_append( handles, handle ); + g_hash_table_insert( priv->base.request_info, uri, handles ); + + if( need_request ) { + g_critical("we've not supported network requests for years"); + } +} + +/******************************************************************** + * gnc_html_load_to_stream : actually do the work of loading the HTML + * or binary data referenced by a URL and feeding it into the GtkHTML + * widget. + ********************************************************************/ + +static void +gnc_html_load_to_stream( GncHtmlGtkhtml* self, GtkHTMLStream* handle, + URLType type, const gchar* location, + const gchar* label ) +{ + gchar* fdata = NULL; + int fdata_len = 0; + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + + DEBUG( "type %s, location %s, label %s", type ? type : "(null)", + location ? location : "(null)", label ? label : "(null)"); + + g_return_if_fail( self != NULL ); + + if( gnc_html_stream_handlers != NULL ) { + GncHTMLStreamCB stream_handler; + + stream_handler = g_hash_table_lookup( gnc_html_stream_handlers, type ); + if( stream_handler ) { + gboolean ok = stream_handler( location, &fdata, &fdata_len ); + + if( ok ) { + fdata = fdata ? fdata : g_strdup( "" ); + gtk_html_write( GTK_HTML(priv->html), handle, fdata, fdata_len ); + gtk_html_end( GTK_HTML(priv->html), handle, GTK_HTML_STREAM_OK ); + } else { + fdata = fdata ? fdata : + g_strdup_printf( error_404_format, + _(error_404_title), _(error_404_body) ); + gtk_html_write( GTK_HTML(priv->html), handle, fdata, strlen(fdata) ); + gtk_html_end( GTK_HTML(priv->html), handle, GTK_HTML_STREAM_ERROR ); + } + + g_free( fdata ); + + if( label ) { + while( gtk_events_pending() ) { + gtk_main_iteration(); + } + gtk_html_jump_to_anchor( GTK_HTML(priv->html), label ); + } + + return; + } + } + + do { + if( !safe_strcmp( type, URL_TYPE_SECURE ) || + !safe_strcmp( type, URL_TYPE_HTTP ) ) { + + if( !safe_strcmp( type, URL_TYPE_SECURE ) ) { + if( !https_allowed() ) { + gnc_error_dialog( priv->base.parent, + _("Secure HTTP access is disabled. " + "You can enable it in the Network section of " + "the Preferences dialog.")); + break; + } + } + + if( !http_allowed() ) { + gnc_error_dialog( priv->base.parent, + _("Network HTTP access is disabled. " + "You can enable it in the Network section of " + "the Preferences dialog.")); + } else { + char *fullurl; + + fullurl = gnc_build_url( type, location, label ); + gnc_html_start_request( self, fullurl, handle ); + } + + } else { + PWARN( "load_to_stream for inappropriate type\n" + "\turl = '%s#%s'\n", + location ? location : "(null)", + label ? label : "(null)" ); + fdata = g_strdup_printf( error_404_format, + _(error_404_title), _(error_404_body) ); + gtk_html_write( GTK_HTML(priv->html), handle, fdata, strlen (fdata) ); + gtk_html_end( GTK_HTML(priv->html), handle, GTK_HTML_STREAM_ERROR ); + g_free( fdata ); + } + + } while( FALSE ); +} + +/******************************************************************** + * gnc_html_link_clicked_cb - called when user left-clicks on html + * anchor. + ********************************************************************/ + +static void +gnc_html_link_clicked_cb( GtkHTML* html, const gchar* url, gpointer data ) +{ + URLType type; + gchar* location = NULL; + gchar* label = NULL; + GncHtmlGtkhtml* self = GNC_HTML_GTKHTML(data); + + DEBUG("Clicked %s", url); + type = gnc_html_parse_url( GNC_HTML(self), url, &location, &label ); + gnc_html_show_url( GNC_HTML(self), type, location, label, 0 ); + g_free( location ); + g_free( label ); +} + + +/******************************************************************** + * gnc_html_url_requested_cb - called when a URL needs to be + * loaded within the loading of a page (embedded image). + ********************************************************************/ + +static void +gnc_html_url_requested_cb( GtkHTML* html, gchar* url, + GtkHTMLStream* handle, gpointer data ) +{ + URLType type; + gchar* location = NULL; + gchar* label = NULL; + GncHtmlGtkhtml* self = GNC_HTML_GTKHTML(data); + + DEBUG( "requesting %s", url ); + type = gnc_html_parse_url( GNC_HTML(self), url, &location, &label ); + gnc_html_load_to_stream( self, handle, type, location, label ); + g_free( location ); + g_free( label ); +} + + +/******************************************************************** + * gnc_html_object_requested_cb - called when an applet needs to be + * loaded. + ********************************************************************/ + +static gboolean +gnc_html_object_requested_cb( GtkHTML* html, GtkHTMLEmbedded* eb, + gpointer data ) +{ + GncHtmlGtkhtml* self = GNC_HTML_GTKHTML(data); + GncHTMLObjectCB h; + + DEBUG( " " ); + if( !eb || !(eb->classid) || !gnc_html_object_handlers ) return FALSE; + + gtkhtml_pre_3_10_1_bug_workaround( eb ); + h = g_hash_table_lookup( gnc_html_object_handlers, eb->classid ); + if( h ) { + return h( GNC_HTML(self), eb, data ); + } else { + return FALSE; + } +} + + +/******************************************************************** + * gnc_html_on_url_cb - called when user rolls over html anchor + ********************************************************************/ + +static void +gnc_html_on_url_cb( GtkHTML* html, const gchar* url, gpointer data ) +{ + GncHtmlGtkhtml* self = GNC_HTML_GTKHTML(data); + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + + DEBUG( "Rollover %s", url ? url : "(null)" ); + g_free( priv->base.current_link ); + priv->base.current_link = g_strdup( url ); + if( priv->base.flyover_cb ) { + (priv->base.flyover_cb)( GNC_HTML(self), url, priv->base.flyover_cb_data ); + } +} + + +/******************************************************************** + * gnc_html_set_base_cb + ********************************************************************/ + +static void +gnc_html_set_base_cb( GtkHTML* gtkhtml, const gchar* base, + gpointer data ) +{ + GncHtmlGtkhtml* self = GNC_HTML_GTKHTML(data); + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + URLType type; + gchar* location = NULL; + gchar* label = NULL; + + DEBUG( "Setting base location to %s", base ); + type = gnc_html_parse_url( GNC_HTML(self), base, &location, &label ); + + g_free( priv->base.base_location ); + g_free( label ); + + priv->base.base_type = type; + priv->base.base_location = location; +} + + +/******************************************************************** + * gnc_html_button_press_cb + * mouse button callback (if any) + ********************************************************************/ + +static int +gnc_html_button_press_cb( GtkWidget* widg, GdkEventButton* event, + gpointer user_data ) +{ + GncHtmlGtkhtml* self = GNC_HTML_GTKHTML(user_data); + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + + DEBUG( "Button Press" ); + if( priv->base.button_cb != NULL ) { + (priv->base.button_cb)( GNC_HTML(self), event, priv->base.button_cb_data ); + return TRUE; + } else { + return FALSE; + } +} + +/******************************************************************** + * gnc_html_open_scm + * insert some scheme-generated HTML + ********************************************************************/ + +static void +gnc_html_open_scm( GncHtmlGtkhtml* self, const gchar * location, + const gchar * label, int newwin ) +{ + PINFO("location='%s'", location ? location : "(null)"); +} + + +/******************************************************************** + * gnc_html_show_data + * display some HTML that the creator of the gnc-html got from + * somewhere. + ********************************************************************/ + +static void +impl_gtkhtml_show_data( GncHtml* self, const char * data, int datalen ) +{ + GtkHTMLStream * handle; + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + + DEBUG( "datalen %d, data %20.20s", datalen, data ); + handle = gtk_html_begin( GTK_HTML(priv->html) ); + gtk_html_write( GTK_HTML(priv->html), handle, data, datalen ); + gtk_html_end( GTK_HTML(priv->html), handle, GTK_HTML_STREAM_OK ); +} + +/******************************************************************** + * gnc_html_show_url + * + * open a URL. This is called when the user clicks a link or + * for the creator of the gnc_html window to explicitly request + * a URL. + ********************************************************************/ + +static void +impl_gtkhtml_show_url( GncHtml* self, URLType type, + const gchar* location, const gchar* label, + gboolean new_window_hint ) +{ + GncHTMLUrlCB url_handler; + GtkHTMLStream * handle; + gboolean new_window; + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + + DEBUG(" "); + + if( self == NULL ) return; + if( location == NULL ) return; + + /* make sure it's OK to show this URL type in this window */ + if( new_window_hint == 0 ) { + if( priv->base.urltype_cb ) { + new_window = !((priv->base.urltype_cb)( type )); + } else { + new_window = FALSE; + } + } else { + new_window = TRUE; + } + + if( !new_window ) { + gnc_html_cancel( GNC_HTML(self) ); + } + + if( gnc_html_url_handlers ) { + url_handler = g_hash_table_lookup( gnc_html_url_handlers, type ); + } else { + url_handler = NULL; + } + + if( url_handler ) { + GNCURLResult result; + gboolean ok; + + result.load_to_stream = FALSE; + result.url_type = type; + result.location = NULL; + result.label = NULL; + result.base_type = URL_TYPE_FILE; + result.base_location = NULL; + result.error_message = NULL; + + ok = url_handler( location, label, new_window, &result ); + if( !ok ) { + if( result.error_message ) { + gnc_error_dialog( priv->base.parent, "%s", result.error_message ); + } else { + /* %s is a URL (some location somewhere). */ + gnc_error_dialog( priv->base.parent, _("There was an error accessing %s."), location ); + } + + if( priv->base.load_cb ) { + priv->base.load_cb( GNC_HTML(self), result.url_type, + location, label, priv->base.load_cb_data ); + } + } else if( result.load_to_stream ) { + gnc_html_history_node *hnode; + const char *new_location; + const char *new_label; + GtkHTMLStream * stream; + + new_location = result.location ? result.location : location; + new_label = result.label ? result.label : label; + hnode = gnc_html_history_node_new( result.url_type, new_location, new_label ); + + gnc_html_history_append( priv->base.history, hnode ); + + g_free( priv->base.base_location ); + priv->base.base_type = result.base_type; + priv->base.base_location = + g_strdup( extract_base_name( result.base_type, new_location ) ); + DEBUG( "resetting base location to %s", + priv->base.base_location ? priv->base.base_location : "(null)" ); + + stream = gtk_html_begin( GTK_HTML(priv->html) ); + gnc_html_load_to_stream( GNC_HTML_GTKHTML(self), stream, result.url_type, + new_location, new_label ); + + if( priv->base.load_cb != NULL ) { + priv->base.load_cb( GNC_HTML(self), result.url_type, + new_location, new_label, priv->base.load_cb_data ); + } + } + + g_free( result.location ); + g_free( result.label ); + g_free( result.base_location ); + g_free( result.error_message ); + + return; + } + + if( safe_strcmp( type, URL_TYPE_SCHEME ) == 0 ) { + gnc_html_open_scm( GNC_HTML_GTKHTML(self), location, label, new_window ); + + } else if( safe_strcmp( type, URL_TYPE_JUMP ) == 0 ) { + gtk_html_jump_to_anchor( GTK_HTML(priv->html), label ); + + } else if( safe_strcmp( type, URL_TYPE_SECURE ) == 0 || + safe_strcmp( type, URL_TYPE_HTTP ) == 0 || + safe_strcmp( type, URL_TYPE_FILE ) == 0 ) { + + do { + if( safe_strcmp( type, URL_TYPE_SECURE ) == 0 ) { + if( !https_allowed() ) { + gnc_error_dialog( priv->base.parent, + _("Secure HTTP access is disabled. " + "You can enable it in the Network section of " + "the Preferences dialog.") ); + break; + } + } + + if( safe_strcmp( type, URL_TYPE_HTTP ) == 0 ) { + if( !http_allowed() ) { + gnc_error_dialog( priv->base.parent, + _("Network HTTP access is disabled. " + "You can enable it in the Network section of " + "the Preferences dialog.") ); + break; + } + } + + priv->base.base_type = type; + + if( priv->base.base_location != NULL ) g_free( priv->base.base_location ); + priv->base.base_location = extract_base_name( type, location ); + + /* FIXME : handle new_window = 1 */ + gnc_html_history_append( priv->base.history, + gnc_html_history_node_new( type, location, label ) ); + handle = gtk_html_begin( GTK_HTML(priv->html) ); + gnc_html_load_to_stream( GNC_HTML_GTKHTML(self), handle, type, location, label ); + + } while( FALSE ); + + } else { + PERR( "URLType %s not supported.", type ); + } + + if( priv->base.load_cb != NULL ) { + (priv->base.load_cb)( GNC_HTML(self), type, location, label, priv->base.load_cb_data ); + } +} + + +/******************************************************************** + * gnc_html_reload + * reload the current page + ********************************************************************/ + +static void +impl_gtkhtml_reload( GncHtml* self ) +{ + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + gnc_html_history_node * n; + + DEBUG(" "); + n = gnc_html_history_get_current( priv->base.history ); + if( n != NULL ) { + gnc_html_show_url( self, n->type, n->location, n->label, 0 ); + } +} + + +/******************************************************************** + * gnc_html_gtkhtml_new + * create and set up a new gtkhtml widget. + ********************************************************************/ + +GncHtml* +gnc_html_gtkhtml_new( void ) +{ + GncHtmlGtkhtml* self = g_object_new( GNC_TYPE_HTML_GTKHTML, NULL ); + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + + return GNC_HTML(self); +} + +/******************************************************************** + * gnc_html_cancel + * cancel any outstanding HTML fetch requests. + ********************************************************************/ + +static gboolean +gtkhtml_cancel_helper(gpointer key, gpointer value, gpointer user_data) +{ + g_free(key); + g_list_free((GList *)value); + return TRUE; +} + +static void +impl_gtkhtml_cancel( GncHtml* self ) +{ + GncHtmlGtkhtmlPrivate* priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + /* remove our own references to requests */ + //gnc_http_cancel_requests( priv->http ); + + g_hash_table_foreach_remove( priv->base.request_info, gtkhtml_cancel_helper, NULL ); +} + +static void +impl_gtkhtml_copy_to_clipboard( GncHtml* self ) +{ + GncHtmlGtkhtmlPrivate* priv; + + g_return_if_fail( self != NULL ); + + priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + gtk_html_copy( GTK_HTML(priv->html) ); +} + +/************************************************************** + * gnc_html_export_to_file : wrapper around the builtin function in gtkhtml + **************************************************************/ + +static gboolean +raw_html_receiver( gpointer engine, + const gchar* data, + size_t len, + gpointer user_data ) +{ + FILE *fh = (FILE *) user_data; + size_t written; + + do { + written = fwrite (data, 1, len, fh); + len -= written; + } while (len > 0); + return TRUE; +} + +static gboolean +impl_gtkhtml_export_to_file( GncHtml* self, const char *filepath ) +{ + FILE *fh; + GncHtmlGtkhtmlPrivate* priv; + + g_return_val_if_fail( self != NULL, FALSE ); + g_return_val_if_fail( filepath != NULL, FALSE ); + + priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + fh = g_fopen( filepath, "w" ); + if( fh == 0 ) + return FALSE; + + gtk_html_save( GTK_HTML(priv->html), GINT_TO_POINTER(raw_html_receiver), fh ); + fclose (fh); + + return TRUE; +} + +#ifdef GTKHTML_USES_GTKPRINT +static void +draw_page_cb(GtkPrintOperation *operation, GtkPrintContext *context, + gint page_nr, gpointer user_data) +{ + GncHtmlGtkhtml* self = GNC_HTML_GTKHTML(user_data); + GncHtmlGtkhtmlPrivate* priv; + + priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + gtk_html_print_page( GTK_HTML(priv->html), context ); +} + +static void +impl_gtkhtml_print( GncHtml* self ) +{ + GtkPrintOperation *print; + GtkPrintOperationResult res; + GncHtmlGtkhtmlPrivate* priv; + + priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + print = gtk_print_operation_new(); + + gnc_print_operation_init(print); + gtk_print_operation_set_use_full_page(print, FALSE); + gtk_print_operation_set_unit(print, GTK_UNIT_POINTS); + gtk_print_operation_set_n_pages(print, 1); + g_signal_connect(print, "draw_page", G_CALLBACK(draw_page_cb), self); + + res = gtk_print_operation_run(print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + GTK_WINDOW(priv->base.parent), NULL); + + if( res == GTK_PRINT_OPERATION_RESULT_APPLY ) { + gnc_print_operation_save_print_settings( print ); + } + + g_object_unref(print); +} + +#else /* !GTKHTML_USES_GTKPRINT */ +void +gnc_html_print( GncHtml* html ) +{ + PrintSession *ps; + + ps = gnc_print_session_create( FALSE ); + if( ps == NULL ) { + /* user cancelled */ + return; + } + + gtk_html_print( GTK_HTML(html->html), ps->context ); + gnc_print_session_done( ps ); +} +#endif /* GTKHTML_USES_GTKPRINT */ + +static void +impl_gtkhtml_set_parent( GncHtml* self, GtkWindow* parent ) +{ + GncHtmlGtkhtmlPrivate* priv; + + g_return_if_fail( self != NULL ); + + priv = GNC_HTML_GTKHTML_GET_PRIVATE(self); + priv->base.parent = GTK_WIDGET(parent); +} + +const gchar* +gnc_html_get_embedded_param( gpointer eb, const gchar* param_name ) +{ + GtkHTMLEmbedded* gtk_eb = (GtkHTMLEmbedded*)eb; + + return (const gchar *)g_hash_table_lookup(gtk_eb->params, param_name); +} + +static void +gtkhtml_pre_3_10_1_bug_workaround(GtkHTMLEmbedded *eb) +{ + /* HACK ALERT! Compensate for bug in gtkhtml < 3.10.1 + Gtkhtml set the width parameter twice (=width, =height), so both, + width (==height) and height (<1) were incorrect. */ + if( eb->height < 1 ) { + eb->height = eb->width; /* only squares here :( */ + } +} diff --git a/src/html/gnc-html-gtkhtml.h b/src/html/gnc-html-gtkhtml.h new file mode 100644 index 0000000000..e0339aa3a6 --- /dev/null +++ b/src/html/gnc-html-gtkhtml.h @@ -0,0 +1,59 @@ +/******************************************************************** + * gnc-html-gtkhtml.h -- display html with gnc special tags * + * Copyright (C) 2000 Bill Gribble * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_GTKHTML_H +#define GNC_HTML_GTKHTML_H + +#include +#include "gnc-html.h" + +G_BEGIN_DECLS + +#define GNC_TYPE_HTML_GTKHTML (gnc_html_gtkhtml_get_type()) +#define GNC_HTML_GTKHTML(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNC_TYPE_HTML_GTKHTML, GncHtmlGtkhtml)) +#define GNC_HTML_GTKHTML_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GNC_TYPE_HTML_GTKHTML, GncHtmlGtkhtmlClass)) +#define GNC_IS_HTML_GTKHTML(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GNC_TYPE_HTML_GTKHTML)) +#define GNC_IS_HTML_GTKHTML_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GNC_TYPE_HTML_GTKHTML)) +#define GNC_HTML_GTKHTML_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GNC_TYPE_HTML_GTKHTML, GncHtmlGtkhtmlClass)) + +typedef struct _GncHtmlGtkhtml GncHtmlGtkhtml; +typedef struct _GncHtmlGtkhtmlClass GncHtmlGtkhtmlClass; +typedef struct _GncHtmlGtkhtmlPrivate GncHtmlGtkhtmlPrivate; + +struct _GncHtmlGtkhtml { + GncHtml parent_instance; + + /*< private >*/ + GncHtmlGtkhtmlPrivate* priv; +}; + +struct _GncHtmlGtkhtmlClass { + GncHtmlClass parent_class; +}; + +GType gnc_html_gtkhtml_get_type( void ); + +GncHtml* gnc_html_gtkhtml_new( void ); + +G_END_DECLS + +#endif diff --git a/src/gnome-utils/gnc-html-history.c b/src/html/gnc-html-history.c similarity index 100% rename from src/gnome-utils/gnc-html-history.c rename to src/html/gnc-html-history.c diff --git a/src/gnome-utils/gnc-html-history.h b/src/html/gnc-html-history.h similarity index 100% rename from src/gnome-utils/gnc-html-history.h rename to src/html/gnc-html-history.h diff --git a/src/html/gnc-html-p.h b/src/html/gnc-html-p.h new file mode 100644 index 0000000000..a742574781 --- /dev/null +++ b/src/html/gnc-html-p.h @@ -0,0 +1,49 @@ +/******************************************************************** + * gnc-html-p.h -- display html with gnc special tags * + * Copyright (C) 2000 Bill Gribble * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_P_H +#define GNC_HTML_P_H + +struct _GncHtmlPrivate { + GtkWidget* parent; /* window this html goes into */ + GtkWidget* container; /* parent of the gtkhtml widget */ + gchar* current_link; /* link under mouse pointer */ + + URLType base_type; /* base of URL (path - filename) */ + gchar* base_location; + + GHashTable* request_info; /* hash uri to GList of GtkHTMLStream * */ + + /* callbacks */ + GncHTMLUrltypeCB urltype_cb; /* is this type OK for this instance? */ + GncHTMLLoadCB load_cb; + GncHTMLFlyoverCB flyover_cb; + GncHTMLButtonCB button_cb; + + gpointer flyover_cb_data; + gpointer load_cb_data; + gpointer button_cb_data; + + gnc_html_history * history; +}; + +#endif diff --git a/src/html/gnc-html-webkit-p.h b/src/html/gnc-html-webkit-p.h new file mode 100644 index 0000000000..9204110ebf --- /dev/null +++ b/src/html/gnc-html-webkit-p.h @@ -0,0 +1,35 @@ +/******************************************************************** + * gnc-html-webkit-p.h -- display html with gnc special tags * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_WEBKIT_P_H +#define GNC_HTML_WEBKIT_P_H + +#include "gnc-html-p.h" + +struct _GncHtmlWebkitPrivate { + struct _GncHtmlPrivate base; + + WebKitWebView* web_view; /* webkit widget itself */ + gchar* html_string; /* html string being displayed */ +}; + +#endif diff --git a/src/html/gnc-html-webkit.c b/src/html/gnc-html-webkit.c new file mode 100644 index 0000000000..795a24969e --- /dev/null +++ b/src/html/gnc-html-webkit.c @@ -0,0 +1,930 @@ +/******************************************************************** + * gnc-html_webkit.c -- display HTML with some special gnucash tags.* + * * + * Copyright (C) 2000 Bill Gribble * + * Copyright (C) 2001 Linas Vepstas * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + ********************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Account.h" +#include "gnc-gui-query.h" +#include "gnc-engine.h" +#include "gnc-html.h" +#include "gnc-html-webkit.h" +#include "gnc-html-history.h" +#include "gnc-html-graph-gog-webkit.h" + +G_DEFINE_TYPE(GncHtmlWebkit, gnc_html_webkit, GNC_TYPE_HTML ) + +static void gnc_html_webkit_dispose( GObject* obj ); +static void gnc_html_webkit_finalize( GObject* obj ); +static void gnc_html_webkit_class_init( GncHtmlWebkitClass* klass ); +static void gnc_html_webkit_init( GncHtmlWebkit* gs ); + +#define GNC_HTML_WEBKIT_GET_PRIVATE(o) (GNC_HTML_WEBKIT(o)->priv) + +#include "gnc-html-webkit-p.h" + +/* indicates the debugging module that this .o belongs to. */ +static QofLogModule log_module = GNC_MOD_HTML; + +/* hashes for URLType -> protocol and protocol -> URLType */ +//extern GHashTable* gnc_html_type_to_proto_hash; +extern GHashTable* gnc_html_proto_to_type_hash; + +/* hashes an HTML classid to a handler function */ +extern GHashTable* gnc_html_object_handlers; + +/* hashes handlers for loading different URLType data */ +extern GHashTable* gnc_html_stream_handlers; + +/* hashes handlers for handling different URLType data */ +extern GHashTable* gnc_html_url_handlers; + +static char error_404_format[] = "

%s

%s"; +static char error_404_title[] = N_("Not found"); +static char error_404_body[] = N_("The specified URL could not be loaded."); + +static void webkit_navigation_requested_cb( WebKitWebView* web_view, GObject* arg1, + GObject* arg2, gpointer data ); +static void webkit_on_url_cb( WebKitWebView* web_view, gchar* title, gchar* url, + gpointer data ); +static gchar* handle_embedded_object( GncHtmlWebkit* self, gchar* html_str ); +#if 0 +static void gnc_html_set_base_cb( GtkHTML* gtkhtml, const gchar* base, gpointer data ); +static void gnc_html_link_clicked_cb( GtkHTML* html, const gchar* url, gpointer data ); +static gboolean gnc_html_object_requested_cb( GtkHTML* html, GtkHTMLEmbedded* eb, + gpointer data ); +#endif +static int gnc_html_button_press_cb( GtkWidget* widg, GdkEventButton* event, + gpointer user_data ); +static void impl_webkit_show_url( GncHtml* self, URLType type, + const gchar* location, const gchar* label, + gboolean new_window_hint ); +static void impl_webkit_show_data( GncHtml* self, const gchar* data, int datalen ); +static void impl_webkit_reload( GncHtml* self ); +static void impl_webkit_copy_to_clipboard( GncHtml* self ); +static gboolean impl_webkit_export_to_file( GncHtml* self, const gchar* filepath ); +static void impl_webkit_print( GncHtml* self ); +static void impl_webkit_cancel( GncHtml* self ); +static void impl_webkit_set_parent( GncHtml* self, GtkWindow* parent ); + +static void +gnc_html_webkit_init( GncHtmlWebkit* self ) +{ + GncHtmlWebkitPrivate* priv; + GncHtmlWebkitPrivate* new_priv; + + new_priv = g_realloc( GNC_HTML(self)->priv, sizeof(GncHtmlWebkitPrivate) ); + priv = self->priv = new_priv; + GNC_HTML(self)->priv = (GncHtmlPrivate*)priv; + + priv->html_string = NULL; + priv->web_view = WEBKIT_WEB_VIEW(webkit_web_view_new()); + + gtk_container_add( GTK_CONTAINER(priv->base.container), + GTK_WIDGET(priv->web_view) ); + +#ifdef HAVE_GTK_2_10 + g_object_ref_sink( priv->base.container ); +#else + g_object_ref( priv->base.container ); + gtk_object_sink( GTK_OBJECT(priv->base.container) ); +#endif + + /* signals */ + g_signal_connect( priv->web_view, "navigation-requested", + G_CALLBACK(webkit_navigation_requested_cb), + self); + + g_signal_connect( priv->web_view, "hovering-over-link", + G_CALLBACK(webkit_on_url_cb), + self ); + +#if 0 + g_signal_connect( priv->html, "set_base", + G_CALLBACK(gnc_html_set_base_cb), + self); + + g_signal_connect(priv->html, "link_clicked", + G_CALLBACK(gnc_html_link_clicked_cb), + self); + + g_signal_connect (priv->html, "object_requested", + G_CALLBACK (gnc_html_object_requested_cb), + self); + + g_signal_connect (priv->html, "button_press_event", + G_CALLBACK (gnc_html_button_press_cb), + self); + + g_signal_connect (priv->html, "submit", + G_CALLBACK(gnc_html_submit_cb), + self); +#endif + + LEAVE("retval %p", self); +} + +static void +gnc_html_webkit_class_init( GncHtmlWebkitClass* klass ) +{ + GObjectClass* gobject_class = G_OBJECT_CLASS(klass); + GncHtmlClass* html_class = GNC_HTML_CLASS(klass); + + gobject_class->dispose = gnc_html_webkit_dispose; + gobject_class->finalize = gnc_html_webkit_finalize; + + html_class->show_url = impl_webkit_show_url; + html_class->show_data = impl_webkit_show_data; + html_class->reload = impl_webkit_reload; + html_class->copy_to_clipboard = impl_webkit_copy_to_clipboard; + html_class->export_to_file = impl_webkit_export_to_file; + html_class->print = impl_webkit_print; + html_class->cancel = impl_webkit_cancel; + html_class->set_parent = impl_webkit_set_parent; + + // Initialize graphing support + gnc_html_graph_gog_webkit_init(); +} + +static void +gnc_html_webkit_dispose( GObject* obj ) +{ + GncHtmlWebkit* self = GNC_HTML_WEBKIT(obj); + GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + + if( priv->web_view != NULL ) { + gtk_container_remove( GTK_CONTAINER(priv->base.container), + GTK_WIDGET(priv->web_view) ); + g_object_unref( G_OBJECT(priv->web_view) ); + priv->web_view = NULL; + } + + G_OBJECT_CLASS(gnc_html_webkit_parent_class)->dispose( obj ); +} + +static void +gnc_html_webkit_finalize( GObject* obj ) +{ + GncHtmlWebkit* self = GNC_HTML_WEBKIT(obj); + +// if( self->priv != NULL ) { +// g_free( self->priv ); + self->priv = NULL; +// } + + G_OBJECT_CLASS(gnc_html_webkit_parent_class)->finalize( obj ); +} + +/*****************************************************************************/ + +static char* +extract_base_name(URLType type, const gchar* path) +{ + gchar machine_rexp[] = "^(//[^/]*)/*(/.*)?$"; + gchar path_rexp[] = "^/*(.*)/+([^/]*)$"; + regex_t compiled_m, compiled_p; + regmatch_t match[4]; + gchar * machine=NULL, * location = NULL, * base=NULL; + gchar * basename=NULL; + + DEBUG(" "); + if(!path) return NULL; + + regcomp(&compiled_m, machine_rexp, REG_EXTENDED); + regcomp(&compiled_p, path_rexp, REG_EXTENDED); + + if (!safe_strcmp (type, URL_TYPE_HTTP) || + !safe_strcmp (type, URL_TYPE_SECURE) || + !safe_strcmp (type, URL_TYPE_FTP)) { + + /* step 1: split the machine name away from the path + * components */ + if(!regexec(&compiled_m, path, 4, match, 0)) { + /* $1 is the machine name */ + if(match[1].rm_so != -1) { + machine = g_strndup(path+match[1].rm_so, + match[1].rm_eo - match[1].rm_so); + } + /* $2 is the path */ + if(match[2].rm_so != -1) { + location = g_strndup(path+match[2].rm_so, + match[2].rm_eo - match[2].rm_so); + } + } + + } else { + location = g_strdup(path); + } + /* step 2: split up the path into prefix and file components */ + if(location) { + if(!regexec(&compiled_p, location, 4, match, 0)) { + if(match[1].rm_so != -1) { + base = g_strndup(location+match[1].rm_so, + match[1].rm_eo - match[1].rm_so); + } else { + base = NULL; + } + } + } + + regfree(&compiled_m); + regfree(&compiled_p); + + if(machine) { + if(base && (strlen(base) > 0)) { + basename = g_strconcat(machine, "/", base, "/", NULL); + } else { + basename = g_strconcat(machine, "/", NULL); + } + } else { + if(base && (strlen(base) > 0)) { + basename = g_strdup(base); + } else { + basename = NULL; + } + } + + g_free(machine); + g_free(base); + g_free(location); + return basename; +} + +static gboolean +http_allowed() +{ + return TRUE; +} + +static gboolean +https_allowed() +{ + return TRUE; +} + +static gchar* +handle_embedded_object( GncHtmlWebkit* self, gchar* html_str ) +{ + // Find the tag and get the classid from it. This will provide the correct + // object callback handler. Pass the entity text to the handler. What should + // come back is embedded image information. + gchar* object_tag; + gchar* end_object_tag; + gchar* object_contents; + gchar* html_str_start; + gchar* html_str_middle; + gchar* html_str_result; + gchar* classid; + gchar* classid_end; + gchar* object_classid; + GncHTMLObjectCB h; + + object_tag = g_strstr_len( html_str, -1, "" ); + if( end_object_tag == NULL ) { + // Hmmm... no object end tag + return html_str; + } + end_object_tag += strlen( "" ); + object_contents = g_strndup( object_tag, (end_object_tag-object_tag) ); + + h = g_hash_table_lookup( gnc_html_object_handlers, object_classid ); + if( h != NULL ) { + (void)h( GNC_HTML(self), object_contents, &html_str_middle ); + } else { + html_str_middle = g_strdup_printf( "No handler found for classid \"%s\"", object_classid ); + } + + html_str_start = g_strndup( html_str, (object_tag-html_str) ); + html_str_result = g_strdup_printf( "%s%s%s", html_str_start, html_str_middle, end_object_tag ); + + g_free( html_str_start ); + g_free( html_str_middle ); + return html_str_result; +} + +/******************************************************************** + * load_to_stream : actually do the work of loading the HTML + * or binary data referenced by a URL and feeding it into the GtkHTML + * widget. + ********************************************************************/ + +static void +load_to_stream( GncHtmlWebkit* self, URLType type, + const gchar* location, const gchar* label ) +{ + gchar* fdata = NULL; + int fdata_len = 0; + GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + + DEBUG( "type %s, location %s, label %s", type ? type : "(null)", + location ? location : "(null)", label ? label : "(null)"); + + g_return_if_fail( self != NULL ); + + if( gnc_html_stream_handlers != NULL ) { + GncHTMLStreamCB stream_handler; + + stream_handler = g_hash_table_lookup( gnc_html_stream_handlers, type ); + if( stream_handler ) { + gboolean ok = stream_handler( location, &fdata, &fdata_len ); + + if( ok ) { + fdata = fdata ? fdata : g_strdup( "" ); + + // Until webkitgtk supports download requests, look for "web_view, fdata, "base-uri" ); + } + + g_free( fdata ); + + if( label ) { + while( gtk_events_pending() ) { + gtk_main_iteration(); + } +// gtk_html_jump_to_anchor( GTK_HTML(priv->html), label ); + g_assert( FALSE ); + } + + return; + } + } + + do { + if( !safe_strcmp( type, URL_TYPE_SECURE ) || + !safe_strcmp( type, URL_TYPE_HTTP ) ) { + + if( !safe_strcmp( type, URL_TYPE_SECURE ) ) { + if( !https_allowed() ) { + gnc_error_dialog( priv->base.parent, + _("Secure HTTP access is disabled. " + "You can enable it in the Network section of " + "the Preferences dialog.")); + break; + } + } + + if( !http_allowed() ) { + gnc_error_dialog( priv->base.parent, + _("Network HTTP access is disabled. " + "You can enable it in the Network section of " + "the Preferences dialog.")); + } else { + char *fullurl; + + fullurl = gnc_build_url( type, location, label ); + } + + } else { + PWARN( "load_to_stream for inappropriate type\n" + "\turl = '%s#%s'\n", + location ? location : "(null)", + label ? label : "(null)" ); + fdata = g_strdup_printf( error_404_format, + _(error_404_title), _(error_404_body) ); + webkit_web_view_load_html_string( priv->web_view, fdata, "base-uri" ); + g_free( fdata ); + } + + } while( FALSE ); +} + +#if 0 +/******************************************************************** + * gnc_html_link_clicked_cb - called when user left-clicks on html + * anchor. + ********************************************************************/ + +static void +gnc_html_link_clicked_cb( GtkHTML* html, const gchar* url, gpointer data ) +{ + URLType type; + gchar* location = NULL; + gchar* label = NULL; + GncHtmlWebkit* self = GNC_HTML_WEBKIT(data); + + DEBUG("Clicked %s", url); + type = gnc_html_parse_url( GNC_HTML(self), url, &location, &label ); + gnc_html_show_url( GNC_HTML(self), type, location, label, 0 ); + g_free( location ); + g_free( label ); +} +#endif + +/******************************************************************** + * webkit_navigation_requested_cb - called when a URL needs to be + * loaded within the loading of a page (embedded image). + ********************************************************************/ + +static void +webkit_navigation_requested_cb( WebKitWebView* web_view, GObject* arg1, + GObject* arg2, gpointer data ) +{ + URLType type; + gchar* location = NULL; + gchar* label = NULL; + GncHtmlWebkit* self = GNC_HTML_WEBKIT(data); + WebKitNetworkRequest* req = WEBKIT_NETWORK_REQUEST(arg2); + const gchar* url = webkit_network_request_get_uri( req ); + + DEBUG( "requesting %s", url ); + type = gnc_html_parse_url( GNC_HTML(self), url, &location, &label ); + gnc_html_show_url( GNC_HTML(self), type, location, label, 0 ); +// load_to_stream( self, type, location, label ); + g_free( location ); + g_free( label ); +} + +#if 0 +/******************************************************************** + * gnc_html_object_requested_cb - called when an applet needs to be + * loaded. + ********************************************************************/ + +static gboolean +gnc_html_object_requested_cb( GtkHTML* html, GtkHTMLEmbedded* eb, + gpointer data ) +{ + GncHtmlWebkit* self = GNC_HTML_WEBKIT(data); + GncHTMLObjectCB h; + + DEBUG( " " ); + if( !eb || !(eb->classid) || !gnc_html_object_handlers ) return FALSE; + + h = g_hash_table_lookup( gnc_html_object_handlers, eb->classid ); + if( h ) { + return h( GNC_HTML(self), eb, data ); + } else { + return FALSE; + } +} +#endif + +/******************************************************************** + * webkit_on_url_cb - called when user rolls over html anchor + ********************************************************************/ + +static void +webkit_on_url_cb( WebKitWebView* web_view, gchar* title, gchar* url, gpointer data ) +{ + GncHtmlWebkit* self = GNC_HTML_WEBKIT(data); + GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + + DEBUG( "Rollover %s", url ? url : "(null)" ); + g_free( priv->base.current_link ); + priv->base.current_link = g_strdup( url ); + if( priv->base.flyover_cb ) { + (priv->base.flyover_cb)( GNC_HTML(self), url, priv->base.flyover_cb_data ); + } +} + +#if 0 +/******************************************************************** + * gnc_html_set_base_cb + ********************************************************************/ + +static void +gnc_html_set_base_cb( GtkHTML* gtkhtml, const gchar* base, + gpointer data ) +{ + GncHtmlWebkit* self = GNC_HTML_WEBKIT(data); + GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + URLType type; + gchar* location = NULL; + gchar* label = NULL; + + DEBUG( "Setting base location to %s", base ); + type = gnc_html_parse_url( GNC_HTML(self), base, &location, &label ); + + g_free( priv->base.base_location ); + g_free( label ); + + priv->base.base_type = type; + priv->base.base_location = location; +} +#endif + +/******************************************************************** + * gnc_html_button_press_cb + * mouse button callback (if any) + ********************************************************************/ + +static int +gnc_html_button_press_cb( GtkWidget* widg, GdkEventButton* event, + gpointer user_data ) +{ + GncHtmlWebkit* self = GNC_HTML_WEBKIT(user_data); + GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + + DEBUG( "Button Press" ); + if( priv->base.button_cb != NULL ) { + (priv->base.button_cb)( GNC_HTML(self), event, priv->base.button_cb_data ); + return TRUE; + } else { + return FALSE; + } +} + +/******************************************************************** + * gnc_html_open_scm + * insert some scheme-generated HTML + ********************************************************************/ + +static void +gnc_html_open_scm( GncHtmlWebkit* self, const gchar * location, + const gchar * label, int newwin ) +{ + PINFO("location='%s'", location ? location : "(null)"); +} + + +/******************************************************************** + * gnc_html_show_data + * display some HTML that the creator of the gnc-html got from + * somewhere. + ********************************************************************/ + +static void +impl_webkit_show_data( GncHtml* self, const gchar* data, int datalen ) +{ + GncHtmlWebkitPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML_WEBKIT(self) ); + + DEBUG( "datalen %d, data %20.20s", datalen, data ); + + priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + webkit_web_view_load_html_string( priv->web_view, data, "base-uri" ); +} + +/******************************************************************** + * gnc_html_show_url + * + * open a URL. This is called when the user clicks a link or + * for the creator of the gnc_html window to explicitly request + * a URL. + ********************************************************************/ + +static void +impl_webkit_show_url( GncHtml* self, URLType type, + const gchar* location, const gchar* label, + gboolean new_window_hint ) +{ + GncHTMLUrlCB url_handler; + gboolean new_window; + GncHtmlWebkitPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML_WEBKIT(self) ); + g_return_if_fail( location != NULL ); + + priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + + /* make sure it's OK to show this URL type in this window */ + if( new_window_hint == 0 ) { + if( priv->base.urltype_cb ) { + new_window = !((priv->base.urltype_cb)( type )); + } else { + new_window = FALSE; + } + } else { + new_window = TRUE; + } + + if( !new_window ) { + gnc_html_cancel( GNC_HTML(self) ); + } + + if( gnc_html_url_handlers ) { + url_handler = g_hash_table_lookup( gnc_html_url_handlers, type ); + } else { + url_handler = NULL; + } + + if( url_handler ) { + GNCURLResult result; + gboolean ok; + + result.load_to_stream = FALSE; + result.url_type = type; + result.location = NULL; + result.label = NULL; + result.base_type = URL_TYPE_FILE; + result.base_location = NULL; + result.error_message = NULL; + + ok = url_handler( location, label, new_window, &result ); + if( !ok ) { + if( result.error_message ) { + gnc_error_dialog( priv->base.parent, "%s", result.error_message ); + } else { + /* %s is a URL (some location somewhere). */ + gnc_error_dialog( priv->base.parent, _("There was an error accessing %s."), location ); + } + + if( priv->base.load_cb ) { + priv->base.load_cb( GNC_HTML(self), result.url_type, + location, label, priv->base.load_cb_data ); + } + } else if( result.load_to_stream ) { + gnc_html_history_node *hnode; + const char *new_location; + const char *new_label; + + new_location = result.location ? result.location : location; + new_label = result.label ? result.label : label; + hnode = gnc_html_history_node_new( result.url_type, new_location, new_label ); + + gnc_html_history_append( priv->base.history, hnode ); + + g_free( priv->base.base_location ); + priv->base.base_type = result.base_type; + priv->base.base_location = + g_strdup( extract_base_name( result.base_type, new_location ) ); + DEBUG( "resetting base location to %s", + priv->base.base_location ? priv->base.base_location : "(null)" ); + + load_to_stream( GNC_HTML_WEBKIT(self), result.url_type, + new_location, new_label ); + + if( priv->base.load_cb != NULL ) { + priv->base.load_cb( GNC_HTML(self), result.url_type, + new_location, new_label, priv->base.load_cb_data ); + } + } + + g_free( result.location ); + g_free( result.label ); + g_free( result.base_location ); + g_free( result.error_message ); + + return; + } + + if( safe_strcmp( type, URL_TYPE_SCHEME ) == 0 ) { + gnc_html_open_scm( GNC_HTML_WEBKIT(self), location, label, new_window ); + + } else if( safe_strcmp( type, URL_TYPE_JUMP ) == 0 ) { +// gtk_html_jump_to_anchor( GTK_HTML(priv->html), label ); + g_assert( FALSE ); + + } else if( safe_strcmp( type, URL_TYPE_SECURE ) == 0 || + safe_strcmp( type, URL_TYPE_HTTP ) == 0 || + safe_strcmp( type, URL_TYPE_FILE ) == 0 ) { + + do { + if( safe_strcmp( type, URL_TYPE_SECURE ) == 0 ) { + if( !https_allowed() ) { + gnc_error_dialog( priv->base.parent, + _("Secure HTTP access is disabled. " + "You can enable it in the Network section of " + "the Preferences dialog.") ); + break; + } + } + + if( safe_strcmp( type, URL_TYPE_HTTP ) == 0 ) { + if( !http_allowed() ) { + gnc_error_dialog( priv->base.parent, + _("Network HTTP access is disabled. " + "You can enable it in the Network section of " + "the Preferences dialog.") ); + break; + } + } + + priv->base.base_type = type; + + if( priv->base.base_location != NULL ) g_free( priv->base.base_location ); + priv->base.base_location = extract_base_name( type, location ); + + /* FIXME : handle new_window = 1 */ + gnc_html_history_append( priv->base.history, + gnc_html_history_node_new( type, location, label ) ); + load_to_stream( GNC_HTML_WEBKIT(self), type, location, label ); + + } while( FALSE ); + + } else { + PERR( "URLType %s not supported.", type ); + } + + if( priv->base.load_cb != NULL ) { + (priv->base.load_cb)( GNC_HTML(self), type, location, label, priv->base.load_cb_data ); + } +} + + +/******************************************************************** + * gnc_html_reload + * reload the current page + ********************************************************************/ + +static void +impl_webkit_reload( GncHtml* self ) +{ + gnc_html_history_node * n; + GncHtmlWebkitPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML_WEBKIT(self) ); + + priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + n = gnc_html_history_get_current( priv->base.history ); + if( n != NULL ) { + gnc_html_show_url( self, n->type, n->location, n->label, 0 ); + } +} + + +/******************************************************************** + * gnc_html_new + * create and set up a new webkit widget. + ********************************************************************/ + +GncHtml* +gnc_html_webkit_new( void ) +{ + GncHtmlWebkit* self = g_object_new( GNC_TYPE_HTML_WEBKIT, NULL ); + GncHtmlWebkitPrivate* priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + + return GNC_HTML(self); +} + +/******************************************************************** + * gnc_html_cancel + * cancel any outstanding HTML fetch requests. + ********************************************************************/ + +static gboolean +webkit_cancel_helper(gpointer key, gpointer value, gpointer user_data) +{ + g_free(key); + g_list_free((GList *)value); + return TRUE; +} + +static void +impl_webkit_cancel( GncHtml* self ) +{ + GncHtmlWebkitPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML_WEBKIT(self) ); + + priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + + /* remove our own references to requests */ + //gnc_http_cancel_requests( priv->http ); + + g_hash_table_foreach_remove( priv->base.request_info, webkit_cancel_helper, NULL ); +} + +static void +impl_webkit_copy_to_clipboard( GncHtml* self ) +{ + GncHtmlWebkitPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML_WEBKIT(self) ); + + priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + if( webkit_web_view_can_copy_clipboard( priv->web_view ) ) { + webkit_web_view_copy_clipboard( priv->web_view ); + } +} + +/************************************************************** + * gnc_html_export_to_file : wrapper around the builtin function in webkit + **************************************************************/ +static gboolean +impl_webkit_export_to_file( GncHtml* self, const char *filepath ) +{ + FILE *fh; + GncHtmlWebkitPrivate* priv; + + g_return_val_if_fail( self != NULL, FALSE ); + g_return_val_if_fail( GNC_IS_HTML_WEBKIT(self), FALSE ); + g_return_val_if_fail( filepath != NULL, FALSE ); + + priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + if( priv->html_string == NULL ) { + return FALSE; + } + fh = g_fopen( filepath, "w" ); + if( fh != NULL ) { + gint written; + gint len = strlen( priv->html_string ); + + written = fwrite( priv->html_string, 1, len, fh ); + fclose (fh); + + if( written != len ) { + return FALSE; + } + + return TRUE; + } else { + return FALSE; + } +} + +static void +impl_webkit_print( GncHtml* self ) +{ + GncHtmlWebkitPrivate* priv; +// static void (*webkit_web_frame_print)( WebKitWebFrame* frame ) = NULL; + WebKitWebFrame* frame; + extern void webkit_web_frame_print( WebKitWebFrame* frame ); + + /* HACK ALERT + * + * The api to print isn't exported, but exists and works, so let's dig for it. + */ +#if 0 + if( webkit_web_frame_print == NULL ) { + void* handle = dlopen( "/usr/lib/libwebkit-1.0.so", RTLD_LAZY ); + webkit_web_frame_print = dlsym( handle, "webkit_web_frame_print" ); + dlclose( handle ); + } +#endif + + priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + frame = webkit_web_view_get_main_frame( priv->web_view ); + webkit_web_frame_print( frame ); +} + +static void +impl_webkit_set_parent( GncHtml* self, GtkWindow* parent ) +{ + GncHtmlWebkitPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML_WEBKIT(self) ); + + priv = GNC_HTML_WEBKIT_GET_PRIVATE(self); + priv->base.parent = GTK_WIDGET(parent); +} diff --git a/src/html/gnc-html-webkit.h b/src/html/gnc-html-webkit.h new file mode 100644 index 0000000000..e59980c357 --- /dev/null +++ b/src/html/gnc-html-webkit.h @@ -0,0 +1,59 @@ +/******************************************************************** + * gnc-html-webkit.h -- display html with gnc special tags * + * Copyright (C) 2009 Phil Longstaff * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_WEBKIT_H +#define GNC_HTML_WEBKIT_H + +#include +#include "gnc-html.h" + +G_BEGIN_DECLS + +#define GNC_TYPE_HTML_WEBKIT (gnc_html_webkit_get_type()) +#define GNC_HTML_WEBKIT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNC_TYPE_HTML_WEBKIT, GncHtmlWebkit)) +#define GNC_HTML_WEBKIT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GNC_TYPE_HTML_WEBKIT, GncHtmlWebkitClass)) +#define GNC_IS_HTML_WEBKIT(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GNC_TYPE_HTML_WEBKIT)) +#define GNC_IS_HTML_WEBKIT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GNC_TYPE_HTML_WEBKIT)) +#define GNC_HTML_WEBKIT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GNC_TYPE_HTML_WEBKIT, GncHtmlWebkitClass)) + +typedef struct _GncHtmlWebkit GncHtmlWebkit; +typedef struct _GncHtmlWebkitClass GncHtmlWebkitClass; +typedef struct _GncHtmlWebkitPrivate GncHtmlWebkitPrivate; + +struct _GncHtmlWebkit { + GncHtml parent_instance; + + /*< private >*/ + GncHtmlWebkitPrivate* priv; +}; + +struct _GncHtmlWebkitClass { + GncHtmlClass parent_class; +}; + +GType gnc_html_webkit_get_type( void ); + +GncHtml* gnc_html_webkit_new( void ); + +G_END_DECLS + +#endif diff --git a/src/html/gnc-html.c b/src/html/gnc-html.c new file mode 100644 index 0000000000..b7cc07bd21 --- /dev/null +++ b/src/html/gnc-html.c @@ -0,0 +1,800 @@ +/******************************************************************** + * gnc-html.c -- display HTML with some special gnucash tags. * + * * + * Copyright (C) 2000 Bill Gribble * + * Copyright (C) 2001 Linas Vepstas * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + ********************************************************************/ + +// libgtkhtml docs: +// http://www.fifi.org/doc/libgtkhtml-dev/html/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Account.h" +#include "print-session.h" +#include "gnc-engine.h" +#include "gnc-html.h" +#include "gnc-html-history.h" + +/* indicates the debugging module that this .o belongs to. */ +static QofLogModule log_module = GNC_MOD_HTML; + +/* hashes for URLType -> protocol and protocol -> URLType */ +static GHashTable * gnc_html_type_to_proto_hash = NULL; +GHashTable * gnc_html_proto_to_type_hash = NULL; + +/* hashes an HTML classid to a handler function */ +GHashTable* gnc_html_object_handlers = NULL; + +/* hashes handlers for loading different URLType data */ +GHashTable* gnc_html_stream_handlers = NULL; + +/* hashes handlers for handling different URLType data */ +GHashTable* gnc_html_url_handlers = NULL; + +/* hashes an HTML classid to a handler function */ +extern GHashTable* gnc_html_object_handlers; + +static char error_404_format[] = +"

%s

%s"; +static char error_404_title[] = N_("Not found"); +static char error_404_body[] = +N_("The specified URL could not be loaded."); + +G_DEFINE_ABSTRACT_TYPE(GncHtml, gnc_html, GTK_TYPE_BIN) + +static void gnc_html_class_init( GncHtmlClass* klass ); +static void gnc_html_dispose( GObject* obj ); +static void gnc_html_finalize( GObject* obj ); + +//#define GNC_HTML_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), GNC_TYPE_HTML, GncHtmlPrivate)) +#define GNC_HTML_GET_PRIVATE(o) (GNC_HTML(o)->priv) + +#include "gnc-html-p.h" + +static void +gnc_html_class_init( GncHtmlClass* klass ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->dispose = gnc_html_dispose; + gobject_class->finalize = gnc_html_finalize; + + klass->show_url = NULL; + klass->show_data = NULL; + klass->reload = NULL; + klass->copy_to_clipboard = NULL; + klass->export_to_file = NULL; + klass->print = NULL; + klass->cancel = NULL; + klass->parse_url = NULL; + klass->set_parent = NULL; +} + +static void +gnc_html_init( GncHtml* self ) +{ + GncHtmlPrivate* priv; + priv = self->priv = g_new0( GncHtmlPrivate, 1 ); + + priv->container = gtk_scrolled_window_new( NULL, NULL ); + gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(priv->container), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC ); + priv->request_info = g_hash_table_new( g_str_hash, g_str_equal ); + priv->history = gnc_html_history_new(); +} + +static void +gnc_html_dispose( GObject* obj ) +{ + GncHtml* self = GNC_HTML(obj); + GncHtmlPrivate* priv = GNC_HTML_GET_PRIVATE(self); + + if( priv->container != NULL ) { + gtk_widget_destroy( GTK_WIDGET(priv->container) ); + g_object_unref( G_OBJECT(priv->container) ); + priv->container = NULL; + } + if( priv->request_info != NULL ) { + g_hash_table_destroy( priv->request_info ); + priv->request_info = NULL; + } + if( priv->history != NULL ) { + gnc_html_history_destroy( priv->history ); + priv->history = NULL; + } + + G_OBJECT_CLASS(gnc_html_parent_class)->dispose( obj ); +} + +static void +gnc_html_finalize( GObject* obj ) +{ + GncHtml* self = GNC_HTML(obj); + + if( self->priv != NULL ) { + g_free( self->priv ); + self->priv = NULL; + } + + G_OBJECT_CLASS(gnc_html_parent_class)->finalize( obj ); +} + +/***********************************************************************************/ + +static char* +extract_machine_name( const gchar* path ) +{ + gchar machine_rexp[] = "^(//[^/]*)/*(.*)?$"; + regex_t compiled_m; + regmatch_t match[4]; + gchar* machine = NULL; + + if( path == NULL ) return NULL; + + regcomp( &compiled_m, machine_rexp, REG_EXTENDED ); + + /* step 1: split the machine name away from the path + * components */ + if( !regexec( &compiled_m, path, 4, match, 0 ) ) { + /* $1 is the machine name */ + if( match[1].rm_so != -1 ) { + machine = g_strndup( path+match[1].rm_so, match[1].rm_eo - match[1].rm_so ); + } + } + return machine; +} + +/******************************************************************** + * gnc_html_parse_url + * this takes a URL and determines the protocol type, location, and + * possible anchor name from the URL. + ********************************************************************/ + +URLType +gnc_html_parse_url( GncHtml* self, const gchar* url, + gchar** url_location, gchar** url_label ) +{ + gchar uri_rexp[] = "^(([^:][^:]+):)?([^#]+)?(#(.*))?$"; + regex_t compiled; + regmatch_t match[6]; + gchar* protocol = NULL; + gchar* path = NULL; + gchar* label = NULL; + gboolean found_protocol = FALSE; + gboolean found_path = FALSE; + gboolean found_label = FALSE; + URLType retval; + GncHtmlPrivate* priv = GNC_HTML_GET_PRIVATE(self); + + g_return_val_if_fail( self != NULL, NULL ); + g_return_val_if_fail( GNC_IS_HTML(self), NULL ); + + DEBUG( "parsing %s, base_location %s", + url ? url : "(null)", + self ? (priv->base_location ? priv->base_location + : "(null base_location)") + : "(null html)"); + + regcomp( &compiled, uri_rexp, REG_EXTENDED ); + + if( !regexec( &compiled, url, 6, match, 0 ) ) { + if( match[2].rm_so != -1 ) { + protocol = g_new0( gchar, match[2].rm_eo - match[2].rm_so + 1 ); + strncpy( protocol, url + match[2].rm_so, match[2].rm_eo - match[2].rm_so ); + protocol[match[2].rm_eo - match[2].rm_so] = 0; + found_protocol = TRUE; + } + if( match[3].rm_so != -1 ) { + path = g_new0( gchar, match[3].rm_eo - match[3].rm_so + 1 ); + strncpy( path, url+match[3].rm_so, match[3].rm_eo - match[3].rm_so ); + path[match[3].rm_eo - match[3].rm_so] = 0; + found_path = TRUE; + } + if( match[5].rm_so != -1 ) { + label = g_new0( gchar, match[5].rm_eo - match[5].rm_so + 1 ); + strncpy( label, url+match[5].rm_so, match[5].rm_eo - match[5].rm_so ); + label[match[5].rm_eo - match[5].rm_so] = 0; + found_label = TRUE; + } + } + + regfree( &compiled ); + + if( found_protocol ) { + retval = g_hash_table_lookup( gnc_html_proto_to_type_hash, protocol ); + if( retval == NULL ) { + PWARN( "unhandled URL type for '%s'", url ? url : "(null)" ); + retval = URL_TYPE_OTHER; + } + } else if( found_label && !found_path ) { + retval = URL_TYPE_JUMP; + } else { + if( self ) { + retval = priv->base_type; + } else { + retval = URL_TYPE_FILE; + } + } + + g_free( protocol ); + + if( !safe_strcmp( retval, URL_TYPE_FILE ) ) { + if( !found_protocol && path && self && priv->base_location ) { + if( g_path_is_absolute( path ) ) { + *url_location = g_strdup( path ); + } else { + *url_location = g_build_filename( priv->base_location, path, (gchar*)NULL ); + } + g_free( path ); + } else { + *url_location = g_strdup( path ); + g_free( path ); + } + + } else if( !safe_strcmp( retval, URL_TYPE_JUMP ) ) { + *url_location = NULL; + g_free( path ); + + } else { + /* case URL_TYPE_OTHER: */ + + if( !found_protocol && path && self && priv->base_location ) { + if( g_path_is_absolute( path ) ) { + *url_location = g_build_filename( extract_machine_name( priv->base_location ), + path, (gchar*)NULL ); + } else { + *url_location = g_build_filename( priv->base_location, path, (gchar*)NULL ); + } + g_free( path ); + } else { + *url_location = g_strdup( path ); + g_free( path ); + } + } + + *url_label = label; + return retval; +} + +/******************************************************************** + * gnc_html_show_data + * display some HTML that the creator of the gnc-html got from + * somewhere. + ********************************************************************/ + +void +gnc_html_show_data( GncHtml* self, const gchar* data, int datalen ) +{ + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + if( GNC_HTML_GET_CLASS(self)->show_data != NULL ) { + GNC_HTML_GET_CLASS(self)->show_data( self, data, datalen ); + } else { + DEBUG( "'show_data' not implemented" ); + } +} + + +/******************************************************************** + * gnc_html_show_url + * + * open a URL. This is called when the user clicks a link or + * for the creator of the gnc_html window to explicitly request + * a URL. + ********************************************************************/ + +void +gnc_html_show_url( GncHtml* self, URLType type, + const gchar* location, const gchar* label, + gboolean new_window_hint ) +{ + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + if( GNC_HTML_GET_CLASS(self)->show_url != NULL ) { + GNC_HTML_GET_CLASS(self)->show_url( self, type, location, label, new_window_hint ); + } else { + DEBUG( "'show_url' not implemented" ); + } +} + + +/******************************************************************** + * gnc_html_reload + * reload the current page + ********************************************************************/ + +void +gnc_html_reload( GncHtml* self ) +{ + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + if( GNC_HTML_GET_CLASS(self)->reload != NULL ) { + GNC_HTML_GET_CLASS(self)->reload( self ); + } else { + DEBUG( "'reload' not implemented" ); + } +} + +/******************************************************************** + * gnc_html_cancel + * cancel any outstanding HTML fetch requests. + ********************************************************************/ + +void +gnc_html_cancel( GncHtml* self ) +{ + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + if( GNC_HTML_GET_CLASS(self)->cancel != NULL ) { + GNC_HTML_GET_CLASS(self)->cancel( self ); + } else { + DEBUG( "'cancel' not implemented" ); + } +} + + +/******************************************************************** + * gnc_html_destroy + * destroy the struct + ********************************************************************/ + +void +gnc_html_destroy( GncHtml* self ) +{ + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + g_object_unref( G_OBJECT(self) ); +} + +void +gnc_html_set_urltype_cb( GncHtml* self, GncHTMLUrltypeCB urltype_cb ) +{ + GncHtmlPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + priv = GNC_HTML_GET_PRIVATE(self); + priv->urltype_cb = urltype_cb; +} + +void +gnc_html_set_load_cb( GncHtml* self, GncHTMLLoadCB load_cb, gpointer data ) +{ + GncHtmlPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + priv = GNC_HTML_GET_PRIVATE(self); + priv->load_cb = load_cb; + priv->load_cb_data = data; +} + +void +gnc_html_set_flyover_cb( GncHtml* self, GncHTMLFlyoverCB flyover_cb, gpointer data ) +{ + GncHtmlPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + priv = GNC_HTML_GET_PRIVATE(self); + priv->flyover_cb = flyover_cb; + priv->flyover_cb_data = data; +} + +void +gnc_html_set_button_cb( GncHtml* self, GncHTMLButtonCB button_cb, gpointer data ) +{ + GncHtmlPrivate* priv; + + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + priv = GNC_HTML_GET_PRIVATE(self); + priv->button_cb = button_cb; + priv->button_cb_data = data; +} + +void +gnc_html_copy_to_clipboard( GncHtml* self ) +{ + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + if( GNC_HTML_GET_CLASS(self)->copy_to_clipboard != NULL ) { + GNC_HTML_GET_CLASS(self)->copy_to_clipboard( self ); + } else { + DEBUG( "'copy_to_clipboard' not implemented" ); + } +} + +/************************************************************** + * gnc_html_export_to_file : wrapper around the builtin function in gtkhtml + **************************************************************/ + +gboolean +gnc_html_export_to_file( GncHtml* self, const gchar* filepath ) +{ + g_return_val_if_fail( self != NULL, FALSE ); + g_return_val_if_fail( GNC_IS_HTML(self), FALSE ); + + if( GNC_HTML_GET_CLASS(self)->export_to_file != NULL ) { + return GNC_HTML_GET_CLASS(self)->export_to_file( self, filepath ); + } else { + DEBUG( "'export_to_file' not implemented" ); + return FALSE; + } +} + +void +gnc_html_print( GncHtml* self ) +{ + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + if( GNC_HTML_GET_CLASS(self)->print != NULL ) { + GNC_HTML_GET_CLASS(self)->print( self ); + } else { + DEBUG( "'print' not implemented" ); + } +} + +gnc_html_history * +gnc_html_get_history( GncHtml* self ) +{ + g_return_val_if_fail( self != NULL, NULL ); + g_return_val_if_fail( GNC_IS_HTML(self), NULL ); + + return GNC_HTML_GET_PRIVATE(self)->history; +} + + +GtkWidget * +gnc_html_get_widget( GncHtml* self ) +{ + g_return_val_if_fail( self != NULL, NULL ); + g_return_val_if_fail( GNC_IS_HTML(self), NULL ); + + return GNC_HTML_GET_PRIVATE(self)->container; +} + +void +gnc_html_set_parent( GncHtml* self, GtkWindow* parent ) +{ + g_return_if_fail( self != NULL ); + g_return_if_fail( GNC_IS_HTML(self) ); + + if( GNC_HTML_GET_CLASS(self)->set_parent != NULL ) { + GNC_HTML_GET_CLASS(self)->set_parent( self, parent ); + } else { + DEBUG( "'set_parent' not implemented" ); + } +} + +/* Register the URLType if it doesn't already exist. + * Returns TRUE if successful, FALSE if the type already exists. + */ +gboolean +gnc_html_register_urltype( URLType type, const char *protocol ) +{ + if (!gnc_html_type_to_proto_hash) { + gnc_html_type_to_proto_hash = g_hash_table_new (g_str_hash, g_str_equal); + gnc_html_proto_to_type_hash = g_hash_table_new (g_str_hash, g_str_equal); + } + if (!protocol) return FALSE; + if (g_hash_table_lookup (gnc_html_type_to_proto_hash, type)) + return FALSE; + + g_hash_table_insert (gnc_html_type_to_proto_hash, type, (gpointer)protocol); + if (*protocol) + g_hash_table_insert (gnc_html_proto_to_type_hash, (gpointer)protocol, type); + + return TRUE; +} + +void +gnc_html_initialize( void ) +{ + int i; + static struct { + URLType type; + char * protocol; + } types[] = { + { URL_TYPE_FILE, "file" }, + { URL_TYPE_JUMP, "" }, + { URL_TYPE_HTTP, "http" }, + { URL_TYPE_FTP, "ftp" }, + { URL_TYPE_SECURE, "https" }, + { URL_TYPE_REGISTER, "gnc-register" }, + { URL_TYPE_ACCTTREE, "gnc-acct-tree" }, + { URL_TYPE_REPORT, "gnc-report" }, + { URL_TYPE_OPTIONS, "gnc-options" }, + { URL_TYPE_SCHEME, "gnc-scm" }, + { URL_TYPE_HELP, "gnc-help" }, + { URL_TYPE_XMLDATA, "gnc-xml" }, + { URL_TYPE_PRICE, "gnc-price" }, + { URL_TYPE_BUDGET, "gnc-budget" }, + { URL_TYPE_OTHER, "" }, + { NULL, NULL }}; + + for (i = 0; types[i].type; i++) + gnc_html_register_urltype (types[i].type, types[i].protocol); +} + +char * +gnc_build_url (URLType type, const gchar * location, const gchar * label) +{ + char * type_name; + + DEBUG(" "); + type_name = g_hash_table_lookup (gnc_html_type_to_proto_hash, type); + if (!type_name) + type_name = ""; + + if(label) { + return g_strdup_printf("%s%s%s#%s", type_name, (*type_name ? ":" : ""), + (location ? location : ""), + label ? label : ""); + } + else { + return g_strdup_printf("%s%s%s", type_name, (*type_name ? ":" : ""), + (location ? location : "")); + } +} + +/******************************************************************** + * gnc_html_encode_string + * RFC 1738 encoding of string for submission with an HTML form. + * GPL code lifted from gtkhtml. copyright notice: + * + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * Copyright (C) 1997 Torben Weis (weis@kde.org) + * Copyright (C) 1999 Helix Code, Inc. + ********************************************************************/ + +char * +gnc_html_encode_string(const char * str) +{ + static gchar *safe = "$-._!*(),"; /* RFC 1738 */ + unsigned pos = 0; + GString *encoded = g_string_new (""); + gchar buffer[5], *ptr; + guchar c; + + if(!str) return NULL; + + while(pos < strlen(str)) { + c = (unsigned char) str[pos]; + + if ((( c >= 'A') && ( c <= 'Z')) || + (( c >= 'a') && ( c <= 'z')) || + (( c >= '0') && ( c <= '9')) || + (strchr(safe, c))) { + encoded = g_string_append_c (encoded, c); + } + else if ( c == ' ' ) { + encoded = g_string_append_c (encoded, '+'); + } + else if ( c == '\n' ) { + encoded = g_string_append (encoded, "%0D%0A"); + } + else if ( c != '\r' ) { + sprintf( buffer, "%%%02X", (int)c ); + encoded = g_string_append (encoded, buffer); + } + pos++; + } + + ptr = encoded->str; + + g_string_free (encoded, FALSE); + + return (char *)ptr; +} + + +char * +gnc_html_decode_string(const char * str) +{ + static gchar * safe = "$-._!*(),"; /* RFC 1738 */ + GString * decoded = g_string_new (""); + const gchar * ptr; + guchar c; + guint hexval; + ptr = str; + + if(!str) return NULL; + + while(*ptr) { + c = (unsigned char) *ptr; + if ((( c >= 'A') && ( c <= 'Z')) || + (( c >= 'a') && ( c <= 'z')) || + (( c >= '0') && ( c <= '9')) || + (strchr(safe, c))) { + decoded = g_string_append_c (decoded, c); + } + else if ( c == '+' ) { + decoded = g_string_append_c (decoded, ' '); + } + else if (!strncmp(ptr, "%0D0A", 5)) { + decoded = g_string_append (decoded, "\n"); + ptr += 4; + } + else if(c == '%') { + ptr++; + if (1 == sscanf(ptr, "%02X", &hexval)) + decoded = g_string_append_c(decoded, (char)hexval); + else + decoded = g_string_append_c(decoded, ' '); + ptr++; + } + ptr++; + } + ptr = decoded->str; + g_string_free (decoded, FALSE); + + return (char *)ptr; +} + +/******************************************************************** + * escape/unescape_newlines : very simple string encoding for GPG + * ASCII-armored text. + ********************************************************************/ + +char * +gnc_html_unescape_newlines(const gchar * in) +{ + const char * ip = in; + char * cstr = NULL; + GString * rv = g_string_new(""); + + for(ip=in; *ip; ip++) { + if((*ip == '\\') && (*(ip+1)=='n')) { + g_string_append(rv, "\n"); + ip++; + } + else { + g_string_append_c(rv, *ip); + } + } + + g_string_append_c(rv, 0); + cstr = rv->str; + g_string_free(rv, FALSE); + return cstr; +} + +char * +gnc_html_escape_newlines(const gchar * in) +{ + char *out; + const char * ip = in; + GString * escaped = g_string_new(""); + + for(ip=in; *ip; ip++) { + if(*ip == '\012') { + g_string_append(escaped, "\\n"); + } + else { + g_string_append_c(escaped, *ip); + } + } + g_string_append_c(escaped, 0); + out = escaped->str; + g_string_free(escaped, FALSE); + return out; +} + +void +gnc_html_register_object_handler( const char * classid, + GncHTMLObjectCB hand ) +{ + g_return_if_fail( classid != NULL ); + + if( gnc_html_object_handlers == NULL ) { + gnc_html_object_handlers = g_hash_table_new( g_str_hash, g_str_equal ); + } + + gnc_html_unregister_object_handler( classid ); + if( hand != NULL ) { + g_hash_table_insert( gnc_html_object_handlers, g_strdup( classid ), hand ); + } +} + +void +gnc_html_unregister_object_handler( const gchar* classid ) +{ + gchar* keyptr = NULL; + gchar* valptr = NULL; + gchar** p_keyptr = &keyptr; + gchar** p_valptr = &valptr; + + if( g_hash_table_lookup_extended( gnc_html_object_handlers, + classid, + (gpointer *)p_keyptr, + (gpointer *)p_valptr) ) { + g_hash_table_remove( gnc_html_object_handlers, classid ); + g_free( keyptr ); + } +} + +void +gnc_html_register_stream_handler( URLType url_type, GncHTMLStreamCB hand ) +{ + g_return_if_fail( url_type != NULL && *url_type != '\0' ); + + if( gnc_html_stream_handlers == NULL ) { + gnc_html_stream_handlers = g_hash_table_new( g_str_hash, g_str_equal ); + } + + gnc_html_unregister_stream_handler( url_type ); + if( hand != NULL ) { + g_hash_table_insert( gnc_html_stream_handlers, url_type, hand ); + } +} + +void +gnc_html_unregister_stream_handler( URLType url_type ) +{ + g_hash_table_remove( gnc_html_stream_handlers, url_type ); +} + +void +gnc_html_register_url_handler( URLType url_type, GncHTMLUrlCB hand ) +{ + g_return_if_fail( url_type != NULL && *url_type != '\0' ); + + if( gnc_html_url_handlers == NULL ) { + gnc_html_url_handlers = g_hash_table_new( g_str_hash, g_str_equal ); + } + + gnc_html_unregister_url_handler( url_type ); + if( hand != NULL ) { + g_hash_table_insert( gnc_html_url_handlers, url_type, hand ); + } +} + +void +gnc_html_unregister_url_handler( URLType url_type ) +{ + g_hash_table_remove( gnc_html_url_handlers, url_type ); +} diff --git a/src/html/gnc-html.h b/src/html/gnc-html.h new file mode 100644 index 0000000000..6136c624a4 --- /dev/null +++ b/src/html/gnc-html.h @@ -0,0 +1,268 @@ +/******************************************************************** + * gnc-html.h -- display html with gnc special tags * + * Copyright (C) 2000 Bill Gribble * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program 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, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef GNC_HTML_H +#define GNC_HTML_H + +/** + * A GncHtml object is an abstract base for an html engine used to display reports and + * charts in gnucash. It must be overridden to create specific objects using specific + * html engines (e.g. gtkhtml, webkit). + */ + +#include + +G_BEGIN_DECLS + +#define GNC_TYPE_HTML (gnc_html_get_type()) +#define GNC_HTML(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNC_TYPE_HTML, GncHtml)) +#define GNC_HTML_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GNC_TYPE_HTML, GncHtmlClass)) +#define GNC_IS_HTML(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GNC_TYPE_HTML)) +#define GNC_IS_HTML_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((o), GNC_TYPE_HTML)) +#define GNC_HTML_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GNC_TYPE_HTML, GncHtmlClass)) + +GType gnc_html_get_type(void); + +typedef struct _GncHtml GncHtml; +typedef struct _GncHtmlClass GncHtmlClass; +typedef struct _GncHtmlPrivate GncHtmlPrivate; + +#include "gnc-html-extras.h" + +/* The result structure of url handlers. Strings should be g_malloc'd + * by the handler and will be freed by gnc_html. */ +typedef struct +{ + /* The following members are used if the handler succeeds (returns TRUE). */ + + gboolean load_to_stream; /* If TRUE, the url should be loaded from + * a stream using the rest of the data in + * the struct into the original gnc_html + * object. If FALSE, the handler will + * perform all needed actions itself. */ + + URLType url_type; /* Defaults to original */ + gchar* location; /* If NULL, use original (NULL is default) */ + gchar* label; /* If NULL, use original (NULL is default) */ + + URLType base_type; + gchar* base_location; + + /* The following members are used if the handler fails (returns FALSE). */ + gchar* error_message; +} GNCURLResult; + +typedef gboolean (* GncHTMLObjectCB)(GncHtml* html, gpointer eb, + gpointer data); +typedef gboolean (* GncHTMLStreamCB)(const gchar* location, gchar** data, int* datalen); +typedef gboolean (* GncHTMLUrlCB)(const gchar* location, const gchar* label, + gboolean new_window, GNCURLResult* result); + +/** + * Registers a new URLType. + * returns TRUE if succesful, FALSE if type already exists. + * + * @param type New URL type + * @param prococol Protocol - should be an empty string if there is no corresponding protocol. + * @return TRUE if successful, FALSE if type already exists or protocol is NULL. + */ +gboolean gnc_html_register_urltype( URLType type, const gchar* protocol ); + +/** + * Initializes the html subsystem + */ +void gnc_html_initialize( void ); + +gchar* gnc_html_encode_string( const gchar* in ); +gchar* gnc_html_decode_string( const gchar* in ); +gchar* gnc_html_escape_newlines( const gchar* in ); +gchar* gnc_html_unescape_newlines( const gchar* in ); + +/* object handlers deal with objects in HTML. + * the handlers are looked up at object load time. */ +void gnc_html_register_object_handler( const gchar* classid, GncHTMLObjectCB hand ); +void gnc_html_unregister_object_handler( const gchar* classid ); + +/* stream handlers load data for particular URLTypes. */ +void gnc_html_register_stream_handler( URLType url_type, GncHTMLStreamCB hand ); +void gnc_html_unregister_stream_handler( URLType url_type ); + +/* handlers for particular URLTypes. */ +void gnc_html_register_url_handler( URLType url_type, GncHTMLUrlCB hand ); +void gnc_html_unregister_url_handler( URLType url_type ); + +#include "gnc-html-history.h" + +typedef int (* GncHTMLUrltypeCB)(URLType ut); +typedef void (* GncHTMLFlyoverCB)(GncHtml* html, const gchar* url, + gpointer data); +typedef void (* GncHTMLLoadCB)(GncHtml* html, URLType type, + const gchar* location, const gchar* label, + gpointer data); +typedef int (* GncHTMLButtonCB)(GncHtml* html, GdkEventButton* event, + gpointer data); + +struct _GncHtmlClass +{ + GtkBinClass parent_class; + + /* Methods */ + void (*show_url)( GncHtml* html, + URLType type, + const gchar* location, + const gchar* label, + gboolean new_window_hint ); + void (*show_data)( GncHtml* html, const gchar* data, int datalen ); + void (*reload)( GncHtml* html ); + void (*copy_to_clipboard)( GncHtml* html ); + gboolean (*export_to_file)( GncHtml* html, const gchar* file ); + void (*print)( GncHtml* html ); + void (*cancel)( GncHtml* html ); + URLType (*parse_url)( GncHtml* html, const gchar* url, + gchar** url_location, gchar** url_label ); + void (*set_parent)( GncHtml* html, GtkWindow* parent ); +}; + +struct _GncHtml +{ + GtkBin parent_instance; + + /*< private >*/ + GncHtmlPrivate* priv; +}; + +/** + * Destroys a GncHtml object. + * + * @param html GncHtml object to destroy + */ +void gnc_html_destroy( GncHtml* html ); + +/** + * Displays a URL in a GncHtml object. + * + * @param html GncHtml object + */ +void gnc_html_show_url( GncHtml* html, URLType type, const gchar* location, + const gchar* label, gboolean new_window_hint ); + +/** + * Displays an HTML string in a GncHtml object. + * + * @param html GncHtml object + */ +void gnc_html_show_data( GncHtml* html, const gchar* data, int datalen ); + +/** + * Reloads the current GncHtml object. + * + * @param html GncHtml object + */ +void gnc_html_reload( GncHtml* html ); + +/** + * Copies the html to the clipboard + * + * @param html GncHtml object + */ +void gnc_html_copy_to_clipboard( GncHtml* html ); + +/** + * Exports the html to an external file. + * + * @param html GncHtml object + * @param filename External file name + * @param TRUE if successful, FALSE if unsuccessful + */ +gboolean gnc_html_export_to_file( GncHtml* html, const gchar* filename ); + +/** + * Prints the report. + * + * @param html GncHtml object + */ +void gnc_html_print( GncHtml* html ); + +/** + * Cancels the current operation + * + * @param html GncHtml object + */ +void gnc_html_cancel( GncHtml* html ); + +/** + * Parses a URL into URI and label + * + * @param html GncHtml object + * @param url URL + * @param url_location Pointer where to store address of string containing main URI + * @param url_label Pointer where to store address of string containing label + */ +URLType gnc_html_parse_url( GncHtml* html, const gchar* url, + gchar** url_location, gchar** url_label ); + +/** + * Returns the history for this html engine + * + * @param html GncHtml object + * @return History + */ +gnc_html_history* gnc_html_get_history( GncHtml* html ); + +/** + * Returns the main widget for this html engine + * + * @param html GncHtml object + * @return Main widget + */ +GtkWidget* gnc_html_get_widget( GncHtml* html ); + +/** + * Sets the parent window for this html engine. The engine will be embedded in this parent. + * + * @param html GncHtml object + * @param parent Parent window + */ +void gnc_html_set_parent( GncHtml* html, GtkWindow* parent ); + +/* setting callbacks */ +void gnc_html_set_urltype_cb( GncHtml* html, GncHTMLUrltypeCB urltype_cb ); +void gnc_html_set_load_cb( GncHtml* html, GncHTMLLoadCB load_cb, gpointer data ); +void gnc_html_set_flyover_cb( GncHtml* html, GncHTMLFlyoverCB newwin_cb, gpointer data ); +void gnc_html_set_button_cb( GncHtml* html, GncHTMLButtonCB button_cb, gpointer data ); + +/* object handlers deal with objects in HTML. + * the handlers are looked up at object load time. */ +void gnc_html_register_object_handler( const gchar* classid, GncHTMLObjectCB hand ); +void gnc_html_unregister_object_handler( const gchar* classid ); + +/* stream handlers load data for particular URLTypes. */ +void gnc_html_register_stream_handler( URLType url_type, GncHTMLStreamCB hand ); +void gnc_html_unregister_stream_handler( URLType url_type ); + +/* handlers for particular URLTypes. */ +void gnc_html_register_url_handler( URLType url_type, GncHTMLUrlCB hand ); +void gnc_html_unregister_url_handler( URLType url_type ); + +const gchar* gnc_html_get_embedded_param( gpointer eb, const gchar* param_name ); + +#endif diff --git a/src/html/gnc-html.i b/src/html/gnc-html.i new file mode 100644 index 0000000000..e94a2b1880 --- /dev/null +++ b/src/html/gnc-html.i @@ -0,0 +1,51 @@ +%module sw_gnc_html +%{ +/* Includes the header in the wrapper code */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SCM scm_init_sw_gnc_html_module(void); +%} + +%import "base-typemaps.i" + +/* Parse the header file to generate wrappers */ +%include "gnc-html-extras.h" + + +%init { + { + char tmp[100]; + +#define SET_ENUM(e) snprintf(tmp, 100, "(set! %s (%s))", (e), (e)); \ + scm_c_eval_string(tmp); + + SET_ENUM("URL-TYPE-FILE"); + SET_ENUM("URL-TYPE-JUMP"); + SET_ENUM("URL-TYPE-HTTP"); + SET_ENUM("URL-TYPE-FTP"); + SET_ENUM("URL-TYPE-SECURE"); + SET_ENUM("URL-TYPE-REGISTER"); + SET_ENUM("URL-TYPE-ACCTTREE"); + SET_ENUM("URL-TYPE-REPORT"); + SET_ENUM("URL-TYPE-OPTIONS"); + SET_ENUM("URL-TYPE-SCHEME"); + SET_ENUM("URL-TYPE-HELP"); + SET_ENUM("URL-TYPE-XMLDATA"); + SET_ENUM("URL-TYPE-PRICE"); + SET_ENUM("URL-TYPE-OTHER"); + +#undefine SET_ENUM + } + +} diff --git a/src/html/gncmod-html.c b/src/html/gncmod-html.c new file mode 100644 index 0000000000..c0b564bad4 --- /dev/null +++ b/src/html/gncmod-html.c @@ -0,0 +1,82 @@ +/********************************************************************* + * gncmod-html.c + * module definition/initialization for the html utilities + * + * Copyright (c) 2001 Linux Developers Group, Inc. + *********************************************************************/ + +#include "config.h" + +#include +#include +#include + +#include "gnc-module.h" +#include "gnc-module-api.h" + +#include "gnc-html.h" +#include "qof.h" + +GNC_MODULE_API_DECL(libgncmod_html) + +/* version of the gnc module system interface we require */ +int libgncmod_html_gnc_module_system_interface = 0; + +/* module versioning uses libtool semantics. */ +int libgncmod_html_gnc_module_current = 0; +int libgncmod_html_gnc_module_revision = 0; +int libgncmod_html_gnc_module_age = 0; + + +char * +libgncmod_html_gnc_module_path( void ) +{ + return g_strdup( "gnucash/html" ); +} + +char * +libgncmod_html_gnc_module_description( void ) +{ + return g_strdup( "Utilities for using HTML with GnuCash" ); +} + +static void +lmod( char* mn ) +{ + char* form = g_strdup_printf( "(use-modules %s)\n", mn ); + scm_c_eval_string( form ); + g_free( form ); +} + +extern SCM scm_init_sw_gnc_html_module( void ); + +int +libgncmod_html_gnc_module_init( int refcount ) +{ + /* load the engine (we depend on it) */ + if( !gnc_module_load( "gnucash/engine", 0 ) ) { + return FALSE; + } + + /* load the calculation module (we depend on it) */ + if( !gnc_module_load( "gnucash/calculation", 0 ) ) { + return FALSE; + } + + if( !gnc_module_load( "gnucash/app-utils", 0 ) ) { + return FALSE; + } + + scm_init_sw_gnc_html_module(); + gnc_html_initialize(); + lmod( "(sw_gnc_html)" ); +// lmod( "(gnucash gnc_html)" ); + + return TRUE; +} + +int +libgncmod_html_gnc_module_end( int refcount ) +{ + return TRUE; +} diff --git a/src/import-export/Makefile.am b/src/import-export/Makefile.am index 38626fcb28..49730bbdc6 100644 --- a/src/import-export/Makefile.am +++ b/src/import-export/Makefile.am @@ -68,8 +68,6 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/gnome-utils \ -I${top_srcdir}/src/libqof/qof \ ${GNOME_CFLAGS} \ - ${GTKHTML_CFLAGS} \ - ${GDK_PIXBUF_CFLAGS} \ ${GLADE_CFLAGS} \ ${GUILE_INCS} \ ${GLIB_CFLAGS} diff --git a/src/import-export/csv/Makefile.am b/src/import-export/csv/Makefile.am index f80f4d3c68..4e9ad433df 100644 --- a/src/import-export/csv/Makefile.am +++ b/src/import-export/csv/Makefile.am @@ -43,7 +43,6 @@ AM_CPPFLAGS = \ -I${top_srcdir}/lib/libc \ -I${top_srcdir}/lib \ ${GNOME_CFLAGS} \ - ${GTKHTML_CFLAGS} \ ${GLADE_CFLAGS} \ ${GUILE_INCS} \ ${GLIB_CFLAGS} \ diff --git a/src/import-export/hbci/Makefile.am b/src/import-export/hbci/Makefile.am index 15e1ace983..69af333687 100644 --- a/src/import-export/hbci/Makefile.am +++ b/src/import-export/hbci/Makefile.am @@ -74,7 +74,6 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/libqof/qof \ ${GUILE_INCS} \ ${GNOME_CFLAGS} \ - ${GTKHTML_CFLAGS} \ ${GLADE_CFLAGS} \ ${GLIB_CFLAGS} \ ${AQBANKING_CFLAGS} diff --git a/src/import-export/hbci/druid-hbci-initial.c b/src/import-export/hbci/druid-hbci-initial.c index d6aec8c9b2..e2535b1bdc 100644 --- a/src/import-export/hbci/druid-hbci-initial.c +++ b/src/import-export/hbci/druid-hbci-initial.c @@ -45,7 +45,7 @@ #include "druid-utils.h" #include "gnc-ui-util.h" #include "gnc-ui.h" -#include "gnc-html.h" +//#include "gnc-html.h" //#include "import-account-matcher.h" #include "gnc-component-manager.h" #include "gnc-session.h" diff --git a/src/import-export/log-replay/Makefile.am b/src/import-export/log-replay/Makefile.am index 472f0701d0..9e2266aebe 100644 --- a/src/import-export/log-replay/Makefile.am +++ b/src/import-export/log-replay/Makefile.am @@ -21,7 +21,8 @@ libgncmod_log_replay_la_LIBADD = \ ${top_builddir}/src/gnc-module/libgnc-module.la \ ${top_builddir}/src/libqof/qof/libgnc-qof.la \ ${GTK_LIBS} \ - ${GLIB_LIBS} + ${GLIB_LIBS} \ + ${QOF_LIBS} AM_CPPFLAGS = \ -I${top_srcdir}/src \ @@ -34,7 +35,6 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/import-export \ -I${top_srcdir}/src/libqof/qof \ ${GNOME_CFLAGS} \ - ${GTKHTML_CFLAGS} \ ${GLADE_CFLAGS} \ ${GUILE_INCS} \ ${GLIB_CFLAGS} diff --git a/src/import-export/ofx/Makefile.am b/src/import-export/ofx/Makefile.am index 290dce216f..d88ac004af 100644 --- a/src/import-export/ofx/Makefile.am +++ b/src/import-export/ofx/Makefile.am @@ -35,7 +35,6 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/import-export \ -I${top_srcdir}/src/libqof/qof \ ${GNOME_CFLAGS} \ - ${GTKHTML_CFLAGS} \ ${GLADE_CFLAGS} \ ${GUILE_INCS} \ ${GLIB_CFLAGS} \ diff --git a/src/import-export/qif-import/Makefile.am b/src/import-export/qif-import/Makefile.am index 61de8a1493..e10e03acbf 100644 --- a/src/import-export/qif-import/Makefile.am +++ b/src/import-export/qif-import/Makefile.am @@ -65,9 +65,7 @@ AM_CPPFLAGS = \ ${GUILE_INCS} \ ${GLIB_CFLAGS} \ ${GLADE_CFLAGS} \ - ${GNOME_CFLAGS} \ - ${GDK_PIXBUF_CFLAGS} \ - ${GTKHTML_CFLAGS} + ${GNOME_CFLAGS} uidir = $(GNC_UI_DIR) ui_DATA = \ diff --git a/src/report/report-gnome/Makefile.am b/src/report/report-gnome/Makefile.am index 75bb53e338..027340ab20 100644 --- a/src/report/report-gnome/Makefile.am +++ b/src/report/report-gnome/Makefile.am @@ -11,11 +11,11 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/app-utils \ -I${top_srcdir}/src/gnome-utils \ -I${top_srcdir}/src/gnome \ + -I${top_srcdir}/src/html \ -I${top_srcdir}/src/report/report-system \ -I${top_srcdir}/src/libqof/qof \ ${GLADE_CFLAGS} \ ${GUILE_INCS} \ - ${GTKHTML_CFLAGS} \ ${GNOME_CFLAGS} \ ${GLIB_CFLAGS} @@ -44,6 +44,7 @@ libgncmod_report_gnome_la_LIBADD = \ ${top_builddir}/src/engine/libgncmod-engine.la \ ${top_builddir}/src/app-utils/libgncmod-app-utils.la \ ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \ + ${top_builddir}/src/html/libgncmod-html.la \ ${top_builddir}/src/report/report-system/libgncmod-report-system.la \ ${top_builddir}/src/libqof/qof/libgnc-qof.la \ ${GLADE_LIBS} \ diff --git a/src/report/report-gnome/gnc-plugin-page-report.c b/src/report/report-gnome/gnc-plugin-page-report.c index e43e0f31ff..b5df494580 100644 --- a/src/report/report-gnome/gnc-plugin-page-report.c +++ b/src/report/report-gnome/gnc-plugin-page-report.c @@ -53,6 +53,7 @@ #include "gnc-gnome-utils.h" #include "gnc-html-history.h" #include "gnc-html.h" +#include "gnc-html-factory.h" #include "gnc-file.h" #include "gnc-plugin.h" #include "gnc-plugin-page-report.h" @@ -112,7 +113,8 @@ typedef struct GncPluginPageReportPrivate gboolean reloading; /// the gnc_html abstraction this PluginPage contains - gnc_html *html; +// gnc_html *html; + GncHtml *html; /// the container the above HTML widget is in. GtkContainer *container; @@ -138,7 +140,8 @@ static void gnc_plugin_page_report_update_edit_menu (GncPluginPage *page, gboole static gboolean gnc_plugin_page_report_finish_pending (GncPluginPage *page); static int gnc_plugin_page_report_check_urltype(URLType t); -static void gnc_plugin_page_report_load_cb(gnc_html * html, URLType type, +//static void gnc_plugin_page_report_load_cb(gnc_html * html, URLType type, +static void gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, const gchar * location, const gchar * label, gpointer data); static void gnc_plugin_page_report_expose_event_cb(GtkWidget *unused, GdkEventExpose *unused1, gpointer data); @@ -324,7 +327,9 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page ) priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); topLvl = GTK_WINDOW(gnc_ui_get_toplevel()); - priv->html = gnc_html_new( topLvl ); +// priv->html = gnc_html_new( topLvl ); + priv->html = gnc_html_factory_create_html(); + gnc_html_set_parent( priv->html, topLvl ); gnc_html_history_set_node_destroy_cb(gnc_html_get_history(priv->html), gnc_plugin_page_report_history_destroy_cb, @@ -357,6 +362,7 @@ gnc_plugin_page_report_create_widget( GncPluginPage *page ) gnc_window_set_progressbar_window( GNC_WINDOW(page->window) ); gnc_html_show_url(priv->html, type, url_location, url_label, 0); + g_free(url_location); gnc_window_set_progressbar_window( NULL ); g_signal_connect(priv->container, "expose_event", @@ -427,7 +433,8 @@ gnc_plugin_page_report_setup( GncPluginPage *ppage ) * called after a report is loaded into the gnc_html widget ********************************************************************/ static void -gnc_plugin_page_report_load_cb(gnc_html * html, URLType type, +//gnc_plugin_page_report_load_cb(gnc_html * html, URLType type, +gnc_plugin_page_report_load_cb(GncHtml * html, URLType type, const gchar * location, const gchar * label, gpointer data) { @@ -1428,7 +1435,7 @@ gnc_plugin_page_report_export_cb( GtkAction *action, GncPluginPageReport *report result = (res != SCM_BOOL_F); } else - result = gnc_html_export (priv->html, filepath); + result = gnc_html_export_to_file (priv->html, filepath); if (!result) { @@ -1485,7 +1492,7 @@ gnc_plugin_page_report_copy_cb(GtkAction *action, GncPluginPageReport *report) GncPluginPageReportPrivate *priv; priv = GNC_PLUGIN_PAGE_REPORT_GET_PRIVATE(report); - gnc_html_copy(priv->html); + gnc_html_copy_to_clipboard(priv->html); } /******************************************************************** diff --git a/src/report/report-gnome/window-report.c b/src/report/report-gnome/window-report.c index 38707f4e76..b3ae92f464 100644 --- a/src/report/report-gnome/window-report.c +++ b/src/report/report-gnome/window-report.c @@ -40,6 +40,7 @@ #include "gnc-report.h" #include "gnc-ui.h" #include "option-util.h" +#include "gnc-html.h" #include "window-report.h" #include "guile-mappings.h" diff --git a/src/report/report-gnome/window-report.h b/src/report/report-gnome/window-report.h index 19919b36f3..d4ae7a4bf3 100644 --- a/src/report/report-gnome/window-report.h +++ b/src/report/report-gnome/window-report.h @@ -25,7 +25,7 @@ #include -#include "gnc-html.h" +//#include "gnc-html.h" #include "qof.h" typedef struct gnc_report_window_s gnc_report_window; diff --git a/src/report/report-system/gnc-report.c b/src/report/report-system/gnc-report.c index d86e8bc0b0..248e5e5d01 100644 --- a/src/report/report-system/gnc-report.c +++ b/src/report/report-system/gnc-report.c @@ -144,6 +144,7 @@ gnc_run_report (gint report_id, char ** data) str = g_strdup_printf("(gnc:report-run %d)", report_id); scm_text = gfec_eval_string(str, error_handler); + g_free(str); if (scm_text == SCM_UNDEFINED || !SCM_STRINGP (scm_text)) return FALSE; diff --git a/src/report/report-system/report-system.scm b/src/report/report-system/report-system.scm index 56066bea5f..a35d5259fc 100644 --- a/src/report/report-system/report-system.scm +++ b/src/report/report-system/report-system.scm @@ -17,7 +17,8 @@ (gnc:module-load "gnucash/engine" 0) (gnc:module-load "gnucash/app-utils" 0) -(gnc:module-load "gnucash/gnome-utils" 0) ; for the html routines +(gnc:module-load "gnucash/html" 0) +(gnc:module-load "gnucash/gnome-utils" 0) ;; commodity-utilities.scm (export gnc:get-match-commodity-splits)