[Gnucash-changes] Merge the gog-integ branch into the g2 branch.
Derek Atkins
warlord at cvs.gnucash.org
Sat Apr 23 20:35:24 EDT 2005
Log Message:
-----------
Merge the gog-integ branch into the g2 branch.
As we're now depending on gtk+-2.4, etc. we can build this code now.
It builds. It seems to work for some people, but not for me.
However with it being in g2 it should get more exposure and testing.
Thanks, Josh!
Tags:
----
gnucash-gnome2-dev
Modified Files:
--------------
gnucash:
ChangeLog
GNOME2_STATUS
HACKING
configure.in
gnucash/lib:
Makefile.am
gnucash/src/engine:
gnc-trace.c
gnucash/src/gnome:
gnc-main-window.c
gnucash/src/gnome-utils:
Makefile.am
gnc-gnome-utils.c
gnc-html.c
gnucash/src/report/utility-reports:
Makefile.am
utility-reports.scm
Added Files:
-----------
gnucash/lib/goffice:
ChangeLog
Makefile.am
goffice-config.h
goffice-plugins.mk
goffice.c
goffice.h
goffice.mk
paths.h.in
split.c
split.h
symbols.py
gnucash/lib/goffice/app:
Makefile.am
go-app.h
go-cmd-context-impl.h
go-cmd-context.c
go-cmd-context.h
go-doc-control-impl.h
go-doc-control.c
go-doc-control.h
go-doc-impl.h
go-doc.c
go-doc.h
go-error-stack.h
go-object.c
go-object.h
go-plugin.h
go-service-impl.h
go-service.c
go-service.h
goffice-app.h
gnucash/lib/goffice/cut-n-paste:
Makefile.am
gnucash/lib/goffice/cut-n-paste/egg-recent-files:
Makefile.am
egg-recent-item.c
egg-recent-item.h
egg-recent-model.c
egg-recent-model.h
egg-recent-util.c
egg-recent-util.h
egg-recent-view-gtk.c
egg-recent-view-gtk.h
egg-recent-view.c
egg-recent-view.h
egg-recent.h
gnucash/lib/goffice/cut-n-paste/pcre:
Makefile.am
get.c
internal.h
maketables.c
pcre.c
pcre.h
pcreposix.c
pcreposix.h
printint.c
study.c
gnucash/lib/goffice/drawing:
Makefile.am
god-anchor.c
god-anchor.h
god-default-attributes.c
god-default-attributes.h
god-drawing-group.c
god-drawing-group.h
god-drawing-renderer-gdk.c
god-drawing-renderer-gdk.h
god-drawing-view.c
god-drawing-view.h
god-drawing.c
god-drawing.h
god-image-store.c
god-image-store.h
god-image.c
god-image.h
god-paragraph-attributes.c
god-paragraph-attributes.h
god-property-table.c
god-property-table.h
god-shape.c
god-shape.h
god-text-model.c
god-text-model.h
gnucash/lib/goffice/graph:
Makefile.am
README
go-data-impl.h
go-data-simple.c
go-data-simple.h
go-data.c
go-data.h
goffice-graph.h
gog-axis-prefs.glade
gog-axis.c
gog-axis.h
gog-chart-impl.h
gog-chart.c
gog-chart.h
gog-control-foocanvas.c
gog-control-foocanvas.h
gog-data-allocator.c
gog-data-allocator.h
gog-data-set.c
gog-data-set.h
gog-error-bar-prefs.glade
gog-error-bar.c
gog-error-bar.h
gog-graph-impl.h
gog-graph.c
gog-graph.h
gog-grid-line.c
gog-grid-line.h
gog-grid.c
gog-grid.h
gog-guru-type-selector.glade
gog-guru.c
gog-guru.glade
gog-guru.h
gog-label.c
gog-label.h
gog-legend.c
gog-legend.h
gog-object-xml.c
gog-object-xml.h
gog-object.c
gog-object.h
gog-outlined-object.c
gog-outlined-object.h
gog-plot-engine.c
gog-plot-engine.h
gog-plot-impl.h
gog-plot.c
gog-plot.h
gog-renderer-gnome-print.c
gog-renderer-gnome-print.h
gog-renderer-impl.h
gog-renderer-pixbuf.c
gog-renderer-pixbuf.h
gog-renderer-svg.c
gog-renderer-svg.h
gog-renderer.c
gog-renderer.h
gog-series-impl.h
gog-series.c
gog-series.h
gog-style-prefs.glade
gog-style.c
gog-style.h
gog-styled-object.c
gog-styled-object.h
gog-theme.c
gog-theme.h
gog-view.c
gog-view.h
gnucash/lib/goffice/graph/plugins:
Makefile.am
gnucash/lib/goffice/graph/plugins/plot_barcol:
Makefile.am
gog-1.5d.c
gog-1.5d.h
gog-barcol-prefs.c
gog-barcol-prefs.glade
gog-barcol.c
gog-barcol.h
gog-line.c
gog-line.h
plot-types.xml.in
plugin.xml.in
gnucash/lib/goffice/graph/plugins/plot_pie:
Makefile.am
gog-pie-prefs.c
gog-pie-prefs.glade
gog-pie-series.glade
gog-pie.c
gog-pie.h
gog-ring-prefs.glade
plot-types.xml.in
plugin.xml.in
gnucash/lib/goffice/graph/plugins/plot_radar:
Makefile.am
gog-radar.c
gog-radar.h
plot-types.xml.in
plugin.xml.in
gnucash/lib/goffice/graph/plugins/plot_surface:
Makefile.am
gog-contour-prefs.c
gog-contour-prefs.glade
gog-surface.c
gog-surface.h
plot-types.xml.in
plugin.xml.in
gnucash/lib/goffice/graph/plugins/plot_xy:
Makefile.am
gog-bubble-prefs.c
gog-bubble-prefs.glade
gog-xy.c
gog-xy.h
plot-types.xml.in
plugin.xml.in
gnucash/lib/goffice/gui-utils:
Makefile.am
go-action-combo-color.c
go-action-combo-color.h
go-action-combo-pixmaps.c
go-action-combo-pixmaps.h
go-action-combo-stack.c
go-action-combo-stack.h
go-action-combo-text.c
go-action-combo-text.h
go-color-group.c
go-color-group.h
go-color-palette.c
go-color-palette.h
go-combo-box.c
go-combo-box.h
go-combo-color.c
go-combo-color.h
go-combo-pixmaps.c
go-combo-pixmaps.h
go-combo-text.c
go-combo-text.h
go-dock-band.c
go-dock-band.h
go-dock-item-grip.c
go-dock-item-grip.h
go-dock-item.c
go-dock-item.h
go-dock-layout.c
go-dock-layout.h
go-dock.c
go-dock.h
go-font-sel.c
go-font-sel.glade
go-font-sel.h
go-gui-utils.c
go-gui-utils.h
go-marshalers.list
gnucash/lib/goffice/pixmaps:
Makefile.am
area.xpm
bar-hboth.png
bar-hminus.png
bar-hplus.png
bar-none.png
bar-vboth.png
bar-vminus.png
bar-vplus.png
bar.xpm
bubble.xpm
chart-pie-2d.svg
chart-rings-2d.svg
chart_area_1_1.png
chart_area_1_1.svg
chart_area_1_2.png
chart_area_1_2.svg
chart_area_1_3.png
chart_area_1_3.svg
chart_bar_1_1.png
chart_bar_1_1.svg
chart_bar_1_2.png
chart_bar_1_2.svg
chart_bar_1_3.png
chart_bar_1_3.svg
chart_bar_2_1.png
chart_bar_2_2.png
chart_bar_2_3.png
chart_bubble_1_1.png
chart_bubble_1_1.svg
chart_bubble_1_2.png
chart_bubble_1_2.svg
chart_column_1_1.png
chart_column_1_1.svg
chart_column_1_2.png
chart_column_1_2.svg
chart_column_1_3.png
chart_column_1_3.svg
chart_column_2_1.png
chart_column_2_2.png
chart_column_2_3.png
chart_column_3_1.png
chart_cone_1_1.png
chart_cone_1_2.png
chart_cone_1_3.png
chart_cone_2_1.png
chart_cone_2_2.png
chart_cone_2_3.png
chart_cone_3_1.png
chart_cylinder_1_1.png
chart_cylinder_1_2.png
chart_cylinder_1_3.png
chart_cylinder_2_1.png
chart_cylinder_2_2.png
chart_cylinder_2_3.png
chart_cylinder_3_1.png
chart_line_1_1.png
chart_line_1_1.svg
chart_line_1_2.png
chart_line_1_2.svg
chart_line_1_3.png
chart_line_1_3.svg
chart_line_2_1.png
chart_line_2_1.svg
chart_line_2_2.png
chart_line_2_2.svg
chart_line_2_3.png
chart_line_2_3.svg
chart_line_3_1.png
chart_pie_1_1.png
chart_pie_1_1.svg
chart_pie_1_2.png
chart_pie_1_3.png
chart_pie_2_1.png
chart_pie_2_1.svg
chart_pie_2_2.png
chart_pie_2_3.png
chart_pyramid_1_1.png
chart_pyramid_1_2.png
chart_pyramid_1_3.png
chart_pyramid_2_1.png
chart_pyramid_2_2.png
chart_pyramid_2_3.png
chart_pyramid_3_1.png
chart_radar_1_1.png
chart_radar_1_1.svg
chart_radar_1_2.png
chart_radar_1_2.svg
chart_radar_1_3.png
chart_radar_1_3.svg
chart_ring_1_1.png
chart_ring_1_1.svg
chart_ring_1_2.png
chart_ring_1_2.svg
chart_scatter_1_1.png
chart_scatter_1_1.svg
chart_scatter_2_1.png
chart_scatter_2_2.png
chart_scatter_3_1.png
chart_scatter_3_1.svg
chart_scatter_3_2.png
chart_scatter_3_2.svg
chart_stock_1_1.png
chart_stock_1_2.png
chart_stock_2_1.png
chart_stock_2_2.png
column.xpm
doughnut.xpm
linegraph.xpm
pie.xpm
radar.xpm
scatter.xpm
stock.xpm
surface.xpm
gnucash/lib/goffice/split:
Makefile.am
application.h
command-context-priv.h
command-context-stderr.c
command-context-stderr.h
command-context.c
command-context.h
dates.c
dates.h
datetime.c
datetime.h
dependent.h
error-info.c
error-info.h
file.h
format.c
format.h
formats.c
func.h
global-gnome-font.c
global-gnome-font.h
gnumeric-gconf-priv.h
gnumeric-gconf.c
gnumeric-gconf.h
gnumeric.h
gui-file.h
gui-gnumeric.h
gui-util.c
gui-util.h
gutils.c
gutils.h
io-context-priv.h
io-context.c
io-context.h
mathfunc.c
mathfunc.h
module-plugin-defs.h
mstyle.c
mstyle.h
number-match.c
number-match.h
numbers.h
plugin-loader-module.c
plugin-loader-module.h
plugin-loader.c
plugin-loader.h
plugin-service-impl.h
plugin-service.c
plugin-service.h
plugin-util.c
plugin-util.h
plugin.c
plugin.h
position.h
ranges.h
regutf8.c
regutf8.h
str.c
str.h
style-border.c
style-border.h
style-color.c
style-color.h
style.c
style.h
validation.c
validation.h
value.c
value.h
workbook-control-gui.h
xml-io-version.h
xml-io.h
gnucash/lib/goffice/split/widgets:
Makefile.am
widget-font-selector.h
widget-format-selector.c
widget-format-selector.h
gnucash/lib/goffice/utils:
Makefile.am
go-color.c
go-color.h
go-file.c
go-file.h
go-font.c
go-font.h
go-format.c
go-format.h
go-gradient.c
go-gradient.h
go-line.c
go-line.h
go-locale.c
go-locale.h
go-marker.c
go-marker.h
go-math.c
go-math.h
go-pattern.c
go-pattern.h
go-units.h
goffice-utils.h
gnucash/src/gnome-utils:
gnc-html-graph-gog.c
gnc-html-graph-gog.h
gnucash/src/report/utility-reports:
test-graphing.scm
Revision Data
-------------
--- /dev/null
+++ lib/goffice/split/dates.h
@@ -0,0 +1,9 @@
+#ifndef GNUMERIC_DATES_H
+#define GNUMERIC_DATES_H
+
+extern const char *day_short [];
+extern const char *day_long [];
+extern const char *month_short [];
+extern const char *month_long [];
+
+#endif
--- /dev/null
+++ lib/goffice/split/command-context-priv.h
@@ -0,0 +1,28 @@
+#ifndef GNUMERIC_GNM_CMD_CONTEXT_PRIV_H
+#define GNUMERIC_GNM_CMD_CONTEXT_PRIV_H
+
+#include "command-context.h"
+
+typedef struct {
+ GTypeInterface base;
+
+ char * (*get_password) (GnmCmdContext *cc,
+ char const *filename);
+ void (*set_sensitive) (GnmCmdContext *cc,
+ gboolean sensitive);
+ void (*progress_set) (GnmCmdContext *cc, gfloat val);
+ void (*progress_message_set) (GnmCmdContext *cc, gchar const *msg);
+ struct {
+ void (*error) (GnmCmdContext *cc, GError *err);
+ void (*error_info) (GnmCmdContext *ctxt, ErrorInfo *error);
+ } error;
+} GnmCmdContextClass;
+
+#define GNM_CMD_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GNM_CMD_CONTEXT_TYPE, GnmCmdContextClass))
+#define IS_GNM_CMD_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNM_CMD_CONTEXT_TYPE))
+
+/* protected, these do not really belong here, they are associated with io-context */
+void cmd_context_progress_set (GnmCmdContext *cc, gfloat f);
+void cmd_context_progress_message_set (GnmCmdContext *cc, char const *msg);
+
+#endif /* GNUMERIC_GNM_CMD_CONTEXT_PRIV_H */
--- /dev/null
+++ lib/goffice/split/format.c
@@ -0,0 +1,2723 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* format.c - attempts to emulate excel's number formatting ability.
+ * Copyright (C) 1998 Chris Lahey, Miguel de Icaza
+ *
+ * Redid the format parsing routine to make it accept more of the Excel
+ * formats. The number rendeing code from Chris has not been touched,
+ * that routine is pretty good.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "format.h"
+
+#include "style-color.h"
+#include "dates.h"
+#include "value.h"
+#include "datetime.h"
+#include "mathfunc.h"
+#include "str.h"
+#include "gutils.h"
+#include "number-match.h"
+
+#include <locale.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#endif
+
+#undef DEBUG_REF_COUNT
+
+/***************************************************************************/
+
+static GnmFormat *default_percentage_fmt;
+static GnmFormat *default_money_fmt;
+static GnmFormat *default_date_fmt;
+static GnmFormat *default_time_fmt;
+static GnmFormat *default_date_time_fmt;
+static GnmFormat *default_general_fmt;
+
+
+/*
+ * Points to the locale information for number display. All strings are
+ * in UTF-8 encoding.
+ */
+static gboolean locale_info_cached = FALSE;
+static GString *lc_decimal = NULL;
+static GString *lc_thousand = NULL;
+static gboolean lc_precedes;
+static gboolean lc_space_sep;
+static GString *lc_currency = NULL;
+
+static gboolean date_order_cached = FALSE;
+
+static gboolean boolean_cached = FALSE;
+static char const *lc_TRUE = NULL;
+static char const *lc_FALSE = NULL;
+
+char const *
+gnm_setlocale (int category, char const *val)
+{
+ locale_info_cached = FALSE;
+ date_order_cached = FALSE;
+ boolean_cached = FALSE;
+ return setlocale (category, val);
+}
+
+static void
+convert1 (GString *res, const char *lstr, const char *name, const char *def)
+{
+ char *tmp;
+
+ if (lstr == NULL || lstr[0] == 0) {
+ g_string_assign (res, def);
+ return;
+ }
+
+ tmp = g_locale_to_utf8 (lstr, -1, NULL, NULL, NULL);
+ if (tmp) {
+ g_string_assign (res, tmp);
+ g_free (tmp);
+ return;
+ }
+
+ g_warning ("Failed to convert locale's %s \"%s\" to UTF-8.", name, lstr);
+ g_string_assign (res, def);
+}
+
+static void
+update_lc (void)
+{
+ struct lconv *lc = localeconv ();
+
+ /*
+ * Extract all information here as lc is not guaranteed to stay
+ * valid after next localeconv call which could be anywhere.
+ */
+
+ convert1 (lc_decimal, lc->decimal_point, "decimal separator", ".");
+ if (g_utf8_strlen (lc_decimal->str, -1) != 1)
+ g_warning ("Decimal separator is not a single character.");
+
+ convert1 (lc_thousand, lc->mon_thousands_sep, "monetary thousands separator",
+ (lc_decimal->str[0] == ',' ? "." : ","));
+ if (g_utf8_strlen (lc_thousand->str, -1) != 1)
+ g_warning ("Monetary thousands separator is not a single character.");
+
+ if (g_string_equal (lc_thousand, lc_decimal)) {
+ g_string_assign (lc_thousand,
+ (lc_decimal->str[0] == ',') ? "." : ",");
+ g_warning ("Monetary thousands separator is the same as the decimal separator; converting '%s' to '%s'",
+ lc_decimal->str, lc_thousand->str);
+ }
+
+ /* Use != 0 rather than == 1 so that CHAR_MAX (undefined) is true */
+ lc_precedes = (lc->p_cs_precedes != 0);
+
+ /* Use == 1 rather than != 0 so that CHAR_MAX (undefined) is false */
+ lc_space_sep = (lc->p_sep_by_space == 1);
+
+ convert1 (lc_currency, lc->currency_symbol, "currency symbol", "$");
+
+ locale_info_cached = TRUE;
+}
+
+GString const *
+format_get_decimal (void)
+{
+ if (!locale_info_cached)
+ update_lc ();
+
+ return lc_decimal;
+}
+
+GString const *
+format_get_thousand (void)
+{
+ if (!locale_info_cached)
+ update_lc ();
+
+ return lc_thousand;
+}
+
+/**
+ * format_get_currency :
+ * @precedes : a pointer to a boolean which is set to TRUE if the currency
+ * should precede
+ * @space_sep: a pointer to a boolean which is set to TRUE if the currency
+ * should have a space separating it from the the value
+ *
+ * Play with the default logic so that things come out nicely for the default
+ * case.
+ */
+GString const *
+format_get_currency (gboolean *precedes, gboolean *space_sep)
+{
+ if (!locale_info_cached)
+ update_lc ();
+
+ if (precedes)
+ *precedes = lc_precedes;
+
+ if (space_sep)
+ *space_sep = lc_space_sep;
+
+ return lc_currency;
+}
+
+/*
+ * format_month_before_day :
+ *
+ * A quick utility routine to guess whether the default date format
+ * uses day/month or month/day
+ */
+gboolean
+format_month_before_day (void)
+{
+#ifdef HAVE_LANGINFO_H
+ static gboolean month_first = TRUE;
+
+ if (!date_order_cached) {
+ char const *ptr = nl_langinfo (D_FMT);
+
+ date_order_cached = TRUE;
+ month_first = TRUE;
+ if (ptr)
+ while (*ptr) {
+ char c = *ptr++;
+ if (c == 'd' || c == 'D') {
+ month_first = FALSE;
+ break;
+ } else if (c == 'm' || c == 'M')
+ break;
+ }
+ }
+
+ return month_first;
+#else
+ static gboolean warning = TRUE;
+ if (warning) {
+ g_warning ("Incomplete locale library, dates will be month day year");
+ warning = FALSE;
+ }
+ return TRUE;
+#endif
+}
+
+/* Use comma as the arg separator unless the decimal point is a
+ * comma, in which case use a semi-colon
+ */
+char
+format_get_arg_sep (void)
+{
+ if (format_get_decimal ()->str[0] == ',')
+ return ';';
+ return ',';
+}
+
+char
+format_get_col_sep (void)
+{
+ if (format_get_decimal ()->str[0] == ',')
+ return '\\';
+ return ',';
+}
+
+char const *
+format_boolean (gboolean b)
+{
+ if (!boolean_cached) {
+ lc_TRUE = _("TRUE");
+ lc_FALSE = _("FALSE");
+ boolean_cached = TRUE;
+ }
+ return b ? lc_TRUE : lc_FALSE;
+}
+
+/**
+ * gnm_set_untranslated_bools :
+ *
+ * Short circuit the current locale so that we can import files
+ * and still produce error messages in the current LC_MESSAGE
+ **/
+void
+gnm_set_untranslated_bools (void)
+{
+ lc_TRUE = "TRUE";
+ lc_FALSE = "FALSE";
+ boolean_cached = TRUE;
+}
+
+/***************************************************************************/
+
+/* WARNING : Global */
+static GHashTable *style_format_hash = NULL;
+
+typedef struct {
+ char const *format;
+ gboolean want_am_pm;
+ gboolean has_fraction;
+ char restriction_type;
+ gboolean suppress_minus;
+ gboolean elapsed_time;
+ gnm_float restriction_value;
+ GnmColor *color;
+} StyleFormatEntry;
+
+/*
+ * The returned string is newly allocated.
+ *
+ * Current format is an optional date specification followed by an
+ * optional number specification.
+ *
+ * A date specification is an arbitrary sequence of characters (other
+ * than '#', '0', '?', or '.') which is copied to the output. The
+ * standard date fields are substituted for. If it ever finds an a or
+ * a p it lists dates in 12 hour time, otherwise, it lists dates in 24
+ * hour time.
+ *
+ * A number specification is as described in the relevant portions of
+ * the excel formatting information. Commas can currently only appear
+ * at the end of the number specification. Fractions are supported
+ * but the parsing is not as nice as it should be.
+ */
+
+
+/*
+ * Parses the year field at the beginning of the format. Returns the
+ * number of characters used.
+ */
+static int
+append_year (GString *string, guchar const *format, struct tm const *time_split)
+{
+ int year = time_split->tm_year + 1900;
+
+ if (format[1] != 'y' && format[1] != 'Y') {
+ g_string_append_c (string, 'y');
+ return 1;
+ }
+
+ if ((format[2] != 'y' && format[2] != 'Y') ||
+ (format[3] != 'y' && format[3] != 'Y')) {
+ g_string_append_printf (string, "%02d", year % 100);
+ return 2;
+ }
+
+ g_string_append_printf (string, "%04d", year);
+ return 4;
+}
+
+/*
+ * Parses the month field at the beginning of the format. Returns the
+ * number of characters used.
+ */
+static int
+append_month (GString *string, int n, struct tm const *time_split)
+{
+ int month = time_split->tm_mon + 1;
+
+ if (n == 1) {
+ g_string_append_printf (string, "%d", month);
+ return 1;
+ }
+
+ if (n == 2) {
+ g_string_append_printf (string, "%02d", month);
+ return 2;
+ }
+
+ if (n == 3) {
+ g_string_append (string, _(month_short[month - 1]) + 1);
+ return 3;
+ }
+
+ if (n == 5) {
+ g_string_append_c (string, _(month_short[month - 1])[1]);
+ return 5;
+ }
+ g_string_append (string, _(month_long[month - 1]));
+ return 4;
+}
+
+/*
+ * Parses the day field at the beginning of the format. Returns the
+ * number of characters used.
+ */
+static int
+append_day (GString *string, guchar const *format, struct tm const *time_split)
+{
+ if (format[1] != 'd' && format[1] != 'D') {
+ g_string_append_printf (string, "%d", time_split->tm_mday);
+ return 1;
+ }
+
+ if (format[2] != 'd' && format[2] != 'D') {
+ g_string_append_printf (string, "%02d", time_split->tm_mday);
+ return 2;
+ }
+
+ if (format[3] != 'd' && format[3] != 'D') {
+ /* Note: day-of-week. */
+ g_string_append (string, _(day_short[time_split->tm_wday]) + 1);
+ return 3;
+ }
+
+ /* Note: day-of-week. */
+ g_string_append (string, _(day_long[time_split->tm_wday]));
+ return 4;
+}
+
+static void
+append_hour (GString *string, int n, struct tm const *time_split,
+ gboolean want_am_pm)
+{
+ int hour = time_split->tm_hour;
+
+ g_string_append_printf (string, "%0*d", MIN (n, 2),
+ (want_am_pm || (n > 2))
+ ? ((hour + 11) % 12) + 1
+ : hour);
+}
+
+static void
+append_hour_elapsed (GString *string, struct tm *tm, gnm_float number)
+{
+ gnm_float whole_days, frac_days;
+ gboolean is_neg;
+ int cs; /* Centi seconds. */
+ const int secs_per_day = 24 * 60 * 60;
+
+ is_neg = (number < 0);
+ frac_days = modfgnum (number, &whole_days);
+
+ /* ick. round assuming no more than 100th of a second, we really need
+ * to know the precision earlier */
+ cs = (int)gnumeric_fake_round (gnumabs (frac_days) * secs_per_day * 100);
+
+ /* FIXME: Why limit hours to int? */
+ cs /= 100;
+ tm->tm_sec = cs % 60;
+ cs /= 60;
+ tm->tm_min = cs % 60;
+ cs /= 60;
+ tm->tm_hour = (is_neg ? -cs : cs) + (int)(whole_days * 24);
+
+ g_string_append_printf (string, "%d", tm->tm_hour);
+}
+
+static void
+append_minute (GString *string, int n, struct tm const *time_split)
+{
+ g_string_append_printf (string, "%0*d", n, time_split->tm_min);
+}
+
+static void
+append_minute_elapsed (GString *string, struct tm *tm, gnm_float number)
+{
+ double res, int_part;
+
+ res = modf (gnumeric_fake_round (number * 24. * 60.), &int_part);
+ tm->tm_min = int_part;
+ tm->tm_sec = res * ((res < 0.) ? -60. : 60.);
+ g_string_append_printf (string, "%d", tm->tm_min);
+}
+
+/*
+ * Renders the second field.
+ */
+static void
+append_second (GString *string, int n, struct tm const *time_split)
+{
+ g_string_append_printf (string, "%0*d", n, time_split->tm_sec);
+}
+
+/*
+ * Renders the second field in elapsed
+ */
+static void
+append_second_elapsed (GString *string, gnm_float number)
+{
+ g_string_append_printf (string, "%d",
+ (int) gnumeric_fake_round (number * 24. * 3600.));
+}
+
+static StyleFormatEntry *
+format_entry_ctor (void)
+{
+ StyleFormatEntry *entry;
+
+ entry = g_new (StyleFormatEntry, 1);
+ entry->restriction_type = '*';
+ entry->restriction_value = 0.;
+ entry->suppress_minus = FALSE;
+ entry->elapsed_time = FALSE;
+ entry->want_am_pm = entry->has_fraction = FALSE;
+ entry->color = NULL;
+ return entry;
+}
+
+/**
+ * format_entry_dtor :
+ *
+ * WARNING : do not call this for temporary formats generated for
+ * 'General'.
+ */
+static void
+format_entry_dtor (gpointer data, gpointer user_data)
+{
+ StyleFormatEntry *entry = data;
+ if (entry->color != NULL) {
+ style_color_unref (entry->color);
+ entry->color = NULL;
+ }
+ g_free ((char *)entry->format);
+ g_free (entry);
+}
+
+static void
+format_entry_set_fmt (StyleFormatEntry *entry,
+ gchar const *begin,
+ gchar const *end)
+{
+ /* empty formats are General if there is a color, or a condition */
+ entry->format = (begin != NULL && end != begin)
+ ? g_strndup (begin, end - begin)
+ : g_strdup ((entry->color || entry->restriction_type != '*')
+ ? "General" : "");
+}
+
+static GnmColor * lookup_color (char const *str, char const *end);
+
+/*
+ * Since the Excel formating codes contain a number of ambiguities, this
+ * routine does some analysis on the format first. This routine should always
+ * return, it cannot fail, in the worst case it should just downgrade to
+ * simplistic formatting
+ */
+static void
+format_compile (GnmFormat *format)
+{
+ gchar const *fmt, *real_start = NULL;
+ StyleFormatEntry *entry = format_entry_ctor ();
+ int num_entries = 1, counter = 0;
+ GSList *ptr;
+
+ format_match_create (format);
+ for (fmt = format->format; *fmt ; fmt++) {
+ if (NULL == real_start && '[' != *fmt)
+ real_start = fmt;
+
+ switch (*fmt) {
+ case '[': {
+ gchar const *begin = fmt + 1;
+ gchar const *end = begin;
+
+ /* find end checking for escapes but not quotes ?? */
+ for (; end[0] != ']' && end[1] != '\0' ; ++end)
+ if (*end == '\\')
+ end++;
+
+ /* Check for conditional */
+ if (*begin == '<') {
+ if (begin[1] == '=') {
+ entry->restriction_type = ',';
+ begin += 2;
+ } else if (begin[1] == '>') {
+ entry->restriction_type = '+';
+ begin += 2;
+ } else {
+ entry->restriction_type = '<';
+ begin++;
+ }
+ } else if (*begin == '>') {
+ if (begin[1] == '=') {
+ entry->restriction_type = '.';
+ begin += 2;
+ } else {
+ entry->restriction_type = '>';
+ begin++;
+ }
+ } else if (*begin == '=') {
+ entry->restriction_type = '=';
+ } else {
+ if (begin[1] == ']' &&
+ (*begin == 'h' || *begin == 'H' ||
+ *begin == 'm' || *begin == 'M' ||
+ *begin == 's' || *begin == 'S'))
+ entry->elapsed_time = TRUE;
+ else if (*begin != '$' && entry->color == NULL) {
+ entry->color = lookup_color (begin, end);
+ /* Only the first colour counts */
+ if (NULL != entry->color) {
+ fmt = end;
+ continue;
+ }
+ }
+ if (NULL == real_start)
+ real_start = fmt;
+ continue;
+ }
+ fmt = end;
+
+ /* fall back on 0 for errors */
+ errno = 0;
+ entry->restriction_value = strtognum (begin, (char **)&end);
+ if (errno == ERANGE || begin == end)
+ entry->restriction_value = 0.;
+
+ /* this is a guess based on checking the results of
+ * 0.00;[<0]0.00
+ * 0.00;[<=0]0.00
+ *
+ * for -1.2.3
+ **/
+ else if (entry->restriction_type == '<')
+ entry->suppress_minus = (entry->restriction_value <= 0.);
+ else if (entry->restriction_type == ',')
+ entry->suppress_minus = (entry->restriction_value < 0.);
+ break;
+ }
+
+ case '\\' :
+ if (fmt[1] != '\0')
+ fmt++; /* skip escaped characters */
+ break;
+
+ case '\'' :
+ case '\"' : {
+ /* skip quoted strings */
+ char const match = *fmt;
+ for (; fmt[0] != match && fmt[1] != '\0'; fmt++)
+ if (*fmt == '\\')
+ fmt++;
+ break;
+ }
+
+ case '/':
+ if (fmt[1] == '?' || (fmt[1] >= '0' && fmt[1] <= '9')) {
+ entry->has_fraction = TRUE;
+ fmt++;
+ }
+ break;
+
+ case 'a': case 'A':
+ case 'p': case 'P':
+ if (fmt[1] == 'm' || fmt[1] == 'M')
+ entry->want_am_pm = TRUE;
+ break;
+
+ case 'M': case 'm':
+ case 'D': case 'd':
+ case 'Y': case 'y':
+ case 'S': case 's':
+ case 'H': case 'h':
+ if (!entry->suppress_minus && !entry->elapsed_time)
+ entry->suppress_minus = TRUE;
+ break;
+
+ case ';':
+ format_entry_set_fmt (entry, real_start, fmt);
+ format->entries = g_slist_append (format->entries, entry);
+ num_entries++;
+
+ entry = format_entry_ctor ();
+ real_start = NULL;
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ format_entry_set_fmt (entry, real_start, fmt);
+ format->entries = g_slist_append (format->entries, entry);
+
+ for (ptr = format->entries; ptr && counter++ < 4 ; ptr = ptr->next) {
+ StyleFormatEntry *entry = ptr->data;
+
+ /* apply the standard restrictions where things are unspecified */
+ if (entry->restriction_type == '*') {
+ entry->restriction_value = 0.;
+ switch (counter) {
+ case 1 : entry->restriction_type = (num_entries > 2) ? '>' : '.';
+ break;
+ case 2 : entry->restriction_type = '<'; break;
+ case 3 : entry->restriction_type = '='; break;
+ case 4 : entry->restriction_type = '@'; break;
+ default :
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * This routine is invoked when the last user of the
+ * format is gone (ie, refcount has reached zero) just
+ * before the GnmFormat structure is actually released.
+ *
+ * resources allocated in format_compile should be disposed here
+ */
+static void
+format_destroy (GnmFormat *format)
+{
+ g_slist_foreach (format->entries, &format_entry_dtor, NULL);
+ g_slist_free (format->entries);
+ format->entries = NULL;
+ if (format->markup != NULL) {
+ pango_attr_list_unref (format->markup);
+ format->markup = NULL;
+ }
+ format_match_release (format);
+}
+
+/* used to generate formats when delocalizing so keep the leadings caps */
+static struct FormatColor {
+ char const * const name;
+ GnmColor *color;
+} format_colors [] = {
+ { N_("Black") },
+ { N_("Blue") },
+ { N_("Cyan") },
+ { N_("Green") },
+ { N_("Magenta") },
+ { N_("Red") },
+ { N_("White") },
+ { N_("Yellow") }
+};
+
+void
+format_color_init (void)
+{
+ int i;
+
+ for (i = G_N_ELEMENTS (format_colors) ; i-- > 0 ; )
+ format_colors[i].color =
+ style_color_new_name (format_colors[i].name);
+}
+
+void
+format_color_shutdown (void)
+{
+ int i;
+
+ for (i = G_N_ELEMENTS (format_colors) ; i-- > 0 ; ) {
+ style_color_unref (format_colors[i].color);
+ format_colors[i].color = NULL;
+ }
+}
+
+static struct FormatColor const *
+lookup_color_by_name (gchar const *str, gchar const *end,
+ gboolean translate)
+{
+ int i, len;
+
+ len = end - str;
+ for (i = G_N_ELEMENTS (format_colors) ; i-- > 0 ; ) {
+ gchar const *name = format_colors[i].name;
+ if (translate)
+ name = _(name);
+
+ if (0 == g_ascii_strncasecmp (name, str, len) && name[len] == '\0')
+ return format_colors + i;
+ }
+ return NULL;
+}
+
+static GnmColor *
+lookup_color (gchar const *str, gchar const *end)
+{
+ struct FormatColor const *color = lookup_color_by_name (str, end, FALSE);
+
+ if (color != NULL) {
+ style_color_ref (color->color);
+ return color->color;
+ }
+ return NULL;
+}
+
+static gnm_float beyond_precision;
+void
+render_number (GString *result,
+ gnm_float number,
+ format_info_t const *info)
+{
+ const GString *thousands_sep = format_get_thousand ();
+ char num_buf[(GNUM_MANT_DIG + GNUM_MAX_EXP) * 2 + 1];
+ gchar *num = num_buf + sizeof (num_buf) - 1;
+ gnm_float frac_part, int_part;
+ int group, zero_count, digit_count = 0;
+ int left_req = info->left_req;
+ int right_req = info->right_req;
+ int left_spaces = info->left_spaces;
+ int right_spaces = info->right_spaces;
+ int right_allowed = info->right_allowed + info->right_optional;
+ int sigdig = 0;
+
+ if (right_allowed >= 0 && !info->has_fraction) {
+ /* Change "rounding" into "truncating". */
+ /* Note, that we assume number >= 0 here. */
+ gnm_float delta = 5 * gpow10 (-right_allowed - 1);
+ number += delta;
+ }
+ frac_part = modfgnum (gnumeric_add_epsilon (number), &int_part);
+
+ *num = '\0';
+ group = (info->group_thousands) ? 3 : -1;
+ for (; int_part > beyond_precision ; int_part /= 10., digit_count++) {
+ if (group-- == 0) {
+ int i;
+ group = 2;
+ for (i = thousands_sep->len - 1; i >= 0; i--)
+ *(--num) = thousands_sep->str[i];
+ }
+ *(--num) = '0';
+ sigdig++;
+ }
+
+ for (; int_part >= 1. ; int_part /= 10., digit_count++) {
+ gnm_float r = floorgnum (int_part);
+ int digit = r - floorgnum (r / 10) * 10;
+
+ if (group-- == 0) {
+ int i;
+ group = 2;
+ for (i = thousands_sep->len - 1; i >= 0; i--)
+ *(--num) = thousands_sep->str[i];
+ }
+ *(--num) = digit + '0';
+ sigdig++;
+ }
+
+ if (left_req > digit_count) {
+ for (left_spaces -= left_req ; left_spaces-- > 0 ;)
+ g_string_append_c (result, ' ');
+ for (left_req -= digit_count ; left_req-- > 0 ;)
+ g_string_append_c (result, '0');
+ }
+
+ g_string_append_len (result, num, num_buf + sizeof (num_buf) - 1 - num);
+
+ /* If the format contains only "#"s to the left of the decimal
+ * point, number in the [0.0,1.0] range are prefixed with a
+ * decimal point
+ */
+ if (info->decimal_separator_seen ||
+ (number > 0.0 &&
+ number < 1.0 &&
+ info->right_allowed == 0 &&
+ info->right_optional > 0))
+ gnm_string_append_gstring (result, format_get_decimal ());
+
+ /* TODO : clip this a DBL_DIG */
+ /* TODO : What if is a fraction ? */
+ right_allowed -= right_req;
+ right_spaces -= right_req;
+ while (right_req-- > 0) {
+ gint digit;
+ frac_part *= 10.0;
+ digit = (gint)frac_part;
+ frac_part -= digit;
+ if (sigdig++ > GNUM_DIG) digit = 0;
+ g_string_append_c (result, digit + '0');
+ }
+
+ zero_count = 0;
+
+ while (right_allowed-- > 0) {
+ gint digit;
+ frac_part *= 10.0;
+ digit = (gint)frac_part;
+ frac_part -= digit;
+
+ if (digit == 0) {
+ right_spaces -= zero_count + 1;
+ zero_count = 0;
+ } else
+ zero_count ++;
+
+ if (sigdig++ > GNUM_DIG) digit = 0;
+ g_string_append_c (result, digit + '0');
+ }
+
+ g_string_truncate (result, result->len - zero_count);
+
+ while (right_spaces-- > 0)
+ g_string_append_c (result, ' ');
+}
+
+static void
+do_render_number (gnm_float number, format_info_t *info, GString *result)
+{
+ info->rendered = TRUE;
+
+#if 0
+ printf ("Rendering: %g with:\n", number);
+ printf ("left_req: %d\n"
+ "right_req: %d\n"
+ "left_spaces: %d\n"
+ "right_spaces:%d\n"
+ "right_allow: %d\n"
+ "supress: %d\n"
+ "decimalseen: %d\n"
+ "decimalp: %s\n",
+ info->left_req,
+ info->right_req,
+ info->left_spaces,
+ info->right_spaces,
+ info->right_allowed + info->right_optional,
+ info->decimal_separator_seen,
+ decimal_point);
+#endif
+
+ render_number (result, info->scale * number, info);
+}
+
+/*
+ * Microsoft Excel has a bug in the handling of year 1900,
+ * I quote from http://catless.ncl.ac.uk/Risks/19.64.html#subj9.1
+ *
+ * > Microsoft EXCEL version 6.0 ("Office 95 version") and version 7.0 ("Office
+ * > 97 version") believe that year 1900 is a leap year. The extra February 29
+ * > cause the following problems.
+ * >
+ * > 1) All day-of-week before March 1, 1900 are incorrect;
+ * > 2) All date sequence (serial number) on and after March 1, 1900 are incorrect.
+ * > 3) Calculations of number of days across March 1, 1900 are incorrect.
+ * >
+ * > The risk of the error will cause must be little. Especially case 1.
+ * > However, import or export date using serial date number will be a problem.
+ * > If no one noticed anything wrong, it must be that no one did it that way.
+ */
+static gboolean
+split_time (struct tm *tm, gnm_float number, GnmDateConventions const *date_conv)
+{
+ guint secs;
+ GDate date;
+
+ datetime_serial_to_g (&date,
+ datetime_serial_raw_to_serial (number), date_conv);
+ g_date_to_struct_tm (&date, tm);
+
+ secs = datetime_serial_raw_to_seconds (number);
+ tm->tm_hour = secs / 3600;
+ secs -= tm->tm_hour * 3600;
+ tm->tm_min = secs / 60;
+ secs -= tm->tm_min * 60;
+ tm->tm_sec = secs;
+
+ return FALSE;
+}
+
+#define NUM_ZEROS 30
+static const char zeros[NUM_ZEROS + 1] = "000000000000000000000000000000";
+static const char qmarks[NUM_ZEROS + 1] = "??????????????????????????????";
+
+/**
+ * style_format_number :
+ * @fmt : #FormatCharacteristics
+ *
+ * generate an unlocalized number format based on @fmt.
+ **/
+static GnmFormat *
+style_format_number (FormatCharacteristics const *fmt)
+{
+ int symbol = fmt->currency_symbol_index;
+ GString *str, *tmp;
+ GnmFormat *sf;
+
+ g_return_val_if_fail (fmt->num_decimals >= 0, NULL);
+ g_return_val_if_fail (fmt->num_decimals <= NUM_ZEROS, NULL);
+
+ str = g_string_new (NULL);
+
+ /* Currency */
+ if (symbol != 0 && currency_symbols[symbol].precedes) {
+ g_string_append (str, currency_symbols[symbol].symbol);
+ if (currency_symbols[symbol].has_space)
+ g_string_append_c (str, ' ');
+ }
+
+ if (fmt->thousands_sep)
+ g_string_append (str, "#,##0");
+ else
+ g_string_append_c (str, '0');
+
+ if (fmt->num_decimals > 0) {
+ g_string_append_c (str, '.');
+ g_string_append_len (str, zeros, fmt->num_decimals);
+ }
+
+ /* Currency */
+ if (symbol != 0 && !currency_symbols[symbol].precedes) {
+ if (currency_symbols[symbol].has_space)
+ g_string_append_c (str, ' ');
+ g_string_append (str, currency_symbols[symbol].symbol);
+ }
+
+ /* There are negatives */
+ if (fmt->negative_fmt > 0) {
+ size_t prelen = str->len;
+
+ switch (fmt->negative_fmt) {
+ case 1 : g_string_append (str, ";[Red]");
+ break;
+ case 2 : g_string_append (str, "_);(");
+ break;
+ case 3 : g_string_append (str, "_);[Red](");
+ break;
+ default :
+ g_assert_not_reached ();
+ };
+
+ tmp = g_string_new_len (str->str, str->len);
+ g_string_append_len (tmp, str->str, prelen);
+ g_string_free (str, TRUE);
+ str = tmp;
+
+ if (fmt->negative_fmt >= 2)
+ g_string_append_c (str, ')');
+ }
+
+ sf = style_format_new_XL (str->str, FALSE);
+ g_string_free (str, TRUE);
+ return sf;
+}
+
+static GnmFormat *
+style_format_fraction (FormatCharacteristics const *fmt)
+{
+ GString *str = g_string_new (NULL);
+ GnmFormat *sf;
+
+ if (fmt->fraction_denominator >= 2) {
+ g_string_printf (str, "# ?/%d", fmt->fraction_denominator);
+ } else {
+ g_return_val_if_fail (fmt->num_decimals > 0, NULL);
+ g_return_val_if_fail (fmt->num_decimals <= NUM_ZEROS, NULL);
+
+ g_string_append (str, "# ");
+ g_string_append_len (str, qmarks, fmt->num_decimals);
+ g_string_append_c (str, '/');
+ g_string_append_len (str, qmarks, fmt->num_decimals);
+ }
+
+ sf = style_format_new_XL (str->str, FALSE);
+ g_string_free (str, TRUE);
+ return sf;
+}
+
+static GnmFormat *
+style_format_percent (FormatCharacteristics const *fmt)
+{
+ GString *str;
+ GnmFormat *sf;
+
+ g_return_val_if_fail (fmt->num_decimals >= 0, NULL);
+ g_return_val_if_fail (fmt->num_decimals <= NUM_ZEROS, NULL);
+
+ str = g_string_new (NULL);
+ g_string_append_c (str, '0');
+ if (fmt->num_decimals > 0) {
+ g_string_append_c (str, '.');
+ g_string_append_len (str, zeros, fmt->num_decimals);
+ }
+ g_string_append_c (str, '%');
+
+ sf = style_format_new_XL (str->str, FALSE);
+ g_string_free (str, TRUE);
+ return sf;
+}
+
+static GnmFormat *
+style_format_science (FormatCharacteristics const *fmt)
+{
+ GString *str;
+ GnmFormat *sf;
+
+ g_return_val_if_fail (fmt->num_decimals >= 0, NULL);
+ g_return_val_if_fail (fmt->num_decimals <= NUM_ZEROS, NULL);
+
+ str = g_string_new (NULL);
+ g_string_append_c (str, '0');
+ if (fmt->num_decimals > 0) {
+ g_string_append_c (str, '.');
+ g_string_append_len (str, zeros, fmt->num_decimals);
+ }
+ g_string_append (str, "E+00");
+
+ sf = style_format_new_XL (str->str, FALSE);
+ g_string_free (str, TRUE);
+ return sf;
+}
+
+static GnmFormat *
+style_format_account (FormatCharacteristics const *fmt)
+{
+ GString *str, *sym, *num;
+ GnmFormat *sf;
+ int symbol = fmt->currency_symbol_index;
+ gboolean quote_currency;
+
+ g_return_val_if_fail (fmt->num_decimals >= 0, NULL);
+ g_return_val_if_fail (fmt->num_decimals <= NUM_ZEROS, NULL);
+
+ str = g_string_new (NULL);
+ /* The number with decimals */
+ num = g_string_new ("#,##0");
+ if (fmt->num_decimals > 0) {
+ g_string_append_c (num, '.');
+ g_string_append_len (num, zeros, fmt->num_decimals);
+ }
+
+ /* The currency symbols with space after or before */
+ sym = g_string_new (NULL);
+ quote_currency = (currency_symbols[symbol].symbol[0] != '[');
+ if (currency_symbols[symbol].precedes) {
+ if (quote_currency)
+ g_string_append_c (sym, '\"');
+ g_string_append (sym, currency_symbols[symbol].symbol);
+ if (quote_currency)
+ g_string_append_c (sym, '\"');
+ g_string_append (sym, "* ");
+ if (currency_symbols[symbol].has_space)
+ g_string_append_c (sym, ' ');
+ } else {
+ g_string_append (sym, "* ");
+ if (currency_symbols[symbol].has_space)
+ g_string_append_c (sym, ' ');
+ if (quote_currency)
+ g_string_append_c (sym, '\"');
+ g_string_append (sym, currency_symbols[symbol].symbol);
+ if (quote_currency)
+ g_string_append_c (sym, '\"');
+ }
+
+ /* Finally build the correct string */
+ if (currency_symbols[symbol].precedes) {
+ g_string_append_printf (str, "_(%s%s_);_(%s(%s);_(%s\"-\"%s_);_(@_)",
+ sym->str, num->str,
+ sym->str, num->str,
+ sym->str, qmarks + NUM_ZEROS-fmt->num_decimals);
+ } else {
+ g_string_append_printf (str, "_(%s%s_);_((%s)%s;_(\"-\"%s%s_);_(@_)",
+ num->str, sym->str,
+ num->str, sym->str,
+ qmarks + NUM_ZEROS-fmt->num_decimals, sym->str);
+ }
+
+ g_string_free (num, TRUE);
+ g_string_free (sym, TRUE);
+
+ sf = style_format_new_XL (str->str, FALSE);
+ g_string_free (str, TRUE);
+ return sf;
+}
+
+
+/*
+ * Finds the decimal char in @str doing the proper parsing of a
+ * format string
+ */
+static char const *
+find_decimal_char (char const *str)
+{
+ for (;*str; str++){
+ if (*str == '.')
+ return str;
+
+ if (*str == ',')
+ continue;
+
+ switch (*str){
+ /* These ones do not have any argument */
+ case '#': case '?': case '0': case '%':
+ case '-': case '+': case ')': case '£':
+ case ':': case '$': case '¥': case '¤':
+ case 'M': case 'm': case 'D': case 'd':
+ case 'Y': case 'y': case 'S': case 's':
+ case '*': case 'h': case 'H': case 'A':
+ case 'a': case 'P': case 'p':
+ break;
+
+ /* Quoted string */
+ case '"':
+ for (str++; *str && *str != '"'; str++)
+ ;
+ break;
+
+ /* Escaped char and spacing format */
+ case '\\': case '_':
+ if (*(str + 1))
+ str++;
+ break;
+
+ /* Scientific number */
+ case 'E': case 'e':
+ for (str++; *str;){
+ if (*str == '+')
+ str++;
+ else if (*str == '-')
+ str++;
+ else if (*str == '0')
+ str++;
+ else
+ break;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* An helper function which modify the number of decimals displayed
+ * and recreate the format string by calling the good function */
+static GnmFormat *
+reformat_decimals (const FormatCharacteristics *fc,
+ GnmFormat * (*format_function) (FormatCharacteristics const * fmt),
+ int step)
+{
+ FormatCharacteristics fc_copy;
+
+ /* Be sure that the number of decimals displayed will remain correct */
+ if ((fc->num_decimals+step > NUM_ZEROS) || (fc->num_decimals+step <0))
+ return NULL;
+ fc_copy = *fc;
+ fc_copy.num_decimals += step;
+
+ return (*format_function) (&fc_copy);
+}
+
+/*
+ * This routine scans the format_string for a decimal dot,
+ * and if it finds it, it removes the first zero after it to
+ * reduce the display precision for the number.
+ *
+ * Returns NULL if the new format would not change things
+ */
+GnmFormat *
+format_remove_decimal (GnmFormat const *fmt)
+{
+ int offset = 1;
+ char *ret, *p;
+ char const *tmp;
+ char const *format_string = fmt->format;
+ GnmFormat *sf;
+
+ switch (fmt->family) {
+ case FMT_NUMBER:
+ case FMT_CURRENCY:
+ return reformat_decimals (&fmt->family_info, &style_format_number, -1);
+ case FMT_ACCOUNT:
+ return reformat_decimals (&fmt->family_info, &style_format_account, -1);
+ case FMT_PERCENT:
+ return reformat_decimals (&fmt->family_info, &style_format_percent, -1);
+ case FMT_SCIENCE:
+ return reformat_decimals (&fmt->family_info, &style_format_science, -1);
+ case FMT_FRACTION: {
+ FormatCharacteristics fc = fmt->family_info;
+
+ if (fc.fraction_denominator >= 2) {
+ if (fc.fraction_denominator > 2 &&
+ ((fc.fraction_denominator & (fc.fraction_denominator - 1)) == 0))
+ /* It's a power of two. */
+ fc.fraction_denominator /= 2;
+ else if (fc.fraction_denominator > 10 &&
+ fc.fraction_denominator % 10 == 0)
+ /* It's probably a power of ten. */
+ fc.fraction_denominator /= 10;
+ else
+ return NULL;
+ } else {
+ if (fc.num_decimals <= 1)
+ return NULL;
+ fc.num_decimals--;
+ }
+ return style_format_fraction (&fc);
+ }
+
+ case FMT_TIME:
+ /* FIXME: we might have decimals on seconds part. */
+ case FMT_DATE:
+ case FMT_TEXT:
+ case FMT_SPECIAL:
+ case FMT_MARKUP:
+ /* Nothing to remove for these formats ! */
+ return NULL;
+ case FMT_UNKNOWN:
+ case FMT_GENERAL:
+ ; /* Nothing. */
+ }
+
+ /* Use the old code for more special formats to try to add a
+ decimal */
+
+ /*
+ * Consider General format as 0. with several optional decimal places.
+ * This is WRONG. FIXME FIXME FIXME
+ * We need to look at the number of decimals in the current value
+ * and use that as a base.
+ */
+ if (style_format_is_general (fmt))
+ format_string = "0.########";
+
+ tmp = find_decimal_char (format_string);
+ if (!tmp)
+ return NULL;
+
+ ret = g_strdup (format_string);
+ p = ret + (tmp - format_string);
+
+ /* If there is more than 1 thing after the decimal place
+ * leave the decimal.
+ * If there is only 1 thing after the decimal remove the decimal too.
+ */
+ if ((p[1] == '0' || p[1] == '#') && (p[2] == '0' || p[2] == '#'))
+ ++p;
+ else
+ offset = 2;
+
+ strcpy (p, p + offset);
+
+ sf = style_format_new_XL (ret, FALSE);
+ g_free (ret);
+ return sf;
+}
+
+/*
+ * This routine scans format_string for the decimal
+ * character and when it finds it, it adds a zero after
+ * it to force the rendering of the number with one more digit
+ * of decimal precision.
+ *
+ * Returns NULL if the new format would not change things
+ */
+GnmFormat *
+format_add_decimal (GnmFormat const *fmt)
+{
+ char const *pre = NULL;
+ char const *post = NULL;
+ char *res;
+ char const *format_string = fmt->format;
+ GnmFormat *sf;
+
+ switch (fmt->family) {
+ case FMT_NUMBER:
+ case FMT_CURRENCY:
+ return reformat_decimals (&fmt->family_info, &style_format_number, +1);
+ case FMT_ACCOUNT:
+ return reformat_decimals (&fmt->family_info, &style_format_account, +1);
+ case FMT_PERCENT:
+ return reformat_decimals (&fmt->family_info, &style_format_percent, +1);
+ case FMT_SCIENCE:
+ return reformat_decimals (&fmt->family_info, &style_format_science, +1);
+ case FMT_FRACTION: {
+ FormatCharacteristics fc = fmt->family_info;
+ if (fc.fraction_denominator >= 2) {
+ if (fc.fraction_denominator <= INT_MAX / 2 &&
+ ((fc.fraction_denominator & (fc.fraction_denominator - 1)) == 0))
+ /* It's a power of two. */
+ fc.fraction_denominator *= 2;
+ else if (fc.fraction_denominator <= INT_MAX / 10 &&
+ fc.fraction_denominator % 10 == 0)
+ /* It's probably a power of ten. */
+ fc.fraction_denominator *= 10;
+ else
+ return NULL;
+ } else {
+ if (fc.num_decimals >= 5)
+ return NULL;
+ fc.num_decimals++;
+ }
+ return style_format_fraction (&fc);
+ }
+
+ case FMT_TIME:
+ /* FIXME: we might have decimals on seconds part. */
+ case FMT_DATE:
+ case FMT_TEXT:
+ case FMT_SPECIAL:
+ case FMT_MARKUP:
+ /* Nothing to add for these formats ! */
+ return NULL;
+ case FMT_UNKNOWN:
+ case FMT_GENERAL:
+ ; /* Nothing. */
+ }
+
+ /* Use the old code for more special formats to try to add a
+ decimal */
+
+ if (style_format_is_general (fmt)) {
+ format_string = "0";
+ pre = format_string + 1;
+ post = pre;
+ } else {
+ pre = find_decimal_char (format_string);
+
+ /* If there is no decimal append to the last '0' */
+ if (pre == NULL) {
+ pre = strrchr (format_string, '0');
+
+ /* If there are no 0s append to the ':s' */
+ if (pre == NULL) {
+ pre = strrchr (format_string, 's');
+ if (pre > format_string && pre[-1] == ':') {
+ if (pre[1] == 's')
+ pre += 2;
+ else
+ ++pre;
+ } else
+ return NULL;
+ } else
+ ++pre;
+ post = pre;
+ } else
+ post = pre + 1;
+ }
+ res = g_malloc ((pre - format_string + 1) +
+ 1 + /* for the decimal */
+ 1 + /* for the extra 0 */
+ strlen (post) +
+ 1 /*terminate */);
+ if (!res)
+ return NULL;
+
+ strncpy (res, format_string, pre - format_string);
+ res[pre-format_string + 0] = '.';
+ res[pre-format_string + 1] = '0';
+ strcpy (res + (pre - format_string) + 2, post);
+
+ sf = style_format_new_XL (res, FALSE);
+ g_free (res);
+ return sf;
+}
+
+GnmFormat *
+format_toggle_thousands (GnmFormat const *fmt)
+{
+ FormatCharacteristics fc;
+
+ fc = fmt->family_info;
+ fc.thousands_sep = !fc.thousands_sep;
+
+ switch (fmt->family) {
+ case FMT_NUMBER:
+ case FMT_CURRENCY:
+ return style_format_number (&fc);
+
+ case FMT_ACCOUNT:
+ /*
+ * FIXME: this doesn't actually work as no 1000 seps
+ * are used for accounting.
+ */
+ return style_format_account (&fc);
+ case FMT_GENERAL:
+ fc.currency_symbol_index = 0;
+ return style_format_number (&fc);
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+/*********************************************************************/
+
+static void
+format_number (GString *result,
+ gnm_float number, int col_width, StyleFormatEntry const *entry,
+ GnmDateConventions const *date_conv)
+{
+ guchar const *format = (guchar *)(entry->format);
+ format_info_t info;
+ gboolean can_render_number = FALSE;
+ gboolean hour_seen = FALSE;
+ gboolean time_display_elapsed = FALSE;
+ gboolean ignore_further_elapsed = FALSE;
+
+ gunichar fill_char = 0;
+ int fill_start = -1;
+
+ gboolean need_time_split = TRUE;
+ struct tm tm;
+ gnm_float signed_number;
+
+ memset (&info, 0, sizeof (info));
+ signed_number = number;
+ if (number < 0.) {
+ number = -number;
+ if (!entry->suppress_minus)
+ g_string_append_c (result, '-');
+ }
+ info.has_fraction = entry->has_fraction;
+ info.scale = 1;
+
+ while (*format) {
+ /* This is just g_utf8_get_char, but we're in a hurry. */
+ gunichar c = (*format & 0x80) ? g_utf8_get_char (format) : *format;
+
+ switch (c) {
+
+ case '[':
+ /* Currency symbol */
+ if (format[1] == '$') {
+ gboolean no_locale = TRUE;
+ for (format += 2; *format && *format != ']' ; ++format)
+ /* strip digits from [$<currency>-{digit}+] */
+ if (*format == '-')
+ no_locale = FALSE;
+ else if (no_locale)
+ g_string_append_c (result, *format);
+ if (!*format)
+ continue;
+ } else if (!ignore_further_elapsed)
+ time_display_elapsed = TRUE;
+ break;
+
+ case '#':
+ can_render_number = TRUE;
+ if (info.decimal_separator_seen)
+ info.right_optional++;
+ break;
+
+ case '?':
+ can_render_number = TRUE;
+ if (info.decimal_separator_seen)
+ info.right_spaces++;
+ else
+ info.left_spaces++;
+ break;
+
+ case '0':
+ can_render_number = TRUE;
+ if (info.decimal_separator_seen){
+ info.right_req++;
+ info.right_allowed++;
+ info.right_spaces++;
+ } else {
+ info.left_spaces++;
+ info.left_req++;
+ }
+ break;
+
+ case '.': {
+ int c = *(format + 1);
+
+ can_render_number = TRUE;
+ if (c && (c != '0' && c != '#' && c != '?'))
+ number /= 1000;
+ else
+ info.decimal_separator_seen = TRUE;
+ break;
+ }
+
+ case ',':
+ if (can_render_number) {
+ guchar const *tmp = format;
+ while (*++tmp == ',')
+ ;
+ if (*tmp == '\0' || *tmp == '.' || *tmp == ';')
+ /* NOTE : format-tmp is NEGATIVE */
+ info.scale = gpow10 (3*(format-tmp));
+ info.group_thousands = TRUE;
+ format = tmp;
+ continue;
+ } else
+ gnm_string_append_gstring (result, format_get_thousand ());
+ break;
+
+ /* FIXME: this is a gross hack */
+ /* FIXME: Missing support for scientific notation.
+ * #00.00e### that keeps axponent to a multiple of 3
+ * XL seems to just special case that.
+ **/
+ case 'E': case 'e': {
+ gboolean const is_lower = (*format++ == 'e');
+ gboolean shows_plus = FALSE;
+ int prec = info.right_optional + info.right_req;
+
+ can_render_number = TRUE;
+ if (*format == '+') {
+ shows_plus = TRUE;
+ format++;
+ } else if (*format == '-')
+ format++;
+
+ while (*format == '0' || *format == '#')
+ format++;
+
+ g_string_append_printf (result,
+ is_lower ? "%.*" GNUM_FORMAT_e : "%.*" GNUM_FORMAT_E,
+ prec, number);
+ return;
+ }
+
+ case '\\':
+ if (format[1] != '\0') {
+ if (can_render_number && !info.rendered)
+ do_render_number (number, &info, result);
+
+ format++;
+ g_string_append_len (result, format,
+ g_utf8_skip[*(format)]);
+ }
+ break;
+
+ case '"': {
+ guchar const *tmp = ++format;
+ if (can_render_number && !info.rendered)
+ do_render_number (number, &info, result);
+
+ for (; *tmp && *tmp != '"'; tmp++)
+ ;
+ g_string_append_len (result, format, tmp-format);
+ format = tmp;
+ if (!*format)
+ continue;
+ break;
+ }
+
+ case '/': /* fractions */
+ if (can_render_number && info.left_spaces > info.left_req) {
+ int size = 0;
+ int numerator = -1, denominator = -1;
+
+ while (format[size + 1] == '?')
+ ++size;
+
+ /* check for explicit denominator */
+ if (size == 0) {
+ char *end;
+
+ errno = 0;
+ denominator = strtol ((char *)format + 1, &end, 10);
+ if ((char *)format + 1 != end && errno != ERANGE) {
+ size = (const guchar *)end - (format + 1);
+ format = (guchar *)end;
+ numerator = (int)((number - (int)number) * denominator + 0.5);
+ }
+ } else {
+ static int const powers[9] = {
+ 10, 100, 1000, 10000, 100000,
+ 1000000, 10000000, 100000000, 1000000000
+ };
+
+ format += size + 1;
+ if (size > (int)G_N_ELEMENTS (powers))
+ size = G_N_ELEMENTS (powers);
+ continued_fraction (number - (int)number, powers[size - 1],
+ &numerator, &denominator);
+ }
+
+ if (denominator > 0) {
+ /* improper fractions */
+ if (!info.rendered) {
+ info.rendered = TRUE;
+ numerator += ((int)number) * denominator;
+ }
+
+ /*
+ * FIXME: the space-aligning here doesn't come out
+ * right except in mono-space fonts.
+ */
+ if (numerator > 0) {
+ g_string_append_printf (result,
+ "%*d/%-*d",
+ info.left_spaces, numerator,
+ size, denominator);
+ } else {
+ g_string_append_printf (result,
+ "%-*s",
+ info.left_spaces + 1 + size,
+ "");
+ }
+ continue;
+ }
+ }
+
+ case '-':
+ case '(':
+ case '+':
+ case ':':
+ case ' ': /* eg # ?/? */
+ case '$':
+ case 0x00A3 : /* pound */
+ case 0x00A5 : /* yen */
+ case 0x20AC : /* Euro */
+ case ')':
+ if (can_render_number && !info.rendered)
+ do_render_number (number, &info, result);
+ g_string_append_unichar (result, c);
+ break;
+
+ /* percent */
+ case '%':
+ if (!info.rendered) {
+ number *= 100;
+ if (can_render_number)
+ do_render_number (number, &info, result);
+ else
+ can_render_number = TRUE;
+ }
+ g_string_append_c (result, '%');
+ break;
+
+ case '_':
+ if (can_render_number && !info.rendered)
+ do_render_number (number, &info, result);
+ if (format[1])
+ format++;
+ g_string_append_c (result, ' ');
+ break;
+
+ case '*':
+ /* Intentionally forget any previous fill characters
+ * (no need to be smart).
+ * FIXME : make the simplifying assumption that we are
+ * not going to fill in the middle of a number. This
+ * assumption is WRONG! but ok until we rewrite the
+ * format engine.
+ */
+ if (format[1]) {
+ if (can_render_number && !info.rendered)
+ do_render_number (number, &info, result);
+ ++format;
+ fill_char = g_utf8_get_char (format);
+ fill_start = result->len;
+ }
+ break;
+
+ case 'M':
+ case 'm': {
+ int n;
+
+ /* FIXME : Yuck
+ * This is a problem waiting to happen.
+ * rewrite.
+ */
+ for (n = 1; format[1] == 'M' || format[1] == 'm'; format++)
+ n++;
+ if (format[1] == ']')
+ format++;
+ if (time_display_elapsed) {
+ need_time_split = time_display_elapsed = FALSE;
+ ignore_further_elapsed = TRUE;
+ append_minute_elapsed (result, &tm, number);
+ break;
+ }
+
+ if (need_time_split)
+ need_time_split = split_time (&tm, signed_number, date_conv);
+ if (hour_seen ||
+ (format[1] == ':' &&
+ (format[2] == 's' || format[2] == 'S'))) {
+ append_minute (result, n, &tm);
+ } else
+ append_month (result, n, &tm);
+ break;
+ }
+
+ case 'D':
+ case 'd':
+ if (need_time_split)
+ need_time_split = split_time (&tm, signed_number, date_conv);
+ format += append_day (result, format, &tm) - 1;
+ break;
+
+ case 'Y':
+ case 'y':
+ if (need_time_split)
+ need_time_split = split_time (&tm, signed_number, date_conv);
+ format += append_year (result, format, &tm) - 1;
+ break;
+
+ case 'S':
+ case 's': {
+ int n;
+
+ for (n = 1; format[1] == 's' || format[1] == 'S'; format++)
+ n++;
+ if (format[1] == ']')
+ format++;
+ if (time_display_elapsed) {
+ need_time_split = time_display_elapsed = FALSE;
+ ignore_further_elapsed = TRUE;
+ append_second_elapsed (result, number);
+ } else {
+ if (need_time_split)
+ need_time_split = split_time (&tm, signed_number, date_conv);
+ append_second (result, n, &tm);
+ }
+ break;
+ }
+
+ case 'H':
+ case 'h': {
+ int n;
+
+ for (n = 1; format[1] == 'h' || format[1] == 'H'; format++)
+ n++;
+ if (format[1] == ']')
+ format++;
+ if (time_display_elapsed) {
+ need_time_split = time_display_elapsed = FALSE;
+ ignore_further_elapsed = TRUE;
+ append_hour_elapsed (result, &tm, number);
+ } else {
+ /* h == hour optionally in 24 hour mode
+ * h followed by am/pm puts it in 12 hour mode
+ *
+ * more than 2 h eg 'hh' force 12 hour mode.
+ * NOTE : This is a non-XL extension
+ */
+ if (need_time_split)
+ need_time_split = split_time (&tm, signed_number, date_conv);
+
+ append_hour (result, n, &tm, entry->want_am_pm);
+ }
+ hour_seen = TRUE;
+ break;
+ }
+
+ case 'A':
+ case 'a':
+ if (need_time_split)
+ need_time_split = split_time (&tm, signed_number, date_conv);
+ if (tm.tm_hour < 12){
+ g_string_append_c (result, *format);
+ format++;
+ if (*format == 'm' || *format == 'M'){
+ g_string_append_c (result, *format);
+ if (*(format + 1) == '/')
+ format++;
+ }
+ } else {
+ if (*(format + 1) == 'm' || *(format + 1) == 'M')
+ format++;
+ if (*(format + 1) == '/')
+ format++;
+ }
+ break;
+
+ case 'P': case 'p':
+ if (need_time_split)
+ need_time_split = split_time (&tm, signed_number, date_conv);
+ if (tm.tm_hour >= 12){
+ g_string_append_c (result, *format);
+ if (*(format + 1) == 'm' || *(format + 1) == 'M'){
+ format++;
+ g_string_append_c (result, *format);
+ }
+ } else {
+ if (*(format + 1) == 'm' || *(format + 1) == 'M')
+ format++;
+ }
+ break;
+
+ default:
+ /* TODO : After release check this.
+ * shouldn't we tack on the explicit characters here ?
+ */
+ break;
+ }
+ format = g_utf8_next_char (format);
+ }
+
+ if (!info.rendered && can_render_number)
+ do_render_number (number, &info, result);
+
+ /* This is kinda ugly. It does not handle variable width fonts */
+ if (fill_char != '\0') {
+ int count = col_width - result->len;
+ while (count-- > 0)
+ g_string_insert_unichar (result, fill_start, fill_char);
+ }
+}
+
+static gboolean
+style_format_condition (StyleFormatEntry const *entry, GnmValue const *value)
+{
+ if (entry->restriction_type == '*')
+ return TRUE;
+
+ switch (value->type) {
+ case VALUE_BOOLEAN:
+ case VALUE_STRING:
+ return entry->restriction_type == '@';
+
+ case VALUE_FLOAT:
+ switch (entry->restriction_type) {
+ case '<': return value->v_float.val < entry->restriction_value;
+ case '>': return value->v_float.val > entry->restriction_value;
+ case '=': return value->v_float.val == entry->restriction_value;
+ case ',': return value->v_float.val <= entry->restriction_value;
+ case '.': return value->v_float.val >= entry->restriction_value;
+ case '+': return value->v_float.val != entry->restriction_value;
+ default:
+ return FALSE;
+ }
+
+ case VALUE_INTEGER:
+ switch (entry->restriction_type) {
+ case '<': return value->v_int.val < entry->restriction_value;
+ case '>': return value->v_int.val > entry->restriction_value;
+ case '=': return value->v_int.val == entry->restriction_value;
+ case ',': return value->v_int.val <= entry->restriction_value;
+ case '.': return value->v_int.val >= entry->restriction_value;
+ case '+': return value->v_int.val != entry->restriction_value;
+ default:
+ return FALSE;
+ }
+
+ case VALUE_ERROR:
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * fmt_general_float:
+ *
+ * @val : the integer value being formated.
+ * @col_width : the approximate width in characters.
+ */
+static void
+fmt_general_float (GString *result, gnm_float val, double col_width)
+{
+ gnm_float tmp;
+ int log_val, prec;
+
+ if (col_width < 0.) {
+ g_string_append_printf (result, "%.*" GNUM_FORMAT_g, GNUM_DIG, val);
+ return;
+ }
+
+ if (val < 0.) {
+ /* leave space for minus sign */
+ /* FIXME : idealy we would use the width of a minus sign */
+ col_width -= 1.;
+ tmp = log10gnum (-val);
+ } else
+ tmp = (val > 0.) ? log10gnum (val) : 0;
+
+ /* leave space for the decimal */
+ /* FIXME : idealy we would use the width of a decimal point */
+ prec = (int) floor (col_width - .4);
+ if (prec < 0)
+ prec = 0;
+
+ if (tmp > 0.) {
+ log_val = ceilgnum (tmp);
+
+ /* Decrease precision to leave space for the E+00 */
+ if (log_val > prec)
+ for (prec -= 4; log_val >= 100 ; log_val /= 10)
+ prec--;
+ } else {
+ log_val = floorgnum (tmp);
+
+ /* Display 0 for cols that are too narrow for scientific
+ * notation with abs (value) < 1 */
+ if (col_width < 5. && -log_val >= prec) {
+ g_string_append_c (result, '0');
+ return;
+ }
+
+ /* Include leading zeros eg 0.0x has 2 leading zero */
+ if (log_val >= -4)
+ prec += log_val;
+
+ /* Decrease precision to leave space for the E+00 */
+ else for (prec -= 4; log_val <= -100 ; log_val /= 10)
+ prec--;
+ }
+
+ if (prec < 1)
+ prec = 1;
+ else if (prec > GNUM_DIG)
+ prec = GNUM_DIG;
+
+ /* FIXME : glib bug. it does not handle G, use g (fixed in 1.2.9) */
+ g_string_append_printf (result, "%.*" GNUM_FORMAT_g, prec, val);
+}
+
+/**
+ * fmt_general_int :
+ *
+ * @val : the integer value being formated.
+ * @col_width : the approximate width in characters.
+ */
+static void
+fmt_general_int (GString *result, int val, int col_width)
+{
+ if (col_width > 0) {
+ int log_val;
+
+ if (val < 0) {
+ /* leave space for minus sign */
+ col_width--;
+ log_val = ceil (log10 ((unsigned int)-val));
+ } else
+ log_val = (val > 0) ? ceil (log10 (val)) : 0;
+
+ /* Switch to scientific notation if things are too wide */
+ if (log_val > col_width) {
+ /* FIXME : glib bug. it does not handle G, use g */
+ /* Decrease available width by 5 to account for .+E00 */
+ g_string_append_printf (result, "%.*g", col_width - 5, (double)val);
+ return;
+ }
+ }
+
+ /* FIXME: we can do better than this. */
+ g_string_append_printf (result, "%d", val);
+}
+
+/*
+ * Returns NULL when the value should be formated as text
+ */
+void
+format_value_gstring (GString *result, GnmFormat const *format,
+ GnmValue const *value, GnmColor **color,
+ double col_width, GnmDateConventions const *date_conv)
+{
+ StyleFormatEntry const *entry = NULL; /* default to General */
+ GSList *list;
+ gboolean need_abs = FALSE;
+
+ if (color)
+ *color = NULL;
+
+ g_return_if_fail (value != NULL);
+
+ if (format == NULL)
+ format = VALUE_FMT (value);
+
+ /* Use top left corner of an array result.
+ * This wont work for ranges because we dont't have a location
+ */
+ if (value->type == VALUE_ARRAY)
+ value = value_area_fetch_x_y (value, 0, 0, NULL);
+
+ if (format) {
+ for (list = format->entries; list; list = list->next)
+ if (style_format_condition (list->data, value))
+ break;
+
+ if (list == NULL &&
+ (value->type == VALUE_INTEGER || value->type == VALUE_FLOAT))
+ list = format->entries;
+
+ /* If nothing matches treat it as General */
+ if (list != NULL) {
+ entry = list->data;
+
+ /* Empty formats should be ignored */
+ if (entry->format[0] == '\0')
+ return;
+
+ if (color && entry->color != NULL)
+ *color = style_color_ref (entry->color);
+
+ if (strcmp (entry->format, "@") == 0) {
+ /* FIXME : Formatting a value as a text returns
+ * the entered text. We need access to the
+ * parse format */
+ entry = NULL;
+
+ /* FIXME : Just containing General is enough to be
+ * general for now. We'll ignore prefixes and suffixes
+ * for the time being */
+ } else if (strstr (entry->format, "General") != NULL)
+ entry = NULL;
+ }
+
+ /* More than one format? -- abs the value. */
+ need_abs = entry && format->entries->next;
+ }
+
+ switch (value->type) {
+ case VALUE_EMPTY:
+ return;
+ case VALUE_BOOLEAN:
+ g_string_append (result, format_boolean (value->v_bool.val));
+ return;
+ case VALUE_INTEGER: {
+ int val = value->v_int.val;
+ if (need_abs)
+ val = ABS (val);
+
+ if (entry == NULL)
+ fmt_general_int (result, val, col_width);
+ else
+ format_number (result, val, (int)col_width, entry, date_conv);
+ return;
+ }
+ case VALUE_FLOAT: {
+ gnm_float val = value->v_float.val;
+
+ if (!finitegnum (val)) {
+ g_string_append (result, value_error_name (GNM_ERROR_VALUE, TRUE));
+ return;
+ }
+
+ if (need_abs)
+ val = gnumabs (val);
+
+ if (entry == NULL) {
+ if (INT_MAX >= val && val >= INT_MIN && val == floorgnum (val))
+ fmt_general_int (result, (int)val, col_width);
+ else
+ fmt_general_float (result, val, col_width);
+ } else
+ format_number (result, val, (int)col_width, entry, date_conv);
+ return;
+ }
+ case VALUE_ERROR:
+ g_string_append (result, value->v_err.mesg->str);
+ return;
+ case VALUE_STRING:
+ g_string_append (result, value->v_str.val->str);
+ return;
+ case VALUE_CELLRANGE:
+ g_string_append (result, value_error_name (GNM_ERROR_VALUE, TRUE));
+ return;
+ case VALUE_ARRAY: /* Array of arrays ?? */
+ g_string_append (result, _("ARRAY"));
+ return;
+
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+}
+
+gchar *
+format_value (GnmFormat const *format, GnmValue const *value, GnmColor **color,
+ double col_width, GnmDateConventions const *date_conv)
+{
+ GString *result = g_string_sized_new (20);
+ format_value_gstring (result, format, value, color, col_width, date_conv);
+ return g_string_free (result, FALSE);
+}
+
+
+void
+number_format_init (void)
+{
+ style_format_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ beyond_precision = gpow10 (GNUM_DIG + 1);
+
+ lc_decimal = g_string_new (NULL);
+ lc_thousand = g_string_new (NULL);
+ lc_currency = g_string_new (NULL);
+}
+
+static void
+cb_format_leak (gpointer key, gpointer value, gpointer user_data)
+{
+ GnmFormat *format = value;
+
+ fprintf (stderr, "Leaking gnm-format at %p [%s].\n",
+ format, format->format);
+}
+
+void
+number_format_shutdown (void)
+{
+ g_string_free (lc_decimal, TRUE);
+ lc_decimal = NULL;
+
+ g_string_free (lc_thousand, TRUE);
+ lc_thousand = NULL;
+
+ g_string_free (lc_currency, TRUE);
+ lc_currency = NULL;
+
+ if (default_percentage_fmt) {
+ style_format_unref (default_percentage_fmt);
+ default_percentage_fmt = NULL;
+ }
+
+ if (default_money_fmt) {
+ style_format_unref (default_money_fmt);
+ default_money_fmt = NULL;
+ }
+
+ if (default_date_fmt) {
+ style_format_unref (default_date_fmt);
+ default_date_fmt = NULL;
+ }
+
+ if (default_time_fmt) {
+ style_format_unref (default_time_fmt);
+ default_time_fmt = NULL;
+ }
+
+ if (default_date_time_fmt) {
+ style_format_unref (default_date_time_fmt);
+ default_date_time_fmt = NULL;
+ }
+
+ if (default_general_fmt) {
+ style_format_unref (default_general_fmt);
+ default_general_fmt = NULL;
+ }
+
+ g_hash_table_foreach (style_format_hash, cb_format_leak, NULL);
+ g_hash_table_destroy (style_format_hash);
+ style_format_hash = NULL;
+}
+
+/****************************************************************************/
+
+static char *
+translate_format_color (GString *res, char const *ptr, gboolean translate_to_en)
+{
+ char *end;
+ struct FormatColor const *color;
+
+ g_string_append_c (res, '[');
+
+ /*
+ * Special [h*], [m*], [*s] is using for
+ * and [$*] are for currencies.
+ * measuring times, not for specifying colors.
+ */
+ if (ptr[1] == 'h' || ptr[1] == 's' || ptr[1] == 'm' || ptr[1] == '$')
+ return NULL;
+
+ end = strchr (ptr, ']');
+ if (end == NULL)
+ return NULL;
+
+ color = lookup_color_by_name (ptr+1, end, translate_to_en);
+ if (color != NULL) {
+ g_string_append (res, translate_to_en
+ ? color->name : _(color->name));
+ g_string_append_c (res, ']');
+ return end;
+ }
+ return NULL;
+}
+
+char *
+style_format_delocalize (char const *descriptor_string)
+{
+ g_return_val_if_fail (descriptor_string != NULL, NULL);
+
+ if (*descriptor_string == '\0')
+ return g_strdup ("");
+
+ if (strcmp (descriptor_string, _("General"))) {
+ GString const *thousands_sep = format_get_thousand ();
+ GString const *decimal = format_get_decimal ();
+ char const *ptr = descriptor_string;
+ GString *res = g_string_sized_new (strlen (ptr));
+
+ for ( ; *ptr ; ++ptr) {
+ if (strncmp (ptr, decimal->str, decimal->len) == 0) {
+ ptr += decimal->len - 1;
+ g_string_append_c (res, '.');
+ } else if (strncmp (ptr, thousands_sep->str, thousands_sep->len) == 0) {
+ ptr += thousands_sep->len - 1;
+ g_string_append_c (res, ',');
+ } else if (*ptr == '\"') {
+ do {
+ g_string_append_c (res, *ptr++);
+ } while (*ptr && *ptr != '\"');
+ if (*ptr)
+ g_string_append_c (res, *ptr);
+ } else if (*ptr == '[') {
+ char *tmp = translate_format_color (res, ptr, TRUE);
+ if (tmp != NULL)
+ ptr = tmp;
+ } else {
+ if (*ptr == '\\' && ptr[1] != '\0') {
+ ptr++;
+ /* Ignore '\' if we probably added it */
+ if (strncmp (ptr, decimal->str, decimal->len) != 0 &&
+ strncmp (ptr, thousands_sep->str, thousands_sep->len) != 0)
+ g_string_append_c (res, '\\');
+ }
+ g_string_append_c (res, *ptr);
+ }
+ }
+ return g_string_free (res, FALSE);
+ } else
+ return g_strdup ("General");
+}
+
+static gboolean
+cb_attrs_as_string (PangoAttribute *a, GString *accum)
+{
+ PangoColor const *c;
+
+ switch (a->klass->type) {
+ case PANGO_ATTR_FAMILY :
+ g_string_append_printf (accum, "[family=%s",
+ ((PangoAttrString *)a)->value);
+ break;
+ case PANGO_ATTR_SIZE :
+ g_string_append_printf (accum, "[size=%d",
+ ((PangoAttrInt *)a)->value);
+ break;
+ case PANGO_ATTR_STYLE :
+ g_string_append_printf (accum, "[italic=%d",
+ (((PangoAttrInt *)a)->value == PANGO_STYLE_ITALIC) ? 1 : 0);
+ break;
+ case PANGO_ATTR_WEIGHT :
+ g_string_append_printf (accum, "[bold=%d",
+ (((PangoAttrInt *)a)->value >= PANGO_WEIGHT_BOLD) ? 1 : 0);
+ break;
+ case PANGO_ATTR_STRIKETHROUGH :
+ g_string_append_printf (accum, "[strikthrough=%d",
+ ((PangoAttrInt *)a)->value ? 1 : 0);
+ break;
+ case PANGO_ATTR_UNDERLINE :
+ switch (((PangoAttrInt *)a)->value) {
+ case PANGO_UNDERLINE_NONE :
+ g_string_append (accum, "[underline=none");
+ break;
+ case PANGO_UNDERLINE_SINGLE :
+ g_string_append (accum, "[underline=single");
+ break;
+ case PANGO_UNDERLINE_DOUBLE :
+ g_string_append (accum, "[underline=double");
+ break;
+ }
+ break;
+
+ case PANGO_ATTR_FOREGROUND :
+ c = &((PangoAttrColor *)a)->color;
+ g_string_append_printf (accum, "[color=%02xx%02xx%02x",
+ ((c->red & 0xff00) >> 8),
+ ((c->green & 0xff00) >> 8),
+ ((c->blue & 0xff00) >> 8));
+ break;
+ default :
+ return FALSE; /* ignored */
+ }
+ g_string_append_printf (accum, ":%d:%d]", a->start_index, a->end_index);
+ return FALSE;
+}
+
+static PangoAttrList *
+gnm_format_parse_markup (char *str)
+{
+ PangoAttrList *attrs;
+ PangoAttribute *a;
+ char *closer, *val, *val_end;
+ unsigned len;
+ int r, g, b;
+
+ g_return_val_if_fail (*str == '@', NULL);
+
+ attrs = pango_attr_list_new ();
+ for (str++ ; *str ; str = closer + 1) {
+ g_return_val_if_fail (*str == '[', attrs);
+ str++;
+
+ val = strchr (str, '=');
+ g_return_val_if_fail (val != NULL, attrs);
+ len = val - str;
+ val++;
+
+ val_end = strchr (val, ':');
+ g_return_val_if_fail (val_end != NULL, attrs);
+
+ closer = strchr (val_end, ']');
+ g_return_val_if_fail (closer != NULL, attrs);
+ *val_end = '\0';
+ *closer = '\0';
+
+ a = NULL;
+ if (len == 4) {
+ if (0 == strncmp (str, "size", 4))
+ a = pango_attr_size_new (atoi (val));
+ else if (0 == strncmp (str, "bold", 4))
+ a = pango_attr_weight_new (atoi (val) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
+ } else if (len == 5 && 0 == strncmp (str, "color", 5) &&
+ 3 == sscanf (val, "%02xx%02xx%02x", &r, &g, &b))
+ a = pango_attr_foreground_new ((r << 8) | r, (g << 8) | g, (b << 8) | b);
+ else if (len == 6) {
+ if (0 == strncmp (str, "family", 6))
+ a = pango_attr_family_new (val);
+ else if (0 == strncmp (str, "italic", 6))
+ a = pango_attr_style_new (atoi (val) ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
+ } else if (len == 9 && 0 == strncmp (str, "underline", 9)) {
+ if (0 == strcmp (val, "none"))
+ a = pango_attr_underline_new (PANGO_UNDERLINE_NONE);
+ else if (0 == strcmp (val, "single"))
+ a = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
+ else if (0 == strcmp (val, "double"))
+ a = pango_attr_underline_new (PANGO_UNDERLINE_DOUBLE);
+ } else if (len == 13 && 0 == strncmp (str, "strikethrough", 13))
+ a = pango_attr_strikethrough_new (atoi (val) != 0);
+
+ if (a != NULL && val_end != NULL &&
+ 2 == sscanf (val_end+1, "%d:%d]", &a->start_index, &a->end_index))
+ pango_attr_list_insert (attrs, a);
+
+ *val_end = ':';
+ *closer = ']';
+ }
+
+ return attrs;
+}
+
+/**
+ * style_format_new_XL :
+ *
+ * Looks up and potentially creates a GnmFormat from the supplied string in
+ * XL format.
+ *
+ * @descriptor_string: XL descriptor in UTF-8 encoding.
+ */
+GnmFormat *
+style_format_new_XL (char const *descriptor_string, gboolean delocalize)
+{
+ GnmFormat *format;
+ char *desc_copy = NULL;
+
+ /* Safety net */
+ if (descriptor_string == NULL) {
+ g_warning ("Invalid format descriptor string, using General");
+ descriptor_string = "General";
+ } else if (delocalize)
+ descriptor_string = desc_copy = style_format_delocalize (descriptor_string);
+
+ format = (GnmFormat *) g_hash_table_lookup (style_format_hash, descriptor_string);
+
+ if (!format) {
+ format = g_new0 (GnmFormat, 1);
+ format->format = g_strdup (descriptor_string);
+ format->entries = NULL;
+ format->regexp_str = NULL;
+ format->match_tags = NULL;
+ format->family = cell_format_classify (format, &format->family_info);
+ if (format->family == FMT_MARKUP)
+ format->markup = gnm_format_parse_markup (format->format);
+ else if (!style_format_is_general (format))
+ format_compile (format);
+
+ g_hash_table_insert (style_format_hash, format->format, format);
+ }
+ format->ref_count++;
+#ifdef DEBUG_REF_COUNT
+ g_message (__FUNCTION__ " format=%p '%s' ref_count=%d",
+ format, format->format, format->ref_count);
+#endif
+
+ g_free (desc_copy);
+ return format;
+}
+
+/**
+ * style_format_new_markup :
+ * @markup : #PangoAttrList
+ * @add_ref :
+ *
+ * Create a MARKUP format. If @add_ref is FALSE absorb the reference to
+ * @markup, otherwise add a reference.
+ */
+GnmFormat *
+style_format_new_markup (PangoAttrList *markup, gboolean add_ref)
+{
+ GnmFormat *format = g_new0 (GnmFormat, 1);
+ GString *accum = g_string_new ("@");
+
+ pango_attr_list_filter (markup,
+ (PangoAttrFilterFunc) cb_attrs_as_string, accum);
+
+ format->format = g_string_free (accum, FALSE);
+ format->entries = NULL;
+ format->regexp_str = NULL;
+ format->match_tags = NULL;
+ format->family = FMT_MARKUP;
+ format->markup = markup;
+ if (add_ref)
+ pango_attr_list_ref (markup);
+
+ g_hash_table_insert (style_format_hash, format->format, format);
+ format->ref_count++;
+
+#ifdef DEBUG_REF_COUNT
+ g_message (__FUNCTION__ " format=%p '%s' ref_count=%d",
+ format, format->format, format->ref_count);
+#endif
+
+ return format;
+}
+
+
+GnmFormat *
+style_format_build (FormatFamily family, const FormatCharacteristics *info)
+{
+ switch (family) {
+ case FMT_GENERAL:
+ case FMT_TEXT:
+ return style_format_new_XL (cell_formats[family][0], FALSE);
+
+ case FMT_NUMBER: {
+ /* Make sure no currency is selected */
+ FormatCharacteristics info_copy = *info;
+ info_copy.currency_symbol_index = 0;
+ return style_format_number (&info_copy);
+ }
+
+ case FMT_CURRENCY:
+ return style_format_number (info);
+
+ case FMT_ACCOUNT:
+ return style_format_account (info);
+
+ case FMT_PERCENT:
+ return style_format_percent (info);
+
+ case FMT_SCIENCE:
+ return style_format_science (info);
+
+ default:
+ case FMT_DATE:
+ case FMT_TIME:
+ return NULL;
+ };
+}
+
+
+/**
+ * style_format_str_as_XL
+ *
+ * The caller is responsible for freeing the resulting string.
+ */
+char *
+style_format_str_as_XL (char const *ptr, gboolean localized)
+{
+ GString const *thousands_sep, *decimal;
+ GString *res;
+
+ g_return_val_if_fail (ptr != NULL,
+ g_strdup (localized ? _("General") : "General"));
+
+ if (!localized)
+ return g_strdup (ptr);
+
+ if (!strcmp (ptr, "General"))
+ return g_strdup (_("General"));
+
+ thousands_sep = format_get_thousand ();
+ decimal = format_get_decimal ();
+
+ res = g_string_sized_new (strlen (ptr));
+
+ /* TODO : XL seems to do an adaptive escaping of
+ * things.
+ * eg '#,##0.00 ' in a locale that uses ' '
+ * as the thousands would become
+ * '# ##0.00 '
+ * rather than
+ * '# ##0.00\ '
+ *
+ * TODO : Minimal quotes.
+ * It also seems to have a display mode vs a storage mode.
+ * Internally it adds a few quotes around strings.
+ * Then tries not to display the quotes unless needed.
+ */
+ for ( ; *ptr ; ++ptr)
+ switch (*ptr) {
+ case '.':
+ gnm_string_append_gstring (res, decimal);
+ break;
+ case ',':
+ gnm_string_append_gstring (res, thousands_sep);
+ break;
+
+ case '\"':
+ do {
+ g_string_append_c (res, *ptr++);
+ } while (*ptr && *ptr != '\"');
+ if (*ptr)
+ g_string_append_c (res, *ptr);
+ break;
+
+ case '\\':
+ g_string_append_c (res, '\\');
+ if (ptr[1] != '\0') {
+ g_string_append_c (res, ptr[1]);
+ ++ptr;
+ }
+ break;
+
+ case '[': {
+ char *tmp = translate_format_color (res, ptr, FALSE);
+ if (tmp != NULL)
+ ptr = tmp;
+ break;
+ }
+
+ default:
+ if (strncmp (ptr, decimal->str, decimal->len) == 0 ||
+ strncmp (ptr, thousands_sep->str, thousands_sep->len) == 0)
+ g_string_append_c (res, '\\');
+ g_string_append_c (res, *ptr);
+ }
+
+ return g_string_free (res, FALSE);
+}
+
+/**
+ * style_format_as_XL :
+ * @sf :
+ * @localized : should the string be in cannonical or locale specific form.
+ *
+ * Return a string which the caller is responsible for freeing.
+ */
+char *
+style_format_as_XL (GnmFormat const *fmt, gboolean localized)
+{
+ g_return_val_if_fail (fmt != NULL,
+ g_strdup (localized ? _("General") : "General"));
+
+ return style_format_str_as_XL (fmt->format, localized);
+}
+
+gboolean
+style_format_equal (GnmFormat const *a, GnmFormat const *b)
+{
+ g_return_val_if_fail (a != NULL, FALSE);
+ g_return_val_if_fail (b != NULL, FALSE);
+
+ /*
+ * The way we create GnmFormat *s ensures that we don't need
+ * to compare anything but pointers.
+ */
+ return (a == b);
+}
+
+/**
+ * style_format_ref :
+ * @sf :
+ *
+ * Add a reference to a GnmFormat
+ */
+void
+style_format_ref (GnmFormat *sf)
+{
+ g_return_if_fail (sf != NULL);
+
+ sf->ref_count++;
+#ifdef DEBUG_REF_COUNT
+ g_message (__FUNCTION__ " format=%p '%s' ref_count=%d",
+ sf, sf->format, sf->ref_count);
+#endif
+}
+
+/**
+ * style_format_unref :
+ * @sf :
+ *
+ * Remove a reference to a GnmFormat, freeing when it goes to zero.
+ */
+void
+style_format_unref (GnmFormat *sf)
+{
+ if (sf == NULL)
+ return;
+
+ g_return_if_fail (sf->ref_count > 0);
+
+ sf->ref_count--;
+#ifdef DEBUG_REF_COUNT
+ g_message (__FUNCTION__ " format=%p '%s' ref_count=%d",
+ sf, sf->format, sf->ref_count);
+#endif
+ if (sf->ref_count != 0)
+ return;
+
+ g_hash_table_remove (style_format_hash, sf->format);
+
+ format_destroy (sf);
+ g_free (sf->format);
+ g_free (sf);
+}
+
+GnmFormat *
+style_format_general (void)
+{
+ if (!default_general_fmt)
+ default_general_fmt =
+ style_format_new_XL (cell_formats[FMT_GENERAL][0], FALSE);
+
+ return default_general_fmt;
+}
+
+GnmFormat *
+style_format_default_date (void)
+{
+ if (!default_date_fmt)
+ default_date_fmt =
+ style_format_new_XL (cell_formats[FMT_DATE][0], FALSE);
+
+ return default_date_fmt;
+}
+
+GnmFormat *
+style_format_default_time (void)
+{
+ if (!default_time_fmt)
+ default_time_fmt =
+ style_format_new_XL (cell_formats[FMT_TIME][0], FALSE);
+
+ return default_time_fmt;
+}
+
+GnmFormat *
+style_format_default_date_time (void)
+{
+ if (!default_date_time_fmt)
+ default_date_time_fmt =
+ style_format_new_XL (cell_formats[FMT_TIME][4], FALSE);
+
+ return default_date_time_fmt;
+}
+
+GnmFormat *
+style_format_default_percentage (void)
+{
+ if (!default_percentage_fmt)
+ default_percentage_fmt =
+ style_format_new_XL (cell_formats[FMT_PERCENT][1], FALSE);
+
+ return default_percentage_fmt;
+}
+
+GnmFormat *
+style_format_default_money (void)
+{
+ if (!default_money_fmt)
+ default_money_fmt =
+ style_format_new_XL (cell_formats[FMT_CURRENCY][2], FALSE);
+
+ return default_money_fmt;
+}
--- /dev/null
+++ lib/goffice/split/formats.c
@@ -0,0 +1,781 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * formats.c: The default formats supported in Gnumeric
+ *
+ * For information on how to translate these format strings properly,
+ * refer to the doc/translating.sgml file in the Gnumeric distribution.
+ *
+ * Author:
+ * Miguel de Icaza (miguel at kernel.org)
+ */
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+
+#include "format.h"
+#include <string.h>
+
+/* The various formats */
+static char const * const
+cell_format_general [] = {
+ "General",
+ NULL
+};
+
+static char const * const
+cell_format_numbers [] = {
+ "0",
+ "0.00",
+ "#,##0",
+ "#,##0.00",
+ "#,##0_);(#,##0)",
+ "#,##0_);[Red](#,##0)",
+ "#,##0.00_);(#,##0.00)",
+ "#,##0.00_);[Red](#,##0.00)",
+ "0.0",
+ NULL
+};
+
+/* Some are generated */
+static char const *
+cell_format_currency [] = {
+ NULL, /* "$#,##0", */
+ NULL, /* "$#,##0_);($#,##0)", */
+ NULL, /* "$#,##0_);[Red]($#,##0)", */
+ NULL, /* "$#,##0.00", */
+ NULL, /* "$#,##0.00_);($#,##0.00)", */
+ NULL, /* "$#,##0.00_);[Red]($#,##0.00)", */
+ NULL,
+};
+
+/* Some are generated */
+static char const *
+cell_format_account [] = {
+ NULL, /* "_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(@_)", */
+ "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)",
+ NULL, /* "_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)", */
+ "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)",
+ NULL
+};
+
+/*****************************************************************/
+/* Some are generated */
+/* WARNING WARNING WARNING : do not reorder these !! */
+/* the generated versions and the excel plugin assume this order */
+static char const *
+cell_format_date [] = {
+ "m/d/yy", /* 0 */
+ "m/d/yyyy", /* 1 */
+ "d-mmm-yy", /* 2 */
+ "d-mmm-yyyy", /* 3 */
+ "d-mmm", /* 4 */
+ "d-mm", /* 5 */
+ "mmm/d", /* 6 */
+ "mm/d", /* 7 */
+ "mm/dd/yy", /* 8 */
+ "mm/dd/yyyy", /* 9 */
+ "mmm/dd/yy", /* 10 */
+ "mmm/dd/yyyy", /* 11 */
+ "mmm/ddd/yy", /* 12 */
+ "mmm/ddd/yyyy", /* 13 */
+ "mm/ddd/yy", /* 14 */
+ "mm/ddd/yyyy", /* 15 */
+ "mmm-yy", /* 16 */
+ "mmm-yyyy", /* 17 */
+ "mmmm-yy", /* 18 */
+ "mmmm-yyyy", /* 19 */
+ "m/d/yy h:mm", /* 20 */
+ "m/d/yyyy h:mm", /* 21 */
+ "yyyy/mm/d", /* 22 */
+ "yyyy/mmm/d", /* 23 */
+ "yyyy/mm/dd", /* 24 */
+ "yyyy/mmm/dd", /* 25 */
+ "yyyy-mm-d", /* 26 */
+ "yyyy-mmm-d", /* 27 */
+ "yyyy-mm-dd", /* 28 */
+ "yyyy-mmm-dd", /* 29 */
+ "yy", /* 30 */
+ "yyyy", /* 31 */
+ "mmmmm", /* 32 stored as custom in xls */
+ "mmmmm-yy", /* 33 stored as custom in xls */
+ NULL
+};
+
+/*****************************************************/
+
+/* Some are generated */
+static char const *
+cell_format_time [] = {
+ "h:mm AM/PM",
+ "h:mm:ss AM/PM",
+ "h:mm",
+ "h:mm:ss",
+ "m/d/yy h:mm",
+ "[h]:mm", /* keep this before mm:ss so that 24:00 will */
+ "[h]:mm:ss", /* match an hour based format (bug #86338) */
+ "mm:ss",
+ "mm:ss.0",
+ "[mm]:ss",
+ "[ss]",
+ NULL
+};
+
+static char const * const
+cell_format_percent [] = {
+ "0%",
+ "0.00%",
+ NULL,
+};
+
+static char const * const
+cell_format_fraction [] = {
+ "# ?/?",
+ "# ?" "?/?" "?", /* Don't accidentally use trigraph. */
+ "# ?" "?" "?/?" "?" "?",
+ "# ?/2",
+ "# ?/4",
+ "# ?/8",
+ "# ?/16",
+ "# ?/10",
+ "# ?/100",
+ NULL
+};
+
+static char const * const
+cell_format_science [] = {
+ "0.00E+00",
+ "##0.0E+0",
+ NULL
+};
+
+static char const *
+cell_format_text [] = {
+ "@",
+ NULL,
+};
+
+char const * const * const
+cell_formats [] = {
+ cell_format_general,
+ cell_format_numbers,
+ cell_format_currency,
+ cell_format_account,
+ cell_format_date,
+ cell_format_time,
+ cell_format_percent,
+ cell_format_fraction,
+ cell_format_science,
+ cell_format_text,
+ NULL
+};
+
+/* The compiled regexp for cell_format_classify */
+static go_regex_t re_simple_number;
+static go_regex_t re_red_number;
+static go_regex_t re_brackets_number;
+static go_regex_t re_percent_science;
+static go_regex_t re_account;
+static go_regex_t re_fraction;
+
+static const char *
+my_regerror (int err, const go_regex_t *preg)
+{
+ static char buffer[1024];
+ go_regerror (err, preg, buffer, sizeof (buffer));
+ return buffer;
+}
+
+void
+currency_date_format_init (void)
+{
+ gboolean precedes, space_sep;
+ char const *curr = format_get_currency (&precedes, &space_sep)->str;
+ char *pre, *post, *pre_rep, *post_rep;
+ int err;
+
+ /* Compile the regexps for format classification */
+
+ /* This one is for simple numbers - it is an extended regexp */
+ char const * const simple_number_pattern = "^(\"?(\\$|£|Â¥|â¬|\\[\\$.{1,3}-?[0-9]{0,3}\\])\"? ?)?(#,##)?0(\\.0{1,30})?( ?(\\$|£|Â¥|â¬|\\[\\$.{1,3}-?[0-9]{0,3}\\]))?$";
+
+ /* This one is for matching formats like 0.00;[Red]0.00 */
+ char const * const red_number_pattern = "^(.*);\\[[Rr][Ee][Dd]\\]\\1$";
+
+ /* This one is for matching formats like 0.00_);(0.00) */
+ char const * const brackets_number_pattern = "^(.*)_\\);(\\[[Rr][Ee][Dd]\\])?\\(\\1\\)$";
+
+ /* This one is for FMT_PERCENT and FMT_SCIENCE, extended regexp */
+ char const *pattern_percent_science = "^0(.0{1,30})?(%|E+00)$";
+
+ char const *pattern_fraction = "^#\\\\? (\\?+)/(\\?+|[1-9]\\d*)$";
+
+ /* This one is for FMT_ACCOUNT */
+
+ /*
+ * 1. "$* " Yes, it is needed (because we use match[])
+ * 2. "$* " This one is like \1, but doesn't have the \{0,1\}
+ * 3. "$"
+ * 4. "#,##0.00"
+ * 5. ".00"
+ * 6. "* $" Same here.
+ * 7. "* $"
+ * 8. "$"
+ */
+
+ char const *pattern_account = "^_\\((((.*)\\* ?)?)(#,##0(\\.0{1,30})?)((\\* ?}(.*))?)_\\);_\\(\\1\\(\\4\\)\\6;_\\(\\1\"-\"\\?{0,30}\\6_\\);_\\(@_\\)$";
+
+
+ err = go_regcomp (&re_simple_number, simple_number_pattern, 0);
+ if (err)
+ g_warning ("Error in regcomp() for simple number, please report the bug [%s] [%s]",
+ my_regerror (err, &re_simple_number), simple_number_pattern);
+
+ err = go_regcomp (&re_red_number, red_number_pattern, 0);
+ if (err)
+ g_warning ("Error in regcomp() for red number, please report the bug [%s] [%s]",
+ my_regerror (err, &re_red_number), red_number_pattern);
+
+ err = go_regcomp (&re_brackets_number, brackets_number_pattern, 0);
+ if (err)
+ g_warning ("Error in regcomp() for brackets number, please report the bug [%s] [%s]",
+ my_regerror (err, &re_brackets_number), brackets_number_pattern);
+
+ err = go_regcomp (&re_percent_science, pattern_percent_science, 0);
+ if (err)
+ g_warning ("Error in regcomp() for percent and science, please report the bug [%s] [%s]",
+ my_regerror (err, &re_percent_science), pattern_percent_science);
+
+ err = go_regcomp (&re_fraction, pattern_fraction, 0);
+ if (err)
+ g_warning ("Error in regcomp() for fraction, please report the bug [%s] [%s]",
+ my_regerror (err, &re_fraction), pattern_fraction);
+
+ err = go_regcomp (&re_account, pattern_account, 0);
+ if (err)
+ g_warning ("Error in regcomp() for account, please report the bug [%s] [%s]",
+ my_regerror (err, &re_account), pattern_account);
+
+ if (precedes) {
+ post_rep = post = (char *)"";
+ pre_rep = (char *)"* ";
+ pre = g_strconcat ("\"", curr,
+ (space_sep ? "\" " : "\""), NULL);
+ } else {
+ pre_rep = pre = (char *)"";
+ post_rep = (char *)"* ";
+ post = g_strconcat ((space_sep ? " \"" : "\""),
+ curr, "\"", NULL);
+ }
+
+ cell_format_currency [0] = g_strdup_printf (
+ "%s#,##0%s",
+ pre, post);
+ cell_format_currency [1] = g_strdup_printf (
+ "%s#,##0%s_);(%s#,##0%s)",
+ pre, post, pre, post);
+ cell_format_currency [2] = g_strdup_printf (
+ "%s#,##0%s_);[Red](%s#,##0%s)",
+ pre, post, pre, post);
+ cell_format_currency [3] = g_strdup_printf (
+ "%s#,##0.00%s",
+ pre, post);
+ cell_format_currency [4] = g_strdup_printf (
+ "%s#,##0.00%s_);(%s#,##0.00%s)",
+ pre, post, pre, post);
+ cell_format_currency [5] = g_strdup_printf (
+ "%s#,##0.00%s_);[Red](%s#,##0.00%s)",
+ pre, post, pre, post);
+
+ cell_format_account [0] = g_strdup_printf (
+ "_(%s%s#,##0%s%s_);_(%s%s(#,##0)%s%s;_(%s%s\"-\"%s%s_);_(@_)",
+ pre, pre_rep, post_rep, post,
+ pre, pre_rep, post_rep, post,
+ pre, pre_rep, post_rep, post);
+ cell_format_account [2] = g_strdup_printf (
+ "_(%s%s#,##0.00%s%s_);_(%s%s(#,##0.00)%s%s;_(%s%s\"-\"??%s%s_);_(@_)",
+ pre, pre_rep, post_rep, post,
+ pre, pre_rep, post_rep, post,
+ pre, pre_rep, post_rep, post);
+
+ g_free (*pre ? pre : post);
+
+ if (!format_month_before_day ()) {
+ cell_format_date [0] = "d/m/yy";
+ cell_format_date [1] = "d/m/yyyy";
+ cell_format_date [2] = "mmm-d-yy";
+ cell_format_date [3] = "mmm-d-yyyy";
+ cell_format_date [4] = "mmm-d";
+ cell_format_date [5] = "mm-d";
+ cell_format_date [6] = "d/mmm";
+ cell_format_date [7] = "d/mm";
+ cell_format_date [8] = "dd/mm/yy";
+ cell_format_date [9] = "dd/mm/yyyy";
+ cell_format_date [10] = "dd/mmm/yy";
+ cell_format_date [11] = "dd/mmm/yyyy";
+ cell_format_date [12] = "ddd/mmm/yy";
+ cell_format_date [13] = "ddd/mmm/yyyy";
+ cell_format_date [14] = "ddd/mm/yy";
+ cell_format_date [15] = "ddd/mm/yyyy";
+ cell_format_date [20] = "d/m/yy h:mm";
+ cell_format_date [21] = "d/m/yyyy h:mm";
+
+ cell_format_time [4] = "d/m/yy h:mm";
+ } else {
+ cell_format_date [0] = "m/d/yy";
+ cell_format_date [1] = "m/d/yyyy";
+ cell_format_date [2] = "d-mmm-yy";
+ cell_format_date [3] = "d-mmm-yyyy";
+ cell_format_date [4] = "d-mmm";
+ cell_format_date [5] = "d-mm";
+ cell_format_date [6] = "mmm/d";
+ cell_format_date [7] = "mm/d";
+ cell_format_date [8] = "mm/dd/yy";
+ cell_format_date [9] = "mm/dd/yyyy";
+ cell_format_date [10] = "mmm/dd/yy";
+ cell_format_date [11] = "mmm/dd/yyyy";
+ cell_format_date [12] = "mmm/ddd/yy";
+ cell_format_date [13] = "mmm/ddd/yyyy";
+ cell_format_date [14] = "mm/ddd/yy";
+ cell_format_date [15] = "mm/ddd/yyyy";
+ cell_format_date [20] = "m/d/yy h:mm";
+ cell_format_date [21] = "m/d/yyyy h:mm";
+
+ cell_format_time [4] = "m/d/yy h:mm";
+ }
+}
+
+void
+currency_date_format_shutdown (void)
+{
+ /* We need to free allocated strings since */
+ /* currency_date_format_init/shutdown may */
+ /* be called repeatedly by the format selector */
+ /* when switching locales */
+
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ g_free ((char *)(cell_format_currency [i]));
+ cell_format_currency [i] = NULL;
+ }
+ g_free ((char *)(cell_format_account [0]));
+ cell_format_account [0] = NULL;
+ g_free ((char *)(cell_format_account [2]));
+ cell_format_account [3] = NULL;
+
+ go_regfree (&re_simple_number);
+ go_regfree (&re_red_number);
+ go_regfree (&re_brackets_number);
+ go_regfree (&re_percent_science);
+ go_regfree (&re_account);
+ go_regfree (&re_fraction);
+}
+
+CurrencySymbol const currency_symbols[] =
+{
+ { "", "None", TRUE, FALSE }, /* These first six elements */
+ { "$", "$", TRUE, FALSE }, /* Must stay in this order */
+ { "£", "£", TRUE, FALSE }, /* GBP */
+ { "Â¥", "Â¥", TRUE, FALSE }, /* JPY */
+
+ /* Add yours to this list ! */
+ { "[$â¬-1]", "⬠Euro (100 â¬)", FALSE, TRUE},
+ { "[$â¬-2]", "⬠Euro (⬠100)", TRUE, TRUE},
+
+ /* The first column has three letter acronyms
+ * for each currency. They MUST start with '[$'
+ * The second column has the long names of the currencies.
+ */
+
+ /* 2002/08/04 Updated to match iso 4217 */
+ { "[$AED]", N_("United Arab Emirates, Dirhams"), TRUE, TRUE },
+ { "[$AFA]", N_("Afghanistan, Afghanis"), TRUE, TRUE },
+ { "[$ALL]", N_("Albania, Leke"), TRUE, TRUE },
+ { "[$AMD]", N_("Armenia, Drams"), TRUE, TRUE },
+ { "[$ANG]", N_("Netherlands Antilles, Guilders"), TRUE, TRUE },
+ { "[$AOA]", N_("Angola, Kwanza"), TRUE, TRUE },
+ { "[$ARS]", N_("Argentina, Pesos"), TRUE, TRUE },
+ { "[$AUD]", N_("Australia, Dollars"), TRUE, TRUE },
+ { "[$AWG]", N_("Aruba, Guilders"), TRUE, TRUE },
+ { "[$AZM]", N_("Azerbaijan, Manats"), TRUE, TRUE },
+ { "[$BAM]", N_("Bosnia and Herzegovina, Convertible Marka"), TRUE, TRUE },
+ { "[$BBD]", N_("Barbados, Dollars"), TRUE, TRUE },
+ { "[$BDT]", N_("Bangladesh, Taka"), TRUE, TRUE },
+ { "[$BGL]", N_("Bulgaria, Leva"), TRUE, TRUE },
+ { "[$BHD]", N_("Bahrain, Dinars"), TRUE, TRUE },
+ { "[$BIF]", N_("Burundi, Francs"), TRUE, TRUE },
+ { "[$BMD]", N_("Bermuda, Dollars"), TRUE, TRUE },
+ { "[$BND]", N_("Brunei Darussalam, Dollars"), TRUE, TRUE },
+ { "[$BOB]", N_("Bolivia, Bolivianos"), TRUE, TRUE },
+ { "[$BRL]", N_("Brazil, Brazil Real"), TRUE, TRUE },
+ { "[$BSD]", N_("Bahamas, Dollars"), TRUE, TRUE },
+ { "[$BTN]", N_("Bhutan, Ngultrum"), TRUE, TRUE },
+ { "[$BWP]", N_("Botswana, Pulas"), TRUE, TRUE },
+ { "[$BYR]", N_("Belarus, Rubles"), TRUE, TRUE },
+ { "[$BZD]", N_("Belize, Dollars"), TRUE, TRUE },
+ { "[$CAD]", N_("Canada, Dollars"), TRUE, TRUE },
+ { "[$CDF]", N_("Congo/Kinshasa, Congolese Francs"), TRUE, TRUE },
+ { "[$CHF]", N_("Switzerland, Francs"), TRUE, TRUE },
+ { "[$CLP]", N_("Chile, Pesos"), TRUE, TRUE },
+ { "[$CNY]", N_("China, Yuan Renminbi"), TRUE, TRUE },
+ { "[$COP]", N_("Colombia, Pesos"), TRUE, TRUE },
+ { "[$CRC]", N_("Costa Rica, Colones"), TRUE, TRUE },
+ { "[$CUP]", N_("Cuba, Pesos"), TRUE, TRUE },
+ { "[$CVE]", N_("Cape Verde, Escudos"), TRUE, TRUE },
+ { "[$CYP]", N_("Cyprus, Pounds"), TRUE, TRUE },
+ { "[$CZK]", N_("Czech Republic, Koruny"), TRUE, TRUE },
+ { "[$DJF]", N_("Djibouti, Francs"), TRUE, TRUE },
+ { "[$DKK]", N_("Denmark, Kroner"), TRUE, TRUE },
+ { "[$DOP]", N_("Dominican Republic, Pesos"), TRUE, TRUE },
+ { "[$DZD]", N_("Algeria, Algeria Dinars"), TRUE, TRUE },
+ { "[$EEK]", N_("Estonia, Krooni"), TRUE, TRUE },
+ { "[$EGP]", N_("Egypt, Pounds"), TRUE, TRUE },
+ { "[$ERN]", N_("Eritrea, Nakfa"), TRUE, TRUE },
+ { "[$ETB]", N_("Ethiopia, Birr"), TRUE, TRUE },
+ { "[$EUR]", N_("Euro Member Countries, Euro"), TRUE, TRUE },
+ { "[$FJD]", N_("Fiji, Dollars"), TRUE, TRUE },
+ { "[$FKP]", N_("Falkland Islands (Malvinas), Pounds"), TRUE, TRUE },
+ { "[$GBP]", N_("United Kingdom, Pounds"), TRUE, TRUE },
+ { "[$GEL]", N_("Georgia, Lari"), TRUE, TRUE },
+ { "[$GGP]", N_("Guernsey, Pounds"), TRUE, TRUE },
+ { "[$GHC]", N_("Ghana, Cedis"), TRUE, TRUE },
+ { "[$GIP]", N_("Gibraltar, Pounds"), TRUE, TRUE },
+ { "[$GMD]", N_("Gambia, Dalasi"), TRUE, TRUE },
+ { "[$GNF]", N_("Guinea, Francs"), TRUE, TRUE },
+ { "[$GTQ]", N_("Guatemala, Quetzales"), TRUE, TRUE },
+ { "[$GYD]", N_("Guyana, Dollars"), TRUE, TRUE },
+ { "[$HKD]", N_("Hong Kong, Dollars"), TRUE, TRUE },
+ { "[$HNL]", N_("Honduras, Lempiras"), TRUE, TRUE },
+ { "[$HRK]", N_("Croatia, Kuna"), TRUE, TRUE },
+ { "[$HTG]", N_("Haiti, Gourdes"), TRUE, TRUE },
+ { "[$HUF]", N_("Hungary, Forint"), TRUE, TRUE },
+ { "[$IDR]", N_("Indonesia, Rupiahs"), TRUE, TRUE },
+ { "[$ILS]", N_("Israel, New Shekels"), TRUE, TRUE },
+ { "[$IMP]", N_("Isle of Man, Pounds"), TRUE, TRUE },
+ { "[$INR]", N_("India, Rupees"), TRUE, TRUE },
+ { "[$IQD]", N_("Iraq, Dinars"), TRUE, TRUE },
+ { "[$IRR]", N_("Iran, Rials"), TRUE, TRUE },
+ { "[$ISK]", N_("Iceland, Kronur"), TRUE, TRUE },
+ { "[$JEP]", N_("Jersey, Pounds"), TRUE, TRUE },
+ { "[$JMD]", N_("Jamaica, Dollars"), TRUE, TRUE },
+ { "[$JOD]", N_("Jordan, Dinars"), TRUE, TRUE },
+ { "[$JPY]", N_("Japan, Yen"), TRUE, TRUE },
+ { "[$KES]", N_("Kenya, Shillings"), TRUE, TRUE },
+ { "[$KGS]", N_("Kyrgyzstan, Soms"), TRUE, TRUE },
+ { "[$KHR]", N_("Cambodia, Riels"), TRUE, TRUE },
+ { "[$KMF]", N_("Comoros, Francs"), TRUE, TRUE },
+ { "[$KPW]", N_("Korea (North), Won"), TRUE, TRUE },
+ { "[$KRW]", N_("Korea (South), Won"), TRUE, TRUE },
+ { "[$KWD]", N_("Kuwait, Dinars"), TRUE, TRUE },
+ { "[$KYD]", N_("Cayman Islands, Dollars"), TRUE, TRUE },
+ { "[$KZT]", N_("Kazakstan, Tenge"), TRUE, TRUE },
+ { "[$LAK]", N_("Laos, Kips"), TRUE, TRUE },
+ { "[$LBP]", N_("Lebanon, Pounds"), TRUE, TRUE },
+ { "[$LKR]", N_("Sri Lanka, Rupees"), TRUE, TRUE },
+ { "[$LRD]", N_("Liberia, Dollars"), TRUE, TRUE },
+ { "[$LSL]", N_("Lesotho, Maloti"), TRUE, TRUE },
+ { "[$LTL]", N_("Lithuania, Litai"), TRUE, TRUE },
+ { "[$LVL]", N_("Latvia, Lati"), TRUE, TRUE },
+ { "[$LYD]", N_("Libya, Dinars"), TRUE, TRUE },
+ { "[$MAD]", N_("Morocco, Dirhams"), TRUE, TRUE },
+ { "[$MDL]", N_("Moldova, Lei"), TRUE, TRUE },
+ { "[$MGF]", N_("Madagascar, Malagasy Francs"), TRUE, TRUE },
+ { "[$MKD]", N_("Macedonia, Denars"), TRUE, TRUE },
+ { "[$MMK]", N_("Myanmar (Burma), Kyats"), TRUE, TRUE },
+ { "[$MNT]", N_("Mongolia, Tugriks"), TRUE, TRUE },
+ { "[$MOP]", N_("Macau, Patacas"), TRUE, TRUE },
+ { "[$MRO]", N_("Mauritania, Ouguiyas"), TRUE, TRUE },
+ { "[$MTL]", N_("Malta, Liri"), TRUE, TRUE },
+ { "[$MUR]", N_("Mauritius, Rupees"), TRUE, TRUE },
+ { "[$MVR]", N_("Maldives (Maldive Islands), Rufiyaa"), TRUE, TRUE },
+ { "[$MWK]", N_("Malawi, Kwachas"), TRUE, TRUE },
+ { "[$MXN]", N_("Mexico, Pesos"), TRUE, TRUE },
+ { "[$MYR]", N_("Malaysia, Ringgits"), TRUE, TRUE },
+ { "[$MZM]", N_("Mozambique, Meticais"), TRUE, TRUE },
+ { "[$NAD]", N_("Namibia, Dollars"), TRUE, TRUE },
+ { "[$NGN]", N_("Nigeria, Nairas"), TRUE, TRUE },
+ { "[$NIO]", N_("Nicaragua, Gold Cordobas"), TRUE, TRUE },
+ { "[$NOK]", N_("Norway, Krone"), TRUE, TRUE },
+ { "[$NPR]", N_("Nepal, Nepal Rupees"), TRUE, TRUE },
+ { "[$NZD]", N_("New Zealand, Dollars"), TRUE, TRUE },
+ { "[$OMR]", N_("Oman, Rials"), TRUE, TRUE },
+ { "[$PAB]", N_("Panama, Balboa"), TRUE, TRUE },
+ { "[$PEN]", N_("Peru, Nuevos Soles"), TRUE, TRUE },
+ { "[$PGK]", N_("Papua New Guinea, Kina"), TRUE, TRUE },
+ { "[$PHP]", N_("Philippines, Pesos"), TRUE, TRUE },
+ { "[$PKR]", N_("Pakistan, Rupees"), TRUE, TRUE },
+ { "[$PLN]", N_("Poland, Zlotys"), TRUE, TRUE },
+ { "[$PYG]", N_("Paraguay, Guarani"), TRUE, TRUE },
+ { "[$QAR]", N_("Qatar, Rials"), TRUE, TRUE },
+ { "[$ROL]", N_("Romania, Lei"), TRUE, TRUE },
+ { "[$RUR]", N_("Russia, Rubles"), TRUE, TRUE },
+ { "[$RWF]", N_("Rwanda, Rwanda Francs"), TRUE, TRUE },
+ { "[$SAR]", N_("Saudi Arabia, Riyals"), TRUE, TRUE },
+ { "[$SBD]", N_("Solomon Islands, Dollars"), TRUE, TRUE },
+ { "[$SCR]", N_("Seychelles, Rupees"), TRUE, TRUE },
+ { "[$SDD]", N_("Sudan, Dinars"), TRUE, TRUE },
+ { "[$SEK]", N_("Sweden, Kronor"), TRUE, TRUE },
+ { "[$SGD]", N_("Singapore, Dollars"), TRUE, TRUE },
+ { "[$SHP]", N_("Saint Helena, Pounds"), TRUE, TRUE },
+ { "[$SIT]", N_("Slovenia, Tolars"), TRUE, TRUE },
+ { "[$SKK]", N_("Slovakia, Koruny"), TRUE, TRUE },
+ { "[$SLL]", N_("Sierra Leone, Leones"), TRUE, TRUE },
+ { "[$SOS]", N_("Somalia, Shillings"), TRUE, TRUE },
+ { "[$SPL]", N_("Seborga, Luigini"), TRUE, TRUE },
+ { "[$SRG]", N_("Suriname, Guilders"), TRUE, TRUE },
+ { "[$STD]", N_("Sao Tome and Principe, Dobras"), TRUE, TRUE },
+ { "[$SVC]", N_("El Salvador, Colones"), TRUE, TRUE },
+ { "[$SYP]", N_("Syria, Pounds"), TRUE, TRUE },
+ { "[$SZL]", N_("Swaziland, Emalangeni"), TRUE, TRUE },
+ { "[$THB]", N_("Thailand, Baht"), TRUE, TRUE },
+ { "[$TJR]", N_("Tajikistan, Rubles"), TRUE, TRUE },
+ { "[$TMM]", N_("Turkmenistan, Manats"), TRUE, TRUE },
+ { "[$TND]", N_("Tunisia, Dinars"), TRUE, TRUE },
+ { "[$TOP]", N_("Tonga, Pa'anga"), TRUE, TRUE },
+ { "[$TRL]", N_("Turkey, Liras"), TRUE, TRUE },
+ { "[$TTD]", N_("Trinidad and Tobago, Dollars"), TRUE, TRUE },
+ { "[$TVD]", N_("Tuvalu, Tuvalu Dollars"), TRUE, TRUE },
+ { "[$TWD]", N_("Taiwan, New Dollars"), TRUE, TRUE },
+ { "[$TZS]", N_("Tanzania, Shillings"), TRUE, TRUE },
+ { "[$UAH]", N_("Ukraine, Hryvnia"), TRUE, TRUE },
+ { "[$UGX]", N_("Uganda, Shillings"), TRUE, TRUE },
+ { "[$USD]", N_("United States of America, Dollars"), TRUE, TRUE },
+ { "[$UYU]", N_("Uruguay, Pesos"), TRUE, TRUE },
+ { "[$UZS]", N_("Uzbekistan, Sums"), TRUE, TRUE },
+ { "[$VEB]", N_("Venezuela, Bolivares"), TRUE, TRUE },
+ { "[$VND]", N_("Viet Nam, Dong"), TRUE, TRUE },
+ { "[$VUV]", N_("Vanuatu, Vatu"), TRUE, TRUE },
+ { "[$WST]", N_("Samoa, Tala"), TRUE, TRUE },
+ { "[$XAF]", N_("Communaute Financiere Africaine BEAC, Francs"), TRUE, TRUE },
+ { "[$XAG]", N_("Silver, Ounces"), TRUE, TRUE },
+ { "[$XAU]", N_("Gold, Ounces"), TRUE, TRUE },
+ { "[$XCD]", N_("East Caribbean Dollars"), TRUE, TRUE },
+ { "[$XDR]", N_("International Monetary Fund (IMF) Special Drawing Rights"), TRUE, TRUE },
+ { "[$XOF]", N_("Communaute Financiere Africaine BCEAO, Francs"), TRUE, TRUE },
+ { "[$XPD]", N_("Palladium, Ounces"), TRUE, TRUE },
+ { "[$XPF]", N_("Comptoirs Francais du Pacifique Francs"), TRUE, TRUE },
+ { "[$XPT]", N_("Platinum, Ounces"), TRUE, TRUE },
+ { "[$YER]", N_("Yemen, Rials"), TRUE, TRUE },
+ { "[$YUM]", N_("Yugoslavia, New Dinars"), TRUE, TRUE },
+ { "[$ZAR]", N_("South Africa, Rand"), TRUE, TRUE },
+ { "[$ZMK]", N_("Zambia, Kwacha"), TRUE, TRUE },
+ { "[$ZWD]", N_("Zimbabwe, Zimbabwe Dollars"), TRUE, TRUE },
+
+ { NULL, NULL, FALSE, FALSE }
+};
+
+/* Returns the index in currency_symbols of the symbol in ptr */
+static int
+find_currency (char const *ptr, int len)
+{
+ int i;
+
+ for (i = 0; currency_symbols[i].symbol; i++)
+ if (strncmp(currency_symbols[i].symbol, ptr, len) == 0)
+ return i;
+
+ return -1;
+}
+
+static FormatFamily
+cell_format_simple_number (char const * const fmt, FormatCharacteristics *info)
+{
+ FormatFamily result = FMT_NUMBER;
+ int cur = -1;
+ regmatch_t match[7];
+
+ if (go_regexec (&re_simple_number, fmt, G_N_ELEMENTS (match), match, 0) == 0) {
+
+ if (match[2].rm_eo == -1 && match[6].rm_eo == -1) {
+ result = FMT_NUMBER;
+ info->currency_symbol_index = 0;
+ } else {
+ result = FMT_CURRENCY;
+ if (match[6].rm_eo == -1)
+ cur = find_currency (fmt + match[2].rm_so,
+ match[2].rm_eo
+ - match[2].rm_so);
+ else if (match[2].rm_eo == -1)
+ cur = find_currency (fmt + match[6].rm_so,
+ match[6].rm_eo
+ - match[6].rm_so);
+ if (cur == -1)
+ return FMT_UNKNOWN;
+ info->currency_symbol_index = cur;
+ }
+
+ if (match[3].rm_eo != -1)
+ info->thousands_sep = TRUE;
+
+ info->num_decimals = 0;
+ if (match[4].rm_eo != -1)
+ info->num_decimals = match[4].rm_eo -
+ match[4].rm_so - 1;
+
+ return result;
+ } else {
+ return FMT_UNKNOWN;
+ }
+}
+
+static FormatFamily
+cell_format_is_number (char const * const fmt, FormatCharacteristics *info)
+{
+ FormatFamily result = FMT_NUMBER;
+ char const *ptr = fmt;
+ int cur = -1;
+ regmatch_t match[9];
+
+ /* FMT_CURRENCY or FMT_NUMBER ? */
+ if ((result = cell_format_simple_number (fmt, info)) != FMT_UNKNOWN)
+ return result;
+
+ if (go_regexec (&re_red_number, fmt, G_N_ELEMENTS (match), match, 0) == 0) {
+ char *tmp = g_strndup(fmt+match[1].rm_so,
+ match[1].rm_eo-match[1].rm_so);
+ result = cell_format_simple_number (tmp, info);
+ g_free(tmp);
+ info->negative_fmt = 1;
+ return result;
+ }
+
+ if (go_regexec (&re_brackets_number, fmt, G_N_ELEMENTS (match), match, 0) == 0) {
+ char *tmp = g_strndup(fmt+match[1].rm_so,
+ match[1].rm_eo-match[1].rm_so);
+ result = cell_format_simple_number (tmp, info);
+ g_free(tmp);
+ if (match[2].rm_eo != -1)
+ info->negative_fmt = 3;
+ else
+ info->negative_fmt = 2;
+ return result;
+ }
+
+ /* FMT_PERCENT or FMT_SCIENCE ? */
+ if (go_regexec (&re_percent_science, fmt, G_N_ELEMENTS (match), match, 0) == 0) {
+ info->num_decimals = 0;
+ if (match[1].rm_eo != -1)
+ info->num_decimals = match[1].rm_eo -
+ match[1].rm_so - 1;
+
+ if (ptr[match[2].rm_so] == '%')
+ return FMT_PERCENT;
+ else
+ return FMT_SCIENCE;
+ }
+
+ /* FMT_ACCOUNT */
+ if (go_regexec (&re_account, fmt, G_N_ELEMENTS (match), match, 0) == 0) {
+ info->num_decimals = 0;
+ if (match[5].rm_eo != -1)
+ info->num_decimals = match[5].rm_eo -
+ match[5].rm_so - 1;
+
+ if (match[1].rm_eo == -1 && match[6].rm_eo == -1)
+ return FMT_UNKNOWN;
+ else {
+ if (match[8].rm_eo == -1)
+ cur = find_currency (ptr + match[3].rm_so,
+ match[3].rm_eo
+ - match[3].rm_so);
+ else if (match[3].rm_eo == -1)
+ cur = find_currency (ptr + match[8].rm_so,
+ match[8].rm_eo
+ - match[8].rm_so);
+ else
+ return FMT_UNKNOWN;
+
+ }
+
+ if (cur == -1)
+ return FMT_UNKNOWN;
+ info->currency_symbol_index = cur;
+
+ return FMT_ACCOUNT;
+ }
+
+ return FMT_UNKNOWN;
+
+}
+
+static gboolean
+cell_format_is_fraction (char const *fmt, FormatCharacteristics *info)
+{
+ regmatch_t match[3];
+ const char *denominator;
+
+ if (go_regexec (&re_fraction, fmt, G_N_ELEMENTS (match), match, 0) != 0)
+ return FALSE;
+
+ denominator = fmt + match[2].rm_so;
+ if (denominator[0] == '?') {
+ info->num_decimals = match[1].rm_eo - match[1].rm_so;
+ info->fraction_denominator = 0;
+ } else {
+ info->num_decimals = 0;
+ info->fraction_denominator = atoi (denominator);
+ }
+ return TRUE;
+}
+
+
+FormatFamily
+cell_format_classify (GnmFormat const *sf, FormatCharacteristics *info)
+{
+ char const *fmt = sf->format;
+ FormatFamily res;
+ int i;
+
+ g_return_val_if_fail (fmt != NULL, FMT_GENERAL);
+ g_return_val_if_fail (info != NULL, FMT_GENERAL);
+
+ /* Init the result to something sane */
+ info->thousands_sep = FALSE;
+ info->num_decimals = 2;
+ info->negative_fmt = 0;
+ info->list_element = 0;
+ info->currency_symbol_index = 1; /* '$' */
+ info->date_has_days = FALSE;
+ info->date_has_months = FALSE;
+ info->fraction_denominator = 0;
+
+ if (*fmt == '\0')
+ return FMT_UNKNOWN;
+
+ /* Note: ->family is not yet ready. */
+ if (g_ascii_strcasecmp (sf->format, cell_format_general[0]) == 0)
+ return FMT_GENERAL;
+
+ if (fmt[0] == '@' && fmt[1] == '[')
+ return FMT_MARKUP;
+
+ /* Can we parse it ? */
+ if ((res = cell_format_is_number (fmt, info)) != FMT_UNKNOWN)
+ return res;
+
+ if (cell_format_is_fraction (fmt, info))
+ return FMT_FRACTION;
+
+ /* Is it in the lists */
+ for (i = 0; cell_formats[i] != NULL ; ++i) {
+ int j = 0;
+ char const * const * elem = cell_formats[i];
+ for (; elem[j] ; ++j)
+ if (g_ascii_strcasecmp (_(elem[j]), fmt) == 0) {
+ info->list_element = j;
+ switch (i) {
+ case FMT_DATE:
+ info->date_has_days =
+ (NULL != g_utf8_strchr (elem[j],-1,'d'));
+ info->date_has_months =
+ (NULL != g_utf8_strchr (elem[j],-1,'m'));
+ break;
+ default:
+ break;
+ }
+
+ return i;
+ }
+ }
+ return FMT_UNKNOWN;
+}
--- /dev/null
+++ lib/goffice/split/mathfunc.h
@@ -0,0 +1,187 @@
+#ifndef GNUMERIC_MATHFUNC_H
+#define GNUMERIC_MATHFUNC_H
+
+#include "numbers.h"
+#include <math.h>
+#include <glib.h>
+
+#ifdef qgamma
+/* It was reported that mips-sgi-irix6.5 has a weird and conflicting define
+ for qgamma. See bug 1689. */
+#warning "Your <math.h> is somewhat broken; we'll work around that."
+#undef qgamma
+#endif
+
+#define M_PIgnum GNM_const(3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117)
+#define M_PI_2gnum (M_PIgnum / 2)
+/* The following are very good given a good compiler. */
+#define M_LN2gnum (loggnum (2))
+#define M_LN10gnum (loggnum (10))
+#define M_SQRT2gnum (sqrtgnum (2))
+
+extern gnm_float gnm_nan;
+extern gnm_float gnm_pinf;
+extern gnm_float gnm_ninf;
+
+/* ------------------------------------------------------------------------- */
+
+gnm_float gnumeric_add_epsilon (gnm_float x);
+gnm_float gnumeric_sub_epsilon (gnm_float x);
+gnm_float gnumeric_fake_floor (gnm_float x);
+gnm_float gnumeric_fake_ceil (gnm_float x);
+gnm_float gnumeric_fake_round (gnm_float x);
+gnm_float gnumeric_fake_trunc (gnm_float x);
+
+/* ------------------------------------------------------------------------- */
+
+gnm_float log1pmx (gnm_float x);
+gnm_float swap_log_tail (gnm_float lp);
+gnm_float lgamma1p (gnm_float a);
+gnm_float pow1p (gnm_float x, gnm_float y);
+gnm_float pow1pm1 (gnm_float x, gnm_float y);
+gnm_float gnm_trunc (gnm_float x);
+gnm_float logfbit (gnm_float x);
+
+gnm_float beta (gnm_float a, gnm_float b);
+gnm_float lbeta3 (gnm_float a, gnm_float b, int *sign);
+
+gnm_float bessel_i (gnm_float x, gnm_float alpha, gnm_float expo);
+gnm_float bessel_k (gnm_float x, gnm_float alpha, gnm_float expo);
+
+/* "d": density. */
+/* "p": distribution function. */
+/* "q": inverse distribution function. */
+
+/* The normal distribution. */
+gnm_float dnorm (gnm_float x, gnm_float mu, gnm_float sigma, gboolean give_log);
+gnm_float pnorm (gnm_float x, gnm_float mu, gnm_float sigma, gboolean lower_tail, gboolean log_p);
+gnm_float qnorm (gnm_float p, gnm_float mu, gnm_float sigma, gboolean lower_tail, gboolean log_p);
+
+/* The log-normal distribution. */
+gnm_float plnorm (gnm_float x, gnm_float logmean, gnm_float logsd, gboolean lower_tail, gboolean log_p);
+gnm_float qlnorm (gnm_float x, gnm_float logmean, gnm_float logsd, gboolean lower_tail, gboolean log_p);
+
+/* The gamma distribution. */
+gnm_float dgamma (gnm_float x, gnm_float shape, gnm_float scale, gboolean give_log);
+gnm_float pgamma (gnm_float x, gnm_float p, gnm_float scale, gboolean lower_tail, gboolean log_p);
+gnm_float qgamma (gnm_float p, gnm_float alpha, gnm_float scale, gboolean lower_tail, gboolean log_p);
+
+/* The beta distribution. */
+gnm_float dbeta (gnm_float x, gnm_float a, gnm_float b, gboolean give_log);
+gnm_float pbeta (gnm_float x, gnm_float pin, gnm_float qin, gboolean lower_tail, gboolean log_p);
+gnm_float qbeta (gnm_float alpha, gnm_float p, gnm_float q, gboolean lower_tail, gboolean log_p);
+
+/* The t distribution. */
+gnm_float dt (gnm_float x, gnm_float n, gboolean give_log);
+gnm_float pt (gnm_float x, gnm_float n, gboolean lower_tail, gboolean log_p);
+gnm_float qt (gnm_float p, gnm_float ndf, gboolean lower_tail, gboolean log_p);
+
+/* The F distribution. */
+gnm_float pf (gnm_float x, gnm_float n1, gnm_float n2, gboolean lower_tail, gboolean log_p);
+gnm_float qf (gnm_float x, gnm_float n1, gnm_float n2, gboolean lower_tail, gboolean log_p);
+
+/* The chi-squared distribution. */
+gnm_float pchisq (gnm_float x, gnm_float df, gboolean lower_tail, gboolean log_p);
+gnm_float qchisq (gnm_float p, gnm_float df, gboolean lower_tail, gboolean log_p);
+
+/* The Weibull distribution. */
+gnm_float dweibull (gnm_float x, gnm_float shape, gnm_float scale, gboolean give_log);
+gnm_float pweibull (gnm_float x, gnm_float shape, gnm_float scale, gboolean lower_tail, gboolean log_p);
+
+/* The Poisson distribution. */
+gnm_float dpois (gnm_float x, gnm_float lambda, gboolean give_log);
+gnm_float ppois (gnm_float x, gnm_float lambda, gboolean lower_tail, gboolean log_p);
+gnm_float qpois (gnm_float p, gnm_float lambda, gboolean lower_tail, gboolean log_p);
+
+/* The exponential distribution. */
+gnm_float dexp (gnm_float x, gnm_float scale, gboolean give_log);
+gnm_float pexp (gnm_float x, gnm_float scale, gboolean lower_tail, gboolean log_p);
+
+/* Binomial distribution. */
+gnm_float dbinom (gnm_float x, gnm_float n, gnm_float p, gboolean give_log);
+gnm_float pbinom (gnm_float x, gnm_float n, gnm_float p, gboolean lower_tail, gboolean log_p);
+gnm_float qbinom (gnm_float x, gnm_float n, gnm_float p, gboolean lower_tail, gboolean log_p);
+
+/* Negative binomial distribution. */
+gnm_float dnbinom (gnm_float x, gnm_float n, gnm_float p, gboolean give_log);
+gnm_float pnbinom (gnm_float x, gnm_float n, gnm_float p, gboolean lower_tail, gboolean log_p);
+gnm_float qnbinom (gnm_float p, gnm_float n, gnm_float pr, gboolean lower_tail, gboolean log_p);
+
+/* Hyper-geometrical distribution. */
+gnm_float dhyper (gnm_float x, gnm_float r, gnm_float b, gnm_float n, gboolean give_log);
+gnm_float phyper (gnm_float x, gnm_float NR, gnm_float NB, gnm_float n, gboolean lower_tail, gboolean log_p);
+
+/* Geometrical distribution. */
+gnm_float dgeom (gnm_float x, gnm_float p, gboolean give_log);
+gnm_float pgeom (gnm_float x, gnm_float p, gboolean lower_tail, gboolean log_p);
+
+/* Cauchy distribution. */
+gnm_float dcauchy (gnm_float x, gnm_float location, gnm_float scale, gboolean give_log);
+gnm_float pcauchy (gnm_float x, gnm_float location, gnm_float scale, gboolean lower_tail, gboolean log_p);
+
+/* Random number generation. */
+gnm_float random_01 (void);
+gnm_float random_poisson (gnm_float lambda);
+gnm_float random_binomial (gnm_float p, int trials);
+gnm_float random_negbinom (gnm_float p, int f);
+gnm_float random_exponential (gnm_float b);
+gnm_float random_bernoulli (gnm_float p);
+gnm_float random_normal (void);
+gnm_float random_cauchy (gnm_float a);
+gnm_float random_lognormal (gnm_float zeta, gnm_float sigma);
+gnm_float random_weibull (gnm_float a, gnm_float b);
+gnm_float random_laplace (gnm_float a);
+gnm_float random_rayleigh (gnm_float sigma);
+gnm_float random_rayleigh_tail (gnm_float a, gnm_float sigma);
+gnm_float random_gamma (gnm_float a, gnm_float b);
+gnm_float random_pareto (gnm_float a, gnm_float b);
+gnm_float random_fdist (gnm_float nu1, gnm_float nu2);
+gnm_float random_beta (gnm_float a, gnm_float b);
+gnm_float random_logistic (gnm_float a);
+gnm_float random_geometric (gnm_float p);
+gnm_float random_hypergeometric (unsigned int n1, unsigned int n2,
+ unsigned int t);
+gnm_float random_logarithmic (gnm_float p);
+gnm_float random_chisq (gnm_float nu);
+gnm_float random_tdist (gnm_float nu);
+gnm_float random_gumbel1 (gnm_float a, gnm_float b);
+gnm_float random_gumbel2 (gnm_float a, gnm_float b);
+gnm_float random_levy (gnm_float c, gnm_float alpha);
+gnm_float random_levy_skew (gnm_float c, gnm_float alpha,
+ gnm_float beta);
+gnm_float random_exppow (gnm_float a, gnm_float b);
+gnm_float random_landau (void);
+gnm_float random_gaussian_tail (gnm_float a, gnm_float sigma);
+
+/* The probability density functions. */
+gnm_float random_exppow_pdf (gnm_float x, gnm_float a, gnm_float b);
+gnm_float random_laplace_pdf (gnm_float x, gnm_float a);
+
+/* ------------------------------------------------------------------------- */
+
+/* Matrix functions. */
+void mmult (gnm_float *A, gnm_float *B, int cols_a, int rows_a, int cols_b,
+ gnm_float *product);
+
+/* ------------------------------------------------------------------------- */
+
+/* Misc. */
+gnm_float gpow10 (int n);
+gnm_float gpow2 (int n);
+int gcd (int a, int b);
+gnm_float combin (int n, int k);
+gnm_float permut (int n, int k);
+gnm_float fact (int n);
+
+/* ------------------------------------------------------------------------- */
+
+void continued_fraction (gnm_float val, int max_denom, int *res_num, int *res_denom);
+void stern_brocot (float val, int max_denom, int *res_num, int *res_denom);
+
+/* ------------------------------------------------------------------------- */
+
+void mathfunc_init (void);
+
+/* ------------------------------------------------------------------------- */
+
+#endif
--- /dev/null
+++ lib/goffice/split/datetime.c
@@ -0,0 +1,689 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * datetime.c: Date and time routines grabbed from elsewhere.
+ *
+ * Authors:
+ * Miguel de Icaza (miguel at gnu.org)
+ * Morten Welinder <terra at gnome.org>
+ * Jukka-Pekka Iivonen <iivonen at iki.fi>
+ * Andreas J. Guelzow <aguelzow at taliesin.ca>
+ */
+
+#include <config.h>
+#include "gnumeric.h"
+#include "datetime.h"
+
+#include "number-match.h"
+#include "value.h"
+#include <math.h>
+
+#define SECS_PER_DAY (24 * 60 * 60)
+#define HALF_SEC (0.5 / SECS_PER_DAY)
+
+/* ------------------------------------------------------------------------- */
+
+/* One less that the Julian day number of 19000101. */
+static int date_origin = 0;
+/* Julian day number of 19040101. */
+static int date_origin_1904 = 0;
+
+/*
+ * The serial number of 19000228. Excel allocates a serial number for
+ * the non-existing date 19000229.
+ */
+static int const date_serial_19000228 = 59;
+
+static void
+date_init (void)
+{
+ /* Day 1 means 1st of January of 1900 */
+ GDate* date = g_date_new_dmy (1, 1, 1900);
+ date_origin = g_date_get_julian (date) - 1;
+
+ /* Day 0 means 1st of January of 1904 */
+ g_date_set_dmy (date, 1, 1, 1904);
+ date_origin_1904 = g_date_get_julian (date);
+ g_date_free (date);
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+datetime_g_to_serial (GDate const *date, GnmDateConventions const *conv)
+{
+ int day;
+
+ if (!date_origin)
+ date_init ();
+
+ if (conv && conv->use_1904)
+ return g_date_get_julian (date) - date_origin_1904;
+ day = g_date_get_julian (date) - date_origin;
+ return day + (day > date_serial_19000228);
+}
+
+/* ------------------------------------------------------------------------- */
+
+void
+datetime_serial_to_g (GDate *res, int serial, GnmDateConventions const *conv)
+{
+ if (!date_origin)
+ date_init ();
+
+ g_date_clear (res, 1);
+ if (conv && conv->use_1904)
+ g_date_set_julian (res, serial + date_origin_1904);
+ else if (serial > date_serial_19000228) {
+ if (serial == date_serial_19000228 + 1)
+ g_warning ("Request for date 19000229.");
+ g_date_set_julian (res, serial + date_origin - 1);
+ } else
+ g_date_set_julian (res, serial + date_origin);
+}
+
+/* ------------------------------------------------------------------------- */
+
+gnm_float
+datetime_value_to_serial_raw (GnmValue const *v, GnmDateConventions const *conv)
+{
+ gnm_float serial;
+
+ if (VALUE_IS_NUMBER (v))
+ serial = value_get_as_float (v);
+ else {
+ char const *str = value_peek_string (v);
+ GnmValue *conversion = format_match (str, NULL, conv);
+
+ if (conversion) {
+ if (VALUE_IS_NUMBER (conversion))
+ serial = value_get_as_float (conversion);
+ else
+ serial = 0;
+ value_release (conversion);
+ } else
+ serial = 0;
+ }
+ return serial;
+}
+
+/* ------------------------------------------------------------------------- */
+
+gnm_float
+datetime_timet_to_serial_raw (time_t t, GnmDateConventions const *conv)
+{
+ struct tm *tm = localtime (&t);
+ int secs;
+ GDate date;
+
+ g_date_clear (&date, 1);
+ g_date_set_time (&date, t);
+ secs = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
+ return datetime_g_to_serial (&date, conv) +
+ secs / (gnm_float)SECS_PER_DAY;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+datetime_serial_raw_to_serial (gnm_float raw)
+{
+ return (int) floorgnum (raw + HALF_SEC);
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+datetime_value_to_serial (GnmValue const *v, GnmDateConventions const *conv)
+{
+ return datetime_serial_raw_to_serial (
+ datetime_value_to_serial_raw (v, conv));
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+datetime_timet_to_serial (time_t t, GnmDateConventions const *conv)
+{
+ return datetime_serial_raw_to_serial (datetime_timet_to_serial_raw (t, conv));
+}
+
+/* ------------------------------------------------------------------------- */
+
+time_t
+datetime_serial_to_timet (int serial, GnmDateConventions const *conv)
+{
+ GDate gd;
+ struct tm tm;
+
+ datetime_serial_to_g (&gd, serial, conv);
+ g_date_to_struct_tm (&gd, &tm);
+
+ return mktime (&tm);
+}
+
+/* ------------------------------------------------------------------------- */
+
+gboolean
+datetime_value_to_g (GDate *res, GnmValue const *v, GnmDateConventions const *conv)
+{
+ int serial = datetime_value_to_serial (v, conv);
+ if (serial == 0)
+ return FALSE;
+ datetime_serial_to_g (res, serial, conv);
+ return TRUE;
+}
+
+/* ------------------------------------------------------------------------- */
+/* This is time-only assuming a 24h day. It probably loses completely on */
+/* days with summer time ("daylight savings") changes. */
+
+int
+datetime_serial_raw_to_seconds (gnm_float raw)
+{
+ raw += HALF_SEC;
+ return (raw - floorgnum (raw)) * SECS_PER_DAY;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+datetime_value_to_seconds (GnmValue const *v)
+{
+ /* we just want the seconds, actual date does not matter. So we can ignore
+ * the date convention (1900 vs 1904) */
+ return datetime_serial_raw_to_seconds (datetime_value_to_serial_raw (v, NULL));
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+datetime_timet_to_seconds (time_t t)
+{
+ /* we just want the seconds, actual date does not matter. So we can ignore
+ * the date convention (1900 vs 1904) */
+ return datetime_serial_raw_to_seconds (datetime_timet_to_serial_raw (t, NULL));
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+datetime_g_days_between (GDate const* date1, GDate const *date2)
+{
+ g_assert (g_date_valid (date1));
+ g_assert (g_date_valid (date2));
+
+ return (int) (g_date_get_julian (date2) - g_date_get_julian (date1));
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+datetime_g_months_between (GDate const *date1, GDate const *date2)
+{
+ g_assert (g_date_valid (date1));
+ g_assert (g_date_valid (date2));
+
+ /* find the difference according to the month and year ordinals,
+ but discount the last month if there are not enough days. */
+ return 12 * (g_date_get_year (date2) - g_date_get_year (date1))
+ + g_date_get_month (date2) - g_date_get_month (date1)
+ - (g_date_get_day (date2) >= g_date_get_day (date1) ? 0 : 1);
+}
+
+/* ------------------------------------------------------------------------- */
+
+int
+datetime_g_years_between (GDate const *date1, GDate const *date2)
+{
+ int months;
+
+ g_assert (g_date_valid (date1));
+ g_assert (g_date_valid (date2));
+
+ months = datetime_g_months_between (date1, date2);
+ return months > 0 ? months / 12 : -(-months / 12);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * datetime_isoweeknum (GDate *date)
+ * @date date
+ *
+ * Returns the ISO 8601 week number.
+ */
+static int
+datetime_isoweeknum (GDate const *date)
+{
+ int year;
+ int week;
+ int wday, jan1wday, nextjan1wday;
+ GDate jan1date, nextjan1date;
+
+ g_assert (g_date_valid (date));
+
+ year = g_date_get_year (date);
+ wday = g_date_get_weekday (date);
+ g_date_set_dmy (&jan1date, 1, 1, year);
+ jan1wday = g_date_get_weekday (&jan1date);
+
+ week = g_date_get_monday_week_of_year (date);
+
+ /* Does date belong to last week of previous year? */
+ if ((week == 0) && (jan1wday > G_DATE_THURSDAY)) {
+ GDate tmpdate;
+ g_date_set_dmy (&tmpdate, 31, 12, year - 1);
+ return datetime_isoweeknum (&tmpdate);
+ }
+
+ if ((jan1wday <= G_DATE_THURSDAY) &&
+ (jan1wday > G_DATE_MONDAY))
+ week++;
+
+ if (week == 53) {
+ g_date_set_dmy (&nextjan1date, 1, 1, year + 1);
+ nextjan1wday = g_date_get_weekday (&nextjan1date);
+ if (nextjan1wday <= G_DATE_THURSDAY)
+ week = 1;
+ }
+
+ return week;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * datetime_weeknum (GDate *date, int method)
+ * @date date
+ * @method week numbering method
+ *
+ * Returns week number according to the given method.
+ * 1: Week starts on Sunday. Days before first Sunday are in week 0.
+ * 2: Week starts on Monday. Days before first Monday are in week 0.
+ * 150: ISO 8601 week number. See datetime_isoweeknum.
+ */
+int
+datetime_weeknum (GDate const *date, int method)
+{
+ int res;
+
+ g_assert (g_date_valid (date));
+ g_assert (method == WEEKNUM_METHOD_SUNDAY ||
+ method == WEEKNUM_METHOD_MONDAY ||
+ method == WEEKNUM_METHOD_ISO);
+
+ switch (method) {
+ case WEEKNUM_METHOD_SUNDAY:
+ res = g_date_get_sunday_week_of_year (date); break;
+ case WEEKNUM_METHOD_MONDAY:
+ res = g_date_get_monday_week_of_year (date); break;
+ case WEEKNUM_METHOD_ISO:
+ res = datetime_isoweeknum (date); break;
+ default: res = -1;
+ }
+
+ return res;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static gint32
+days_between_BASIS_MSRB_30_360 (GDate const *from, GDate const *to)
+{
+ int y1, m1, d1, y2, m2, d2;
+
+ y1 = g_date_get_year (from);
+ m1 = g_date_get_month (from);
+ d1 = g_date_get_day (from);
+ y2 = g_date_get_year (to);
+ m2 = g_date_get_month (to);
+ d2 = g_date_get_day (to);
+
+ if (m1 == 2 && g_date_is_last_of_month (from))
+ d1 = 30;
+ if (d2 == 31 && d1 >= 30)
+ d2 = 30;
+ if (d1 == 31)
+ d1 = 30;
+
+ return (y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1);
+}
+
+static gint32
+days_between_BASIS_MSRB_30_360_SYM (GDate const *from, GDate const *to)
+{
+ int y1, m1, d1, y2, m2, d2;
+
+ y1 = g_date_get_year (from);
+ m1 = g_date_get_month (from);
+ d1 = g_date_get_day (from);
+ y2 = g_date_get_year (to);
+ m2 = g_date_get_month (to);
+ d2 = g_date_get_day (to);
+
+ if (m1 == 2 && g_date_is_last_of_month (from))
+ d1 = 30;
+ if (m2 == 2 && g_date_is_last_of_month (to))
+ d2 = 30;
+ if (d2 == 31 && d1 >= 30)
+ d2 = 30;
+ if (d1 == 31)
+ d1 = 30;
+
+ return (y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1);
+}
+
+static gint32
+days_between_BASIS_30E_360 (GDate const *from, GDate const *to)
+{
+ int y1, m1, d1, y2, m2, d2;
+
+ y1 = g_date_get_year (from);
+ m1 = g_date_get_month (from);
+ d1 = g_date_get_day (from);
+ y2 = g_date_get_year (to);
+ m2 = g_date_get_month (to);
+ d2 = g_date_get_day (to);
+
+ if (d1 == 31)
+ d1 = 30;
+ if (d2 == 31)
+ d2 = 30;
+
+ return (y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1);
+}
+
+static gint32
+days_between_BASIS_30Ep_360 (GDate const *from, GDate const *to)
+{
+ int y1, m1, d1, y2, m2, d2;
+
+ y1 = g_date_get_year (from);
+ m1 = g_date_get_month (from);
+ d1 = g_date_get_day (from);
+ y2 = g_date_get_year (to);
+ m2 = g_date_get_month (to);
+ d2 = g_date_get_day (to);
+
+ if (d1 == 31)
+ d1 = 30;
+ if (d2 == 31) {
+ d2 = 1;
+ m2++;
+ /* No need to check for m2 == 13 since 12*30 == 360 */
+ }
+
+ return (y2 - y1) * 360 + (m2 - m1) * 30 + (d2 - d1);
+}
+
+/*
+ * days_between_basis
+ *
+ * @from : GDate *
+ * @to : GDate *
+ * @basis : basis_t
+ * see datetime.h and doc/fn-financial-basis.txt for details
+ *
+ * @in_order : dates are considered in order
+ *
+ * returns : Number of days strictly between from and to +1
+ *
+ */
+
+gint32
+days_between_basis (GDate const *from, GDate const *to, basis_t basis)
+{
+ int sign = 1;
+
+ if (g_date_compare (from, to) == 1) {
+ GDate const *tmp = from;
+ from = to;
+ to = tmp;
+ sign = -1;
+ }
+
+ switch (basis) {
+ case BASIS_ACT_ACT:
+ case BASIS_ACT_360:
+ case BASIS_ACT_365:
+ return sign * (g_date_get_julian (to) - g_date_get_julian (from));
+ case BASIS_30E_360:
+ return sign * days_between_BASIS_30E_360 (from, to);
+ case BASIS_30Ep_360:
+ return sign * days_between_BASIS_30Ep_360 (from, to);
+ case BASIS_MSRB_30_360_SYM:
+ return sign * days_between_BASIS_MSRB_30_360_SYM (from, to);
+ case BASIS_MSRB_30_360:
+ default:
+ return sign * days_between_BASIS_MSRB_30_360 (from, to);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * coup_cd
+ *
+ * @res :
+ * @settlement: GDate *
+ * @maturity : GDate * must follow settlement strictly
+ * @freq : int divides 12 evenly
+ * @eom : gboolean whether to do special end of month
+ * handling
+ * @next : gboolean whether next or previous date
+ *
+ * returns : GDate * next or previous coupon date
+ *
+ * this function does not depend on the basis of counting!
+ */
+void
+coup_cd (GDate *result, GDate const *settlement, GDate const *maturity,
+ int freq, gboolean eom, gboolean next)
+{
+ int months, periods;
+ gboolean is_eom_special;
+
+ is_eom_special = eom && g_date_is_last_of_month (maturity);
+
+ g_date_clear (result, 1);
+
+ months = 12 / freq;
+ periods = (g_date_get_year(maturity) - g_date_get_year (settlement));
+ if (periods > 0)
+ periods = (periods - 1) * freq;
+
+ do {
+ g_date_set_julian (result, g_date_get_julian (maturity));
+ periods++;
+ g_date_subtract_months (result, periods * months);
+ if (is_eom_special) {
+ int ndays = g_date_get_days_in_month
+ (g_date_get_month (result),
+ g_date_get_year (result));
+ g_date_set_day (result, ndays);
+ }
+ } while (g_date_compare (settlement, result) < 0 );
+
+ if (next) {
+ g_date_set_julian (result, g_date_get_julian (maturity));
+ periods--;
+ g_date_subtract_months (result, periods * months);
+ if (is_eom_special) {
+ int ndays = g_date_get_days_in_month
+ (g_date_get_month (result),
+ g_date_get_year (result));
+ g_date_set_day (result, ndays);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+/*
+ * Returns the number of days in the coupon period of the settlement date.
+ * Currently, returns negative numbers if the branch is not implemented.
+ */
+gnm_float
+coupdays (GDate const *settlement, GDate const *maturity,
+ GnmCouponConvention const *conv)
+{
+ GDate prev, next;
+
+ switch (conv->basis) {
+ case BASIS_MSRB_30_360:
+ case BASIS_ACT_360:
+ case BASIS_30E_360:
+ case BASIS_30Ep_360:
+ return 360 / conv->freq;
+ case BASIS_ACT_365:
+ return 365.0 / conv->freq;
+ case BASIS_ACT_ACT:
+ default:
+ coup_cd (&next, settlement, maturity, conv->freq, conv->eom, TRUE);
+ coup_cd (&prev, settlement, maturity, conv->freq, conv->eom, FALSE);
+ return days_between_basis (&prev, &next, BASIS_ACT_ACT);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+/*
+ * Returns the number of days from the beginning of the coupon period to
+ * the settlement date.
+ */
+gnm_float
+coupdaybs (GDate const *settlement, GDate const *maturity,
+ GnmCouponConvention const *conv)
+{
+ GDate prev_coupon;
+ coup_cd (&prev_coupon, settlement, maturity, conv->freq, conv->eom, FALSE);
+ return days_between_basis (&prev_coupon, settlement, conv->basis);
+}
+
+/**
+ * coupdaysnc :
+ * @settlement :
+ * @maturity :
+ * @freq :
+ * @basis :
+ * @eom :
+ *
+ * Returns the number of days from the settlement date to the next
+ * coupon date.
+ **/
+gnm_float
+coupdaysnc (GDate const *settlement, GDate const *maturity,
+ GnmCouponConvention const *conv)
+{
+ GDate next_coupon;
+ coup_cd (&next_coupon, settlement, maturity, conv->freq, conv->eom, TRUE);
+ return days_between_basis (settlement, &next_coupon, conv->basis);
+}
+
+int
+gnm_date_convention_base (GnmDateConventions const *conv)
+{
+ g_return_val_if_fail (conv != NULL, 1900);
+ return conv->use_1904 ? 1904 : 1900;
+}
+
+/*
+ * Returns the number of days in the year for the given date accoring to
+ * the day counting system specified by 'basis' argument. Basis may have
+ * one of the following values:
+ *
+ * 0 for US 30/360 (days in a month/days in a year)
+ * 1 for actual days/actual days
+ * 2 for actual days/360
+ * 3 for actual days/365
+ * 4 for European 30/360
+ *
+ * This function returns 360 for basis 0, 2, and 4, it returns value
+ * 365 for basis 3, and value 365 or 366 for basis 1 accoring to the
+ * year of the given date (366 is returned if the date is in a leap
+ * year).
+ */
+int
+annual_year_basis (GnmValue const *value_date, basis_t basis,
+ GnmDateConventions const *date_conv)
+{
+ GDate date;
+
+ switch (basis) {
+ case BASIS_MSRB_30_360:
+ return 360;
+ case BASIS_ACT_ACT:
+ if (!datetime_value_to_g (&date, value_date, date_conv))
+ return -1;
+ return g_date_is_leap_year (g_date_get_year (&date))
+ ? 366 : 365;
+ case BASIS_ACT_360:
+ return 360;
+ case BASIS_ACT_365:
+ return 365;
+ case BASIS_30E_360:
+ return 360;
+ default:
+ return -1;
+ }
+}
+
+
+gnm_float
+yearfrac (GDate const *from, GDate const *to, basis_t basis)
+{
+ int days = days_between_basis (from, to, basis);
+ gnm_float peryear;
+
+ if (days < 0) {
+ const GDate *tmp;
+ days = -days;
+ tmp = from; from = to; to = tmp;
+ }
+
+ switch (basis) {
+ case BASIS_ACT_ACT: {
+ int y1 = g_date_get_year (from);
+ int y2 = g_date_get_year (to);
+ GDate d1, d2;
+ int feb29s, years;
+
+ d1 = *from;
+ g_date_add_years (&d1, 1);
+ if (g_date_compare (to, &d1) > 0) {
+ /* More than one year. */
+ years = y2 + 1 - y1;
+
+ g_date_clear (&d1, 1);
+ g_date_set_dmy (&d1, 1, 1, y1);
+
+ g_date_clear (&d2, 1);
+ g_date_set_dmy (&d2, 1, 1, y2 + 1);
+
+ feb29s = g_date_get_julian (&d2) - g_date_get_julian (&d1) -
+ 365 * (y2 + 1 - y1);
+ } else {
+ /* Less than one year. */
+ years = 1;
+
+ if ((g_date_is_leap_year (y1) && g_date_get_month (from) < 3) ||
+ (g_date_is_leap_year (y2) &&
+ (g_date_get_month (to) * 0x100 + g_date_get_day (to) >= 2 * 0x100 + 29)))
+ feb29s = 1;
+ else
+ feb29s = 0;
+ }
+
+ peryear = 365 + (gnm_float)feb29s / years;
+
+ break;
+ }
+
+ default:
+ peryear = annual_year_basis (NULL, basis, NULL);
+ }
+
+ return days / peryear;
+}
--- /dev/null
+++ lib/goffice/split/gutils.h
@@ -0,0 +1,100 @@
+#ifndef GNUMERIC_UTILS_H
+#define GNUMERIC_UTILS_H
+
+#include "gnumeric.h"
+#include "numbers.h"
+#include <sys/types.h>
+
+/* Misc convenience routines that would be nice to have in glib */
+
+typedef gpointer (*GnmMapFunc) (gpointer value);
+
+GSList *gnm_hash_keys (GHashTable *hash);
+GSList *gnm_hash_values (GHashTable *hash);
+
+GSList *gnm_slist_map (GSList const *list, GnmMapFunc map_func);
+GSList *gnm_slist_create (gpointer item1, ...);
+void gnm_slist_free_custom (GSList *list, GFreeFunc free_func);
+#define gnm_string_slist_copy(list) gnm_slist_map (list, (GnmMapFunc) g_strdup)
+GSList *gnm_strsplit_to_slist (char const *str, char const *delimiter);
+#define GNM_SLIST_FOREACH(list,valtype,val,stmnt) \
+G_STMT_START { \
+ GSList const *gnm_l; \
+ for (gnm_l = (list); gnm_l != NULL; gnm_l = gnm_l->next) { \
+ valtype *val = gnm_l->data; \
+ stmnt \
+ ; \
+ } \
+} G_STMT_END
+#define GNM_SLIST_PREPEND(list,item) \
+ (list = g_slist_prepend (list, item))
+#define GNM_SLIST_APPEND(list,item) \
+ (list = g_slist_append (list, item))
+#define GNM_SLIST_REMOVE(list,item) \
+ (list = g_slist_remove (list, item))
+#define GNM_SLIST_CONCAT(list_a,list_b) \
+ (list_a = g_slist_concat (list_a, list_b))
+#define GNM_SLIST_REVERSE(list) \
+ (list = g_slist_reverse (list))
+#define GNM_SLIST_SORT(list,cmp_func) \
+ (list = g_slist_sort (list, cmp_func))
+
+void gnm_ptr_array_insert (GPtrArray *array, gpointer value, int index);
+gint gnm_list_index_custom (GList *list, gpointer data, GCompareFunc cmp_func);
+void gnm_list_free_custom (GList *list, GFreeFunc free_func);
+#define GNM_LIST_FOREACH(list,valtype,val,stmnt) \
+G_STMT_START { \
+ GList *gnm_l; \
+ for (gnm_l = (list); gnm_l != NULL; gnm_l = gnm_l->next) { \
+ valtype *val = gnm_l->data; \
+ stmnt \
+ ; \
+ } \
+} G_STMT_END
+#define GNM_LIST_PREPEND(list,item) \
+ (list = g_list_prepend (list, item))
+#define GNM_LIST_APPEND(list,item) \
+ (list = g_list_append (list, item))
+#define GNM_LIST_REMOVE(list,item) \
+ (list = g_list_remove (list, item))
+#define GNM_LIST_CONCAT(list_a,list_b) \
+ (list_a = g_list_concat (list_a, list_b))
+#define GNM_LIST_REVERSE(list) \
+ (list = g_list_reverse (list))
+#define GNM_LIST_SORT(list,cmp_func) \
+ (list = g_list_sort (list, cmp_func))
+
+int gnm_str_compare (void const *x, void const *y);
+guint gnm_ascii_strcase_hash (gconstpointer v);
+gint gnm_ascii_strcase_equal (gconstpointer v, gconstpointer v2);
+gint gnm_utf8_collate_casefold (char const *a, char const *b);
+char *gnm_utf8_strcapital (char const *p, ssize_t len);
+void gnm_strescape (GString *target, char const *str);
+char const *gnm_strunescape (GString *target, char const *str);
+void gnm_string_append_gstring (GString *target, const GString *src);
+char const *gnm_guess_encoding (char const *raw, size_t len,
+ char const *user_guess,
+ char **utf8_str);
+
+char const *gnm_get_real_name (void);
+void gnm_destroy_password (char *passwd);
+
+/* System and user paths */
+char *gnm_sys_lib_dir (char const *subdir);
+char *gnm_sys_data_dir (char const *subdir);
+char *gnm_sys_glade_dir (void);
+char *gnm_sys_plugin_dir (void);
+char *gnm_usr_dir (char const *subdir);
+char *gnm_usr_plugin_dir (void);
+
+GnmMemChunk *gnm_mem_chunk_new (char const *, size_t, size_t);
+void gnm_mem_chunk_destroy (GnmMemChunk *, gboolean);
+gpointer gnm_mem_chunk_alloc (GnmMemChunk *);
+gpointer gnm_mem_chunk_alloc0 (GnmMemChunk *);
+void gnm_mem_chunk_free (GnmMemChunk *, gpointer);
+void gnm_mem_chunk_foreach_leak (GnmMemChunk *, GFunc, gpointer);
+
+void gnm_time_counter_push (void);
+gdouble gnm_time_counter_pop (void);
+
+#endif /* GNUMERIC_UTILS_H */
--- /dev/null
+++ lib/goffice/split/application.h
@@ -0,0 +1,101 @@
+#ifndef GNUMERIC_APPLICATION_H
+#define GNUMERIC_APPLICATION_H
+
+#include "gnumeric.h"
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#define GNM_APP_TYPE (gnm_app_get_type ())
+typedef gboolean (*GnmWbIterFunc) (Workbook *, gpointer data);
+
+GType gnm_app_get_type (void);
+GObject *gnm_app_get_app (void);
+
+/* List of active workbooks */
+void gnm_app_workbook_list_add (Workbook *wb);
+void gnm_app_workbook_list_remove (Workbook *wb);
+GList * gnm_app_workbook_list (void);
+Workbook *gnm_app_workbook_get_by_name (char const *name);
+Workbook *gnm_app_workbook_get_by_index (int i);
+gboolean gnm_app_workbook_foreach (GnmWbIterFunc func, gpointer data);
+
+GSList const*gnm_app_history_get_list (gboolean force_reload);
+void gnm_app_history_add (char const *filename);
+
+/* Prefs */
+gboolean gnm_app_use_auto_complete (void);
+gboolean gnm_app_use_transition_keys (void);
+void gnm_app_set_transition_keys (gboolean);
+gboolean gnm_app_live_scrolling (void);
+int gnm_app_auto_expr_recalc_lag (void);
+
+/* stuff that should move */
+GdkPixbuf *gnm_app_get_pixbuf (char const *name);
+void gnm_app_release_pref_dialog (void);
+gpointer gnm_app_get_pref_dialog (void);
+void gnm_app_set_pref_dialog (gpointer dialog);
+
+double gnm_app_display_dpi_get (gboolean horizontal);
+double gnm_app_dpi_to_pixels (void);
+
+/* Clipboard */
+void gnm_app_clipboard_clear (gboolean drop_selection);
+void gnm_app_clipboard_cut_copy (WorkbookControl *wbc, gboolean is_cut,
+ SheetView *sv, GnmRange const *area,
+ gboolean animate_range);
+void gnm_app_clipboard_cut_copy_obj (WorkbookControl *wbc, gboolean is_cut,
+ SheetView *sv, GSList *objects);
+void gnm_app_clipboard_unant (void);
+gboolean gnm_app_clipboard_is_empty (void);
+gboolean gnm_app_clipboard_is_cut (void);
+Sheet *gnm_app_clipboard_sheet_get (void);
+SheetView *gnm_app_clipboard_sheet_view_get (void);
+GnmCellRegion *gnm_app_clipboard_contents_get (void);
+GnmRange const *gnm_app_clipboard_area_get (void);
+
+/**********************************************************************
+ * Temporary home for extra actions until we rework this in 1.5
+ * with libgoffice
+ **/
+
+typedef struct _GnmAction GnmAction;
+typedef void (*GnmActionHandler) (GnmAction const *action, WorkbookControl *wbc,
+ gpointer user_data);
+struct _GnmAction {
+ char *id; /* id of the function that will handle this */
+ char *label; /* untranslated, gettext domain will be passed later */
+ char *icon_name; /* optionally NULL */
+ /* simplistic for now :
+ * is the action always available (File -> New) or only available
+ * when we are not editing (Cell -> Format)
+ * Later on this needs to be more comprehensive with things like
+ * per-sheetobject flags
+ **/
+ gboolean always_available;
+
+ GnmActionHandler handler;
+};
+typedef struct {
+ GSList *actions;
+ char *layout;
+ char const *domain;
+ gpointer user_data;
+} GnmAppExtraUI;
+
+GnmAction *gnm_action_new (char const *name, char const *label,
+ char const *icon, gboolean always_available,
+ GnmActionHandler handler);
+void gnm_action_free (GnmAction *action);
+
+GnmAppExtraUI *gnm_app_add_extra_ui (GSList *actions, char *layout,
+ char const *domain,
+ gpointer user_data);
+void gnm_app_remove_extra_ui (GnmAppExtraUI *extra_ui);
+void gnm_app_foreach_extra_ui (GFunc func, gpointer data);
+
+/**********************************************************************/
+
+/* internal implementation util */
+void _gnm_app_flag_windows_changed (void);
+
+#endif /* GNUMERIC_APPLICATION_H */
--- /dev/null
+++ lib/goffice/split/plugin-util.h
@@ -0,0 +1,12 @@
+#ifndef GNUMERIC_PLUGIN_UTIL_H
+#define GNUMERIC_PLUGIN_UTIL_H
+
+#include "gnumeric.h"
+#include "error-info.h"
+
+#include <stdio.h>
+
+FILE *gnumeric_fopen_error_info (char const *file_name, char const *mode,
+ ErrorInfo **ret_error);
+
+#endif /* GNUMERIC_PLUGIN_UTIL_H */
--- /dev/null
+++ lib/goffice/split/style-border.c
@@ -0,0 +1,861 @@
+/* vim: set sw=8: */
+
+/*
+ * border.c: Managing drawing and printing cell borders
+ *
+ * Copyright (C) 1999-2001 Jody Goldberg (jody at gnome.org)
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <config.h>
+#include "gnumeric.h"
+#include "style-border.h"
+
+#include "style-color.h"
+#include "style.h"
+#include "mstyle.h"
+//#include "sheet-style.h"
+//#include "sheet.h"
+
+struct LineDotPattern {
+ const gint elements;
+ const gint8 *const pattern;
+ const double *const pattern_d;
+};
+
+static const gint8 dashed_pattern[] = { 3, 1 };
+static const double dashed_pattern_d[] = { 3., 1. };
+static const struct LineDotPattern dashed_line =
+{ sizeof (dashed_pattern), dashed_pattern, dashed_pattern_d };
+
+static const gint8 med_dashed_pattern[] = { 9, 3 };
+static const double med_dashed_pattern_d[] = { 9., 3. };
+static const struct LineDotPattern med_dashed_line =
+{ sizeof (med_dashed_pattern), med_dashed_pattern, med_dashed_pattern_d };
+
+static const gint8 dotted_pattern[] = { 2, 2 };
+static const double dotted_pattern_d[] = { 2., 2. };
+static const struct LineDotPattern dotted_line =
+{ sizeof (dotted_pattern), dotted_pattern, dotted_pattern_d };
+
+static const gint8 hair_pattern[] = { 1, 1 };
+static const double hair_pattern_d[] = { 1., 1. };
+static const struct LineDotPattern hair_line =
+{ sizeof (hair_pattern), hair_pattern, hair_pattern_d };
+
+static const gint8 dash_dot_pattern[] = { 8, 3, 3, 3 };
+static const double dash_dot_pattern_d[] = { 8., 3., 3., 3. };
+static const struct LineDotPattern dash_dot_line =
+{ sizeof (dash_dot_pattern), dash_dot_pattern, dash_dot_pattern_d };
+
+static const gint8 med_dash_dot_pattern[] = { 9, 3, 3, 3 };
+static const double med_dash_dot_pattern_d[] = { 9., 3., 3., 3. };
+static const struct LineDotPattern med_dash_dot_line =
+{ sizeof (med_dash_dot_pattern), med_dash_dot_pattern, med_dash_dot_pattern_d };
+
+static const gint8 dash_dot_dot_pattern[] = { 3, 3, 9, 3, 3, 3 };
+static const double dash_dot_dot_pattern_d[] = { 3., 3., 9., 3., 3., 3. };
+static const struct LineDotPattern dash_dot_dot_line =
+{ sizeof (dash_dot_dot_pattern), dash_dot_dot_pattern, dash_dot_dot_pattern_d };
+
+static const gint8 med_dash_dot_dot_pattern[] = { 3, 3, 3, 3, 9, 3 };
+static const double med_dash_dot_dot_pattern_d[] = { 3., 3., 3., 3., 9., 3. };
+static const struct LineDotPattern med_dash_dot_dot_line =
+{ sizeof (med_dash_dot_dot_pattern), med_dash_dot_dot_pattern, med_dash_dot_dot_pattern_d };
+
+static const gint8 slant_pattern[] = { 11, 1, 5, 1 };
+static const double slant_pattern_d[] = { 11., 1., 5., 1. };
+static const struct LineDotPattern slant_line =
+{ sizeof (slant_pattern), slant_pattern, slant_pattern_d };
+
+struct {
+ gint width;
+ gint offset;
+ struct LineDotPattern const * pattern;
+} static const style_border_data[] = {
+ /* 0x0 : STYLE_BORDER_NONE */ { 0, 0, NULL },
+ /* 0x1 : STYLE_BORDER_THIN */ { 0, 0, NULL },
+ /* 0x2 : STYLE_BORDER_MEDIUM */ { 2, 0, NULL },
+ /* 0x3 : STYLE_BORDER_DASHED */ { 1, 0, &dashed_line },
+ /* 0x4 : STYLE_BORDER_DOTTED */ { 1, 0, &dotted_line },
+ /* 0x5 : STYLE_BORDER_THICK */ { 3, 0, NULL },
+ /* 0x6 : STYLE_BORDER_DOUBLE */ { 0, 0, NULL },
+ /* 0x7 : STYLE_BORDER_HAIR */ { 1, 0, &hair_line },
+ /* 0x8 : STYLE_BORDER_MEDIUM_DASH */ { 2, 9, &med_dashed_line },
+ /* 0x9 : STYLE_BORDER_DASH_DOT */ { 1, 0, &dash_dot_line },
+ /* 0xa : STYLE_BORDER_MEDIUM_DASH_DOT */ { 2, 17,&med_dash_dot_line },
+ /* 0xb : STYLE_BORDER_DASH_DOT_DOT */ { 1, 0, &dash_dot_dot_line },
+ /* 0xc : STYLE_BORDER_MEDIUM_DASH_DOT_DOT */ { 2, 21,&med_dash_dot_dot_line },
+ /* 0xd : STYLE_BORDER_SLANTED_DASH_DOT */ { 2, 6, &slant_line },/* How to slant */
+ /* 0xe : STYLE_BORDER_INCONSISTENT */ { 3, 0, &hair_line },
+};
+
+static GHashTable *border_hash = NULL;
+
+static gint
+style_border_equal (gconstpointer v1, gconstpointer v2)
+{
+ GnmBorder const *k1 = (GnmBorder const *) v1;
+ GnmBorder const *k2 = (GnmBorder const *) v2;
+
+ /*
+ * ->color is a pointer, but the comparison is safe because
+ * all colours are cached, see style_color_new.
+ */
+ return (k1->color == k2->color) &&
+ (k1->line_type == k2->line_type);
+}
+
+static guint
+style_border_hash (gconstpointer v)
+{
+ GnmBorder const *b = (GnmBorder const *) v;
+
+ /*
+ * HACK ALERT!
+ *
+ * ->color is a pointer, but the comparison is safe because
+ * all colours are cached, see style_color_new.
+ *
+ * We assume that casting a pointer to (unsigned) does something
+ * useful. That's probably ok.
+ */
+ return (((unsigned)b->color) ^ b->line_type);
+}
+
+GnmBorder *
+style_border_none (void)
+{
+ static GnmBorder * none = NULL;
+ if (none == NULL) {
+ none = g_new0 (GnmBorder, 1);
+ none->line_type = STYLE_BORDER_NONE;
+ none->color = style_color_grid ();
+ none->begin_margin = none->end_margin = none->width = 0;
+ none->ref_count = 1;
+ }
+
+ g_return_val_if_fail (none != NULL, NULL);
+
+ return none;
+}
+
+/**
+ * style_border_none_set_color:
+ * @color :
+ *
+ * This function updates the color of style_border_none when the wanted grid
+ * color is known. style_border_none tells how to render the grid. Because
+ * the grid color may be different for different sheets, the functions which
+ * render the grid call this function first. The rule for selecting the
+ * grid color, which is the same as in Excel, is: - if the auto pattern
+ * color is default (which is black), the grid color is gray, as returned by
+ * style_color_grid (). - otherwise, the auto pattern color is used for the
+ * grid.
+ * NOTE : Absorbs a reference to @color.
+ */
+void
+style_border_none_set_color (GnmColor *color)
+{
+ GnmBorder *none = style_border_none ();
+ GnmColor *nc;
+
+ if (color == none->color) {
+ style_color_unref (color);
+ return;
+ }
+
+ nc = none->color;
+ none->color = color;
+ style_color_unref (nc);
+
+ if (none->gc) {
+ gdk_gc_set_rgb_fg_color (none->gc, &none->color->color);
+ }
+}
+
+/**
+ * style_border_fetch :
+ *
+ * @line_type : dash style
+ * @color : colour
+ * @orientation : Not currently used.
+ *
+ * Fetches a GnmBorder from the cache, creating one if necessary. Absorbs
+ * the colour reference. In the future we may have different dash styles for
+ * the same pattern depending on whether this is a horizontal or vertical line.
+ */
+GnmBorder *
+style_border_fetch (StyleBorderType const line_type,
+ GnmColor *color,
+ StyleBorderOrientation orientation)
+{
+ GnmBorder *border;
+ GnmBorder key;
+
+ g_return_val_if_fail (line_type >= STYLE_BORDER_NONE, NULL);
+ g_return_val_if_fail (line_type < STYLE_BORDER_MAX, NULL);
+
+ if (line_type == STYLE_BORDER_NONE) {
+ if (color)
+ style_color_unref (color);
+ return style_border_ref (style_border_none ());
+ }
+
+ g_return_val_if_fail (color != NULL, NULL);
+ key.line_type = line_type;
+ key.color = color;
+
+ if (border_hash) {
+ border = g_hash_table_lookup (border_hash, &key);
+ if (border != NULL) {
+ if (color)
+ style_color_unref (color);
+ return style_border_ref (border);
+ }
+ } else
+ border_hash = g_hash_table_new (style_border_hash,
+ style_border_equal);
+
+ border = g_new0 (GnmBorder, 1);
+ *border = key;
+ g_hash_table_insert (border_hash, border, border);
+ border->ref_count = 1;
+ border->gc = NULL;
+ border->gc_screen = NULL;
+ border->width = style_border_get_width (line_type);
+ if (border->line_type == STYLE_BORDER_DOUBLE) {
+ border->begin_margin = 1;
+ border->end_margin = 1;
+ } else {
+ border->begin_margin = (border->width) > 1 ? 1 : 0;
+ border->end_margin = (border->width) > 2 ? 1 : 0;
+ }
+
+ return border;
+}
+
+gboolean
+style_border_visible_in_blank (GnmBorder const *border)
+{
+ g_return_val_if_fail (border != NULL, FALSE);
+
+ return border->line_type != STYLE_BORDER_NONE;
+}
+
+gint
+style_border_get_width (StyleBorderType const line_type)
+{
+ g_return_val_if_fail (line_type >= STYLE_BORDER_NONE, 0);
+ g_return_val_if_fail (line_type < STYLE_BORDER_MAX, 0);
+
+ if (line_type == STYLE_BORDER_NONE)
+ return 0;
+
+ return style_border_data [line_type].width;
+}
+
+StyleBorderOrientation
+style_border_get_orientation (StyleBorderLocation type)
+{
+ switch (type) {
+ case STYLE_BORDER_LEFT:
+ case STYLE_BORDER_RIGHT:
+ return STYLE_BORDER_VERTICAL;
+ case STYLE_BORDER_DIAG:
+ case STYLE_BORDER_REV_DIAG:
+ return STYLE_BORDER_DIAGONAL;
+ case STYLE_BORDER_TOP:
+ case STYLE_BORDER_BOTTOM:
+ default:
+ return STYLE_BORDER_HORIZONTAL;
+ }
+}
+
+void
+style_border_set_gc_dash (GdkGC *gc, StyleBorderType const i)
+{
+ GdkLineStyle style = GDK_LINE_SOLID;
+
+ g_return_if_fail (gc != NULL);
+ g_return_if_fail (i >= STYLE_BORDER_NONE);
+ g_return_if_fail (i < STYLE_BORDER_MAX);
+
+ if (style_border_data[i].pattern != NULL)
+ style = GDK_LINE_ON_OFF_DASH;
+
+ /* NOTE : Tricky. We Use CAP_NOT_LAST because with butt lines
+ * of width > 0 seem to exclude the far point (under Xfree86-4).
+ * The Docs for X11R6 say that NotLast will give the same behavior for
+ * lines of width 0. Strangely the R5 docs say this for 0 AND 1.
+ */
+ gdk_gc_set_line_attributes (gc, style_border_data[i].width, style,
+ GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
+
+ if (style_border_data[i].pattern != NULL) {
+ struct LineDotPattern const * const pat =
+ style_border_data[i].pattern;
+
+ gdk_gc_set_dashes (gc, style_border_data[i].offset,
+ (gint8 *)pat->pattern, pat->elements);
+ }
+
+ /* The background should never be drawn */
+ gdk_gc_set_rgb_bg_color (gc, &gs_white);
+}
+
+static inline GdkGC *
+style_border_get_gc (GnmBorder const *border, GdkDrawable *drawable)
+{
+ GdkScreen *this_screen;
+ if (border == NULL)
+ return NULL;
+
+ this_screen = gdk_drawable_get_screen (drawable);
+ if (border->gc_screen != this_screen) {
+ if (border->gc)
+ g_object_unref (G_OBJECT (border->gc));
+ if (border->gc_screen)
+ g_object_unref (G_OBJECT (border->gc_screen));
+ ((GnmBorder *)border)->gc = gdk_gc_new (drawable);
+ ((GnmBorder *)border)->gc_screen = this_screen;
+ g_object_ref (this_screen);
+ style_border_set_gc_dash (border->gc, border->line_type);
+ gdk_gc_set_rgb_fg_color (border->gc, &border->color->color);
+ }
+
+ return border->gc;
+}
+
+static void
+style_border_set_pc_dash (StyleBorderType const i,
+ GnomePrintContext *context)
+{
+ GdkLineStyle style = GDK_LINE_SOLID;
+ int w;
+
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (i >= STYLE_BORDER_NONE);
+ g_return_if_fail (i < STYLE_BORDER_MAX);
+
+ if (i == STYLE_BORDER_NONE)
+ return;
+
+ if (style_border_data[i].pattern != NULL)
+ style = GDK_LINE_ON_OFF_DASH;
+
+ w = style_border_data[i].width;
+ if (w == 0)
+ w = 1;
+ gnome_print_setlinewidth (context, w);
+
+ if (style_border_data[i].pattern != NULL) {
+ struct LineDotPattern const * const pat =
+ style_border_data[i].pattern;
+ gnome_print_setdash (context, pat->elements,
+ pat->pattern_d, style_border_data[i].offset);
+ }
+}
+
+static inline gboolean
+style_border_set_pc (GnmBorder const * const border,
+ GnomePrintContext *context)
+{
+ if (border == NULL)
+ return FALSE;
+
+ gnome_print_gsave (context);
+ style_border_set_pc_dash (border->line_type, context);
+ gnome_print_setrgbcolor (context,
+ border->color->color.red / (double) 0xffff,
+ border->color->color.green / (double) 0xffff,
+ border->color->color.blue / (double) 0xffff);
+ return TRUE;
+}
+
+GnmBorder *
+style_border_ref (GnmBorder *border)
+{
+ /* NULL is ok */
+ if (border != NULL)
+ ++border->ref_count;
+ return border;
+}
+
+void
+style_border_unref (GnmBorder *border)
+{
+ if (border == NULL)
+ return;
+
+ g_return_if_fail (border->ref_count > 0);
+
+ border->ref_count--;
+ if (border->ref_count != 0)
+ return;
+
+ /* Just to be on the safe side.
+ * We are allowed to deref the border_none,
+ * but not to free it.
+ */
+ g_return_if_fail (border != style_border_none ());
+
+ /* Remove here, before we mess with the hashed fields. */
+ g_hash_table_remove (border_hash, border);
+
+ if (border->color) {
+ style_color_unref (border->color);
+ border->color = NULL;
+ }
+
+ if (border->gc) {
+ g_object_unref (G_OBJECT (border->gc));
+ border->gc = NULL;
+ }
+
+ if (border->gc_screen) {
+ g_object_unref (G_OBJECT (border->gc_screen));
+ border->gc_screen = NULL;
+ }
+
+ g_free (border);
+}
+
+#if 0
+static gboolean
+style_border_hmargins (GnmBorder const * const * prev_vert,
+ GnmRow const *sr, int col,
+ int offsets [2][2])
+{
+ GnmBorder const *border = sr->top [col];
+ GnmBorder const *t0 = prev_vert [col];
+ GnmBorder const *t1 = prev_vert [col+1];
+ GnmBorder const *b0 = sr->vertical [col];
+ GnmBorder const *b1 = sr->vertical [col+1];
+
+ if (border->line_type == STYLE_BORDER_DOUBLE) {
+ /* pull inwards or outwards */
+ if (!style_border_is_blank (t0)) {
+ if (t0->line_type == STYLE_BORDER_DOUBLE)
+ offsets [1][0] = t0->end_margin;
+ else
+ offsets [1][0] = -t0->begin_margin;
+ } else if (!style_border_is_blank (b0))
+ offsets [1][0] = -b0->begin_margin;
+ else
+ offsets [1][0] = 0;
+
+ if (!style_border_is_blank (t1)) {
+ if (t1->line_type == STYLE_BORDER_DOUBLE)
+ offsets [1][1] = -t1->begin_margin;
+ else
+ offsets [1][1] = t1->end_margin;
+ } else if (!style_border_is_blank (b1))
+ offsets [1][1] = b1->end_margin;
+ else
+ offsets [1][1] = 0;
+
+ if (!style_border_is_blank (b0)) {
+ if (b0->line_type == STYLE_BORDER_DOUBLE)
+ offsets [0][0] = b0->end_margin;
+ else
+ offsets [0][0]= -b0->begin_margin;
+ } else if (!style_border_is_blank (t0))
+ offsets [0][0]= -t0->begin_margin;
+ else
+ offsets [0][0]= 0;
+
+ if (!style_border_is_blank (b1)) {
+ if (b1->line_type == STYLE_BORDER_DOUBLE)
+ offsets [0][1] = -b1->begin_margin;
+ else
+ offsets [0][1] = b1->end_margin;
+ } else if (!style_border_is_blank (t1))
+ offsets [0][1] = t1->end_margin;
+ else
+ offsets [0][1] = 0;
+ return TRUE;
+ }
+
+ offsets [0][0] = offsets [0][1] = 0;
+ if (border->line_type == STYLE_BORDER_NONE) {
+ /* No need to check for show grid. That is done when the
+ * borders are loaded. Do not over write background patterns
+ */
+ if (!style_border_is_blank (b0))
+ offsets [0][0] = 1 + b0->end_margin;
+ else if (!style_border_is_blank (t0))
+ offsets [0][0] = 1 + t0->end_margin;
+ else if (sr->top [col-1] == NULL)
+ offsets [0][0] = 1;
+
+ if (!style_border_is_blank (b1))
+ offsets [0][1] = -1 - b1->begin_margin;
+ else if (!style_border_is_blank (t1))
+ offsets [0][1] = -1 - t1->begin_margin;
+ else if (sr->top [col+1] == NULL)
+ offsets [0][1] = -1;
+ } else {
+ /* pull outwards */
+ if (style_border_is_blank (sr->top [col-1])) {
+ int offset = 0;
+ if (!style_border_is_blank (b0))
+ offset = b0->begin_margin;
+ if (!style_border_is_blank (t0)) {
+ int tmp = t0->begin_margin;
+ if (offset < tmp)
+ offset = tmp;
+ }
+ offsets [0][0] = -offset;
+ }
+
+ if (style_border_is_blank (sr->top [col+1])) {
+ int offset = 0;
+ if (!style_border_is_blank (b1))
+ offset = b1->end_margin;
+ if (!style_border_is_blank (t1)) {
+ int tmp = t1->end_margin;
+ if (offset < tmp)
+ offset = tmp;
+ }
+ offsets [0][1] = offset;
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+style_border_vmargins (GnmBorder const * const * prev_vert,
+ GnmRow const *sr, int col,
+ int offsets [2][2])
+{
+ GnmBorder const *border = sr->vertical [col];
+ GnmBorder const *l0 = sr->top [col-1];
+ GnmBorder const *r0 = sr->top [col];
+ GnmBorder const *l1 = sr->bottom [col-1];
+ GnmBorder const *r1 = sr->bottom [col];
+
+ if (border->line_type == STYLE_BORDER_DOUBLE) {
+ /* pull inwards or outwards */
+ if (!style_border_is_blank (l0))
+ offsets [1][0] = l0->end_margin;
+ else if (!style_border_is_blank (r0))
+ offsets [1][0] = -r0->begin_margin;
+ else
+ offsets [1][0] = 0;
+
+ if (!style_border_is_blank (l1))
+ offsets [1][1] = -l1->begin_margin;
+ else if (!style_border_is_blank (r1))
+ offsets [1][1] = r1->end_margin;
+ else
+ offsets [1][1] = 0;
+
+ if (!style_border_is_blank (r0))
+ offsets [0][0] = r0->end_margin;
+ else if (!style_border_is_blank (l0))
+ offsets [0][0] = -l0->begin_margin;
+ else
+ offsets [0][0] = 0;
+
+ if (!style_border_is_blank (r1))
+ offsets [0][1] = -r1->begin_margin;
+ else if (!style_border_is_blank (l1))
+ offsets [0][1] = l1->end_margin;
+ else
+ offsets [0][1] = 0;
+ return TRUE;
+ }
+
+ offsets [0][0] = offsets [0][1] = 0;
+ if (border->line_type == STYLE_BORDER_NONE) {
+ /* No need to check for show grid. That is done when the
+ * borders are loaded.
+ */
+ if (!style_border_is_blank (r0))
+ offsets [0][0] = 1 + r0->end_margin;
+ else if (!style_border_is_blank (l0))
+ offsets [0][0] = 1 + l0->end_margin;
+ /* Do not over write background patterns */
+ else if (prev_vert [col] == NULL)
+ offsets [0][0] = 1;
+
+ if (!style_border_is_blank (r1))
+ offsets [0][1] = -1 - r1->begin_margin;
+ else if (!style_border_is_blank (l1))
+ offsets [0][1] = -1 - l1->begin_margin;
+ /* Do not over write background patterns */
+ else if (sr->vertical [col] == NULL)
+ offsets [0][1] = -1;
+ } else {
+ /* pull inwards */
+ int offset = 0;
+ if (!style_border_is_blank (r0))
+ offset = 1 + r0->end_margin;
+ if (!style_border_is_blank (l0)) {
+ int tmp = 1 + l0->end_margin;
+ if (offset < tmp)
+ offset = tmp;
+ }
+ offsets [0][0] = offset;
+
+ offset = 0;
+ if (!style_border_is_blank (r1))
+ offset = 1 + r1->begin_margin;
+ if (!style_border_is_blank (l1)) {
+ int tmp = 1 + l1->begin_margin;
+ if (offset < tmp)
+ offset = tmp;
+ }
+ offsets [0][1] = -offset;
+ }
+ return FALSE;
+}
+
+/**
+ * style_borders_row_draw :
+ *
+ * TODO : This is not the final resting place for this.
+ * It will move into the gui layer eventually.
+ */
+void
+style_borders_row_draw (GnmBorder const * const * prev_vert,
+ GnmRow const *sr,
+ GdkDrawable * const drawable,
+ int x, int y1, int y2,
+ int *colwidths, gboolean draw_vertical)
+{
+ int o[2][2];
+ int col, next_x = x;
+ GdkGC *gc;
+
+ for (col = sr->start_col; col <= sr->end_col ; col++, x = next_x) {
+
+ if (colwidths[col] == -1)
+ continue;
+ next_x = x + colwidths[col];
+
+ gc = style_border_get_gc (sr->top [col], drawable);
+ if (gc != NULL) {
+ int y = y1;
+ if (style_border_hmargins (prev_vert, sr, col, o)) {
+ gdk_draw_line (drawable, gc, x + o[1][0], y1-1,
+ next_x + o[1][1] + 1, y1-1);
+ ++y;
+ }
+
+ /* See note in style_border_set_gc_dash about +1 */
+ gdk_draw_line (drawable, gc, x + o[0][0], y,
+ next_x + o[0][1] + 1, y);
+ }
+
+ if (!draw_vertical)
+ continue;
+
+ gc = style_border_get_gc (sr->vertical [col], drawable);
+ if (gc != NULL) {
+ int x1 = x;
+ if (style_border_vmargins (prev_vert, sr, col, o)) {
+ gdk_draw_line (drawable, gc, x-1, y1 + o[1][0],
+ x-1, y2 + o[1][1] + 1);
+ ++x1;
+ }
+ /* See note in style_border_set_gc_dash about +1 */
+ gdk_draw_line (drawable, gc, x1, y1 + o[0][0],
+ x1, y2 + o[0][1] + 1);
+ }
+ }
+ if (draw_vertical) {
+ gc = style_border_get_gc (sr->vertical [col], drawable);
+ if (gc != NULL) {
+ int x1 = x;
+ if (style_border_vmargins (prev_vert, sr, col, o)) {
+ gdk_draw_line (drawable, gc, x-1, y1 + o[1][0],
+ x-1, y2 + o[1][1] + 1);
+ ++x1;
+ }
+ /* See note in style_border_set_gc_dash about +1 */
+ gdk_draw_line (drawable, gc, x, y1 + o[0][0],
+ x1, y2 + o[0][1] + 1);
+ }
+ }
+}
+#endif /* GnmRow... */
+
+void
+style_border_draw_diag (GnmStyle const *style,
+ GdkDrawable *drawable,
+ int x1, int y1, int x2, int y2)
+{
+ GnmBorder const *diag;
+ GdkGC *gc;
+
+ diag = mstyle_get_border (style, MSTYLE_BORDER_REV_DIAGONAL);
+ if (diag != NULL && diag->line_type != STYLE_BORDER_NONE) {
+ gc = style_border_get_gc (diag, drawable);
+ if (diag->line_type == STYLE_BORDER_DOUBLE) {
+ gdk_draw_line (drawable, gc, x1+1, y1+3, x2-3, y2-1);
+ gdk_draw_line (drawable, gc, x1+3, y1+1, x2-1, y2-3);
+ } else
+ gdk_draw_line (drawable, gc, x1, y1, x2, y2);
+ }
+
+ diag = mstyle_get_border (style, MSTYLE_BORDER_DIAGONAL);
+ if (diag != NULL && diag->line_type != STYLE_BORDER_NONE) {
+ gc = style_border_get_gc (diag, drawable);
+ if (diag->line_type == STYLE_BORDER_DOUBLE) {
+ gdk_draw_line (drawable, gc, x1+1, y2-3, x2-3, y1+1);
+ gdk_draw_line (drawable, gc, x1+3, y2-1, x2-1, y1+3);
+ } else
+ gdk_draw_line (drawable, gc, x1, y2, x2, y1);
+ }
+}
+
+static inline void
+print_hline (GnomePrintContext *context,
+ float x1, float x2, float y, int width)
+{
+ if (width == 0 || width % 2)
+ y -= .5;
+
+ /* exclude far pixel to match gdk */
+ gnome_print_moveto (context, x1, y);
+ gnome_print_lineto (context, x2, y);
+ gnome_print_stroke (context);
+}
+
+static inline void
+print_vline (GnomePrintContext *context,
+ float x, float y1, float y2, int width)
+{
+ if (width == 0 || width % 2)
+ x += .5;
+
+ /* exclude far pixel to match gdk */
+ gnome_print_moveto (context, x, y1);
+ gnome_print_lineto (context, x, y2);
+ gnome_print_stroke (context);
+}
+
+#if 0 // Sheet, Row
+void
+style_borders_row_print (GnmBorder const * const * prev_vert,
+ GnmRow const *sr,
+ GnomePrintContext *context,
+ float x, float y1, float y2,
+ Sheet const *sheet, gboolean draw_vertical)
+{
+ int o[2][2], col;
+ float next_x = x;
+ GnmBorder const *border;
+
+ for (col = sr->start_col; col <= sr->end_col ; col++, x = next_x) {
+ /* TODO : make this sheet agnostic. Pass in an array of
+ * widths and a flag for whether or not to draw grids.
+ */
+ ColRowInfo const *cri = sheet_col_get_info (sheet, col);
+ if (!cri->visible)
+ continue;
+ next_x = x + cri->size_pts;
+
+ border = sr->top [col];
+ if (style_border_set_pc (border, context)) {
+ float y = y1;
+ if (style_border_hmargins (prev_vert, sr, col, o)) {
+ print_hline (context, x + o[1][0],
+ next_x + o[1][1] + 1., y1+1., border->width);
+ --y;
+ }
+
+ print_hline (context, x + o[0][0],
+ next_x + o[0][1] + 1., y, border->width);
+ gnome_print_grestore (context);
+ }
+
+ if (!draw_vertical)
+ continue;
+ border = sr->vertical [col];
+ if (style_border_set_pc (border, context)) {
+ float x1 = x;
+ if (style_border_vmargins (prev_vert, sr, col, o)) {
+ print_vline (context, x-1., y1 - o[1][0],
+ y2 - o[1][1] - 1., border->width);
+ ++x1;
+ }
+ print_vline (context, x1, y1 - o[0][0],
+ y2 - o[0][1] - 1., border->width);
+ gnome_print_grestore (context);
+ }
+ }
+ if (draw_vertical) {
+ border = sr->vertical [col];
+ if (style_border_set_pc (border, context)) {
+ float x1 = x;
+ if (style_border_vmargins (prev_vert, sr, col, o)) {
+ print_vline (context, x-1., y1 - o[1][0] - 1.,
+ y2 - o[1][1], border->width);
+ ++x1;
+ }
+ /* See note in style_border_set_gc_dash about +1 */
+ print_vline (context, x, y1 - o[0][0],
+ y2 - o[0][1] - 1, border->width);
+ gnome_print_grestore (context);
+ }
+ }
+}
+#endif // 0
+
+void
+style_border_print_diag (GnmStyle const *style,
+ GnomePrintContext *context,
+ float x1, float y1, float x2, float y2)
+{
+ GnmBorder const *diag;
+
+ diag = mstyle_get_border (style, MSTYLE_BORDER_REV_DIAGONAL);
+ if (diag != NULL && diag->line_type != STYLE_BORDER_NONE) {
+ style_border_set_pc (diag, context);
+ if (diag->line_type == STYLE_BORDER_DOUBLE) {
+ gnome_print_moveto (context, x1+1.5, y1-3.);
+ gnome_print_lineto (context, x2-2., y2+ .5);
+ gnome_print_stroke (context);
+ gnome_print_moveto (context, x1+ 3., y1-1.5);
+ gnome_print_lineto (context, x2- .5, y2+2.);
+ } else {
+ gnome_print_moveto (context, x1+.5, y1-.5);
+ gnome_print_lineto (context, x2+.5, y2-.5);
+ }
+ gnome_print_stroke (context);
+ gnome_print_grestore (context);
+ }
+
+ diag = mstyle_get_border (style, MSTYLE_BORDER_DIAGONAL);
+ if (diag != NULL && diag->line_type != STYLE_BORDER_NONE) {
+ style_border_set_pc (diag, context);
+ if (diag->line_type == STYLE_BORDER_DOUBLE) {
+ gnome_print_moveto (context, x1+1.5, y2+2.);
+ gnome_print_lineto (context, x2-2., y1-1.5);
+ gnome_print_stroke (context);
+ gnome_print_moveto (context, x1+3., y2+ .5);
+ gnome_print_lineto (context, x2- .5, y1-3.);
+ } else {
+ gnome_print_moveto (context, x1+.5, y2-.5);
+ gnome_print_lineto (context, x2+.5, y1-.5);
+ }
+ gnome_print_stroke (context);
+ gnome_print_grestore (context);
+ }
+}
--- /dev/null
+++ lib/goffice/split/style-color.h
@@ -0,0 +1,36 @@
+#ifndef GNUMERIC_STYLE_COLOR_H
+#define GNUMERIC_STYLE_COLOR_H
+
+#include "gnumeric.h"
+#include <goffice/utils/go-color.h>
+#include <gdk/gdkcolor.h>
+
+struct _GnmColor {
+ GdkColor color, selected_color;
+ char *name;
+ int ref_count;
+ gboolean is_auto;
+};
+
+/* Colors used by any GnumericSheet item */
+extern GdkColor gs_white, gs_light_gray, gs_dark_gray, gs_black, gs_lavender, gs_yellow;
+
+GnmColor *style_color_new_go (GOColor c);
+GnmColor *style_color_new_name (char const *name);
+GnmColor *style_color_new (gushort red, gushort green, gushort blue);
+GnmColor *style_color_new_i8 (guint8 red, guint8 green, guint8 blue);
+GnmColor *style_color_new_pango (PangoColor *c);
+GnmColor *style_color_auto_font (void);
+GnmColor *style_color_auto_back (void);
+GnmColor *style_color_auto_pattern (void);
+GnmColor *style_color_ref (GnmColor *sc);
+void style_color_unref (GnmColor *sc);
+gint style_color_equal (const GnmColor *k1, const GnmColor *k2);
+GnmColor *style_color_black (void);
+GnmColor *style_color_white (void);
+GnmColor *style_color_grid (void);
+
+void gnumeric_color_init (void);
+void gnumeric_color_shutdown (void);
+
+#endif /* GNUMERIC_STYLE_COLOR_H */
--- /dev/null
+++ lib/goffice/split/dates.c
@@ -0,0 +1,71 @@
+/*
+ * dates.c: Include the string definitions for the date names
+ *
+ * Author:
+ * Miguel de Icaza (miguel at kernel.org)
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "dates.h"
+
+/* FIXME : use nl_langinfo */
+const char *day_short [] =
+{
+ N_("*Sun"),
+ N_("*Mon"),
+ N_("*Tue"),
+ N_("*Wed"),
+ N_("*Thu"),
+ N_("*Fri"),
+ N_("*Sat"),
+ NULL,
+};
+
+const char *day_long [] =
+{
+ N_("Sunday"),
+ N_("Monday"),
+ N_("Tuesday"),
+ N_("Wednesday"),
+ N_("Thursday"),
+ N_("Friday"),
+ N_("Saturday"),
+ NULL
+};
+
+const char *month_short [] =
+{
+ N_("*Jan"),
+ N_("*Feb"),
+ N_("*Mar"),
+ N_("*Apr"),
+ N_("*May"),
+ N_("*Jun"),
+ N_("*Jul"),
+ N_("*Aug"),
+ N_("*Sep"),
+ N_("*Oct"),
+ N_("*Nov"),
+ N_("*Dec"),
+ NULL
+};
+
+const char *month_long [] =
+{
+ N_("January"),
+ N_("February"),
+ N_("March"),
+ N_("April"),
+ N_("May"),
+ N_("June"),
+ N_("July"),
+ N_("August"),
+ N_("September"),
+ N_("October"),
+ N_("November"),
+ N_("December"),
+ NULL
+};
+
--- /dev/null
+++ lib/goffice/split/io-context.c
@@ -0,0 +1,487 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * io-context.c : Place holder for an io error context.
+ * It is intended to become a place to handle errors
+ * as well as storing non-fatal warnings.
+ *
+ * Authors:
+ * Jody Goldberg <jody at gnome.org>
+ * Zbigniew Chyla <cyba at gnome.pl>
+ *
+ * (C) 2000-2002 Jody Goldberg
+ */
+#include <config.h>
+#include "gnumeric.h"
+#include "io-context-priv.h"
+
+//#include "sheet.h"
+//#include "workbook.h"
+#include "command-context.h"
+#include "gui-util.h"
+
+#include <gsf/gsf-impl-utils.h>
+#include <gtk/gtkmain.h>
+
+#define PROGRESS_UPDATE_STEP 0.01
+#define PROGRESS_UPDATE_STEP_END (1.0 / 400)
+#define PROGRESS_UPDATE_PERIOD_SEC 0.20
+
+#define IOC_CLASS(ioc) IO_CONTEXT_CLASS(G_OBJECT_GET_CLASS(ioc))
+
+static void
+io_context_init (IOContext *ioc)
+{
+ ioc->impl = NULL;
+ ioc->info = NULL;
+ ioc->error_occurred = FALSE;
+ ioc->warning_occurred = FALSE;
+
+ ioc->progress_ranges = NULL;
+ ioc->progress_min = 0.0;
+ ioc->progress_max = 1.0;
+ ioc->last_progress = -1.0;
+ ioc->last_time = 0.0;
+ ioc->helper.helper_type = GNM_PROGRESS_HELPER_NONE;
+}
+
+static void
+ioc_finalize (GObject *obj)
+{
+ IOContext *ioc;
+
+ g_return_if_fail (IS_IO_CONTEXT (obj));
+
+ ioc = IO_CONTEXT (obj);
+ error_info_free (ioc->info);
+ if (ioc->impl) {
+ cmd_context_progress_set (ioc->impl, 0.0);
+ cmd_context_progress_message_set (ioc->impl, NULL);
+ g_object_unref (G_OBJECT (ioc->impl));
+ }
+
+ G_OBJECT_CLASS (g_type_class_peek (G_TYPE_OBJECT))->finalize (obj);
+}
+
+static char *
+ioc_get_password (GnmCmdContext *cc, char const *filename)
+{
+ IOContext *ioc = (IOContext *)cc;
+ return gnm_cmd_context_get_password (ioc->impl, filename);
+}
+
+static void
+ioc_set_sensitive (GnmCmdContext *cc, gboolean sensitive)
+{
+ (void)cc; (void)sensitive;
+}
+
+static void
+ioc_error_error (GnmCmdContext *cc, GError *err)
+{
+ gnumeric_io_error_string (IO_CONTEXT (cc), err->message);
+}
+
+static void
+ioc_error_error_info (G_GNUC_UNUSED GnmCmdContext *ctxt,
+ ErrorInfo *error)
+{
+ /* TODO what goes here */
+ error_info_print (error);
+}
+
+void
+gnumeric_io_error_string (IOContext *context, const gchar *str)
+{
+ ErrorInfo *error;
+
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (str != NULL);
+
+ error = error_info_new_str (str);
+ gnumeric_io_error_info_set (context, error);
+}
+
+static void
+io_context_gnm_cmd_context_init (GnmCmdContextClass *cc_class)
+{
+ cc_class->get_password = ioc_get_password;
+ cc_class->set_sensitive = ioc_set_sensitive;
+ cc_class->error.error = ioc_error_error;
+ cc_class->error.error_info = ioc_error_error_info;
+}
+static void
+io_context_class_init (GObjectClass *klass)
+{
+ klass->finalize = ioc_finalize;
+}
+
+GSF_CLASS_FULL (IOContext, io_context,
+ io_context_class_init, io_context_init,
+ G_TYPE_OBJECT, 0,
+ GSF_INTERFACE (io_context_gnm_cmd_context_init, GNM_CMD_CONTEXT_TYPE))
+
+IOContext *
+gnumeric_io_context_new (GnmCmdContext *cc)
+{
+ IOContext *ioc;
+
+ g_return_val_if_fail (IS_GNM_CMD_CONTEXT (cc), NULL);
+
+ ioc = g_object_new (TYPE_IO_CONTEXT, NULL);
+ /* The cc is optional for subclasses, but mandatory in this class. */
+ ioc->impl = cc;
+ g_object_ref (G_OBJECT (ioc->impl));
+
+ return ioc;
+}
+
+void
+gnumeric_io_error_unknown (IOContext *context)
+{
+ g_return_if_fail (context != NULL);
+
+ context->error_occurred = TRUE;
+}
+
+void
+gnumeric_io_error_info_set (IOContext *context, ErrorInfo *error)
+{
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (error != NULL);
+
+ g_return_if_fail (context->info == NULL);
+
+ context->info = error;
+ context->error_occurred = TRUE;
+}
+
+void
+gnumeric_io_error_push (IOContext *context, ErrorInfo *error)
+{
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (error != NULL);
+
+ error_info_add_details (error, context->info);
+ context->info = error;
+}
+
+void
+gnumeric_io_error_display (IOContext *context)
+{
+ GnmCmdContext *cc;
+
+ g_return_if_fail (context != NULL);
+
+ if (context->info != NULL) {
+ if (context->impl)
+ cc = context->impl;
+ else
+ cc = GNM_CMD_CONTEXT (context);
+ gnm_cmd_context_error_info (cc, context->info);
+ }
+}
+
+/* TODO: Rename to gnumeric_io_info_clear */
+void
+gnumeric_io_error_clear (IOContext *context)
+{
+ g_return_if_fail (context != NULL);
+
+ context->error_occurred = FALSE;
+ context->warning_occurred = FALSE;
+ error_info_free (context->info);
+ context->info = NULL;
+}
+
+gboolean
+gnumeric_io_error_occurred (IOContext *context)
+{
+ return context->error_occurred;
+}
+
+gboolean
+gnumeric_io_warning_occurred (IOContext *context)
+{
+ return context->warning_occurred;
+}
+
+void
+io_progress_update (IOContext *ioc, gdouble f)
+{
+ gboolean at_end;
+
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+
+ if (ioc->progress_ranges != NULL) {
+ f = f * (ioc->progress_max - ioc->progress_min)
+ + ioc->progress_min;
+ }
+
+ at_end = (f - ioc->last_progress > PROGRESS_UPDATE_STEP_END &&
+ f + PROGRESS_UPDATE_STEP > 1);
+ if (at_end || f - ioc->last_progress >= PROGRESS_UPDATE_STEP) {
+ GTimeVal tv;
+ double t;
+
+ (void) g_get_current_time (&tv);
+ t = tv.tv_sec + tv.tv_usec / 1000000.0;
+ if (at_end || t - ioc->last_time >= PROGRESS_UPDATE_PERIOD_SEC) {
+ GnmCmdContext *cc;
+
+ if (ioc->impl)
+ cc = ioc->impl;
+ else
+ cc = GNM_CMD_CONTEXT (ioc);
+ cmd_context_progress_set (cc, f);
+ ioc->last_time = t;
+ ioc->last_progress = f;
+ }
+ }
+
+ /* FIXME : abstract this into the workbook control */
+ while (gtk_events_pending ())
+ gtk_main_iteration_do (FALSE);
+}
+
+void
+io_progress_message (IOContext *ioc, const gchar *msg)
+{
+ GnmCmdContext *cc;
+
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+
+ if (ioc->impl)
+ cc = ioc->impl;
+ else
+ cc = GNM_CMD_CONTEXT (ioc);
+ cmd_context_progress_message_set (cc, msg);
+}
+
+void
+io_progress_range_push (IOContext *ioc, gdouble min, gdouble max)
+{
+ ProgressRange *r;
+ gdouble new_min, new_max;
+
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+
+ r = g_new (ProgressRange, 1);
+ r->min = min;
+ r->max = max;
+ ioc->progress_ranges = g_list_append (ioc->progress_ranges, r);
+
+ new_min = min / (ioc->progress_max - ioc->progress_min)
+ + ioc->progress_min;
+ new_max = max / (ioc->progress_max - ioc->progress_min)
+ + ioc->progress_min;
+ ioc->progress_min = new_min;
+ ioc->progress_max = new_max;
+}
+
+void
+io_progress_range_pop (IOContext *ioc)
+{
+ GList *l;
+
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+ g_return_if_fail (ioc->progress_ranges != NULL);
+
+ l = g_list_last (ioc->progress_ranges);
+ ioc->progress_ranges= g_list_remove_link (ioc->progress_ranges, l);
+ g_free (l->data);
+ g_list_free_1 (l);
+
+ ioc->progress_min = 0.0;
+ ioc->progress_max = 1.0;
+ for (l = ioc->progress_ranges; l != NULL; l = l->next) {
+ ProgressRange *r = l->data;
+ gdouble new_min, new_max;
+
+ new_min = r->min / (ioc->progress_max - ioc->progress_min)
+ + ioc->progress_min;
+ new_max = r->max / (ioc->progress_max - ioc->progress_min)
+ + ioc->progress_min;
+ ioc->progress_min = new_min;
+ ioc->progress_max = new_max;
+ }
+}
+
+void
+value_io_progress_set (IOContext *ioc, gint total, gint step)
+{
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+ g_return_if_fail (total >= 0);
+
+ ioc->helper.helper_type = GNM_PROGRESS_HELPER_VALUE;
+ ioc->helper.v.value.total = MAX (total, 1);
+ ioc->helper.v.value.last = -step;
+ ioc->helper.v.value.step = step;
+}
+
+void
+value_io_progress_update (IOContext *ioc, gint value)
+{
+ gdouble complete;
+ gint step, total;
+
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+ g_return_if_fail (ioc->helper.helper_type == GNM_PROGRESS_HELPER_VALUE);
+
+ total = ioc->helper.v.value.total;
+ step = ioc->helper.v.value.step;
+
+ if (value - ioc->helper.v.value.last < step &&
+ value + step < total) {
+ return;
+ }
+ ioc->helper.v.value.last = value;
+
+ complete = (gdouble)value / total;
+ io_progress_update (ioc, complete);
+}
+
+void
+count_io_progress_set (IOContext *ioc, gint total, gint step)
+{
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+ g_return_if_fail (total >= 0);
+
+ ioc->helper.helper_type = GNM_PROGRESS_HELPER_COUNT;
+ ioc->helper.v.count.total = MAX (total, 1);
+ ioc->helper.v.count.last = -step;
+ ioc->helper.v.count.current = 0;
+ ioc->helper.v.count.step = step;
+}
+
+void
+count_io_progress_update (IOContext *ioc, gint inc)
+{
+ gdouble complete;
+ gint current, step, total;
+
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+ g_return_if_fail (ioc->helper.helper_type == GNM_PROGRESS_HELPER_COUNT);
+
+ current = (ioc->helper.v.count.current += inc);
+ step = ioc->helper.v.count.step;
+ total = ioc->helper.v.count.total;
+
+ if (current - ioc->helper.v.count.last < step && current + step < total) {
+ return;
+ }
+ ioc->helper.v.count.last = current;
+
+ complete = (gdouble)current / total;
+ io_progress_update (ioc, complete);
+}
+
+#if 0
+void
+workbook_io_progress_set (IOContext *ioc, Workbook const *wb, gint step)
+{
+ gint n = 0;
+ GList *sheets, *l;
+
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+ g_return_if_fail (IS_WORKBOOK (wb));
+
+ sheets = workbook_sheets (wb);
+ for (l = sheets; l != NULL; l = l->next) {
+ Sheet *sheet = l->data;
+ n += g_hash_table_size (sheet->cell_hash);
+ }
+ g_list_free (sheets);
+
+ ioc->helper.helper_type = GNM_PROGRESS_HELPER_WORKBOOK;
+ ioc->helper.v.workbook.n_elements = MAX (n, 1);
+ ioc->helper.v.workbook.last = -step;
+ ioc->helper.v.workbook.current = 0;
+ ioc->helper.v.workbook.step = step;
+}
+
+void
+workbook_io_progress_update (IOContext *ioc, gint inc)
+{
+ gdouble complete;
+
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+ g_return_if_fail (ioc->helper.helper_type == GNM_PROGRESS_HELPER_WORKBOOK);
+
+ ioc->helper.v.workbook.current += inc;
+ if (ioc->helper.v.workbook.current - ioc->helper.v.workbook.last
+ < ioc->helper.v.workbook.step) {
+ return;
+ }
+ ioc->helper.v.workbook.last = ioc->helper.v.workbook.current;
+
+ complete = 1.0 * ioc->helper.v.workbook.current
+ / ioc->helper.v.workbook.n_elements;
+ io_progress_update (ioc, complete);
+}
+#endif // 0
+
+void
+io_progress_unset (IOContext *ioc)
+{
+ g_return_if_fail (IS_IO_CONTEXT (ioc));
+
+ ioc->helper.helper_type = GNM_PROGRESS_HELPER_NONE;
+}
+
+void
+gnm_io_context_set_num_files (IOContext *ioc, guint count)
+{
+ IOContextClass *klass = IOC_CLASS(ioc);
+ g_return_if_fail (klass != NULL);
+ if (klass->set_num_files != NULL)
+ klass->set_num_files (ioc, count);
+}
+
+void
+gnm_io_context_processing_file (IOContext *ioc, char const *name)
+{
+ IOContextClass *klass = IOC_CLASS(ioc);
+ g_return_if_fail (klass != NULL);
+ if (klass->processing_file != NULL)
+ klass->processing_file (ioc, name);
+}
+
+void
+gnm_io_warning (G_GNUC_UNUSED IOContext *context,
+ char const *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ gnm_io_warning_varargs (context, fmt, args);
+ va_end (args);
+}
+
+void
+gnm_io_warning_varargs (IOContext *context, char const *fmt, va_list args)
+{
+ context->info = error_info_new_vprintf (GNM_WARNING, fmt, args);
+ context->warning_occurred = TRUE;
+}
+
+void
+gnm_io_warning_unknown_font (IOContext *context,
+ G_GNUC_UNUSED char const *font_name)
+{
+ g_return_if_fail (IS_IO_CONTEXT (context));
+}
+
+void
+gnm_io_warning_unknown_function (IOContext *context,
+ G_GNUC_UNUSED char const *funct_name)
+{
+ g_return_if_fail (IS_IO_CONTEXT (context));
+}
+
+void
+gnm_io_warning_unsupported_feature (IOContext *context, char const *feature)
+{
+ g_return_if_fail (IS_IO_CONTEXT (context));
+ g_warning ("%s : are not supported yet", feature);
+}
--- /dev/null
+++ lib/goffice/split/global-gnome-font.c
@@ -0,0 +1,21 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Global Gnome Font data structures. To avoid duplicating this across
+ * workbooks.
+ *
+ * Author:
+ * Miguel de Icaza (miguel at gnu.org)
+ */
+#include "gnumeric.h"
+#include "global-gnome-font.h"
+
+GList *gnumeric_font_family_list = NULL;
+GList *gnumeric_point_size_list = NULL;
+
+int const gnumeric_point_sizes [] = {
+ 4, 8, 9, 10, 11, 12, 14, 16, 18,
+ 20, 22, 24, 26, 28, 36, 48, 72,
+ 0
+};
+
--- /dev/null
+++ lib/goffice/split/plugin.h
@@ -0,0 +1,56 @@
+#ifndef GNUMERIC_PLUGIN_H
+#define GNUMERIC_PLUGIN_H
+
+#include "gnumeric.h"
+#include <glib-object.h>
+
+/*
+ * Use "#define PLUGIN_DEBUG x" to enable some plugin related debugging
+ * messages.
+#undef PLUGIN_DEBUG
+ * Define PLUGIN_ALWAYS_LOAD to disable loading on demand feature
+ */
+
+#define GNM_PLUGIN_TYPE (gnm_plugin_get_type ())
+#define GNM_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_PLUGIN_TYPE, GnmPlugin))
+#define IS_GNM_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_PLUGIN_TYPE))
+
+GType gnm_plugin_get_type (void);
+
+void gnm_plugin_activate (GnmPlugin *pinfo, ErrorInfo **ret_error);
+void gnm_plugin_deactivate (GnmPlugin *pinfo, ErrorInfo **ret_error);
+gboolean gnm_plugin_is_active (GnmPlugin *pinfo);
+gboolean gnm_plugin_can_deactivate (GnmPlugin *pinfo);
+void gnm_plugin_load_service (GnmPlugin *pinfo, GnmPluginService *service, ErrorInfo **ret_error);
+void gnm_plugin_unload_service (GnmPlugin *pinfo, GnmPluginService *service, ErrorInfo **ret_error);
+gboolean gnm_plugin_is_loaded (GnmPlugin *pinfo);
+void gnm_plugin_use_ref (GnmPlugin *pinfo);
+void gnm_plugin_use_unref (GnmPlugin *pinfo);
+
+char const *gnm_plugin_get_dir_name (GnmPlugin *pinfo);
+char const *gnm_plugin_get_id (GnmPlugin *pinfo);
+char const *gnm_plugin_get_name (GnmPlugin *pinfo);
+char const *gnm_plugin_get_description (GnmPlugin *pinfo);
+char const *gnm_plugin_get_textdomain (GnmPlugin *pinfo);
+GSList *gnm_plugin_get_dependencies_ids (GnmPlugin *pinfo);
+GSList *gnm_plugin_get_services (GnmPlugin *pinfo);
+
+/*
+ *
+ */
+
+void plugins_init (GnmCmdContext *context);
+void plugins_shutdown (void);
+void plugins_register_loader (const gchar *id_str, GnmPluginService *service);
+void plugins_unregister_loader (const gchar *id_str);
+GnmPlugin *plugins_get_plugin_by_id (const gchar *plugin_id);
+GSList *plugins_get_available_plugins (void);
+void plugins_rescan (ErrorInfo **ret_error, GSList **ret_new_plugins);
+void plugin_db_mark_plugin_for_deactivation (GnmPlugin *pinfo, gboolean mark);
+gboolean plugin_db_is_plugin_marked_for_deactivation (GnmPlugin *pinfo);
+void plugin_db_activate_plugin_list (GSList *plugins, ErrorInfo **ret_error);
+void plugin_db_deactivate_plugin_list (GSList *plugins, ErrorInfo **ret_error);
+
+void plugin_message (gint level, const gchar *format, ...) G_GNUC_PRINTF (2, 3);
+
+#endif /* GNUMERIC_PLUGIN_H */
--- /dev/null
+++ lib/goffice/split/gnumeric.h
@@ -0,0 +1,180 @@
+#ifndef GNUMERIC_H
+#define GNUMERIC_H
+
+#include <glib.h>
+
+#ifndef __attribute__
+# if !defined(__GNUC__) || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+/* OK, this compiler probably doesn't understand __attribute__ */
+# define __attribute__(Spec) /* empty */
+# endif
+#endif
+
+#define SHEET_MAX_ROWS (16*16*16*16) /* 0, 1, ... */
+#define SHEET_MAX_COLS (4*4*4*4) /* 0, 1, ... */
+
+/*
+ * Note: more than 364238 columns will introduce a column named TRUE.
+ */
+
+typedef struct _GnmApp GnmApp;
+typedef struct _Workbook Workbook;
+typedef struct _WorkbookView WorkbookView;
+typedef struct _WorkbookControl WorkbookControl;
+
+typedef struct _Sheet Sheet;
+typedef struct _SheetView SheetView;
+typedef struct _SheetControl SheetControl;
+
+typedef struct _SheetObject SheetObject;
+typedef struct _SheetObjectAnchor SheetObjectAnchor;
+typedef struct _SheetObjectView SheetObjectView;
+typedef struct _SheetObjectViewContainer SheetObjectViewContainer;
+
+typedef struct _GnmDepContainer GnmDepContainer;
+typedef struct _GnmDependent GnmDependent;
+typedef struct _GnmCell GnmCell;
+typedef struct _GnmComment GnmComment;
+
+typedef union _GnmValue GnmValue;
+typedef struct _GnmValueBool GnmValueBool;
+typedef struct _GnmValueInt GnmValueInt;
+typedef struct _GnmValueFloat GnmValueFloat;
+typedef struct _GnmValueErr GnmValueErr;
+typedef struct _GnmValueStr GnmValueStr;
+typedef struct _GnmValueRange GnmValueRange;
+typedef struct _GnmValueArray GnmValueArray;
+
+typedef enum {
+ GNM_ERROR_NULL,
+ GNM_ERROR_DIV0,
+ GNM_ERROR_VALUE,
+ GNM_ERROR_REF,
+ GNM_ERROR_NAME,
+ GNM_ERROR_NUM,
+ GNM_ERROR_NA,
+ GNM_ERROR_RECALC,
+ GNM_ERROR_UNKNOWN
+} GnmStdError;
+
+typedef struct _RenderedValue RenderedValue;
+
+typedef GSList GnmExprList;
+typedef union _GnmExpr GnmExpr;
+typedef struct _GnmExprConstant GnmExprConstant;
+typedef struct _GnmExprFunction GnmExprFunction;
+typedef struct _GnmExprUnary GnmExprUnary;
+typedef struct _GnmExprBinary GnmExprBinary;
+typedef struct _GnmExprName GnmExprName;
+typedef struct _GnmExprCellRef GnmExprCellRef;
+typedef struct _GnmExprArray GnmExprArray;
+typedef struct _GnmExprSet GnmExprSet;
+
+typedef struct _GnmExprRelocateInfo GnmExprRelocateInfo;
+typedef struct _GnmExprRewriteInfo GnmExprRewriteInfo;
+
+typedef struct _GnmExprConventions GnmExprConventions;
+typedef struct _GnmDateConventions GnmDateConventions;
+
+
+typedef struct _GnmNamedExpr GnmNamedExpr;
+typedef struct _GnmNamedExprCollection GnmNamedExprCollection;
+
+typedef struct _GnmPasteTarget GnmPasteTarget;
+typedef struct _GnmCellRegion GnmCellRegion;
+
+typedef struct _ColRowInfo ColRowInfo;
+typedef struct _ColRowCollection ColRowCollection;
+typedef struct _ColRowSegment ColRowSegment;
+typedef GSList ColRowVisList;
+typedef GSList ColRowStateGroup;
+typedef GSList ColRowStateList;
+typedef GList ColRowIndexList;
+typedef struct _ColRowIndexSet ColRowIndexSet;
+
+typedef struct _GnmFormat GnmFormat;
+typedef struct _GnmFont GnmFont;
+typedef struct _GnmColor GnmColor;
+typedef struct _GnmBorder GnmBorder;
+typedef struct _GnmRow GnmRow;
+typedef struct _GnmStyle GnmStyle;
+
+typedef struct _SheetStyleData SheetStyleData;
+typedef struct _GnmStyleRegion GnmStyleRegion;
+typedef GSList GnmStyleList;
+
+typedef struct _FormatTemplate FormatTemplate; /* does not really belong here */
+
+typedef struct {
+ int col, row;
+} GnmCellPos;
+typedef struct {
+ GnmCellPos start, end;
+} GnmRange;
+typedef struct {
+ Sheet *sheet;
+ GnmRange range;
+} GnmSheetRange;
+typedef struct _GnmCellRef GnmCellRef; /* abs/rel point with sheet */
+typedef struct _GnmRangeRef GnmRangeRef; /* abs/rel range with sheet */
+typedef struct _GnmEvalPos GnmEvalPos;
+typedef struct _GnmParsePos GnmParsePos;
+typedef struct _GnmParseError GnmParseError;
+typedef struct _FunctionEvalInfo FunctionEvalInfo;
+typedef struct _GnmFunc GnmFunc;
+typedef struct _ErrorInfo ErrorInfo;
+
+typedef enum {
+ CELL_ITER_ALL = 0,
+ CELL_ITER_IGNORE_NONEXISTENT = 1 << 0,
+ CELL_ITER_IGNORE_EMPTY = 1 << 1,
+ CELL_ITER_IGNORE_BLANK = (CELL_ITER_IGNORE_NONEXISTENT | CELL_ITER_IGNORE_EMPTY),
+ CELL_ITER_IGNORE_HIDDEN = 1 << 2, /* hidden manually */
+
+ /* contains SUBTOTAL, or hidden row in a filter */
+ CELL_ITER_IGNORE_SUBTOTAL = 1 << 3
+} CellIterFlags;
+typedef GnmValue *(*CellIterFunc) (Sheet *sheet, int col, int row,
+ GnmCell *cell, gpointer user_data);
+
+typedef enum {
+ SPANCALC_SIMPLE = 0x0, /* Just calc spans */
+ SPANCALC_RESIZE = 0x1, /* Calculate sizes of all cells */
+ SPANCALC_RE_RENDER = 0x2, /* Render and Size all cells */
+ SPANCALC_RENDER = 0x4, /* Render and Size any unrendered cells */
+ SPANCALC_ROW_HEIGHT = 0x8 /* Resize the row height */
+} SpanCalcFlags;
+
+typedef enum {
+ GNM_EXPR_EVAL_SCALAR_NON_EMPTY = 0,
+ GNM_EXPR_EVAL_PERMIT_NON_SCALAR = 0x1,
+ GNM_EXPR_EVAL_PERMIT_EMPTY = 0x2
+} GnmExprEvalFlags;
+
+typedef struct _GnmMemChunk GnmMemChunk;
+typedef struct _GnmString GnmString;
+
+typedef struct _GnmCmdContext GnmCmdContext;
+typedef struct _IOContext IOContext;
+typedef struct _GnmFileSaver GnmFileSaver;
+typedef struct _GnmFileOpener GnmFileOpener;
+typedef struct _XmlParseContext XmlParseContext;
+
+typedef struct _GnmPlugin GnmPlugin;
+typedef struct _GnmPluginService GnmPluginService;
+typedef struct _GnmPluginLoader GnmPluginLoader;
+
+typedef struct _GnmSortData GnmSortData;
+typedef struct _GnmSearchReplace GnmSearchReplace;
+typedef struct _GnmConsolidate GnmConsolidate;
+typedef struct _GnmValidation GnmValidation;
+typedef struct _GnmFilter GnmFilter;
+typedef struct _GnmFilterCondition GnmFilterCondition;
+typedef struct _GnmHLink GnmHLink;
+typedef struct _GnmInputMsg GnmInputMsg;
+
+typedef struct _PrintInformation PrintInformation;
+typedef struct _SolverParameters SolverParameters;
+typedef struct _GnmRelocUndo GnmRelocUndo;
+
+#endif /* GNUMERIC_H */
--- /dev/null
+++ lib/goffice/split/plugin-service.c
@@ -0,0 +1,770 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * plugin-service.c: Plugin services - reading XML info, activating, etc.
+ * (everything independent of plugin loading method)
+ *
+ * Author: Zbigniew Chyla (cyba at gnome.pl)
+ */
+
+#include <config.h>
+#include "gnumeric.h"
+#include "plugin-service-impl.h"
+
+#include "gutils.h"
+//#include "workbook.h"
+//#include "workbook-view.h"
+//#include "func.h"
+#include "io-context.h"
+#include "error-info.h"
+#include "file.h"
+//#include "file-priv.h"
+#include "plugin.h"
+#include "xml-io.h"
+
+#include <gsf/gsf-input.h>
+#include <gsf/gsf-output.h>
+#include <libxml/globals.h>
+#include <gsf/gsf-impl-utils.h>
+#include <gsf/gsf-utils.h>
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+static GHashTable *services = NULL;
+
+static FileFormatLevel
+parse_format_level_str (gchar const *format_level_str, FileFormatLevel def)
+{
+ FileFormatLevel format_level;
+
+ if (format_level_str == NULL) {
+ format_level = def;
+ } else if (g_ascii_strcasecmp (format_level_str, "none") == 0) {
+ format_level = FILE_FL_NONE;
+ } else if (g_ascii_strcasecmp (format_level_str, "write_only") == 0) {
+ format_level = FILE_FL_WRITE_ONLY;
+ } else if (g_ascii_strcasecmp (format_level_str, "new") == 0) {
+ format_level = FILE_FL_NEW;
+ } else if (g_ascii_strcasecmp (format_level_str, "manual") == 0) {
+ format_level = FILE_FL_MANUAL;
+ } else if (g_ascii_strcasecmp (format_level_str, "manual_remember") == 0) {
+ format_level = FILE_FL_MANUAL_REMEMBER;
+ } else if (g_ascii_strcasecmp (format_level_str, "auto") == 0) {
+ format_level = FILE_FL_AUTO;
+ } else {
+ format_level = def;
+ }
+
+ return format_level;
+}
+
+static GHashTable *
+get_plugin_file_savers_hash (GnmPlugin *plugin)
+{
+ GHashTable *hash;
+
+ hash = g_object_get_data (G_OBJECT (plugin), "file_savers_hash");
+ if (hash == NULL) {
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ g_object_set_data_full (
+ G_OBJECT (plugin), "file_savers_hash",
+ hash, (GDestroyNotify) g_hash_table_destroy);
+ }
+
+ return hash;
+}
+
+
+static void
+plugin_service_init (GObject *obj)
+{
+ GnmPluginService *service = GNM_PLUGIN_SERVICE (obj);
+
+ service->id = NULL;
+ service->is_active = FALSE;
+ service->is_loaded = FALSE;
+ service->plugin = NULL;
+ service->cbs_ptr = NULL;
+ service->saved_description = NULL;
+}
+
+static void
+plugin_service_finalize (GObject *obj)
+{
+ GnmPluginService *service = GNM_PLUGIN_SERVICE (obj);
+ GObjectClass *parent_class;
+
+ g_free (service->id);
+ service->id = NULL;
+ g_free (service->saved_description);
+ service->saved_description = NULL;
+
+ parent_class = g_type_class_peek (G_TYPE_OBJECT);
+ parent_class->finalize (obj);
+}
+
+static void
+plugin_service_class_init (GObjectClass *gobject_class)
+{
+ GnmPluginServiceClass *plugin_service_class = GPS_CLASS (gobject_class);
+
+ gobject_class->finalize = plugin_service_finalize;
+ plugin_service_class->read_xml = NULL;
+ plugin_service_class->activate = NULL;
+ plugin_service_class->deactivate = NULL;
+ plugin_service_class->get_description = NULL;
+}
+
+GSF_CLASS (GnmPluginService, plugin_service,
+ plugin_service_class_init, plugin_service_init,
+ G_TYPE_OBJECT)
+
+
+/****************************************************************************/
+
+/*
+ * PluginServiceGeneral
+ */
+
+typedef struct{
+ GnmPluginServiceClass plugin_service_class;
+} PluginServiceGeneralClass;
+
+struct _PluginServiceGeneral {
+ GnmPluginService plugin_service;
+ PluginServiceGeneralCallbacks cbs;
+};
+
+
+static void
+plugin_service_general_init (GObject *obj)
+{
+ PluginServiceGeneral *service_general = GNM_PLUGIN_SERVICE_GENERAL (obj);
+
+ GNM_PLUGIN_SERVICE (obj)->cbs_ptr = &service_general->cbs;
+ service_general->cbs.plugin_func_init = NULL;
+ service_general->cbs.plugin_func_cleanup = NULL;
+}
+
+static void
+plugin_service_general_activate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ PluginServiceGeneral *service_general = GNM_PLUGIN_SERVICE_GENERAL (service);
+ ErrorInfo *error = NULL;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ plugin_service_load (service, &error);
+ if (error != NULL) {
+ *ret_error = error_info_new_str_with_details (
+ _("Error while loading plugin service."),
+ error);
+ return;
+ }
+ g_return_if_fail (service_general->cbs.plugin_func_init != NULL);
+ service_general->cbs.plugin_func_init (service, &error);
+ if (error != NULL) {
+ *ret_error = error_info_new_str_with_details (
+ _("Initializing function inside plugin returned error."),
+ error);
+ return;
+ }
+ service->is_active = TRUE;
+}
+
+static void
+plugin_service_general_deactivate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ PluginServiceGeneral *service_general = GNM_PLUGIN_SERVICE_GENERAL (service);
+ ErrorInfo *error = NULL;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ g_return_if_fail (service_general->cbs.plugin_func_cleanup != NULL);
+ service_general->cbs.plugin_func_cleanup (service, &error);
+ if (error != NULL) {
+ *ret_error = error_info_new_str_with_details (
+ _("Cleanup function inside plugin returned error."),
+ error);
+ return;
+ }
+ service->is_active = FALSE;
+}
+
+static char *
+plugin_service_general_get_description (GnmPluginService *service)
+{
+ return g_strdup (_("General"));
+}
+
+static void
+plugin_service_general_class_init (GObjectClass *gobject_class)
+{
+ GnmPluginServiceClass *plugin_service_class = GPS_CLASS (gobject_class);
+
+ plugin_service_class->activate = plugin_service_general_activate;
+ plugin_service_class->deactivate = plugin_service_general_deactivate;
+ plugin_service_class->get_description = plugin_service_general_get_description;
+}
+
+GSF_CLASS (PluginServiceGeneral, plugin_service_general,
+ plugin_service_general_class_init, plugin_service_general_init,
+ GNM_PLUGIN_SERVICE_TYPE)
+
+/** -- **/
+
+/*
+ * PluginServicePluginLoader
+ */
+
+typedef struct{
+ GnmPluginServiceClass plugin_service_class;
+} PluginServicePluginLoaderClass;
+
+struct _PluginServicePluginLoader {
+ GnmPluginService plugin_service;
+ PluginServicePluginLoaderCallbacks cbs;
+};
+
+
+static void
+plugin_service_plugin_loader_init (GObject *obj)
+{
+ PluginServicePluginLoader *service_plugin_loader = GNM_PLUGIN_SERVICE_PLUGIN_LOADER (obj);
+
+ GNM_PLUGIN_SERVICE (obj)->cbs_ptr = &service_plugin_loader->cbs;
+}
+
+GType
+plugin_service_plugin_loader_generate_type (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ PluginServicePluginLoader *service_plugin_loader = GNM_PLUGIN_SERVICE_PLUGIN_LOADER (service);
+ ErrorInfo *error = NULL;
+ GType loader_type;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ plugin_service_load (service, &error);
+ if (error == NULL) {
+ loader_type = service_plugin_loader->cbs.plugin_func_get_loader_type (
+ service, &error);
+ if (error == NULL)
+ return loader_type;
+ *ret_error = error;
+ } else {
+ *ret_error = error_info_new_str_with_details (
+ _("Error while loading plugin service."),
+ error);
+ }
+ return G_TYPE_NONE;
+}
+
+static void
+plugin_service_plugin_loader_activate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ gchar *full_id;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ full_id = g_strconcat (
+ gnm_plugin_get_id (service->plugin), ":", service->id, NULL);
+ plugins_register_loader (full_id, service);
+ g_free (full_id);
+ service->is_active = TRUE;
+}
+
+static void
+plugin_service_plugin_loader_deactivate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ gchar *full_id;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ full_id = g_strconcat (
+ gnm_plugin_get_id (service->plugin), ":", service->id, NULL);
+ plugins_register_loader (full_id, service);
+ g_free (full_id);
+ service->is_active = FALSE;
+}
+
+static char *
+plugin_service_plugin_loader_get_description (GnmPluginService *service)
+{
+ return g_strdup (_("Plugin loader"));
+}
+
+static void
+plugin_service_plugin_loader_class_init (GObjectClass *gobject_class)
+{
+ GnmPluginServiceClass *plugin_service_class = GPS_CLASS (gobject_class);
+
+ plugin_service_class->activate = plugin_service_plugin_loader_activate;
+ plugin_service_class->deactivate = plugin_service_plugin_loader_deactivate;
+ plugin_service_class->get_description = plugin_service_plugin_loader_get_description;
+}
+
+GSF_CLASS (PluginServicePluginLoader, plugin_service_plugin_loader,
+ plugin_service_plugin_loader_class_init, plugin_service_plugin_loader_init,
+ GNM_PLUGIN_SERVICE_TYPE)
+
+
+/*
+ * PluginServiceUI
+ */
+typedef struct{
+ GnmPluginServiceClass plugin_service_class;
+} PluginServiceUIClass;
+
+struct _PluginServiceUI {
+ GnmPluginService plugin_service;
+
+ char *file_name;
+ GSList *actions;
+
+ gpointer layout_id;
+ PluginServiceUICallbacks cbs;
+};
+
+static void
+plugin_service_ui_init (GObject *obj)
+{
+ PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (obj);
+
+ GNM_PLUGIN_SERVICE (obj)->cbs_ptr = &service_ui->cbs;
+ service_ui->file_name = NULL;
+ service_ui->actions = NULL;
+ service_ui->layout_id = NULL;
+ service_ui->cbs.plugin_func_exec_action = NULL;
+}
+
+static void
+plugin_service_ui_finalize (GObject *obj)
+{
+ PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (obj);
+ GObjectClass *parent_class;
+
+ g_free (service_ui->file_name);
+ service_ui->file_name = NULL;
+ gnm_slist_free_custom (service_ui->actions, (GFreeFunc)gnm_action_free);
+ service_ui->actions = NULL;
+
+ parent_class = g_type_class_peek (GNM_PLUGIN_SERVICE_TYPE);
+ parent_class->finalize (obj);
+}
+
+static void
+cb_ui_service_activate (GnmAction const *action, WorkbookControl *wbc, GnmPluginService *service)
+{
+ ErrorInfo *load_error = NULL;
+
+ plugin_service_load (service, &load_error);
+ if (load_error == NULL) {
+ PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
+ ErrorInfo *ignored_error = NULL;
+
+ g_return_if_fail (service_ui->cbs.plugin_func_exec_action != NULL);
+ service_ui->cbs.plugin_func_exec_action (
+ service, action, wbc, &ignored_error);
+ if (ignored_error != NULL) {
+ error_info_print (ignored_error);
+ error_info_free (ignored_error);
+ }
+ } else {
+ error_info_print (load_error);
+ error_info_free (load_error);
+ }
+}
+
+static void
+plugin_service_ui_read_xml (GnmPluginService *service, xmlNode *tree, ErrorInfo **ret_error)
+{
+ PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
+ char *file_name;
+ xmlNode *verbs_node;
+ GSList *actions = NULL;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ file_name = xml_node_get_cstr (tree, "file");
+ if (file_name == NULL) {
+ *ret_error = error_info_new_str (
+ _("Missing file name."));
+ return;
+ }
+ verbs_node = e_xml_get_child_by_name (tree, "actions");
+ if (verbs_node != NULL) {
+ xmlNode *ptr;
+ xmlChar *name, *label, *icon;
+ gboolean always_available;
+ GnmAction *action;
+
+ for (ptr = verbs_node->xmlChildrenNode; ptr != NULL; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL ||
+ strcmp (ptr->name, "action"))
+ continue;
+ name = xml_node_get_cstr (ptr, "name");
+ label = xml_node_get_cstr (ptr, "label");
+ icon = xml_node_get_cstr (ptr, "icon");
+ if (!xml_node_get_bool (ptr, "always_available", &always_available))
+ always_available = FALSE;
+ action = gnm_action_new (name, label, icon, always_available,
+ (GnmActionHandler) cb_ui_service_activate);
+ if (NULL != name) xmlFree (name);
+ if (NULL != name) xmlFree (label);
+ if (NULL != name) xmlFree (icon);
+ if (NULL != action)
+ GNM_SLIST_PREPEND (actions, action);
+ }
+ }
+ GNM_SLIST_REVERSE (actions);
+
+ service_ui->file_name = file_name;
+ service_ui->actions = actions;
+}
+
+static void
+plugin_service_ui_activate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
+ GError *err = NULL;
+ char *full_file_name;
+ char *xml_ui;
+ char const *textdomain;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ full_file_name = g_build_filename (
+ gnm_plugin_get_dir_name (service->plugin),
+ service_ui->file_name, NULL);
+ if (!g_file_get_contents (full_file_name, &xml_ui, NULL, &err)) {
+ *ret_error = error_info_new_printf (
+ _("Cannot read UI description from XML file %s."),
+ full_file_name);
+ g_free (full_file_name);
+ return;
+ }
+ g_free (full_file_name);
+
+ textdomain = gnm_plugin_get_textdomain (service->plugin);
+ service_ui->layout_id = gnm_app_add_extra_ui (
+ service_ui->actions,
+ xml_ui, textdomain, service);
+ service->is_active = TRUE;
+}
+
+static void
+plugin_service_ui_deactivate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ gnm_app_remove_extra_ui (service_ui->layout_id);
+ service_ui->layout_id = NULL;
+ service->is_active = FALSE;
+}
+
+static char *
+plugin_service_ui_get_description (GnmPluginService *service)
+{
+ PluginServiceUI *service_ui = GNM_PLUGIN_SERVICE_UI (service);
+ int n_actions;
+
+ n_actions = g_slist_length (service_ui->actions);
+ return g_strdup_printf (
+ ngettext (
+ N_("User interface with %d action"),
+ N_("User interface with %d actions"),
+ n_actions),
+ n_actions);
+}
+
+static void
+plugin_service_ui_class_init (GObjectClass *gobject_class)
+{
+ GnmPluginServiceClass *plugin_service_class = GPS_CLASS (gobject_class);
+
+ gobject_class->finalize = plugin_service_ui_finalize;
+ plugin_service_class->read_xml = plugin_service_ui_read_xml;
+ plugin_service_class->activate = plugin_service_ui_activate;
+ plugin_service_class->deactivate = plugin_service_ui_deactivate;
+ plugin_service_class->get_description = plugin_service_ui_get_description;
+}
+
+GSF_CLASS (PluginServiceUI, plugin_service_ui,
+ plugin_service_ui_class_init, plugin_service_ui_init,
+ GNM_PLUGIN_SERVICE_TYPE)
+
+/**************************************************************************
+ * PluginServiceGObjectLoader
+ */
+
+static char *
+plugin_service_gobject_loader_get_description (GnmPluginService *service)
+{
+ return g_strdup (_("GObject loader"));
+}
+
+static void
+plugin_service_gobject_loader_read_xml (GnmPluginService *service,
+ G_GNUC_UNUSED xmlNode *tree,
+ G_GNUC_UNUSED ErrorInfo **ret_error)
+{
+ PluginServiceGObjectLoaderClass *gobj_loader_class = GPS_GOBJECT_LOADER_GET_CLASS (service);
+ g_return_if_fail (gobj_loader_class->pending != NULL);
+ g_hash_table_replace (gobj_loader_class->pending, service->id, service);
+}
+
+static void
+plugin_service_gobject_loader_class_init (PluginServiceGObjectLoaderClass *gobj_loader_class)
+{
+ GnmPluginServiceClass *psc = GPS_CLASS (gobj_loader_class);
+
+ psc->get_description = plugin_service_gobject_loader_get_description;
+ psc->read_xml = plugin_service_gobject_loader_read_xml;
+ gobj_loader_class->pending = NULL;
+}
+
+GSF_CLASS (PluginServiceGObjectLoader, plugin_service_gobject_loader,
+ plugin_service_gobject_loader_class_init, NULL,
+ GNM_PLUGIN_SERVICE_SIMPLE_TYPE)
+
+/**************************************************************************
+ * PluginServiceSimple
+ */
+
+static void
+plugin_service_simple_activate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ service->is_active = TRUE;
+}
+
+static void
+plugin_service_simple_deactivate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ service->is_active = FALSE;
+}
+
+static void
+plugin_service_simple_class_init (GObjectClass *gobject_class)
+{
+ GnmPluginServiceClass *psc = GPS_CLASS (gobject_class);
+
+ psc->activate = plugin_service_simple_activate;
+ psc->deactivate = plugin_service_simple_deactivate;
+}
+
+GSF_CLASS (PluginServiceSimple, plugin_service_simple,
+ plugin_service_simple_class_init,
+ NULL,
+ GNM_PLUGIN_SERVICE_TYPE)
+
+/* ---------------------------------------------------------------------- */
+
+void
+plugin_service_load (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+
+ if (service->is_loaded)
+ return;
+ gnm_plugin_load_service (service->plugin, service, ret_error);
+ if (*ret_error == NULL)
+ service->is_loaded = TRUE;
+}
+
+void
+plugin_service_unload (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ ErrorInfo *error = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (!service->is_loaded) {
+ return;
+ }
+ gnm_plugin_unload_service (service->plugin, service, &error);
+ if (error == NULL) {
+ service->is_loaded = FALSE;
+ } else {
+ *ret_error = error;
+ }
+}
+
+GnmPluginService *
+plugin_service_new (GnmPlugin *plugin, xmlNode *tree, ErrorInfo **ret_error)
+{
+ GnmPluginService *service = NULL;
+ char *type_str;
+ ErrorInfo *service_error = NULL;
+ GnmPluginServiceCreate ctor;
+
+ g_return_val_if_fail (IS_GNM_PLUGIN (plugin), NULL);
+ g_return_val_if_fail (tree != NULL, NULL);
+ g_return_val_if_fail (strcmp (tree->name, "service") == 0, NULL);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ type_str = xml_node_get_cstr (tree, "type");
+ if (type_str == NULL) {
+ *ret_error = error_info_new_str (_("No \"type\" attribute on \"service\" element."));
+ return NULL;
+ }
+
+ ctor = g_hash_table_lookup (services, type_str);
+ if (ctor == NULL) {
+ *ret_error = error_info_new_printf (_("Unknown service type: %s."), type_str);
+ g_free (type_str);
+ return NULL;
+ }
+ g_free (type_str);
+
+ service = g_object_new (ctor(), NULL);
+ service->plugin = plugin;
+ service->id = xml_node_get_cstr (tree, "id");
+ if (service->id == NULL)
+ service->id = g_strdup ("default");
+
+ if (GPS_GET_CLASS (service)->read_xml != NULL) {
+ GPS_GET_CLASS (service)->read_xml (service, tree, &service_error);
+ if (service_error != NULL) {
+ *ret_error = error_info_new_str_with_details (
+ _("Error reading service information."), service_error);
+ g_object_unref (service);
+ service = NULL;
+ }
+ }
+
+ return service;
+}
+
+char const *
+plugin_service_get_id (GnmPluginService *service)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN_SERVICE (service), NULL);
+
+ return service->id;
+}
+
+char const *
+plugin_service_get_description (GnmPluginService *service)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN_SERVICE (service), NULL);
+
+ if (service->saved_description == NULL) {
+ service->saved_description = GPS_GET_CLASS (service)->get_description (service);
+ }
+
+ return service->saved_description;
+}
+
+GnmPlugin *
+plugin_service_get_plugin (GnmPluginService *service)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN_SERVICE (service), NULL);
+
+ return service->plugin;
+}
+
+gpointer
+plugin_service_get_cbs (GnmPluginService *service)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN_SERVICE (service), NULL);
+ g_return_val_if_fail (service->cbs_ptr != NULL, NULL);
+
+ return service->cbs_ptr;
+}
+
+void
+plugin_service_activate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (service->is_active) {
+ return;
+ }
+#ifdef PLUGIN_ALWAYS_LOAD
+ {
+ ErrorInfo *load_error = NULL;
+
+ plugin_service_load (service, &load_error);
+ if (load_error != NULL) {
+ *ret_error = error_info_new_str_with_details (
+ _("We must load service before activating it (PLUGIN_ALWAYS_LOAD is set) "
+ "but loading failed."), load_error);
+ return;
+ }
+ }
+#endif
+ GPS_GET_CLASS (service)->activate (service, ret_error);
+}
+
+void
+plugin_service_deactivate (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (!service->is_active) {
+ return;
+ }
+ GPS_GET_CLASS (service)->deactivate (service, ret_error);
+ if (*ret_error == NULL) {
+ ErrorInfo *ignored_error = NULL;
+
+ service->is_active = FALSE;
+ /* FIXME */
+ plugin_service_unload (service, &ignored_error);
+ error_info_free (ignored_error);
+ }
+}
+
+/*****************************************************************************/
+
+void
+plugin_services_init (void)
+{
+ static struct {
+ char const *type_str;
+ GnmPluginServiceCreate ctor;
+ } const builtin_services[] = {
+ { "general", plugin_service_general_get_type},
+ //{ "clipboard", plugin_service_clipboard_get_type},
+ //{ "file_opener", plugin_service_file_opener_get_type},
+ //{ "file_saver", plugin_service_file_saver_get_type},
+ //{ "function_group", plugin_service_function_group_get_type},
+ { "plugin_loader", plugin_service_plugin_loader_get_type},
+ { "ui", plugin_service_ui_get_type}
+/* base classes, not really for direct external use,
+ * put here for expositional purposes
+ */
+#if 0
+ { "gobject_loader", plugin_service_gobject_loader_get_type}
+ { "simple", plugin_service_simple_get_type}
+#endif
+ };
+ unsigned i;
+
+ g_return_if_fail (services == NULL);
+
+ services = g_hash_table_new (g_str_hash, g_str_equal);
+ for (i = 0; i < G_N_ELEMENTS (builtin_services); i++)
+ plugin_service_define (builtin_services[i].type_str,
+ builtin_services[i].ctor);
+}
+
+void
+plugin_services_shutdown (void)
+{
+ g_return_if_fail (services != NULL);
+ g_hash_table_destroy (services);
+ services = NULL;
+}
+
+/**
+ * Allow the definition of new service types
+ **/
+void
+plugin_service_define (char const *type_str, GnmPluginServiceCreate ctor)
+{
+ g_return_if_fail (services != NULL);
+
+ g_return_if_fail (NULL == g_hash_table_lookup (services, type_str));
+
+ g_hash_table_insert (services, (gpointer)type_str, ctor);
+}
--- /dev/null
+++ lib/goffice/split/numbers.h
@@ -0,0 +1,216 @@
+#ifndef GNUMERIC_NUMBERS_H
+#define GNUMERIC_NUMBERS_H
+
+#include <math.h>
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#ifdef HAVE_IEEE754_H
+#include <ieee754.h>
+#endif
+
+#ifdef WITH_LONG_DOUBLE
+
+#ifdef HAVE_SUNMATH_H
+#include <sunmath.h>
+#endif
+
+typedef long double gnm_float;
+#ifdef HAVE_STRTOLD
+#ifdef MUST_PROTOTYPE_STRTOLD
+long double strtold (const char *, char **);
+#endif
+#define strtognum strtold
+#else
+#define NEED_FAKE_STRTOGNUM
+/* Defined in gutils.c */
+gnm_float strtognum (const char *str, char **end);
+#endif
+
+#ifdef HAVE_MODFL
+#define modfgnum modfl
+#else
+#define NEED_FAKE_MODFGNUM
+/* Defined in gutils.c */
+gnm_float modfgnum (gnm_float x, gnm_float *iptr);
+#endif
+
+#ifdef HAVE_LDEXPL
+#define ldexpgnum ldexpl
+#else
+#define NEED_FAKE_LDEXPGNUM
+/* Defined in gutils.c */
+gnm_float ldexpgnum (gnm_float x, int exp);
+#endif
+
+#ifdef HAVE_FREXPL
+#define frexpgnum frexpl
+#else
+#define NEED_FAKE_FREXPGNUM
+/* Defined in gutils.c */
+gnm_float frexpgnum (gnm_float x, int *exp);
+#endif
+
+#ifdef HAVE_ERF
+#define erfgnum erfl
+#else
+#define NEED_FAKE_ERFGNUM
+/* Defined in gutils.c */
+gnm_float erfgnum (gnm_float x);
+#endif
+
+#ifdef HAVE_ERFC
+#define erfcgnum erfcl
+#else
+#define NEED_FAKE_ERFCGNUM
+/* Defined in gutils.c */
+gnm_float erfcgnum (gnm_float x);
+#endif
+
+#ifdef HAVE_YNL
+#define yngnum ynl
+#else
+#define NEED_FAKE_YNGNUM
+/* Defined in gutils.c */
+gnm_float yngnum (int n, gnm_float x);
+#endif
+
+#define acosgnum acosl
+#define acoshgnum acoshl
+#define asingnum asinl
+#define asinhgnum asinhl
+#define atan2gnum atan2l
+#define atangnum atanl
+#define atanhgnum atanhl
+#define ceilgnum ceill
+#define cosgnum cosl
+#define coshgnum coshl
+#define expgnum expl
+#define expm1gnum expm1l
+#define finitegnum finitel
+#define floorgnum floorl
+#define fmodgnum fmodl
+#define gnumabs fabsl
+#define hypotgnum hypotl
+#define isnangnum isnanl
+#define lgammagnum lgammal
+#define lgamma_rgnum lgammal_r
+#define log10gnum log10l
+#define log1pgnum log1pl
+#define loggnum logl
+#define powgnum powl
+#define singnum sinl
+#define sinhgnum sinhl
+#define sqrtgnum sqrtl
+#define tangnum tanl
+#define tanhgnum tanhl
+
+#define GNUM_FORMAT_e "Le"
+#define GNUM_FORMAT_E "LE"
+#define GNUM_FORMAT_f "Lf"
+#define GNUM_FORMAT_g "Lg"
+#define GNUM_DIG LDBL_DIG
+#define GNUM_MANT_DIG LDBL_MANT_DIG
+#define GNUM_MIN_EXP LDBL_MIN_EXP
+#define GNUM_MAX_EXP LDBL_MAX_EXP
+#define GNUM_MIN LDBL_MIN
+#define GNUM_MAX LDBL_MAX
+#define GNUM_EPSILON LDBL_EPSILON
+#define GNM_const(_c) _c ## L
+
+#else /* !WITH_LONG_DOUBLE */
+
+typedef double gnm_float;
+
+#define acosgnum acos
+#define acoshgnum acosh
+#define asingnum asin
+#define asinhgnum asinh
+#define atan2gnum atan2
+#define atangnum atan
+#define atanhgnum atanh
+#define ceilgnum ceil
+#define cosgnum cos
+#define coshgnum cosh
+#define erfcgnum erfc
+#define erfgnum erf
+#define expgnum exp
+#define expm1gnum expm1
+#define floorgnum floor
+#define fmodgnum fmod
+#define frexpgnum frexp
+#define gnumabs fabs
+#define hypotgnum hypot
+#define isnangnum isnan
+#define ldexpgnum ldexp
+#define lgammagnum lgamma
+#define lgamma_rgnum lgamma_r
+#define log10gnum log10
+#define log1pgnum log1p
+#define loggnum log
+#define modfgnum modf
+#define powgnum pow
+#define singnum sin
+#define sinhgnum sinh
+#define sqrtgnum sqrt
+#define strtognum strtod
+#define tangnum tan
+#define tanhgnum tanh
+#define yngnum yn
+
+/* What a circus! */
+#ifdef HAVE_FINITE
+#define finitegnum finite
+#elif defined(HAVE_ISFINITE)
+#define finitegnum isfinite
+#elif defined(FINITE)
+#define finitegnum FINITE
+#error "I don't know an equivalent of finite for your system; you lose"
+#endif
+
+#ifndef HAVE_LGAMMA_R
+#define NEED_FAKE_LGAMMA_R
+/* Defined in gutils.c */
+gnm_float lgamma_rgnum (gnm_float x, int *signp);
+#endif
+
+#ifndef HAVE_EXPM1
+#define NEED_FAKE_EXPM1
+/* Defined in gutils.c */
+gnm_float expm1 (gnm_float x);
+#endif
+
+#ifndef HAVE_ASINH
+#define NEED_FAKE_ASINH
+/* Defined in gutils.c */
+gnm_float asinh (gnm_float x);
+#endif
+
+#ifndef HAVE_ACOSH
+#define NEED_FAKE_ACOSH
+/* Defined in gutils.c */
+gnm_float acosh (gnm_float x);
+#endif
+
+#ifndef HAVE_ATANH
+#define NEED_FAKE_ATANH
+/* Defined in gutils.c */
+gnm_float atanh (gnm_float x);
+#endif
+
+#define GNUM_FORMAT_e "e"
+#define GNUM_FORMAT_E "E"
+#define GNUM_FORMAT_f "f"
+#define GNUM_FORMAT_g "g"
+#define GNUM_DIG DBL_DIG
+#define GNUM_MANT_DIG DBL_MANT_DIG
+#define GNUM_MIN_EXP DBL_MIN_EXP
+#define GNUM_MAX_EXP DBL_MAX_EXP
+#define GNUM_MIN DBL_MIN
+#define GNUM_MAX DBL_MAX
+#define GNUM_EPSILON DBL_EPSILON
+#define GNM_const(_c) _c
+
+#endif
+
+#endif /* GNUMERIC_NUMBERS_H */
--- /dev/null
+++ lib/goffice/split/value.h
@@ -0,0 +1,206 @@
+#ifndef GNUMERIC_VALUE_H
+#define GNUMERIC_VALUE_H
+
+#include <glib.h>
+#include "gnumeric.h"
+#include "position.h"
+#include "numbers.h"
+
+typedef enum {
+ /* Use magic values to act as a signature
+ * DO NOT CHANGE THESE NUMBERS
+ * As of version 0.57 they are using as keys
+ * in the xml
+ */
+ VALUE_EMPTY = 10,
+ VALUE_BOOLEAN = 20, /* Keep bool < int < float */
+ VALUE_INTEGER = 30,
+ VALUE_FLOAT = 40,
+ VALUE_ERROR = 50,
+ VALUE_STRING = 60,
+ VALUE_CELLRANGE = 70,
+ VALUE_ARRAY = 80
+} GnmValueType;
+
+typedef struct {
+ GnmValueType const type;
+ GnmFormat *fmt;
+} GnmValueAny;
+struct _GnmValueBool {
+ GnmValueType const type;
+ GnmFormat *fmt;
+ gboolean val;
+};
+struct _GnmValueInt {
+ GnmValueType const type;
+ GnmFormat *fmt;
+ int val;
+};
+struct _GnmValueFloat {
+ GnmValueType const type;
+ GnmFormat *fmt;
+ gnm_float val;
+};
+struct _GnmValueErr {
+ GnmValueType const type;
+ GnmFormat *fmt;
+ GnmString *mesg;
+ /* Currently unused. Intended to support audit functions */
+ GnmEvalPos src;
+};
+struct _GnmValueStr {
+ GnmValueType const type;
+ GnmFormat *fmt;
+ GnmString *val;
+};
+struct _GnmValueRange {
+ GnmValueType const type;
+ GnmFormat *fmt;
+ GnmRangeRef cell;
+};
+struct _GnmValueArray {
+ GnmValueType const type;
+ GnmFormat *fmt;
+ int x, y;
+ GnmValue ***vals; /* Array [x][y] */
+};
+
+/* FIXME */
+union _GnmValue {
+ GnmValueType const type;
+ GnmValueAny v_any;
+ GnmValueBool v_bool;
+ GnmValueInt v_int;
+ GnmValueFloat v_float;
+ GnmValueErr v_err;
+ GnmValueStr v_str;
+ GnmValueRange v_range;
+ GnmValueArray v_array;
+};
+
+#define VALUE_TYPE(v) ((v)->v_any.type)
+#define VALUE_FMT(v) ((v)->v_any.fmt)
+#define VALUE_IS_EMPTY(v) (((v) == NULL) || ((v)->type == VALUE_EMPTY))
+#define VALUE_IS_EMPTY_OR_ERROR(v) (VALUE_IS_EMPTY(v) || (v)->type == VALUE_ERROR)
+#define VALUE_IS_STRING(v) ((v)->type == VALUE_STRING)
+#define VALUE_IS_NUMBER(v) (((v)->type == VALUE_INTEGER) || \
+ ((v)->type == VALUE_FLOAT) || \
+ ((v)->type == VALUE_BOOLEAN))
+
+typedef enum {
+ IS_EQUAL,
+ IS_LESS,
+ IS_GREATER,
+ TYPE_MISMATCH
+} GnmValDiff;
+
+GnmValue *value_new_empty (void);
+GnmValue *value_new_bool (gboolean b);
+GnmValue *value_new_int (int i);
+GnmValue *value_new_float (gnm_float f);
+GnmValue *value_new_error (GnmEvalPos const *pos, char const *mesg);
+GnmValue *value_new_error_str (GnmEvalPos const *pos, GnmString *mesg);
+GnmValue *value_new_error_std (GnmEvalPos const *pos, GnmStdError err);
+GnmValue *value_new_error_NULL (GnmEvalPos const *pos);
+GnmValue *value_new_error_DIV0 (GnmEvalPos const *pos);
+GnmValue *value_new_error_VALUE (GnmEvalPos const *pos);
+GnmValue *value_new_error_REF (GnmEvalPos const *pos);
+GnmValue *value_new_error_NAME (GnmEvalPos const *pos);
+GnmValue *value_new_error_NUM (GnmEvalPos const *pos);
+GnmValue *value_new_error_NA (GnmEvalPos const *pos);
+GnmValue *value_new_error_RECALC (GnmEvalPos const *pos);
+GnmValue *value_new_string (char const *str);
+GnmValue *value_new_string_nocopy (char *str);
+GnmValue *value_new_string_str (GnmString *str);
+GnmValue *value_new_cellrange_unsafe (GnmCellRef const *a, GnmCellRef const *b);
+GnmValue *value_new_cellrange (GnmCellRef const *a, GnmCellRef const *b,
+ int eval_col, int eval_row);
+GnmValue *value_new_cellrange_r (Sheet *sheet, GnmRange const *r);
+GnmValue *value_new_array (guint cols, guint rows);
+GnmValue *value_new_array_empty (guint cols, guint rows);
+GnmValue *value_new_array_non_init (guint cols, guint rows);
+GnmValue *value_new_from_string (GnmValueType t, char const *str,
+ GnmFormat *sf, gboolean translated);
+
+void value_release (GnmValue *v);
+void value_set_fmt (GnmValue *v, GnmFormat const *fmt);
+void value_dump (GnmValue const *v);
+GnmValue *value_dup (GnmValue const *v);
+
+gnm_float value_diff (GnmValue const *a, GnmValue const *b);
+GnmValDiff value_compare (GnmValue const *a, GnmValue const *b,
+ gboolean case_sensitive);
+int value_cmp (void const *ptr_a, void const *ptr_b);
+int value_cmp_reverse (void const *ptr_a, void const *ptr_b);
+gint value_equal (GnmValue const *a, GnmValue const *b);
+guint value_hash (GnmValue const *v);
+
+char const *value_peek_string (GnmValue const *v);
+char *value_get_as_string (GnmValue const *v);
+void value_get_as_gstring (GnmValue const *v, GString *target,
+ GnmExprConventions const *conv);
+
+int value_get_as_int (GnmValue const *v);
+gnm_float value_get_as_float (GnmValue const *v);
+GnmValue *value_coerce_to_number (GnmValue *v, gboolean *valid,
+ GnmEvalPos const *ep);
+
+GnmValue *value_error_set_pos (GnmValueErr *err, GnmEvalPos const *pos);
+GnmStdError value_error_classify (GnmValue const *v);
+char const *value_error_name (GnmStdError err, gboolean translated);
+
+gboolean value_get_as_bool (GnmValue const *v, gboolean *err);
+gboolean value_get_as_checked_bool (GnmValue const *v);
+GnmRangeRef const *value_get_rangeref (GnmValue const *v);
+
+/* Area functions ( works on VALUE_RANGE or VALUE_ARRAY */
+/* The GnmEvalPos provides a Sheet context; this allows
+ calculation of relative references. 'x','y' give the position */
+typedef GnmValue *(*ValueAreaFunc) (GnmValue const *v, GnmEvalPos const *ep,
+ int x, int y, gpointer user);
+GnmValue *value_area_foreach (GnmValue const *v, GnmEvalPos const *ep,
+ CellIterFlags flags,
+ ValueAreaFunc func, gpointer user);
+int value_area_get_width (GnmValue const *v, GnmEvalPos const *ep);
+int value_area_get_height (GnmValue const *v, GnmEvalPos const *ep);
+GnmValue const *value_area_fetch_x_y (GnmValue const *v, int x, int y,
+ GnmEvalPos const *ep);
+GnmValue const *value_area_get_x_y (GnmValue const *v, int x, int y,
+ GnmEvalPos const *ep);
+
+/* A zero integer, not to be freed or changed. */
+extern GnmValue const *value_zero;
+extern GnmValueErr const value_terminate_err;
+#define VALUE_TERMINATE ((GnmValue *)&value_terminate_err)
+
+void value_array_set (GnmValue *array, int col, int row, GnmValue *v);
+void value_array_resize (GnmValue *v, int width, int height);
+
+/* FIXME: this stuff below ought to go elsewhere. */
+typedef struct {
+ int row;
+ GSList *conditions;
+} database_criteria_t;
+typedef gboolean (*criteria_test_fun_t) (GnmValue const *x, GnmValue const *y);
+typedef struct {
+ criteria_test_fun_t fun;
+ GnmValue *x;
+ int column;
+} func_criteria_t;
+void parse_criteria (GnmValue *criteria,
+ criteria_test_fun_t *fun,
+ GnmValue **test_value,
+ CellIterFlags *iter_flags,
+ GnmDateConventions const *date_conv);
+void free_criterias (GSList *criterias);
+GSList *find_rows_that_match (Sheet *sheet, int first_col,
+ int first_row, int last_col, int last_row,
+ GSList *criterias, gboolean unique_only);
+GSList *parse_database_criteria (GnmEvalPos const *ep, GnmValue *database, GnmValue *criteria);
+int find_column_of_field (GnmEvalPos const *ep, GnmValue *database, GnmValue *field);
+
+/* Protected */
+void value_init (void);
+void value_shutdown (void);
+
+#endif /* GNUMERIC_VALUE_H */
--- /dev/null
+++ lib/goffice/split/regutf8.h
@@ -0,0 +1,35 @@
+#ifndef GNUMERIC_REGUTF8_H
+#define GNUMERIC_REGUTF8_H
+
+#include <glib.h>
+#include <sys/types.h>
+#include <goffice/cut-n-paste/pcre/pcreposix.h>
+
+/* -------------------------------------------------------------------------- */
+
+#ifndef REG_EPAREN
+#define REG_EPAREN REG_BADPAT
+#endif
+
+#ifndef REG_EBRACE
+#define REG_EBRACE REG_BADPAT
+#endif
+
+#ifndef REG_EESCAPE
+#define REG_EESCAPE REG_BADPAT
+#endif
+
+#ifndef REG_NOERROR
+#define REG_NOERROR 0
+#endif
+
+#ifndef REG_OK
+#define REG_OK REG_NOERROR
+#endif
+
+int gnumeric_regcomp_XL (go_regex_t *preg, char const *pattern, int cflags);
+
+const char *gnumeric_regexp_quote1 (GString *target, const char *s);
+void gnumeric_regexp_quote (GString *target, const char *s);
+
+#endif
--- /dev/null
+++ lib/goffice/split/command-context.c
@@ -0,0 +1,199 @@
+/*
+ * command-context.c : Error dispatch utilities.
+ *
+ * Author:
+ * Jody Goldberg <jody at gnome.org>
+ *
+ * (C) 1999-2001 Jody Goldberg
+ */
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "command-context-priv.h"
+#include "ranges.h"
+
+#include <gsf/gsf-impl-utils.h>
+
+#define CC_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GNM_CMD_CONTEXT_TYPE, GnmCmdContextClass))
+
+static GError *
+format_message (GQuark id, char const *message)
+{
+ char const *msg = message ? message : "";
+ return g_error_new_literal (id, 0, msg);
+}
+
+void
+gnm_cmd_context_error (GnmCmdContext *context, GError *err)
+{
+ g_return_if_fail (IS_GNM_CMD_CONTEXT (context));
+ CC_CLASS (context)->error.error (context, err);
+}
+
+void
+gnm_cmd_context_error_info (GnmCmdContext *context, ErrorInfo *error)
+{
+ g_return_if_fail (IS_GNM_CMD_CONTEXT (context));
+ CC_CLASS (context)->error.error_info (context, error);
+}
+
+void
+gnm_cmd_context_error_system (GnmCmdContext *context, char const *message)
+{
+ GError *err = format_message (gnm_error_system (), message);
+ gnm_cmd_context_error (context, err);
+ g_error_free (err);
+}
+
+void
+gnm_cmd_context_error_import (GnmCmdContext *context, char const *message)
+{
+ GError *err = format_message (gnm_error_import (), message);
+ gnm_cmd_context_error (context, err);
+ g_error_free (err);
+}
+
+void
+gnm_cmd_context_error_export (GnmCmdContext *context, char const *message)
+{
+ GError *err = format_message (gnm_error_export (), message);
+ gnm_cmd_context_error (context, err);
+ g_error_free (err);
+}
+
+void
+gnm_cmd_context_error_invalid (GnmCmdContext *context, char const *msg, char const *val)
+{
+ GError *err = g_error_new (gnm_error_invalid(), 0, "Invalid %s : '%s'", msg, val);
+ gnm_cmd_context_error (context, err);
+ g_error_free (err);
+}
+
+void
+gnm_cmd_context_error_calc (GnmCmdContext *context, char const *msg)
+{
+ GError *err = format_message (gnm_error_calc (), msg);
+ gnm_cmd_context_error (context, err);
+ g_error_free (err);
+}
+
+void
+gnm_cmd_context_error_splits_array (GnmCmdContext *context,
+ G_GNUC_UNUSED char const *cmd,
+ GnmRange const *array)
+{
+ GError *err;
+
+ if (array != NULL)
+ err = g_error_new (gnm_error_array(), 1,
+ _("Would split array %s"), range_name (array));
+ else
+ err = g_error_new (gnm_error_array(), 0,
+ _("Would split an array"));
+ gnm_cmd_context_error (context, err);
+}
+
+GQuark
+gnm_error_system (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("gnm_error_system");
+ return quark;
+}
+GQuark
+gnm_error_import (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("gnm_error_import");
+ return quark;
+}
+GQuark
+gnm_error_export (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("gnm_error_export");
+ return quark;
+}
+GQuark
+gnm_error_array (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("gnm_error_array");
+ return quark;
+}
+
+GQuark
+gnm_error_calc (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("gnm_error_calc");
+ return quark;
+}
+
+GQuark
+gnm_error_invalid (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("gnm_error_invalid");
+ return quark;
+}
+
+void
+cmd_context_progress_set (GnmCmdContext *context, gfloat f)
+{
+ g_return_if_fail (IS_GNM_CMD_CONTEXT (context));
+
+ CC_CLASS (context)->progress_set (context, f);
+}
+
+void
+cmd_context_progress_message_set (GnmCmdContext *context, gchar const *msg)
+{
+ g_return_if_fail (IS_GNM_CMD_CONTEXT (context));
+
+ if (msg == NULL)
+ msg = " ";
+ CC_CLASS (context)->progress_message_set (context, msg);
+}
+
+char *
+gnm_cmd_context_get_password (GnmCmdContext *cc, char const *filename)
+{
+ g_return_val_if_fail (IS_GNM_CMD_CONTEXT (cc), NULL);
+
+ return CC_CLASS (cc)->get_password (cc, filename);
+}
+
+void
+gnm_cmd_context_set_sensitive (GnmCmdContext *cc, gboolean sensitive)
+{
+ g_return_if_fail (IS_GNM_CMD_CONTEXT (cc));
+
+ CC_CLASS (cc)->set_sensitive (cc, sensitive);
+}
+
+GType
+gnm_cmd_context_get_type (void)
+{
+ static GType gnm_cmd_context_type = 0;
+
+ if (!gnm_cmd_context_type) {
+ static GTypeInfo const gnm_cmd_context_info = {
+ sizeof (GnmCmdContextClass), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ gnm_cmd_context_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GnmCmdContext", &gnm_cmd_context_info, 0);
+ }
+
+ return gnm_cmd_context_type;
+}
+
--- /dev/null
+++ lib/goffice/split/plugin-loader.h
@@ -0,0 +1,59 @@
+#ifndef GNUMERIC_PLUGIN_LOADER_H
+#define GNUMERIC_PLUGIN_LOADER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libxml/tree.h>
+#include "gnumeric.h"
+#include "error-info.h"
+#include "plugin.h"
+
+#define TYPE_GNM_PLUGIN_LOADER (gnm_plugin_loader_get_type ())
+#define GNM_PLUGIN_LOADER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_GNM_PLUGIN_LOADER, GnmPluginLoader))
+#define GNM_PLUGIN_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_GNM_PLUGIN_LOADER, GnmPluginLoaderClass))
+#define IS_GNM_PLUGIN_LOADER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_GNM_PLUGIN_LOADER))
+#define IS_GNM_PLUGIN_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_GNM_PLUGIN_LOADER))
+
+typedef struct _GnmPluginLoaderClass GnmPluginLoaderClass;
+
+struct _GnmPluginLoader {
+ GObject object;
+
+ GnmPlugin *plugin;
+ gboolean is_base_loaded;
+ gint n_loaded_services;
+};
+
+struct _GnmPluginLoaderClass {
+ GObjectClass parent_class;
+
+ void (*set_attributes) (GnmPluginLoader *loader, GHashTable *attrs, ErrorInfo **ret_error);
+ void (*load_base) (GnmPluginLoader *loader, ErrorInfo **ret_error);
+ void (*unload_base) (GnmPluginLoader *loader, ErrorInfo **ret_error);
+ void (*load_service_general) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ void (*unload_service_general) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ //void (*load_service_file_opener) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ //void (*unload_service_file_opener) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ //void (*load_service_file_saver) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ //void (*unload_service_file_saver) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ // void (*load_service_function_group) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ // void (*unload_service_function_group) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ void (*load_service_plugin_loader) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ void (*unload_service_plugin_loader) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ void (*load_service_ui) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+ void (*unload_service_ui) (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+};
+
+GType gnm_plugin_loader_get_type (void);
+
+void gnm_plugin_loader_set_attributes (GnmPluginLoader *loader,
+ GHashTable *attrs,
+ ErrorInfo **ret_error);
+void gnm_plugin_loader_set_plugin (GnmPluginLoader *loader, GnmPlugin *plugin);
+void gnm_plugin_loader_load_base (GnmPluginLoader *loader, ErrorInfo **ret_error);
+void gnm_plugin_loader_unload_base (GnmPluginLoader *loader, ErrorInfo **ret_error);
+void gnm_plugin_loader_load_service (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+void gnm_plugin_loader_unload_service (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+gboolean gnm_plugin_loader_is_base_loaded (GnmPluginLoader *loader);
+
+#endif /* GNUMERIC_PLUGIN_LOADER_H */
--- /dev/null
+++ lib/goffice/split/value.c
@@ -0,0 +1,1827 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * value.c: Utilies for handling, creating, removing values.
+ *
+ * Authors:
+ * Miguel de Icaza (miguel at gnu.org).
+ * Michael Meeks (mmeeks at gnu.org)
+ * Jody Goldberg (jgolderg at home.com)
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "value.h"
+
+//#include "parse-util.h"
+#include "style.h"
+#include "format.h"
+#include "str.h"
+#include "position.h"
+#include "mathfunc.h"
+#include "gutils.h"
+//#include "workbook.h"
+#include "split.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+#include <string.h>
+#include <ranges.h>
+//#include <sheet.h>
+//#include <cell.h>
+#include <number-match.h>
+
+#ifndef USE_VALUE_POOLS
+#define USE_VALUE_POOLS 1
+#endif
+
+#if USE_VALUE_POOLS
+#define CHUNK_ALLOC(T,p) ((T*)gnm_mem_chunk_alloc (p))
+#define CHUNK_FREE(p,v) gnm_mem_chunk_free ((p), (v))
+#else
+#define CHUNK_ALLOC(T,c) g_new (T,1)
+#define CHUNK_FREE(p,v) g_free ((v))
+#endif
+
+
+static struct {
+ char const *C_name;
+ char const *locale_name;
+ GnmString *locale_name_str;
+} standard_errors[] = {
+ { N_("#NULL!"), NULL, NULL },
+ { N_("#DIV/0!"), NULL, NULL },
+ { N_("#VALUE!"), NULL, NULL },
+ { N_("#REF!"), NULL, NULL },
+ { N_("#NAME?"), NULL, NULL },
+ { N_("#NUM!"), NULL, NULL },
+ { N_("#N/A"), NULL, NULL },
+ { N_("#RECALC!"), NULL, NULL },
+ { N_("#UNKNOWN!"), NULL, NULL }
+};
+
+
+GnmValue *
+value_new_empty (void)
+{
+ /* This is a constant. No need to allocate any memory. */
+ static GnmValueAny v = { VALUE_EMPTY, NULL };
+ return (GnmValue *)&v;
+}
+
+/* Memory pool for ints and bools. */
+static GnmMemChunk *value_int_pool;
+GnmValue *
+value_new_bool (gboolean b)
+{
+ GnmValueBool *v = CHUNK_ALLOC (GnmValueBool, value_int_pool);
+ *((GnmValueType *)&(v->type)) = VALUE_BOOLEAN;
+ v->fmt = NULL;
+ v->val = b;
+ return (GnmValue *)v;
+}
+
+GnmValue *
+value_new_int (int i)
+{
+ GnmValueInt *v = CHUNK_ALLOC (GnmValueInt, value_int_pool);
+ *((GnmValueType *)&(v->type)) = VALUE_INTEGER;
+ v->fmt = NULL;
+ v->val = i;
+ return (GnmValue *)v;
+}
+
+static GnmMemChunk *value_float_pool;
+GnmValue *
+value_new_float (gnm_float f)
+{
+ if (finitegnum (f)) {
+ GnmValueFloat *v = CHUNK_ALLOC (GnmValueFloat, value_float_pool);
+ *((GnmValueType *)&(v->type)) = VALUE_FLOAT;
+ v->fmt = NULL;
+ v->val = f;
+ return (GnmValue *)v;
+ } else {
+ /* FIXME: bogus ep sent here. What to do? */
+ return value_new_error_NUM (NULL);
+ }
+}
+
+/* Memory pool for error values. */
+static GnmMemChunk *value_error_pool;
+GnmValue *
+value_new_error (GnmEvalPos const *ep, char const *mesg)
+{
+ GnmValueErr *v = CHUNK_ALLOC (GnmValueErr, value_error_pool);
+ *((GnmValueType *)&(v->type)) = VALUE_ERROR;
+ v->fmt = NULL;
+ v->mesg = gnm_string_get (mesg);
+ return (GnmValue *)v;
+}
+
+GnmValue *
+value_new_error_str (GnmEvalPos const *ep, GnmString *mesg)
+{
+ GnmValueErr *v = CHUNK_ALLOC (GnmValueErr, value_error_pool);
+ *((GnmValueType *)&(v->type)) = VALUE_ERROR;
+ v->fmt = NULL;
+ v->mesg = gnm_string_ref (mesg);
+ return (GnmValue *)v;
+}
+
+GnmValue *
+value_new_error_std (GnmEvalPos const *pos, GnmStdError err)
+{
+ size_t i = (size_t)err;
+ g_return_val_if_fail (i < G_N_ELEMENTS (standard_errors), NULL);
+
+ return value_new_error_str (pos, standard_errors[i].locale_name_str);
+}
+
+
+GnmValue *
+value_new_error_NULL (GnmEvalPos const *pos)
+{
+ return value_new_error_str (pos, standard_errors[GNM_ERROR_NULL].locale_name_str);
+}
+
+GnmValue *
+value_new_error_DIV0 (GnmEvalPos const *pos)
+{
+ return value_new_error_str (pos, standard_errors[GNM_ERROR_DIV0].locale_name_str);
+}
+
+GnmValue *
+value_new_error_VALUE (GnmEvalPos const *pos)
+{
+ return value_new_error_str (pos, standard_errors[GNM_ERROR_VALUE].locale_name_str);
+}
+
+GnmValue *
+value_new_error_REF (GnmEvalPos const *pos)
+{
+ return value_new_error_str (pos, standard_errors[GNM_ERROR_REF].locale_name_str);
+}
+
+GnmValue *
+value_new_error_NAME (GnmEvalPos const *pos)
+{
+ return value_new_error_str (pos, standard_errors[GNM_ERROR_NAME].locale_name_str);
+}
+
+GnmValue *
+value_new_error_NUM (GnmEvalPos const *pos)
+{
+ return value_new_error_str (pos, standard_errors[GNM_ERROR_NUM].locale_name_str);
+}
+
+GnmValue *
+value_new_error_NA (GnmEvalPos const *pos)
+{
+ return value_new_error_str (pos, standard_errors[GNM_ERROR_NA].locale_name_str);
+}
+
+GnmValue *
+value_new_error_RECALC (GnmEvalPos const *pos)
+{
+ return value_new_error_str (pos, standard_errors[GNM_ERROR_RECALC].locale_name_str);
+}
+
+char const *
+value_error_name (GnmStdError err, gboolean translated)
+{
+ size_t i = (size_t)err;
+ g_return_val_if_fail (i < G_N_ELEMENTS (standard_errors), NULL);
+
+ if (translated)
+ return standard_errors[i].locale_name;
+ else
+ return standard_errors[i].C_name;
+}
+
+/**
+ * value_error_set_pos :
+ * @err :
+ * @pos :
+ *
+ * Change the position of a ValueError.
+ */
+GnmValue *
+value_error_set_pos (GnmValueErr *err, GnmEvalPos const *pos)
+{
+ g_return_val_if_fail (err != NULL, NULL);
+ g_return_val_if_fail (err->type == VALUE_ERROR, NULL);
+
+ err->src = *pos;
+ return (GnmValue *)err;
+}
+
+GnmStdError
+value_error_classify (GnmValue const *v)
+{
+ size_t i;
+
+ g_return_val_if_fail (v != NULL, GNM_ERROR_UNKNOWN);
+
+ if (v->type != VALUE_ERROR)
+ return GNM_ERROR_UNKNOWN;
+
+ for (i = 0; i < G_N_ELEMENTS (standard_errors); i++)
+ if (standard_errors[i].locale_name_str == v->v_err.mesg)
+ return (GnmStdError)i;
+
+ return GNM_ERROR_UNKNOWN;
+}
+
+
+static GnmMemChunk *value_string_pool;
+
+/* NOTE : absorbs the reference */
+GnmValue *
+value_new_string_str (GnmString *str)
+{
+ GnmValueStr *v = CHUNK_ALLOC (GnmValueStr, value_string_pool);
+ *((GnmValueType *)&(v->type)) = VALUE_STRING;
+ v->fmt = NULL;
+ v->val = str;
+ return (GnmValue *)v;
+}
+
+GnmValue *
+value_new_string (char const *str)
+{
+ return value_new_string_str (gnm_string_get (str));
+}
+
+GnmValue *
+value_new_string_nocopy (char *str)
+{
+ return value_new_string_str (gnm_string_get_nocopy (str));
+}
+
+static GnmMemChunk *value_range_pool;
+GnmValue *
+value_new_cellrange_unsafe (GnmCellRef const *a, GnmCellRef const *b)
+{
+ GnmValueRange *v = CHUNK_ALLOC (GnmValueRange, value_range_pool);
+ *((GnmValueType *)&(v->type)) = VALUE_CELLRANGE;
+ v->fmt = NULL;
+ v->cell.a = *a;
+ v->cell.b = *b;
+ return (GnmValue *)v;
+}
+
+/**
+ * value_new_cellrange : Create a new range reference.
+ *
+ * Attempt to do a sanity check for inverted ranges.
+ * NOTE : This is no longer necessary and will be removed.
+ * mixed mode references create the possibility of inversion.
+ * users of these values need to use the utility routines to
+ * evaluate the ranges in their context and normalize then.
+ */
+GnmValue *
+value_new_cellrange (GnmCellRef const *a, GnmCellRef const *b,
+ int eval_col, int eval_row)
+{
+ GnmValueRange *v = CHUNK_ALLOC (GnmValueRange, value_range_pool);
+ int tmp;
+
+ *((GnmValueType *)&(v->type)) = VALUE_CELLRANGE;
+ v->fmt = NULL;
+ v->cell.a = *a;
+ v->cell.b = *b;
+
+ /* Sanity checking to avoid inverted ranges */
+ tmp = a->col;
+ if (a->col_relative != b->col_relative) {
+ /* Make a tmp copy of a in the same mode as b */
+ if (a->col_relative)
+ tmp += eval_col;
+ else
+ tmp -= eval_col;
+ }
+ if (tmp > b->col) {
+ v->cell.a.col = b->col;
+ v->cell.a.col_relative = b->col_relative;
+ v->cell.b.col = a->col;
+ v->cell.b.col_relative = a->col_relative;
+ }
+
+ tmp = a->row;
+ if (a->row_relative != b->row_relative) {
+ /* Make a tmp copy of a in the same mode as b */
+ if (a->row_relative)
+ tmp += eval_row;
+ else
+ tmp -= eval_row;
+ }
+ if (tmp > b->row) {
+ v->cell.a.row = b->row;
+ v->cell.a.row_relative = b->row_relative;
+ v->cell.b.row = a->row;
+ v->cell.b.row_relative = a->row_relative;
+ }
+
+ return (GnmValue *)v;
+}
+
+GnmValue *
+value_new_cellrange_r (Sheet *sheet, GnmRange const *r)
+{
+ GnmValueRange *v = CHUNK_ALLOC (GnmValueRange, value_range_pool);
+ GnmCellRef *a, *b;
+
+ *((GnmValueType *)&(v->type)) = VALUE_CELLRANGE;
+ v->fmt = NULL;
+ a = &v->cell.a;
+ b = &v->cell.b;
+
+ a->sheet = sheet;
+ b->sheet = sheet;
+ a->col = r->start.col;
+ a->row = r->start.row;
+ b->col = r->end.col;
+ b->row = r->end.row;
+ a->col_relative = b->col_relative = FALSE;
+ a->row_relative = b->row_relative = FALSE;
+
+ return (GnmValue *)v;
+}
+
+static GnmMemChunk *value_array_pool;
+GnmValue *
+value_new_array_non_init (guint cols, guint rows)
+{
+ GnmValueArray *v = CHUNK_ALLOC (GnmValueArray, value_array_pool);
+ *((GnmValueType *)&(v->type)) = VALUE_ARRAY;
+ v->fmt = NULL;
+ v->x = cols;
+ v->y = rows;
+ v->vals = g_new (GnmValue **, cols);
+ return (GnmValue *)v;
+}
+
+GnmValue *
+value_new_array (guint cols, guint rows)
+{
+ guint x, y;
+ GnmValueArray *v = (GnmValueArray *)value_new_array_non_init (cols, rows);
+
+ for (x = 0; x < cols; x++) {
+ v->vals[x] = g_new (GnmValue *, rows);
+ for (y = 0; y < rows; y++)
+ v->vals[x][y] = value_new_int (0);
+ }
+ return (GnmValue *)v;
+}
+
+GnmValue *
+value_new_array_empty (guint cols, guint rows)
+{
+ guint x, y;
+ GnmValueArray *v = (GnmValueArray *)value_new_array_non_init (cols, rows);
+
+ for (x = 0; x < cols; x++) {
+ v->vals[x] = g_new (GnmValue *, rows);
+ for (y = 0; y < rows; y++)
+ v->vals[x][y] = NULL;
+ }
+ return (GnmValue *)v;
+}
+
+GnmValue *
+value_new_from_string (GnmValueType t, char const *str, GnmFormat *sf,
+ gboolean translated)
+{
+ GnmValue *res = NULL;
+ switch (t) {
+ case VALUE_EMPTY:
+ res = value_new_empty ();
+ break;
+
+ case VALUE_BOOLEAN:
+ if (translated) {
+ /* FIXME: ascii??? */
+ if (0 == g_ascii_strcasecmp (str, format_boolean (TRUE)))
+ res = value_new_bool (TRUE);
+ else if (0 == g_ascii_strcasecmp (str, format_boolean (FALSE)))
+ res = value_new_bool (FALSE);
+ } else {
+ if (0 == g_ascii_strcasecmp (str, "TRUE"))
+ res = value_new_bool (TRUE);
+ else if (0 == g_ascii_strcasecmp (str, "FALSE"))
+ res = value_new_bool (FALSE);
+ }
+ break;
+
+ case VALUE_INTEGER: {
+ char *end;
+ long l;
+
+ errno = 0;
+ l = strtol (str, &end, 10);
+ if (str != end && *end == '\0' && errno != ERANGE)
+ res = value_new_int ((int)l);
+ break;
+ }
+
+ case VALUE_FLOAT: {
+ char *end;
+ gnm_float d;
+
+ errno = 0;
+ d = strtognum (str, &end);
+ if (str != end && *end == '\0' && errno != ERANGE)
+ res = value_new_float (d);
+ break;
+ }
+
+ case VALUE_ERROR:
+ /*
+ * Tricky. We are currently storing errors in translated
+ * format, so we might have to undo that.
+ */
+ if (!translated) {
+ size_t i;
+ for (i = 0; i < G_N_ELEMENTS (standard_errors); i++)
+ if (strcmp (standard_errors[i].C_name, str) == 0) {
+ res = value_new_error_std (NULL, (GnmStdError)i);
+ break;
+ }
+ }
+ if (!res)
+ res = value_new_error (NULL, str);
+ break;
+
+ case VALUE_STRING:
+ res = value_new_string (str);
+ break;
+
+ /* Should not happen. */
+ case VALUE_ARRAY:
+ case VALUE_CELLRANGE:
+ default:
+ g_warning ("value_new_from_string problem.");
+ return NULL;
+ }
+
+ if (res)
+ value_set_fmt (res, sf);
+ return res;
+}
+
+void
+value_release (GnmValue *value)
+{
+ g_return_if_fail (value != NULL);
+
+ if (VALUE_FMT (value) != NULL)
+ style_format_unref (VALUE_FMT (value));
+
+ switch (value->type) {
+ case VALUE_EMPTY:
+ /* We did not allocate anything, there is nothing to free */
+ return;
+
+ case VALUE_BOOLEAN:
+ case VALUE_INTEGER:
+ CHUNK_FREE (value_int_pool, value);
+ return;
+
+ case VALUE_FLOAT:
+ CHUNK_FREE (value_float_pool, value);
+ return;
+
+ case VALUE_ERROR:
+ /* Do not release VALUE_TERMINATE, it is a magic number */
+ if (value == VALUE_TERMINATE) {
+ g_warning ("Someone freed VALUE_TERMINATE -- shame on them.");
+ return;
+ }
+
+ gnm_string_unref (value->v_err.mesg);
+ CHUNK_FREE (value_error_pool, value);
+ return;
+
+ case VALUE_STRING:
+ gnm_string_unref (value->v_str.val);
+ CHUNK_FREE (value_string_pool, value);
+ return;
+
+ case VALUE_ARRAY: {
+ GnmValueArray *v = (GnmValueArray *)value;
+ int x, y;
+
+ for (x = 0; x < v->x; x++) {
+ for (y = 0; y < v->y; y++) {
+ if (v->vals[x][y])
+ value_release (v->vals[x][y]);
+ }
+ g_free (v->vals[x]);
+ }
+
+ g_free (v->vals);
+ CHUNK_FREE (value_array_pool, value);
+ return;
+ }
+
+ case VALUE_CELLRANGE:
+ CHUNK_FREE (value_range_pool, value);
+ return;
+
+ default:
+ /*
+ * If we don't recognize the type this is probably garbage.
+ * Do not free it to avoid heap corruption
+ */
+ g_warning ("value_release problem.");
+ return;
+ }
+ g_assert_not_reached ();
+}
+
+/**
+ * value_dup :
+ * @src : #GnmValue
+ *
+ * Returns a copy of @src. @src == NULL will return NULL
+ **/
+GnmValue *
+value_dup (GnmValue const *src)
+{
+ GnmValue *res;
+
+ if (src == NULL)
+ return NULL;
+
+ switch (src->type){
+ case VALUE_EMPTY:
+ res = value_new_empty ();
+ break;
+
+ case VALUE_BOOLEAN:
+ res = value_new_bool (src->v_bool.val);
+ break;
+
+ case VALUE_INTEGER:
+ res = value_new_int (src->v_int.val);
+ break;
+
+ case VALUE_FLOAT:
+ res = value_new_float (src->v_float.val);
+ break;
+
+ case VALUE_ERROR:
+ res = value_new_error_str (&src->v_err.src,
+ src->v_err.mesg);
+ break;
+
+ case VALUE_STRING:
+ gnm_string_ref (src->v_str.val);
+ res = value_new_string_str (src->v_str.val);
+ break;
+
+ case VALUE_CELLRANGE:
+ res = value_new_cellrange_unsafe (&src->v_range.cell.a,
+ &src->v_range.cell.b);
+ break;
+
+ case VALUE_ARRAY: {
+ int x, y;
+ GnmValueArray *array = (GnmValueArray *)value_new_array_non_init (
+ src->v_array.x, src->v_array.y);
+
+ for (x = 0; x < array->x; x++) {
+ array->vals[x] = g_new (GnmValue *, array->y);
+ for (y = 0; y < array->y; y++)
+ array->vals[x][y] = value_dup (src->v_array.vals[x][y]);
+ }
+ res = (GnmValue *)array;
+ break;
+ }
+
+ default:
+ g_warning ("value_dup problem.");
+ res = value_new_empty ();
+ }
+ value_set_fmt (res, VALUE_FMT (src));
+ return res;
+}
+
+/**
+ * value_cmp :
+ * @ptr_a :
+ * @ptr_b :
+ *
+ * qsort style comparison function.
+ **/
+int
+value_cmp (void const *ptr_a, void const *ptr_b)
+{
+ GnmValue const *a = *(GnmValue const **)ptr_a;
+ GnmValue const *b = *(GnmValue const **)ptr_b;
+ switch (value_compare (a, b, TRUE)) {
+ case IS_EQUAL : return 0;
+ case IS_LESS : return -1;
+ case IS_GREATER : return 1;
+ default :
+ break;
+ };
+ return a->type - b->type;
+}
+
+int
+value_cmp_reverse (void const *ptr_a, void const *ptr_b)
+{
+ GnmValue const *a = *(GnmValue const **)ptr_a;
+ GnmValue const *b = *(GnmValue const **)ptr_b;
+ switch (value_compare (a, b, TRUE)) {
+ case IS_EQUAL : return 0;
+ case IS_LESS : return 1;
+ case IS_GREATER : return -1;
+ default :
+ break;
+ };
+ return b->type - a->type;
+}
+
+gint
+value_equal (GnmValue const *a, GnmValue const *b)
+{
+ if (a->type != b->type)
+ return FALSE;
+
+ switch (a->type) {
+ case VALUE_BOOLEAN:
+ return a->v_bool.val == b->v_bool.val;
+
+ case VALUE_STRING:
+ return a->v_str.val == b->v_str.val;
+
+ case VALUE_ERROR:
+ return a->v_err.mesg == b->v_err.mesg;
+
+ case VALUE_INTEGER:
+ return a->v_int.val == b->v_int.val;
+
+ case VALUE_FLOAT:
+ return a->v_float.val == b->v_float.val;
+
+ case VALUE_EMPTY:
+ return TRUE;
+
+ /*
+ case VALUE_CELLRANGE:
+ return cellref_equal (&a->v_range.cell.a, &b->v_range.cell.a) &&
+ cellref_equal (&a->v_range.cell.b, &b->v_range.cell.b);
+ */
+
+ case VALUE_ARRAY:
+ if (a->v_array.x == b->v_array.x && a->v_array.y == b->v_array.y) {
+ int x, y;
+
+ for (y = 0; y < a->v_array.y; y++)
+ for (x = 0; x < a->v_array.x; x++)
+ if (!value_equal (a->v_array.vals[x][y],
+ b->v_array.vals[x][y]))
+ return FALSE;
+ return TRUE;
+ } else
+ return FALSE;
+
+#ifndef DEBUG_SWITCH_ENUM
+ default:
+ g_assert_not_reached ();
+ return FALSE;
+#endif
+ }
+}
+
+guint
+value_hash (GnmValue const *v)
+{
+ switch (v->type) {
+ case VALUE_BOOLEAN:
+ return v->v_bool.val ? 0x555aaaa : 0xaaa5555;
+
+ case VALUE_STRING:
+ return g_str_hash (v->v_str.val->str);
+
+ case VALUE_ERROR:
+ return g_str_hash (v->v_err.mesg->str);
+
+ case VALUE_INTEGER:
+ return (guint)(v->v_int.val);
+
+ case VALUE_FLOAT: {
+ int expt;
+ gnm_float mant = frexpgnum (gnumabs (v->v_float.val), &expt);
+ guint h = ((guint)(0x80000000u * mant)) ^ expt;
+ if (v->v_float.val >= 0)
+ h ^= 0x55555555;
+ return h;
+ }
+
+ case VALUE_EMPTY:
+ return 42;
+
+ //case VALUE_CELLRANGE:
+ /* FIXME: take sheet into account? */
+ /*
+ return (cellref_hash (&v->v_range.cell.a) * 3) ^
+ cellref_hash (&v->v_range.cell.b);
+ */
+ //return 42;
+
+ case VALUE_ARRAY: {
+ int i;
+ guint h = (v->v_array.x * 257) ^ (v->v_array.y + 42);
+
+ /* For speed, just walk the diagonal. */
+ for (i = 0; i < v->v_array.x && i < v->v_array.y; i++) {
+ h *= 5;
+ if (v->v_array.vals[i][i])
+ h ^= value_hash (v->v_array.vals[i][i]);
+ }
+ return h;
+ }
+
+#ifndef DEBUG_SWITCH_ENUM
+ default:
+ g_assert_not_reached ();
+ return 0;
+#endif
+ }
+}
+
+
+gboolean
+value_get_as_bool (GnmValue const *v, gboolean *err)
+{
+ *err = FALSE;
+
+ if (v == NULL)
+ return FALSE;
+
+ switch (v->type) {
+ case VALUE_EMPTY:
+ return FALSE;
+
+ case VALUE_BOOLEAN:
+ return v->v_bool.val;
+
+ case VALUE_STRING:
+ return v->v_str.val->str[0] != '\0';
+
+ case VALUE_INTEGER:
+ return v->v_int.val != 0;
+
+ case VALUE_FLOAT:
+ return v->v_float.val != 0.0;
+
+ default:
+ g_warning ("Unhandled value in value_get_boolean.");
+
+ case VALUE_CELLRANGE:
+ case VALUE_ARRAY:
+ case VALUE_ERROR:
+ *err = TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * use only if you are sure the value is ok
+ */
+gboolean
+value_get_as_checked_bool (GnmValue const *v)
+{
+ gboolean result, err;
+
+ result = value_get_as_bool (v, &err);
+
+ g_return_val_if_fail (!err, FALSE);
+
+ return result;
+}
+
+void
+value_get_as_gstring (GnmValue const *v, GString *target,
+ GnmExprConventions const *conv)
+{
+ if (v == NULL)
+ return;
+
+ switch (v->type){
+ case VALUE_EMPTY:
+ return;
+
+ case VALUE_ERROR: {
+ GnmStdError e = value_error_classify (v);
+ if (e == GNM_ERROR_UNKNOWN) {
+ g_string_append_c (target, '#');
+ gnm_strescape (target, v->v_err.mesg->str);
+ } else
+ g_string_append (target, value_error_name (e, conv->output_translated));
+ return;
+ }
+
+ case VALUE_BOOLEAN: {
+ gboolean b = v->v_bool.val;
+ g_string_append (target,
+ conv->output_translated
+ ? format_boolean (b)
+ : (b ? "TRUE" : "FALSE"));
+ return;
+ }
+
+ case VALUE_STRING:
+ g_string_append (target, v->v_str.val->str);
+ return;
+
+ case VALUE_INTEGER:
+ g_string_append_printf (target, "%d", v->v_int.val);
+ return;
+
+ case VALUE_FLOAT:
+ g_string_append_printf (target, "%.*" GNUM_FORMAT_g, GNUM_DIG,
+ v->v_float.val);
+ return;
+
+ case VALUE_ARRAY: {
+ char const *row_sep, *col_sep;
+ char locale_arg_sep[2], locale_col_sep[2];
+ int x, y;
+
+ if (conv->output_argument_sep)
+ row_sep = conv->output_argument_sep;
+ else {
+ locale_arg_sep[0] = format_get_arg_sep ();
+ locale_arg_sep[1] = 0;
+ row_sep = locale_arg_sep;
+ }
+
+ if (conv->output_array_col_sep)
+ col_sep = conv->output_array_col_sep;
+ else {
+ locale_col_sep[0] = format_get_col_sep ();
+ locale_col_sep[1] = 0;
+ col_sep = locale_col_sep;
+ }
+
+ g_string_append_c (target, '{');
+ for (y = 0; y < v->v_array.y; y++){
+ if (y)
+ g_string_append (target, col_sep);
+
+ for (x = 0; x < v->v_array.x; x++){
+ GnmValue const *val = v->v_array.vals[x][y];
+
+ if (x)
+ g_string_append (target, row_sep);
+
+ /* quote strings */
+ if (val->type == VALUE_STRING)
+ gnm_strescape (target, val->v_str.val->str);
+ else
+ value_get_as_gstring (val, target, conv);
+ }
+ }
+ g_string_append_c (target, '}');
+ return;
+ }
+
+#if 0
+ case VALUE_CELLRANGE: {
+ char *tmp;
+ /* Note: this makes only sense for absolute references or
+ * references relative to A1
+ */
+ GnmRange range;
+ range_init_value (&range, v);
+ tmp = global_range_name (v->v_range.cell.a.sheet, &range);
+ g_string_append (target, tmp);
+ g_free (tmp);
+ return;
+ }
+#endif // 0
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+
+/**
+ * value_get_as_string :
+ * @v :
+ *
+ * simplistic value rendering
+ *
+ * Returns a string that must be freed.
+ */
+char *
+value_get_as_string (GnmValue const *v)
+{
+ GString *res = g_string_sized_new (10);
+ value_get_as_gstring (v, res, gnm_expr_conventions_default);
+ return g_string_free (res, FALSE);
+}
+
+/*
+ * Result will stay valid until (a) the value is disposed of, or (b) two
+ * further calls to this function are made.
+ */
+char const *
+value_peek_string (GnmValue const *v)
+{
+ g_return_val_if_fail (v, "");
+
+ if (v->type == VALUE_STRING)
+ return v->v_str.val->str;
+ else if (v->type == VALUE_ERROR)
+ return v->v_err.mesg->str;
+ else {
+ static char *cache[2] = { NULL, NULL };
+ static int next = 0;
+ char const *s;
+
+ g_free (cache[next]);
+ s = cache[next] = value_get_as_string (v);
+ next = (next + 1) % G_N_ELEMENTS (cache);
+ return s;
+ }
+}
+
+/*
+ * FIXME FIXME FIXME : Support errors
+ */
+int
+value_get_as_int (GnmValue const *v)
+{
+ if (v == NULL)
+ return 0;
+ switch (v->type) {
+ case VALUE_EMPTY:
+ return 0;
+
+ case VALUE_STRING:
+ return atoi (v->v_str.val->str);
+
+ case VALUE_CELLRANGE:
+ g_warning ("Getting range as a int: what to do?");
+ return 0;
+
+ case VALUE_INTEGER:
+ return v->v_int.val;
+
+ case VALUE_ARRAY:
+ return 0;
+
+ case VALUE_FLOAT:
+ return (int) gnumeric_fake_trunc (v->v_float.val);
+
+ case VALUE_BOOLEAN:
+ return v->v_bool.val ? 1 : 0;
+
+ case VALUE_ERROR:
+ return 0;
+
+ default:
+ g_warning ("value_get_as_int unknown type.");
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * FIXME FIXME FIXME : Support errors
+ */
+gnm_float
+value_get_as_float (GnmValue const *v)
+{
+ if (v == NULL)
+ return 0.;
+
+ switch (v->type) {
+ case VALUE_EMPTY:
+ return 0.;
+
+ case VALUE_STRING:
+ return strtognum (v->v_str.val->str, NULL);
+
+ case VALUE_CELLRANGE:
+ g_warning ("Getting range as a double: what to do?");
+ return 0.0;
+
+ case VALUE_INTEGER:
+ return (gnm_float) v->v_int.val;
+
+ case VALUE_ARRAY:
+ return 0.0;
+
+ case VALUE_FLOAT:
+ return (gnm_float) v->v_float.val;
+
+ case VALUE_BOOLEAN:
+ return v->v_bool.val ? 1. : 0.;
+
+ case VALUE_ERROR:
+ return 0.;
+
+ default:
+ g_warning ("value_get_as_float type error.");
+ break;
+ }
+ return 0.0;
+}
+
+GnmRangeRef const *
+value_get_rangeref (GnmValue const *v)
+{
+ g_return_val_if_fail (v->type == VALUE_CELLRANGE, NULL);
+ return &v->v_range.cell;
+}
+
+#if 0
+// -- jsled
+/**
+ * value_coerce_to_number :
+ * @v :
+ * @valid :
+ *
+ * If the value can be used as a number return that number
+ * otherwise free it at return an appropriate error
+ **/
+GnmValue *
+value_coerce_to_number (GnmValue *v, gboolean *valid, GnmEvalPos const *ep)
+{
+ g_return_val_if_fail (v != NULL, NULL);
+
+ *valid = FALSE;
+ if (v->type == VALUE_STRING) {
+ //GnmValue *tmp = format_match_number (value_peek_string (v), NULL,
+ //workbook_date_conv (ep->sheet->workbook));
+ GnmValue *tmp = format_match_number (value_peek_string (v), NULL,
+ workbook_date_conv (NULL));
+ value_release (v);
+ if (tmp == NULL)
+ return value_new_error_VALUE (ep);
+ v = tmp;
+ } else if (v->type == VALUE_ERROR)
+ return v;
+
+ if (!VALUE_IS_NUMBER (v)) {
+ value_release (v);
+ return value_new_error_VALUE (ep);
+ }
+
+ *valid = TRUE;
+ return v;
+}
+#endif // 0
+
+void
+value_array_set (GnmValue *array, int col, int row, GnmValue *v)
+{
+ g_return_if_fail (v);
+ g_return_if_fail (array->type == VALUE_ARRAY);
+ g_return_if_fail (col>=0);
+ g_return_if_fail (row>=0);
+ g_return_if_fail (array->v_array.y > row);
+ g_return_if_fail (array->v_array.x > col);
+
+ if (array->v_array.vals[col][row] != NULL)
+ value_release (array->v_array.vals[col][row]);
+ array->v_array.vals[col][row] = v;
+}
+
+void
+value_array_resize (GnmValue *v, int width, int height)
+{
+ int x, y, xcpy, ycpy;
+ GnmValue *newval;
+ GnmValue ***tmp;
+
+ g_warning ("Totally untested");
+ g_return_if_fail (v);
+ g_return_if_fail (v->type == VALUE_ARRAY);
+
+ newval = value_new_array (width, height);
+
+ xcpy = MIN (width, v->v_array.x);
+ ycpy = MIN (height, v->v_array.y);
+
+ for (x = 0; x < xcpy; x++)
+ for (y = 0; y < ycpy; y++) {
+ value_array_set (newval, x, y, v->v_array.vals[x][y]);
+ v->v_array.vals[x][y] = NULL;
+ }
+
+ tmp = v->v_array.vals;
+ v->v_array.vals = newval->v_array.vals;
+ newval->v_array.vals = tmp;
+ newval->v_array.x = v->v_array.x;
+ newval->v_array.y = v->v_array.y;
+ v->v_array.x = width;
+ v->v_array.y = height;
+
+ value_release (newval);
+}
+
+static GnmValDiff
+compare_bool_bool (GnmValue const *va, GnmValue const *vb)
+{
+ gboolean err; /* Ignored */
+ gboolean const a = value_get_as_bool (va, &err);
+ gboolean const b = value_get_as_bool (vb, &err);
+ if (a)
+ return b ? IS_EQUAL : IS_GREATER;
+ return b ? IS_LESS : IS_EQUAL;
+}
+
+static GnmValDiff
+compare_int_int (GnmValue const *va, GnmValue const *vb)
+{
+ int const a = value_get_as_int (va);
+ int const b = value_get_as_int (vb);
+ if (a == b)
+ return IS_EQUAL;
+ else if (a < b)
+ return IS_LESS;
+ else
+ return IS_GREATER;
+}
+
+static GnmValDiff
+compare_float_float (GnmValue const *va, GnmValue const *vb)
+{
+ gnm_float const a = value_get_as_float (va);
+ gnm_float const b = value_get_as_float (vb);
+ if (a == b)
+ return IS_EQUAL;
+ else if (a < b)
+ return IS_LESS;
+ else
+ return IS_GREATER;
+}
+
+/**
+ * value_diff :
+ *
+ * @a : value a
+ * @b : value b
+ *
+ * IGNORES format.
+ *
+ * Returns a non-negative difference between 2 values
+ */
+gnm_float
+value_diff (GnmValue const *a, GnmValue const *b)
+{
+ GnmValueType ta, tb;
+
+ /* Handle trivial and double NULL case */
+ if (a == b)
+ return 0.;
+
+ ta = VALUE_IS_EMPTY (a) ? VALUE_EMPTY : a->type;
+ tb = VALUE_IS_EMPTY (b) ? VALUE_EMPTY : b->type;
+
+ /* string > empty */
+ if (ta == VALUE_STRING) {
+ switch (tb) {
+ /* Strings are > (empty, or number) */
+ case VALUE_EMPTY :
+ if (*a->v_str.val->str == '\0')
+ return 0.;
+ return DBL_MAX;
+
+ /* If both are strings compare as string */
+ case VALUE_STRING :
+ {
+ gint t = g_utf8_collate (a->v_str.val->str, b->v_str.val->str);
+ if (t == 0)
+ return 0.;
+ }
+ case VALUE_INTEGER : case VALUE_FLOAT : case VALUE_BOOLEAN :
+ default :
+ return DBL_MAX;
+ }
+
+ } else if (tb == VALUE_STRING) {
+ switch (ta) {
+ /* (empty, or number) < String */
+ case VALUE_EMPTY :
+ if (*b->v_str.val->str == '\0')
+ return 0.;
+
+ case VALUE_INTEGER : case VALUE_FLOAT : case VALUE_BOOLEAN :
+ default :
+ return DBL_MAX;
+ }
+ }
+
+ /* Booleans > all numbers (Why did excel do this ?? ) */
+ if (ta == VALUE_BOOLEAN && (tb == VALUE_INTEGER || tb == VALUE_FLOAT))
+ return DBL_MAX;
+ if (tb == VALUE_BOOLEAN && (ta == VALUE_INTEGER || ta == VALUE_FLOAT))
+ return DBL_MAX;
+
+ switch ((ta > tb) ? ta : tb) {
+ case VALUE_EMPTY: /* Empty Empty compare */
+ return 0.;
+
+ case VALUE_BOOLEAN:
+ return (compare_bool_bool (a, b) == IS_EQUAL) ? 0. : DBL_MAX;
+
+ case VALUE_INTEGER: {
+ int const ia = value_get_as_int (a);
+ int const ib = value_get_as_int (b);
+ return abs (ia - ib);
+ }
+
+ case VALUE_FLOAT: {
+ gnm_float const da = value_get_as_float (a);
+ gnm_float const db = value_get_as_float (b);
+ return gnumabs (da - db);
+ }
+ default:
+ return TYPE_MISMATCH;
+ }
+}
+
+/**
+ * value_compare :
+ *
+ * @a : value a
+ * @b : value b
+ * @case_sensitive : are string comparisons case sensitive.
+ *
+ * IGNORES format.
+ */
+GnmValDiff
+value_compare (GnmValue const *a, GnmValue const *b, gboolean case_sensitive)
+{
+ GnmValueType ta, tb;
+
+ /* Handle trivial and double NULL case */
+ if (a == b)
+ return IS_EQUAL;
+
+ ta = VALUE_IS_EMPTY (a) ? VALUE_EMPTY : a->type;
+ tb = VALUE_IS_EMPTY (b) ? VALUE_EMPTY : b->type;
+
+ /* string > empty */
+ if (ta == VALUE_STRING) {
+ switch (tb) {
+ /* Strings are > (empty, or number) */
+ case VALUE_EMPTY :
+ if (*a->v_str.val->str == '\0')
+ return IS_EQUAL;
+
+ case VALUE_INTEGER : case VALUE_FLOAT :
+ return IS_GREATER;
+
+ /* Strings are < FALSE ?? */
+ case VALUE_BOOLEAN :
+ return IS_LESS;
+
+ /* If both are strings compare as string */
+ case VALUE_STRING :
+ {
+ gint t;
+
+ if (case_sensitive) {
+ t = g_utf8_collate (a->v_str.val->str, b->v_str.val->str);
+ } else {
+ char *str_a = g_utf8_casefold (a->v_str.val->str, -1);
+ char *str_b = g_utf8_casefold (b->v_str.val->str, -1);
+
+ t = g_utf8_collate (str_a, str_b);
+ g_free (str_a);
+ g_free (str_b);
+ }
+
+ if (t == 0)
+ return IS_EQUAL;
+ else if (t > 0)
+ return IS_GREATER;
+ else
+ return IS_LESS;
+ }
+ default :
+ return TYPE_MISMATCH;
+ }
+ } else if (tb == VALUE_STRING) {
+ switch (ta) {
+ /* (empty, or number) < String */
+ case VALUE_EMPTY :
+ if (*b->v_str.val->str == '\0')
+ return IS_EQUAL;
+
+ case VALUE_INTEGER : case VALUE_FLOAT :
+ return IS_LESS;
+
+ /* Strings are < FALSE ?? */
+ case VALUE_BOOLEAN :
+ return IS_GREATER;
+
+ default :
+ return TYPE_MISMATCH;
+ }
+ }
+
+ /* Booleans > all numbers (Why did excel do this ?? ) */
+ if (ta == VALUE_BOOLEAN && (tb == VALUE_INTEGER || tb == VALUE_FLOAT))
+ return IS_GREATER;
+ if (tb == VALUE_BOOLEAN && (ta == VALUE_INTEGER || ta == VALUE_FLOAT))
+ return IS_LESS;
+
+ switch ((ta > tb) ? ta : tb) {
+ case VALUE_EMPTY: /* Empty Empty compare */
+ return IS_EQUAL;
+
+ case VALUE_BOOLEAN:
+ return compare_bool_bool (a, b);
+
+ case VALUE_INTEGER:
+ return compare_int_int (a, b);
+
+ case VALUE_FLOAT:
+ return compare_float_float (a, b);
+ default:
+ return TYPE_MISMATCH;
+ }
+}
+
+void
+value_set_fmt (GnmValue *v, GnmFormat const *fmt)
+{
+ if (fmt != NULL)
+ style_format_ref ((GnmFormat *)fmt);
+ if (VALUE_FMT (v) != NULL)
+ style_format_unref (VALUE_FMT (v));
+ VALUE_FMT (v) = (GnmFormat *)fmt;
+}
+
+/****************************************************************************/
+
+static gboolean
+criteria_test_equal (GnmValue const *x, GnmValue const *y)
+{
+ if (x == NULL || y == NULL)
+ return FALSE;
+ if (VALUE_IS_NUMBER (x) && VALUE_IS_NUMBER (y))
+ return (value_get_as_float (x) == value_get_as_float (y));
+ else
+ return (x->type == VALUE_STRING &&
+ y->type == VALUE_STRING &&
+ g_ascii_strcasecmp (x->v_str.val->str, y->v_str.val->str) == 0);
+}
+
+static gboolean
+criteria_test_unequal (GnmValue const *x, GnmValue const *y)
+{
+ if (x == NULL)
+ return y != NULL;
+ if (y == NULL)
+ return TRUE;
+ if (VALUE_IS_NUMBER (x) && VALUE_IS_NUMBER (y))
+ return (value_get_as_float (x) != value_get_as_float (y));
+ else
+ /* Hmm... Is this really right? number vs string, not unequal? */
+ return (x->type == VALUE_STRING &&
+ y->type == VALUE_STRING &&
+ g_ascii_strcasecmp (x->v_str.val->str, y->v_str.val->str) != 0);
+}
+
+static gboolean
+criteria_test_less (GnmValue const *x, GnmValue const *y)
+{
+ if (x == NULL || y == NULL)
+ return FALSE;
+ if (VALUE_IS_NUMBER (x) && VALUE_IS_NUMBER (y))
+ return (value_get_as_float (x) < value_get_as_float (y));
+ else
+ return FALSE;
+}
+
+static gboolean
+criteria_test_greater (GnmValue const *x, GnmValue const *y)
+{
+ if (x == NULL || y == NULL)
+ return FALSE;
+ if (VALUE_IS_NUMBER (x) && VALUE_IS_NUMBER (y))
+ return (value_get_as_float (x) > value_get_as_float (y));
+ else
+ return FALSE;
+}
+
+static gboolean
+criteria_test_less_or_equal (GnmValue const *x, GnmValue const *y)
+{
+ if (x == NULL || y == NULL)
+ return FALSE;
+ if (VALUE_IS_NUMBER (x) && VALUE_IS_NUMBER (y))
+ return (value_get_as_float (x) <= value_get_as_float (y));
+ else
+ return FALSE;
+}
+
+static gboolean
+criteria_test_greater_or_equal (GnmValue const *x, GnmValue const *y)
+{
+ if (x == NULL || y == NULL)
+ return FALSE;
+ if (VALUE_IS_NUMBER (x) && VALUE_IS_NUMBER (y))
+ return (value_get_as_float (x) >= value_get_as_float (y));
+ else
+ return FALSE;
+}
+
+#if 0
+--jsled
+/*
+ * Finds a column index of a field.
+ */
+int
+find_column_of_field (GnmEvalPos const *ep, GnmValue *database, GnmValue *field)
+{
+ Sheet *sheet;
+ GnmCell *cell;
+ gchar *field_name;
+ int begin_col, end_col, row, n, column;
+ int offset;
+
+ offset = database->v_range.cell.a.col;
+
+ if (field->type == VALUE_INTEGER)
+ return value_get_as_int (field) + offset - 1;
+
+ if (field->type != VALUE_STRING)
+ return -1;
+
+ sheet = eval_sheet (database->v_range.cell.a.sheet, ep->sheet);
+ field_name = value_get_as_string (field);
+ column = -1;
+
+ /* find the column that is labeled after `field_name' */
+ begin_col = database->v_range.cell.a.col;
+ end_col = database->v_range.cell.b.col;
+ row = database->v_range.cell.a.row;
+
+ for (n = begin_col; n <= end_col; n++) {
+ char const *txt;
+ gboolean match;
+
+ cell = sheet_cell_get (sheet, n, row);
+ if (cell == NULL)
+ continue;
+ cell_eval (cell);
+
+ txt = cell->value
+ ? value_peek_string (cell->value)
+ : "";
+ match = (g_ascii_strcasecmp (field_name, txt) == 0);
+ if (match) {
+ column = n;
+ break;
+ }
+ }
+
+ g_free (field_name);
+ return column;
+}
+
+/*
+ * Frees the allocated memory.
+ */
+void
+free_criterias (GSList *criterias)
+{
+ GSList *list = criterias;
+
+ while (criterias != NULL) {
+ GSList *l;
+ database_criteria_t *criteria = criterias->data;
+
+ for (l = criteria->conditions; l; l = l->next) {
+ func_criteria_t *cond = l->data;
+ value_release (cond->x);
+ g_free (cond);
+ }
+
+ g_slist_free (criteria->conditions);
+ g_free (criteria);
+ criterias = criterias->next;
+ }
+ g_slist_free (list);
+}
+
+/**
+ * parse_criteria :
+ * @crit_val : #GnmValue
+ * @fun : #criteria_test_fun_t result
+ * @test_value : #GnmValue the value to compare against.
+ * @iter_flags :
+ * @date_conv : #GnmDateConventions
+ *
+ * If @crit_val is a number set @text_val and @fun to test for equality, other
+ * wise parse it as a string and see if it matches one of the available
+ * operators. Caller is responsible for freeing @test_value.
+ **/
+void
+parse_criteria (GnmValue *crit_val, criteria_test_fun_t *fun, GnmValue **test_value,
+ CellIterFlags *iter_flags, GnmDateConventions const *date_conv)
+{
+ int len;
+ char const *criteria;
+
+ if (iter_flags)
+ *iter_flags = CELL_ITER_IGNORE_BLANK;
+ if (VALUE_IS_NUMBER (crit_val)) {
+ *fun = criteria_test_equal;
+ *test_value = value_dup (crit_val);
+ return;
+ }
+
+ criteria = value_peek_string (crit_val);
+ if (strncmp (criteria, "<=", 2) == 0) {
+ *fun = criteria_test_less_or_equal;
+ len = 2;
+ } else if (strncmp (criteria, ">=", 2) == 0) {
+ *fun = criteria_test_greater_or_equal;
+ len = 2;
+ } else if (strncmp (criteria, "<>", 2) == 0) {
+ *fun = (criteria_test_fun_t) criteria_test_unequal;
+ len = 2;
+ if (iter_flags)
+ *iter_flags = CELL_ITER_ALL;
+ } else if (*criteria == '<') {
+ *fun = (criteria_test_fun_t) criteria_test_less;
+ len = 1;
+ } else if (*criteria == '=') {
+ *fun = (criteria_test_fun_t) criteria_test_equal;
+ len = 1;
+ } else if (*criteria == '>') {
+ *fun = (criteria_test_fun_t) criteria_test_greater;
+ len = 1;
+ } else {
+ *fun = (criteria_test_fun_t) criteria_test_equal;
+ len = 0;
+ }
+
+ *test_value = format_match (criteria + len, NULL, date_conv);
+ if (*test_value == NULL)
+ *test_value = value_new_string (criteria + len);
+}
+
+static GSList *
+parse_criteria_range (Sheet *sheet, int b_col, int b_row, int e_col, int e_row,
+ int *field_ind)
+{
+ database_criteria_t *new_criteria;
+ GSList *criterias = NULL;
+ GSList *conditions;
+ GnmCell *cell;
+ func_criteria_t *cond;
+ GnmDateConventions const *date_conv = workbook_date_conv (sheet->workbook);
+
+ int i, j;
+
+ for (i = b_row; i <= e_row; i++) {
+ new_criteria = g_new (database_criteria_t, 1);
+ conditions = NULL;
+
+ for (j = b_col; j <= e_col; j++) {
+ cell = sheet_cell_get (sheet, j, i);
+ if (cell != NULL)
+ cell_eval (cell);
+ if (cell_is_empty (cell))
+ continue;
+
+ cond = g_new (func_criteria_t, 1);
+ parse_criteria (cell->value, &cond->fun, &cond->x, NULL, date_conv);
+ cond->column = (field_ind != NULL) ? field_ind[j - b_col] : j - b_col;
+ conditions = g_slist_append (conditions, cond);
+ }
+
+ new_criteria->conditions = conditions;
+ criterias = g_slist_append (criterias, new_criteria);
+ }
+
+ return criterias;
+}
+
+/*
+ * Parses the criteria cell range.
+ */
+GSList *
+parse_database_criteria (GnmEvalPos const *ep, GnmValue *database, GnmValue *criteria)
+{
+ Sheet *sheet;
+ GnmCell *cell;
+ int i;
+ int b_col, b_row, e_col, e_row;
+ int *field_ind;
+
+ sheet = eval_sheet (criteria->v_range.cell.a.sheet, ep->sheet);
+ b_col = criteria->v_range.cell.a.col;
+ b_row = criteria->v_range.cell.a.row;
+ e_col = criteria->v_range.cell.b.col;
+ e_row = criteria->v_range.cell.b.row;
+
+ /* FIXME: are we sure that e_col>=b_col? */
+ /* Find the index numbers for the columns of criterias */
+ field_ind = g_alloca (sizeof (int) * (e_col - b_col + 1));
+ for (i = b_col; i <= e_col; i++) {
+ cell = sheet_cell_get (sheet, i, b_row);
+ if (cell == NULL)
+ continue;
+ cell_eval (cell);
+ if (cell_is_empty (cell))
+ continue;
+ field_ind[i - b_col] =
+ find_column_of_field (ep, database, cell->value);
+ if (field_ind[i - b_col] == -1)
+ return NULL;
+ }
+
+ return parse_criteria_range (sheet, b_col, b_row + 1,
+ e_col, e_row, field_ind);
+}
+
+/* Finds the rows from the given database that match the criteria.
+ */
+GSList *
+find_rows_that_match (Sheet *sheet, int first_col, int first_row,
+ int last_col, int last_row,
+ GSList *criterias, gboolean unique_only)
+{
+ GSList *current, *conditions, *rows;
+ GnmCell *test_cell;
+ int row, add_flag;
+ rows = NULL;
+
+ for (row = first_row; row <= last_row; row++) {
+
+ current = criterias;
+ add_flag = 1;
+ for (current = criterias; current != NULL;
+ current = current->next) {
+ database_criteria_t *current_criteria;
+
+ add_flag = 1;
+ current_criteria = current->data;
+ conditions = current_criteria->conditions;
+
+ while (conditions != NULL) {
+ func_criteria_t const *cond = conditions->data;
+
+ test_cell = sheet_cell_get (sheet,
+ first_col + cond->column, row);
+ if (test_cell != NULL)
+ cell_eval (test_cell);
+ if (cell_is_empty (test_cell))
+ continue;
+
+ if (!cond->fun (test_cell->value, cond->x)) {
+ add_flag = 0;
+ break;
+ }
+ conditions = conditions->next;
+ }
+
+ if (add_flag)
+ break;
+ }
+ if (add_flag) {
+ gint *p;
+
+ if (unique_only) {
+ GSList *c;
+ GnmCell *cell;
+ gint i, trow;
+
+ for (c = rows; c != NULL; c = c->next) {
+ trow = *((gint *) c->data);
+ for (i = first_col; i <= last_col; i++) {
+ char const *t1, *t2;
+ test_cell =
+ sheet_cell_get (sheet, i, trow);
+ cell =
+ sheet_cell_get (sheet, i, row);
+ t1 = cell->value
+ ? value_peek_string (cell->value)
+ : "";
+ t2 = test_cell->value
+ ? value_peek_string (test_cell->value)
+ : "";
+ if (strcmp (t1, t2) != 0)
+ goto row_ok;
+ }
+ goto filter_row;
+row_ok:
+ ;
+ }
+ }
+ p = g_new (gint, 1);
+ *p = row;
+ rows = g_slist_prepend (rows, (gpointer) p);
+filter_row:
+ ;
+ }
+ }
+
+ return g_slist_reverse (rows);
+}
+#endif // 0 --jsled
+
+/****************************************************************************/
+
+GnmValueErr const value_terminate_err = { VALUE_ERROR, NULL, NULL };
+static GnmValueInt const the_value_zero = { VALUE_INTEGER, NULL, 0 };
+GnmValue const *value_zero = (GnmValue const *)&the_value_zero;
+
+void
+value_init (void)
+{
+ size_t i;
+
+ for (i = 0; i < G_N_ELEMENTS (standard_errors); i++) {
+ standard_errors[i].locale_name = _(standard_errors[i].C_name);
+ standard_errors[i].locale_name_str =
+ gnm_string_get (standard_errors[i].locale_name);
+ }
+
+#if USE_VALUE_POOLS
+ /* GnmValueInt and GnmValueBool ought to have the same size. */
+ value_int_pool =
+ gnm_mem_chunk_new ("value int/bool pool",
+ MAX (sizeof (GnmValueInt), sizeof (GnmValueBool)),
+ 16 * 1024 - 128);
+
+ value_float_pool =
+ gnm_mem_chunk_new ("value float pool",
+ sizeof (GnmValueFloat),
+ 16 * 1024 - 128);
+
+ value_error_pool =
+ gnm_mem_chunk_new ("value error pool",
+ sizeof (GnmValueErr),
+ 16 * 1024 - 128);
+
+ value_string_pool =
+ gnm_mem_chunk_new ("value string pool",
+ sizeof (GnmValueStr),
+ 16 * 1024 - 128);
+
+ value_range_pool =
+ gnm_mem_chunk_new ("value range pool",
+ sizeof (GnmValueRange),
+ 16 * 1024 - 128);
+
+ value_array_pool =
+ gnm_mem_chunk_new ("value array pool",
+ sizeof (GnmValueArray),
+ 16 * 1024 - 128);
+#endif
+}
+
+void
+value_shutdown (void)
+{
+ size_t i;
+
+ for (i = 0; i < G_N_ELEMENTS (standard_errors); i++) {
+ gnm_string_unref (standard_errors[i].locale_name_str);
+ standard_errors[i].locale_name_str = NULL;
+ }
+
+#if USE_VALUE_POOLS
+ gnm_mem_chunk_destroy (value_int_pool, FALSE);
+ value_int_pool = NULL;
+
+ gnm_mem_chunk_destroy (value_float_pool, FALSE);
+ value_float_pool = NULL;
+
+ gnm_mem_chunk_destroy (value_error_pool, FALSE);
+ value_error_pool = NULL;
+
+ gnm_mem_chunk_destroy (value_string_pool, FALSE);
+ value_string_pool = NULL;
+
+ gnm_mem_chunk_destroy (value_range_pool, FALSE);
+ value_range_pool = NULL;
+
+ gnm_mem_chunk_destroy (value_array_pool, FALSE);
+ value_array_pool = NULL;
+#endif
+}
+
+/****************************************************************************/
--- /dev/null
+++ lib/goffice/split/xml-io-version.h
@@ -0,0 +1,23 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef GNUMERIC_XML_IO_VERSION_H
+#define GNUMERIC_XML_IO_VERSION_H
+
+typedef enum
+{
+ GNUM_XML_UNKNOWN = -1,
+ GNUM_XML_V1,
+ GNUM_XML_V2,
+ GNUM_XML_V3, /* >= 0.52 */
+ GNUM_XML_V4, /* >= 0.57 */
+ GNUM_XML_V5, /* >= 0.58 */
+ GNUM_XML_V6, /* >= 0.62 */
+ GNUM_XML_V7, /* >= 0.66 */
+ GNUM_XML_V8, /* >= 0.71 */
+ GNUM_XML_V9, /* >= 0.73 add print scaling */
+ GNUM_XML_V10, /* >= 1.03 remove useless Content node in cells */
+
+ /* NOTE : Keep this up to date (and in sync with the schema) */
+ GNUM_XML_LATEST = GNUM_XML_V10
+} GnumericXMLVersion;
+
+#endif /* GNUMERIC_XML_IO_VERSION_H */
--- /dev/null
+++ lib/goffice/split/style.h
@@ -0,0 +1,89 @@
+#ifndef GNUMERIC_STYLE_H
+#define GNUMERIC_STYLE_H
+
+#include <gdk/gdk.h>
+#include <libgnomeprint/gnome-font.h>
+#include <gnumeric.h>
+#include <pango/pango.h>
+
+#define DEFAULT_FONT "Sans"
+#define DEFAULT_SIZE 10.0
+
+/* Alignment definitions */
+/* Do not change these flags they are used as keys in the 1.0.x xml format. */
+typedef enum _StyleHAlignFlags {
+ HALIGN_GENERAL = 0x01,
+ HALIGN_LEFT = 0x02,
+ HALIGN_RIGHT = 0x04,
+ HALIGN_CENTER = 0x08,
+ HALIGN_FILL = 0x10,
+ HALIGN_JUSTIFY = 0x20,
+ HALIGN_CENTER_ACROSS_SELECTION = 0x40
+} StyleHAlignFlags;
+
+typedef enum _StyleVAlignFlags {
+ VALIGN_TOP = 1,
+ VALIGN_BOTTOM = 2,
+ VALIGN_CENTER = 4,
+ VALIGN_JUSTIFY = 8
+} StyleVAlignFlags;
+
+typedef enum _StyleUnderlineType {
+ UNDERLINE_NONE = 0,
+ UNDERLINE_SINGLE = 1,
+ UNDERLINE_DOUBLE = 2
+} StyleUnderlineType;
+
+struct _GnmFont {
+ int ref_count;
+ char *font_name;
+ double size_pts;
+ double scale;
+ struct {
+ /* This does not belong here. */
+ struct {
+ double digit, decimal, sign, E, e, hash;
+ } pixels, pts;
+ } approx_width;
+ double height;
+ struct {
+ PangoContext *context;
+ PangoFontMetrics *metrics;
+ PangoFont *font;
+ PangoFontDescription *font_descr;
+ PangoLayout *layout;
+ } pango;
+
+ GnomeFont *gnome_print_font;
+
+ unsigned int is_bold:1;
+ unsigned int is_italic:1;
+};
+
+void style_init (void);
+void style_shutdown (void);
+
+GnmFont *style_font_new (PangoContext *context,
+ const char *font_name,
+ double size_pts, double scale,
+ gboolean bold, gboolean italic);
+void style_font_ref (GnmFont *sf);
+void style_font_unref (GnmFont *sf);
+
+guint style_font_hash_func (gconstpointer v);
+gint style_font_equal (gconstpointer v, gconstpointer v2);
+
+SpanCalcFlags required_updates_for_style (GnmStyle const *style);
+StyleHAlignFlags style_default_halign (GnmStyle const *mstyle, GnmCell const *c);
+
+extern double gnumeric_default_font_width;
+
+GnomeFont *gnm_font_find_closest_from_weight_slant (const guchar *family,
+ GnomeFontWeight weight,
+ gboolean italic,
+ gdouble size);
+PangoContext *gnm_pango_context_get (void);
+
+// #include "mstyle.h"
+
+#endif /* GNUMERIC_STYLE_H */
--- /dev/null
+++ lib/goffice/split/gui-gnumeric.h
@@ -0,0 +1,17 @@
+#ifndef GUI_GNUMERIC_H
+#define GUI_GNUMERIC_H
+
+#include "gnumeric.h"
+
+typedef struct _ItemCursor ItemCursor;
+typedef struct _ItemGrid ItemGrid;
+typedef struct _ItemBar ItemBar;
+typedef struct _ItemEdit ItemEdit;
+typedef struct _GnmCanvas GnmCanvas;
+typedef struct _GnumericPane GnmPane;
+typedef struct _SheetControlGUI SheetControlGUI;
+typedef struct _WorkbookControlComponent WorkbookControlComponent;
+typedef struct _WorkbookControlGUI WorkbookControlGUI;
+typedef struct _WorkbookControlStandalone WorkbookControlStandalone;
+
+#endif /* GUI_GNUMERIC_H */
--- /dev/null
+++ lib/goffice/split/regutf8.c
@@ -0,0 +1,91 @@
+/*
+ * regutf8.c: UTF-8 regexp routines.
+ *
+ * Author:
+ * Morten Welinder (terra at gnome.org)
+ */
+
+#include <config.h>
+#include <regutf8.h>
+
+int
+gnumeric_regcomp_XL (go_regex_t *preg, char const *pattern, int cflags)
+{
+ GString *res = g_string_new (NULL);
+ int retval;
+
+ while (*pattern) {
+ switch (*pattern) {
+ case '~':
+ pattern++;
+ if (*pattern == '*')
+ g_string_append (res, "\\*");
+ else
+ g_string_append_c (res, *pattern);
+ if (*pattern) pattern++;
+ break;
+
+ case '*':
+ g_string_append (res, ".*");
+ pattern++;
+ break;
+
+ case '?':
+ g_string_append_c (res, '.');
+ pattern++;
+ break;
+
+ default:
+ pattern = gnumeric_regexp_quote1 (res, pattern);
+ }
+ }
+
+ retval = go_regcomp (preg, res->str, cflags);
+ g_string_free (res, TRUE);
+ return retval;
+}
+
+/*
+ * Quote a single UTF-8 encoded character from s into target and return the
+ * location of the next character in s.
+ */
+const char *
+gnumeric_regexp_quote1 (GString *target, const char *s)
+{
+ g_return_val_if_fail (target != NULL, NULL);
+ g_return_val_if_fail (s != NULL, NULL);
+
+ switch (*s) {
+ case '.': case '[': case '\\':
+ case '*': case '+': case '{': case '?':
+ case '^': case '$':
+ case '(': case '|': case ')':
+ g_string_append_c (target, '\\');
+ g_string_append_c (target, *s);
+ return s + 1;
+
+ case 0:
+ return s;
+
+ default:
+ do {
+ g_string_append_c (target, *s);
+ s++;
+ } while ((*s & 0xc0) == 0x80);
+
+ return s;
+ }
+}
+
+/*
+ * Regexp quote a UTF-8 string.
+ */
+void
+gnumeric_regexp_quote (GString *target, const char *s)
+{
+ g_return_if_fail (target != NULL);
+ g_return_if_fail (s != NULL);
+
+ while (*s)
+ s = gnumeric_regexp_quote1 (target, s);
+}
--- /dev/null
+++ lib/goffice/split/format.h
@@ -0,0 +1,133 @@
+#ifndef GNUMERIC_FORMAT_H
+#define GNUMERIC_FORMAT_H
+
+#include <sys/types.h>
+#include "gnumeric.h"
+#include "numbers.h"
+#include "regutf8.h"
+#include <pango/pango-attributes.h>
+
+typedef enum {
+ FMT_UNKNOWN = -1,
+
+ FMT_GENERAL = 0,
+ FMT_NUMBER = 1,
+ FMT_CURRENCY = 2,
+ FMT_ACCOUNT = 3,
+ FMT_DATE = 4,
+ FMT_TIME = 5,
+ FMT_PERCENT = 6,
+ FMT_FRACTION = 7,
+ FMT_SCIENCE = 8,
+ FMT_TEXT = 9,
+ FMT_SPECIAL = 10,
+
+ FMT_MARKUP = 11 /* Internal use only */
+} FormatFamily;
+
+typedef struct {
+ unsigned char const * const symbol;
+ unsigned char const * const description;
+ gboolean const precedes;
+ gboolean const has_space;
+} CurrencySymbol;
+
+typedef struct {
+ gboolean thousands_sep;
+ int num_decimals; /* 0 - 30 */
+ int negative_fmt; /* 0 - 3 */
+ int currency_symbol_index;
+ int list_element;
+ gboolean date_has_days;
+ gboolean date_has_months;
+ int fraction_denominator;
+} FormatCharacteristics;
+
+struct _GnmFormat {
+ int ref_count;
+ char *format;
+ GSList *entries; /* Of type StyleFormatEntry. */
+ char *regexp_str;
+ GByteArray *match_tags;
+ go_regex_t regexp;
+ FormatFamily family;
+ FormatCharacteristics family_info;
+ PangoAttrList *markup; /* only for FMT_MARKUP */
+};
+
+char *style_format_delocalize (char const *descriptor_string);
+GnmFormat *style_format_new_markup (PangoAttrList *markup, gboolean add_ref);
+GnmFormat *style_format_new_XL (char const *descriptor_string,
+ gboolean delocalize);
+GnmFormat *style_format_build (FormatFamily family,
+ FormatCharacteristics const *info);
+
+char *style_format_as_XL (GnmFormat const *fmt,
+ gboolean localized);
+char *style_format_str_as_XL (char const *descriptor_string,
+ gboolean localized);
+
+void style_format_ref (GnmFormat *sf);
+void style_format_unref (GnmFormat *sf);
+gboolean style_format_equal (GnmFormat const *a, GnmFormat const *b);
+
+GnmFormat *style_format_general (void);
+GnmFormat *style_format_default_date (void);
+GnmFormat *style_format_default_time (void);
+GnmFormat *style_format_default_date_time (void);
+GnmFormat *style_format_default_percentage (void);
+GnmFormat *style_format_default_money (void);
+
+#define style_format_is_general(sf) ((sf)->family == FMT_GENERAL)
+#define style_format_is_markup(sf) ((sf)->family == FMT_MARKUP)
+#define style_format_is_text(sf) ((sf)->family == FMT_TEXT)
+
+char *format_value (GnmFormat const *format, GnmValue const *value, GnmColor **color,
+ double col_width, GnmDateConventions const *date_conv);
+void format_value_gstring (GString *result, GnmFormat const *format,
+ GnmValue const *value, GnmColor **color,
+ double col_width, GnmDateConventions const *date_conv);
+
+void format_color_init (void);
+void format_color_shutdown (void);
+
+GnmFormat *format_add_decimal (GnmFormat const *fmt);
+GnmFormat *format_remove_decimal (GnmFormat const *fmt);
+GnmFormat *format_toggle_thousands (GnmFormat const *fmt);
+
+typedef struct {
+ int right_optional, right_spaces, right_req, right_allowed;
+ int left_spaces, left_req;
+ float scale;
+ gboolean rendered;
+ gboolean decimal_separator_seen;
+ gboolean group_thousands;
+ gboolean has_fraction;
+} format_info_t;
+void render_number (GString *result, gnm_float number, format_info_t const *info);
+
+/* Locale support routines */
+void gnm_set_untranslated_bools (void);
+char const * gnm_setlocale (int category, char const *val);
+GString const *format_get_currency (gboolean *precedes, gboolean *space_sep);
+gboolean format_month_before_day (void);
+char format_get_arg_sep (void);
+char format_get_col_sep (void);
+GString const *format_get_thousand (void);
+GString const *format_get_decimal (void);
+char const * format_boolean (gboolean b);
+
+void number_format_init (void);
+void number_format_shutdown (void);
+
+void currency_date_format_init (void);
+void currency_date_format_shutdown (void);
+
+FormatFamily cell_format_classify (GnmFormat const *fmt, FormatCharacteristics *info);
+
+/* Indexed by FormatCharacteristics */
+extern char const * const * const cell_formats [];
+
+extern CurrencySymbol const currency_symbols [];
+
+#endif /* GNUMERIC_FORMAT_H */
--- /dev/null
+++ lib/goffice/split/plugin-service-impl.h
@@ -0,0 +1,82 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-graph-item-impl.h : Implementation details for the abstract graph-item
+ * interface
+ *
+ * Copyright (C) 2001 Zbigniew Chyla (cyba at gnome.pl)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GO_GRAPH_ITEM_IMPL_H
+#define GO_GRAPH_ITEM_IMPL_H
+
+#include "gnumeric.h"
+#include "plugin-service.h"
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct _GnmPluginService {
+ GObject g_object;
+
+ char *id;
+ GnmPlugin *plugin;
+ gboolean is_loaded;
+
+ /* protected */
+ gpointer cbs_ptr;
+ gboolean is_active;
+
+ /* private */
+ char *saved_description;
+};
+
+typedef struct{
+ GObjectClass g_object_class;
+
+ void (*read_xml) (GnmPluginService *service, xmlNode *tree, ErrorInfo **ret_error);
+ void (*activate) (GnmPluginService *service, ErrorInfo **ret_error);
+ void (*deactivate) (GnmPluginService *service, ErrorInfo **ret_error);
+ char *(*get_description) (GnmPluginService *service);
+} GnmPluginServiceClass;
+
+#define GPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNM_PLUGIN_SERVICE_TYPE, GnmPluginServiceClass))
+#define GPS_GET_CLASS(o) GPS_CLASS (G_OBJECT_GET_CLASS (o))
+
+typedef struct{
+ GnmPluginServiceClass plugin_service_class;
+ GHashTable *pending; /* has service instances by type names */
+} PluginServiceGObjectLoaderClass;
+
+struct _PluginServiceGObjectLoader {
+ GnmPluginService plugin_service;
+};
+
+#define GPS_GOBJECT_LOADER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GNM_PLUGIN_SERVICE_TYPE, PluginServiceGObjectLoaderClass))
+#define GPS_GOBJECT_LOADER_GET_CLASS(o) GPS_GOBJECT_LOADER_CLASS (G_OBJECT_GET_CLASS (o))
+
+typedef struct{
+ GnmPluginServiceClass plugin_service_class;
+} PluginServiceSimpleClass;
+
+struct _PluginServiceSimple {
+ GnmPluginService plugin_service;
+};
+
+G_END_DECLS
+
+#endif /* GO_GRAPH_ITEM_IMPL_H */
+
--- /dev/null
+++ lib/goffice/split/plugin-loader-module.c
@@ -0,0 +1,651 @@
+/*
+ * plugin-loader-module.c: Support for "g_module" (shared libraries) plugins.
+ *
+ * Author: Zbigniew Chyla (cyba at gnome.pl)
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "plugin-loader-module.h"
+#include "module-plugin-defs.h"
+
+#include "gutils.h"
+#include "file.h"
+#include "plugin.h"
+#include "plugin-service.h"
+#include "plugin-loader.h"
+
+#include <gsf/gsf-impl-utils.h>
+#include <gsf/gsf-output.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xmlmemory.h>
+#include <string.h>
+
+struct _GnmPluginLoaderModule {
+ GnmPluginLoader loader;
+
+ gchar *module_file_name;
+
+ GModule *handle;
+ void (*plugin_init_func) (void);
+ void (*plugin_cleanup_func) (void);
+};
+
+struct _GnmPluginLoaderModuleClass {
+ GnmPluginLoaderClass parent_class;
+};
+
+#define PARENT_TYPE (gnm_plugin_loader_get_type ())
+
+static GObjectClass *parent_class = NULL;
+
+static void gnm_plugin_loader_module_set_attributes (GnmPluginLoader *loader, GHashTable *attrs, ErrorInfo **ret_error);
+static void gnm_plugin_loader_module_load_base (GnmPluginLoader *loader, ErrorInfo **ret_error);
+static void gnm_plugin_loader_module_unload_base (GnmPluginLoader *loader, ErrorInfo **ret_error);
+
+static void gnm_plugin_loader_module_load_service_general (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+static void gnm_plugin_loader_module_load_service_file_opener (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+static void gnm_plugin_loader_module_load_service_file_saver (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+static void gnm_plugin_loader_module_load_service_function_group (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+static void gnm_plugin_loader_module_load_service_plugin_loader (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+static void gnm_plugin_loader_module_load_service_ui (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error);
+
+static void
+gnm_plugin_loader_module_set_attributes (GnmPluginLoader *loader,
+ GHashTable *attrs,
+ ErrorInfo **ret_error)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
+ gchar *module_file_name = NULL;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ module_file_name = g_hash_table_lookup (attrs, "module_file");
+ if (module_file_name != NULL) {
+ loader_module->module_file_name = g_strdup (module_file_name);
+ } else {
+ *ret_error = error_info_new_str (
+ _("Module file name not given."));
+ }
+}
+
+static void
+gnm_plugin_loader_module_load_base (GnmPluginLoader *loader, ErrorInfo **ret_error)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (g_module_supported ()) {
+ gchar *full_module_file_name;
+ GModule *handle;
+ ModulePluginFileStruct *plugin_file_struct = NULL;
+ gpointer plugin_init_func = NULL, plugin_cleanup_func = NULL;
+
+ full_module_file_name = g_build_filename (gnm_plugin_get_dir_name (loader->plugin),
+ loader_module->module_file_name, NULL);
+ handle = g_module_open (full_module_file_name, 0);
+ if (handle != NULL) {
+ g_module_symbol (handle, "plugin_file_struct", (gpointer) &plugin_file_struct);
+ g_module_symbol (handle, "plugin_init", &plugin_init_func);
+ g_module_symbol (handle, "plugin_cleanup", &plugin_cleanup_func);
+ }
+ if (handle != NULL && plugin_file_struct != NULL &&
+ plugin_file_struct->magic_number == GNUMERIC_MODULE_PLUGIN_MAGIC_NUMBER &&
+ strcmp (plugin_file_struct->version, GNUMERIC_VERSION) == 0) {
+ loader_module->handle = handle;
+ loader_module->plugin_init_func = plugin_init_func;
+ loader_module->plugin_cleanup_func = plugin_cleanup_func;
+ if (loader_module->plugin_init_func != NULL) {
+ loader_module->plugin_init_func ();
+ }
+ } else {
+ if (handle == NULL) {
+ *ret_error = error_info_new_printf (
+ _("Unable to open module file \"%s\"."),
+ full_module_file_name);
+ error_info_add_details (*ret_error, error_info_new_str (g_module_error()));
+ } else {
+ *ret_error = error_info_new_printf (
+ _("Module file \"%s\" has invalid format."),
+ full_module_file_name);
+ if (plugin_file_struct == NULL) {
+ error_info_add_details (*ret_error,
+ error_info_new_str (
+ _("File doesn't contain (\"plugin_file_struct\" symbol).")));
+ } else if (plugin_file_struct->magic_number != GNUMERIC_MODULE_PLUGIN_MAGIC_NUMBER) {
+ error_info_add_details (*ret_error,
+ error_info_new_str (
+ _("File has a bad magic number.")));
+ } else if (strcmp (plugin_file_struct->version, GNUMERIC_VERSION) == 0) {
+ error_info_add_details (*ret_error,
+ error_info_new_printf (
+ _("Plugin version \"%s\" is different from application \"%s\"."),
+ plugin_file_struct->version, GNUMERIC_VERSION));
+ }
+ g_module_close (handle);
+ }
+ }
+ g_free (full_module_file_name);
+ } else {
+ *ret_error = error_info_new_str (
+ _("Dynamic module loading is not supported in this system."));
+ }
+}
+
+static void
+gnm_plugin_loader_module_unload_base (GnmPluginLoader *loader, ErrorInfo **ret_error)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (loader_module->plugin_cleanup_func != NULL) {
+ loader_module->plugin_cleanup_func ();
+ }
+ if (!g_module_close (loader_module->handle)) {
+ *ret_error = error_info_new_printf (
+ _("Unable to close module file \"%s\"."),
+ loader_module->module_file_name);
+ error_info_add_details (*ret_error, error_info_new_str (g_module_error()));
+ }
+ loader_module->handle = NULL;
+ loader_module->plugin_init_func = NULL;
+ loader_module->plugin_cleanup_func = NULL;
+}
+
+static void
+gnm_plugin_loader_module_init (GnmPluginLoaderModule *loader_module)
+{
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER_MODULE (loader_module));
+
+ loader_module->module_file_name = NULL;
+ loader_module->handle = NULL;
+}
+
+static void
+gnm_plugin_loader_module_finalize (GObject *obj)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (obj);
+
+ g_free (loader_module->module_file_name);
+ loader_module->module_file_name = NULL;
+
+ parent_class->finalize (obj);
+}
+
+static void
+gnm_plugin_loader_module_class_init (GObjectClass *gobject_class)
+{
+ GnmPluginLoaderClass *gnm_plugin_loader_class = GNM_PLUGIN_LOADER_CLASS (gobject_class);
+
+ parent_class = g_type_class_peek_parent (gobject_class);
+
+ gobject_class->finalize = gnm_plugin_loader_module_finalize;
+
+ gnm_plugin_loader_class->set_attributes = gnm_plugin_loader_module_set_attributes;
+ gnm_plugin_loader_class->load_base = gnm_plugin_loader_module_load_base;
+ gnm_plugin_loader_class->unload_base = gnm_plugin_loader_module_unload_base;
+ gnm_plugin_loader_class->load_service_general = gnm_plugin_loader_module_load_service_general;
+ //gnm_plugin_loader_class->load_service_file_opener = gnm_plugin_loader_module_load_service_file_opener;
+ //gnm_plugin_loader_class->load_service_file_saver = gnm_plugin_loader_module_load_service_file_saver;
+ //gnm_plugin_loader_class->load_service_function_group = gnm_plugin_loader_module_load_service_function_group;
+ gnm_plugin_loader_class->load_service_plugin_loader = gnm_plugin_loader_module_load_service_plugin_loader;
+ gnm_plugin_loader_class->load_service_ui = gnm_plugin_loader_module_load_service_ui;
+}
+
+GSF_CLASS (GnmPluginLoaderModule, gnm_plugin_loader_module,
+ gnm_plugin_loader_module_class_init,
+ gnm_plugin_loader_module_init, PARENT_TYPE)
+
+/*
+ * Service - general
+ */
+
+typedef struct {
+ void (*module_func_init) (ErrorInfo **ret_error);
+ void (*module_func_cleanup) (ErrorInfo **ret_error);
+} ServiceLoaderDataGeneral;
+
+static void
+gnm_plugin_loader_module_func_init (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ ErrorInfo *error = NULL;
+ ServiceLoaderDataGeneral *loader_data;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_GENERAL (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
+ loader_data->module_func_init (&error);
+ *ret_error = error;
+}
+
+static void
+gnm_plugin_loader_module_func_cleanup (GnmPluginService *service, ErrorInfo **ret_error)
+{
+ ErrorInfo *error = NULL;
+ ServiceLoaderDataGeneral *loader_data;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_GENERAL (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
+ loader_data->module_func_cleanup (&error);
+ *ret_error = error;
+}
+
+static void
+gnm_plugin_loader_module_load_service_general (GnmPluginLoader *loader,
+ GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
+ gpointer module_func_init = NULL, module_func_cleanup = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_GENERAL (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ g_module_symbol (loader_module->handle, "plugin_init_general", &module_func_init);
+ g_module_symbol (loader_module->handle, "plugin_cleanup_general", &module_func_cleanup);
+ if (module_func_init != NULL && module_func_cleanup != NULL) {
+ PluginServiceGeneralCallbacks *cbs;
+ ServiceLoaderDataGeneral *loader_data;
+
+ cbs = plugin_service_get_cbs (service);
+ cbs->plugin_func_init = gnm_plugin_loader_module_func_init;
+ cbs->plugin_func_cleanup = gnm_plugin_loader_module_func_cleanup;
+
+ loader_data = g_new (ServiceLoaderDataGeneral, 1);
+ loader_data->module_func_init = module_func_init;
+ loader_data->module_func_cleanup = module_func_cleanup;
+ g_object_set_data_full (
+ G_OBJECT (service), "loader_data", loader_data, g_free);
+ } else {
+ *ret_error = error_info_new_printf (
+ _("Module file \"%s\" has invalid format."),
+ loader_module->module_file_name);
+ if (module_func_init == NULL) {
+ error_info_add_details (*ret_error,
+ error_info_new_str (
+ _("File doesn't contain \"plugin_init_general\" function.")));
+ }
+ if (module_func_cleanup == NULL) {
+ error_info_add_details (*ret_error,
+ error_info_new_str (
+ _("File doesn't contain \"plugin_cleanup_general\" function.")));
+ }
+ }
+}
+
+#if 0
+/*
+ * Service - file_opener
+ */
+typedef struct {
+ gboolean (*module_func_file_probe) (GnmFileOpener const *fo, GsfInput *input,
+ FileProbeLevel pl);
+ void (*module_func_file_open) (GnmFileOpener const *fo, IOContext *io_context,
+ WorkbookView *wbv, GsfInput *input);
+} ServiceLoaderDataFileOpener;
+
+static gboolean
+gnm_plugin_loader_module_func_file_probe (GnmFileOpener const *fo, GnmPluginService *service,
+ GsfInput *input, FileProbeLevel pl)
+{
+ ServiceLoaderDataFileOpener *loader_data;
+
+ g_return_val_if_fail (IS_GNM_PLUGIN_SERVICE_FILE_OPENER (service), FALSE);
+ g_return_val_if_fail (input != NULL, FALSE);
+
+ loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
+ return loader_data->module_func_file_probe (fo, input, pl);
+}
+
+static void
+gnm_plugin_loader_module_func_file_open (GnmFileOpener const *fo, GnmPluginService *service,
+ IOContext *io_context, WorkbookView *wbv,
+ GsfInput *input)
+{
+ ServiceLoaderDataFileOpener *loader_data;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_FILE_OPENER (service));
+ g_return_if_fail (input != NULL);
+
+ loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
+ loader_data->module_func_file_open (fo, io_context, wbv, input);
+}
+
+static void
+gnm_plugin_loader_module_load_service_file_opener (GnmPluginLoader *loader,
+ GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
+ gchar *func_name_file_probe, *func_name_file_open;
+ gpointer module_func_file_probe = NULL, module_func_file_open = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_FILE_OPENER (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ func_name_file_probe = g_strconcat (
+ plugin_service_get_id (service), "_file_probe", NULL);
+ g_module_symbol (loader_module->handle, func_name_file_probe, &module_func_file_probe);
+ func_name_file_open = g_strconcat (
+ plugin_service_get_id (service), "_file_open", NULL);
+ g_module_symbol (loader_module->handle, func_name_file_open, &module_func_file_open);
+ if (module_func_file_open != NULL) {
+ PluginServiceFileOpenerCallbacks *cbs;
+ ServiceLoaderDataFileOpener *loader_data;
+
+ cbs = plugin_service_get_cbs (service);
+ cbs->plugin_func_file_probe = gnm_plugin_loader_module_func_file_probe;
+ cbs->plugin_func_file_open = gnm_plugin_loader_module_func_file_open;
+
+ loader_data = g_new (ServiceLoaderDataFileOpener, 1);
+ loader_data->module_func_file_probe = module_func_file_probe;
+ loader_data->module_func_file_open = module_func_file_open;
+ g_object_set_data_full (
+ G_OBJECT (service), "loader_data", loader_data, g_free);
+ } else {
+ *ret_error = error_info_new_printf (
+ _("Module file \"%s\" has invalid format."),
+ loader_module->module_file_name);
+ error_info_add_details (*ret_error, error_info_new_printf (
+ _("File doesn't contain \"%s\" function."), func_name_file_open));
+ }
+ g_free (func_name_file_probe);
+ g_free (func_name_file_open);
+}
+
+/*
+ * Service - file_saver
+ */
+
+typedef struct {
+ void (*module_func_file_save) (GnmFileSaver const *fs, IOContext *io_context,
+ WorkbookView const *wbv, GsfOutput *output);
+} ServiceLoaderDataFileSaver;
+
+static void
+gnm_plugin_loader_module_func_file_save (GnmFileSaver const *fs, GnmPluginService *service,
+ IOContext *io_context, WorkbookView const *wbv,
+ GsfOutput *output)
+{
+ ServiceLoaderDataFileSaver *loader_data;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_FILE_SAVER (service));
+ g_return_if_fail (GSF_IS_OUTPUT (output));
+
+ loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
+ loader_data->module_func_file_save (fs, io_context, wbv, output);
+}
+
+static void
+gnm_plugin_loader_module_load_service_file_saver (GnmPluginLoader *loader,
+ GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
+ gchar *func_name_file_save;
+ gpointer module_func_file_save = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_FILE_SAVER (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ func_name_file_save = g_strconcat (
+ plugin_service_get_id (service), "_file_save", NULL);
+ g_module_symbol (loader_module->handle, func_name_file_save, &module_func_file_save);
+ if (module_func_file_save != NULL) {
+ PluginServiceFileSaverCallbacks *cbs;
+ ServiceLoaderDataFileSaver *loader_data;
+
+ cbs = plugin_service_get_cbs (service);
+ cbs->plugin_func_file_save = gnm_plugin_loader_module_func_file_save;
+
+ loader_data = g_new (ServiceLoaderDataFileSaver, 1);
+ loader_data->module_func_file_save = module_func_file_save;
+ g_object_set_data_full (
+ G_OBJECT (service), "loader_data", loader_data, g_free);
+ } else {
+ *ret_error = error_info_new_printf (
+ _("Module file \"%s\" has invalid format."),
+ loader_module->module_file_name);
+ error_info_add_details (*ret_error,
+ error_info_new_printf (
+ _("File doesn't contain \"%s\" function."),
+ func_name_file_save));
+ }
+ g_free (func_name_file_save);
+}
+
+/*
+ * Service - function_group
+ */
+
+typedef struct {
+ GnmFuncDescriptor *module_fn_info_array;
+ GHashTable *function_indices;
+} ServiceLoaderDataFunctionGroup;
+
+static void
+function_group_loader_data_free (gpointer data)
+{
+ ServiceLoaderDataFunctionGroup *ld = data;
+
+ g_hash_table_destroy (ld->function_indices);
+ g_free (ld);
+}
+
+static gboolean
+gnm_plugin_loader_module_func_desc_load (GnmPluginService *service,
+ char const *name,
+ GnmFuncDescriptor *res)
+{
+ ServiceLoaderDataFunctionGroup *loader_data;
+ gpointer func_index_ptr;
+
+ g_return_val_if_fail (IS_GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
+ if (g_hash_table_lookup_extended (loader_data->function_indices, (gpointer) name,
+ NULL, &func_index_ptr)) {
+ int i = GPOINTER_TO_INT (func_index_ptr);
+ *res = loader_data->module_fn_info_array[i];
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+gnm_plugin_loader_module_load_service_function_group (GnmPluginLoader *loader,
+ GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
+ gchar *fn_info_array_name;
+ GnmFuncDescriptor *module_fn_info_array = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ fn_info_array_name = g_strconcat (
+ plugin_service_get_id (service), "_functions", NULL);
+ g_module_symbol (loader_module->handle, fn_info_array_name, (gpointer) &module_fn_info_array);
+ if (module_fn_info_array != NULL) {
+ PluginServiceFunctionGroupCallbacks *cbs;
+ ServiceLoaderDataFunctionGroup *loader_data;
+ gint i;
+
+ cbs = plugin_service_get_cbs (service);
+ cbs->func_desc_load = &gnm_plugin_loader_module_func_desc_load;
+
+ loader_data = g_new (ServiceLoaderDataFunctionGroup, 1);
+ loader_data->module_fn_info_array = module_fn_info_array;
+ loader_data->function_indices = g_hash_table_new (&g_str_hash, &g_str_equal);
+ for (i = 0; module_fn_info_array[i].name != NULL; i++) {
+ g_hash_table_insert (loader_data->function_indices,
+ (gpointer) module_fn_info_array[i].name,
+ GINT_TO_POINTER (i));
+ }
+ g_object_set_data_full (
+ G_OBJECT (service), "loader_data", loader_data, function_group_loader_data_free);
+ } else {
+ *ret_error = error_info_new_printf (
+ _("Module file \"%s\" has invalid format."),
+ loader_module->module_file_name);
+ error_info_add_details (*ret_error,
+ error_info_new_printf (
+ _("File doesn't contain \"%s\" array."),
+ fn_info_array_name));
+ }
+ g_free (fn_info_array_name);
+}
+#endif // 0
+
+/*
+ * Service - plugin_loader
+ */
+
+typedef struct {
+ GType (*module_func_get_loader_type) (ErrorInfo **ret_error);
+} ServiceLoaderDataPluginLoader;
+
+static GType
+gnm_plugin_loader_module_func_get_loader_type (GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ ServiceLoaderDataPluginLoader *loader_data;
+ ErrorInfo *error = NULL;
+ GType loader_type;
+
+ g_return_val_if_fail (IS_GNM_PLUGIN_SERVICE_PLUGIN_LOADER (service), 0);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
+ loader_type = loader_data->module_func_get_loader_type (&error);
+ if (error == NULL) {
+ return loader_type;
+ } else {
+ *ret_error = error;
+ return (GType) 0;
+ }
+}
+
+static void
+gnm_plugin_loader_module_load_service_plugin_loader (GnmPluginLoader *loader,
+ GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
+ gchar *func_name_get_loader_type;
+ gpointer module_func_get_loader_type = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_PLUGIN_LOADER (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ func_name_get_loader_type = g_strconcat (
+ plugin_service_get_id (service), "_get_loader_type", NULL);
+ g_module_symbol (loader_module->handle, func_name_get_loader_type,
+ &module_func_get_loader_type);
+ if (module_func_get_loader_type != NULL) {
+ PluginServicePluginLoaderCallbacks *cbs;
+ ServiceLoaderDataPluginLoader *loader_data;
+
+ cbs = plugin_service_get_cbs (service);
+ cbs->plugin_func_get_loader_type = gnm_plugin_loader_module_func_get_loader_type;
+
+ loader_data = g_new (ServiceLoaderDataPluginLoader, 1);
+ loader_data->module_func_get_loader_type = module_func_get_loader_type;
+ g_object_set_data_full (
+ G_OBJECT (service), "loader_data", loader_data, g_free);
+ } else
+ *ret_error = error_info_new_printf (
+ _("Module doesn't contain \"%s\" function."),
+ func_name_get_loader_type);
+ g_free (func_name_get_loader_type);
+}
+
+/*
+ * Service - ui
+ */
+
+typedef struct {
+ ModulePluginUIActions *module_ui_actions_array;
+ GHashTable *ui_actions_hash;
+} ServiceLoaderDataUI;
+
+static void
+ui_loader_data_free (gpointer data)
+{
+ ServiceLoaderDataUI *ld = data;
+
+ g_hash_table_destroy (ld->ui_actions_hash);
+ g_free (ld);
+}
+
+static void
+gnm_plugin_loader_module_func_exec_action (GnmPluginService *service,
+ GnmAction const *action,
+ WorkbookControl *wbc,
+ ErrorInfo **ret_error)
+{
+ ServiceLoaderDataUI *loader_data;
+ gpointer action_index_ptr;
+ int action_index;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_UI (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ loader_data = g_object_get_data (G_OBJECT (service), "loader_data");
+ if (!g_hash_table_lookup_extended (loader_data->ui_actions_hash, action->id,
+ NULL, &action_index_ptr)) {
+ *ret_error = error_info_new_printf (_("Unknown action: %s"), action->id);
+ return;
+ }
+ action_index = GPOINTER_TO_INT (action_index_ptr);
+ loader_data->module_ui_actions_array [action_index].handler (action, wbc);
+}
+
+static void
+gnm_plugin_loader_module_load_service_ui (GnmPluginLoader *loader,
+ GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ GnmPluginLoaderModule *loader_module = GNM_PLUGIN_LOADER_MODULE (loader);
+ char *ui_actions_array_name;
+ ModulePluginUIActions *module_ui_actions_array = NULL;
+ PluginServiceUICallbacks *cbs;
+ ServiceLoaderDataUI *loader_data;
+ gint i;
+
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_UI (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ ui_actions_array_name = g_strconcat (
+ plugin_service_get_id (service), "_ui_actions", NULL);
+ g_module_symbol (loader_module->handle, ui_actions_array_name, (gpointer) &module_ui_actions_array);
+ if (module_ui_actions_array == NULL) {
+ *ret_error = error_info_new_printf (
+ _("Module file \"%s\" has invalid format."),
+ loader_module->module_file_name);
+ error_info_add_details (*ret_error, error_info_new_printf (
+ _("File doesn't contain \"%s\" array."), ui_actions_array_name));
+ g_free (ui_actions_array_name);
+ return;
+ }
+ g_free (ui_actions_array_name);
+
+ cbs = plugin_service_get_cbs (service);
+ cbs->plugin_func_exec_action = gnm_plugin_loader_module_func_exec_action;
+
+ loader_data = g_new (ServiceLoaderDataUI, 1);
+ loader_data->module_ui_actions_array = module_ui_actions_array;
+ loader_data->ui_actions_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ for (i = 0; module_ui_actions_array[i].name != NULL; i++)
+ g_hash_table_insert (loader_data->ui_actions_hash,
+ (gpointer) module_ui_actions_array[i].name,
+ GINT_TO_POINTER (i));
+ g_object_set_data_full (G_OBJECT (service),
+ "loader_data", loader_data, ui_loader_data_free);
+}
--- /dev/null
+++ lib/goffice/split/command-context-stderr.c
@@ -0,0 +1,112 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * command-context-stderr.c : Error dispatch for line oriented clients
+ *
+ * Author:
+ * Jon K Hellan <hellan at acm.org>
+ *
+ * (C) 2002 Jon K Hellan
+ */
+#include <config.h>
+#include <glib/gi18n.h>
+#include <stdio.h>
+#include "gnumeric.h"
+#include "command-context-stderr.h"
+#include "command-context-priv.h"
+#include <gsf/gsf-impl-utils.h>
+#include "error-info.h"
+#include "ranges.h"
+
+struct _CmdContextStderr {
+ GObject base;
+ int status;
+};
+typedef GObjectClass CmdContextStderrClass;
+
+#define COMMAND_CONTEXT_STDERR_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), CMD_CONTEXT_STDERR_TYPE, CmdContextStderrClass))
+
+GnmCmdContext *
+cmd_context_stderr_new (void)
+{
+ return g_object_new (CMD_CONTEXT_STDERR_TYPE, NULL);
+}
+
+void
+cmd_context_stderr_set_status (CmdContextStderr *ccs, int status)
+{
+ g_return_if_fail (ccs != NULL);
+ g_return_if_fail (IS_COMMAND_CONTEXT_STDERR (ccs));
+
+ ccs->status = status;
+}
+
+int
+cmd_context_stderr_get_status (CmdContextStderr *ccs)
+{
+ g_return_val_if_fail (ccs != NULL, -1);
+ g_return_val_if_fail (IS_COMMAND_CONTEXT_STDERR (ccs), -1);
+
+ return ccs->status;
+}
+
+static void
+ccs_error_error (GnmCmdContext *cc, GError *error)
+{
+ CmdContextStderr *ccs = COMMAND_CONTEXT_STDERR (cc);
+
+ fprintf (stderr, "Error: %s\n", error->message);
+ ccs->status = -1;
+}
+static void
+ccs_error_info (GnmCmdContext *cc, ErrorInfo *error)
+{
+ CmdContextStderr *ccs = COMMAND_CONTEXT_STDERR (cc);
+
+ error_info_print (error);
+ ccs->status = -1;
+}
+
+static char *
+ccs_get_password (G_GNUC_UNUSED GnmCmdContext *cc,
+ G_GNUC_UNUSED char const* filename)
+{
+ return NULL;
+}
+static void
+ccs_set_sensitive (G_GNUC_UNUSED GnmCmdContext *cc,
+ G_GNUC_UNUSED gboolean sensitive)
+{
+}
+
+static void
+ccs_progress_set (GnmCmdContext *cc, gfloat val)
+{
+}
+
+static void
+ccs_progress_message_set (GnmCmdContext *cc, gchar const *msg)
+{
+}
+
+static void
+ccs_init (CmdContextStderr *ccs)
+{
+ ccs->status = 0;
+}
+
+static void
+ccs_gnm_cmd_context_init (GnmCmdContextClass *cc_class)
+{
+ cc_class->get_password = ccs_get_password;
+ cc_class->set_sensitive = ccs_set_sensitive;
+ cc_class->progress_set = ccs_progress_set;
+ cc_class->progress_message_set = ccs_progress_message_set;
+ cc_class->error.error = ccs_error_error;
+ cc_class->error.error_info = ccs_error_info;
+}
+
+GSF_CLASS_FULL (CmdContextStderr, cmd_context_stderr,
+ NULL, ccs_init,
+ G_TYPE_OBJECT, 0,
+ GSF_INTERFACE (ccs_gnm_cmd_context_init, GNM_CMD_CONTEXT_TYPE))
--- /dev/null
+++ lib/goffice/split/number-match.c
@@ -0,0 +1,1295 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * number-match.c: This file includes the support for matching
+ * entered strings as numbers (by trying to apply one of the existing
+ * cell formats).
+ *
+ * The idea is simple: we create a regular expression from the format
+ * string that would match a value entered in that format. Then, on
+ * lookup we try to match the string against every regular expression
+ * we have: if a match is found, then we decode the number using a
+ * precomputed parallel-list of subexpressions.
+ *
+ * Author:
+ * Miguel de Icaza (miguel at gnu.org)
+ */
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "number-match.h"
+
+#include "dates.h"
+#include "numbers.h"
+#include "gutils.h"
+#include "datetime.h"
+#include "style.h"
+#include "format.h"
+#include "value.h"
+#include "mathfunc.h"
+#include "str.h"
+#include "regutf8.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+#include <math.h>
+#undef DEBUG_NUMBER_MATCH
+
+/*
+ * Takes a list of strings (optionally include an * at the beginning
+ * that gets stripped, for i18n purposes). and returns a regexp that
+ * would match them
+ */
+static char *
+create_option_list (char const *const *list)
+{
+ int len = 0;
+ char const *const *p;
+ char *res;
+
+ for (p = list; *p; p++) {
+ char const *v = _(*p);
+
+ if (*v == '*')
+ v++;
+ len += strlen (v) + 1;
+ }
+ len += 5;
+
+ res = g_malloc (len);
+ res[0] = '(';
+ res[1] = 0;
+ for (p = list; *p; p++) {
+ char const *v = _(*p);
+
+ if (*v == '*')
+ v++;
+
+ strcat (res, v);
+ if (*(p + 1))
+ strcat (res, "|");
+ }
+ strcat (res, ")");
+
+ return res;
+}
+
+typedef enum {
+ MATCH_DAY_FULL = 1,
+ MATCH_DAY_NUMBER = 2,
+ MATCH_MONTH_FULL = 3,
+ MATCH_MONTH_SHORT = 4,
+ MATCH_MONTH_NUMBER = 5,
+ MATCH_YEAR_FULL = 6,
+ MATCH_YEAR_SHORT = 7,
+ MATCH_HOUR = 8,
+ MATCH_MINUTE = 9,
+ MATCH_SECOND = 10,
+ MATCH_AMPM = 11,
+ MATCH_NUMBER = 12,
+ MATCH_NUMBER_DECIMALS = 13,
+ MATCH_PERCENT = 14,
+ MATCH_SKIP = 15,
+ MATCH_STRING_CONSTANT = 16,
+ MATCH_CUMMULATIVE_HOURS = 17,
+ MATCH_CUMMULATIVE_MINUTES = 18,
+ MATCH_CUMMULATIVE_SECONDS = 19,
+ MATCH_NUMERATOR = 20,
+ MATCH_DENOMINATOR = 21
+} MatchType;
+
+#define append_type(t) do { guint8 x = t; match_types = g_byte_array_append (match_types, &x, 1); } while (0)
+
+/*
+ * format_create_regexp:
+ * Create a regular expression for the given XL-style format. Note:
+ * the format as well as the regexp are UTF-8 encoded.
+ */
+static char *
+format_create_regexp (unsigned char const *format, GByteArray **dest)
+{
+ GString *regexp;
+ GByteArray *match_types;
+ char *str;
+ gboolean hour_seen = FALSE;
+ gboolean number_seen = FALSE;
+ gboolean fraction = FALSE;
+
+ g_return_val_if_fail (format != NULL, NULL);
+
+#ifdef DEBUG_NUMBER_MATCH
+ printf ("'%s' = ", format);
+#endif
+ regexp = g_string_new ("^");
+ match_types = g_byte_array_new ();
+
+ for (; *format; format = g_utf8_next_char (format)) {
+ gunichar c = g_utf8_get_char (format);
+ switch (c) {
+ case '*':
+ /* FIXME: I don't think this will work for '^'. */
+ if (format[1]) {
+ format++;
+ g_string_append_c (regexp, '[');
+ g_string_append_unichar (regexp, g_utf8_get_char (format));
+ g_string_append_c (regexp, ']');
+ g_string_append_c (regexp, '*');
+ }
+ break;
+
+ case 'P': case 'p':
+ if (format[1] == 'm' || format[1] == 'M')
+ format++;
+ break;
+
+ case '\\': {
+ if (format[1] != '\0')
+ format++;
+ gnumeric_regexp_quote1 (regexp, format);
+ break;
+ }
+
+ case '[' :
+ /* Currency symbol */
+ if (format[1] == '$') {
+ for (format += 2; *format && *format != ']' ; ++format)
+ g_string_append_c (regexp, *format);
+ if (*format == ']')
+ ++format;
+ break;
+ } else if (format[1] == 'h' && format[2] == ']') {
+ g_string_append (regexp, "([-+]?[0-9]+)");
+ append_type (MATCH_CUMMULATIVE_HOURS);
+ hour_seen = TRUE;
+ format += 2;
+ break;
+ } else if (format[1] == 'm' && format[2] == ']') {
+ g_string_append (regexp, "([-+]?[0-9]+)");
+ append_type (hour_seen ? MATCH_MINUTE : MATCH_CUMMULATIVE_MINUTES);
+ format += 2;
+ break;
+ } else if (format[1] == 's' && format[2] == ']') {
+ g_string_append (regexp, "([-+]?[0-9]+)");
+ append_type (MATCH_CUMMULATIVE_SECONDS);
+ format += 2;
+ break;
+ }
+
+ case '%':
+ g_string_append (regexp, "%");
+ append_type (MATCH_PERCENT);
+ break;
+
+ case '#': case '0': case '.': case '+': case '?': {
+ gboolean include_sep = FALSE;
+ gboolean include_decimal = FALSE;
+
+ while (*format == '#' || *format == '0' || *format == '.' ||
+ *format == '-' || *format == 'E' || *format == 'e' ||
+ *format == '+' || *format == '?' || *format == ',') {
+ switch (*format) {
+ case ',': include_sep = TRUE; break;
+ case '.': include_decimal = TRUE; break;
+ }
+ format++;
+ }
+ format--;
+
+ if (format[1] == '/' && number_seen)
+ append_type (MATCH_NUMERATOR);
+ else
+ append_type (MATCH_NUMBER);
+
+ if (include_sep) {
+ /* Not strictly correct.
+ * There should be a limit of 1-3 digits.
+ * However, that creates problems when
+ * There are formats like
+ * $#,##0.00
+ * but not
+ * $###0.00
+ * as a result $1000 would not be recognized.
+ */
+ g_string_append (regexp, "([-+]?[0-9]+(");
+ gnumeric_regexp_quote (regexp, format_get_thousand ()->str);
+ g_string_append (regexp, "[0-9]{3})*)");
+ append_type (MATCH_SKIP);
+ } else {
+ g_string_append (regexp, "([-+]?[0-9]+)");
+ }
+
+ if (include_decimal) {
+ g_string_append (regexp, "?(");
+ gnumeric_regexp_quote (regexp, format_get_decimal ()->str);
+ g_string_append (regexp, "[0-9]+([Ee][-+]?[0-9]+)?)");
+ append_type (MATCH_NUMBER_DECIMALS);
+ }
+
+ number_seen = TRUE;
+ break;
+ }
+
+ case 'h':
+ case 'H':
+ hour_seen = TRUE;
+ if (format[1] == 'h' || format[1] == 'H')
+ format++;
+
+ g_string_append (regexp, "([0-9][0-9]?)");
+ append_type (MATCH_HOUR);
+ break;
+
+ case 'M':
+ case 'm':
+ if (hour_seen) {
+ if (format[1] == 'm' || format[1] == 'M')
+ format++;
+ g_string_append (regexp, "([0-9][0-9]?)");
+ append_type (MATCH_MINUTE);
+ hour_seen = FALSE;
+ } else {
+ if (format[1] == 'm' || format[1] == 'M') {
+ if (format[2] == 'm' || format[2] == 'M') {
+ if (format[3] == 'm' || format[3] == 'M') {
+ char *l;
+
+ l = create_option_list (month_long);
+ g_string_append (regexp, l);
+ g_free (l);
+
+ append_type (MATCH_MONTH_FULL);
+ format++;
+ } else {
+ char *l;
+
+ l = create_option_list (month_short);
+ g_string_append (regexp, l);
+ g_free (l);
+
+ append_type (MATCH_MONTH_SHORT);
+ }
+ format++;
+ } else {
+ g_string_append (regexp, "([0-9][0-9]?)");
+ append_type (MATCH_MONTH_NUMBER);
+ }
+ format++;
+ } else {
+ g_string_append (regexp, "([0-9][0-9]?)");
+ append_type (MATCH_MONTH_NUMBER);
+ }
+ }
+ break;
+
+ case 's':
+ case 'S':
+ /* ICK!
+ * ICK!
+ * 'm' is ambiguous. It can be months or minutes.
+ */
+ {
+ int l = match_types->len;
+ if (l > 0 && match_types->data[l - 1] == MATCH_MONTH_NUMBER)
+ match_types->data[l - 1] = MATCH_MINUTE;
+ }
+
+ if (format[1] == 's' || format[1] == 'S')
+ format++;
+ g_string_append (regexp, "([0-9][0-9]?)");
+ append_type (MATCH_SECOND);
+ break;
+
+ case 'D':
+ case 'd':
+ if (format[1] == 'd' || format[1] == 'D') {
+ if (format[2] == 'd' || format[2] == 'D') {
+ if (format[3] == 'd' || format[3] == 'D') {
+ char *l;
+
+ l = create_option_list (day_long);
+ g_string_append (regexp, l);
+ g_free (l);
+
+ append_type (MATCH_DAY_FULL);
+ format++;
+ } else {
+ char *l;
+
+ l = create_option_list (day_short);
+ g_string_append (regexp, l);
+ g_free (l);
+ }
+ format++;
+ } else {
+ g_string_append (regexp, "([0-9][0-9]?)");
+ append_type (MATCH_DAY_NUMBER);
+ }
+ format++;
+ } else {
+ g_string_append (regexp, "([0-9][0-9]?)");
+ append_type (MATCH_DAY_NUMBER);
+ }
+ break;
+
+ case 'Y':
+ case 'y':
+ if (format[1] == 'y' || format[1] == 'Y') {
+ if (format[2] == 'y' || format[2] == 'Y') {
+ if (format[3] == 'y' || format[3] == 'Y') {
+ g_string_append (regexp, "([0-9][0-9][0-9][0-9])");
+ append_type (MATCH_YEAR_FULL);
+ format++;
+ }
+ format++;
+ } else {
+ g_string_append (regexp, "([0-9][0-9]?)");
+ append_type (MATCH_YEAR_SHORT);
+ }
+ format++;
+ } else {
+ g_string_append (regexp, "([0-9][0-9]?)");
+ append_type (MATCH_YEAR_SHORT);
+ }
+ break;
+
+ case ';':
+ /* TODO : Is it ok to only match the first entry ?? */
+ /* FIXME: What is this? */
+ while (*format)
+ format = g_utf8_next_char (format);
+ format = g_utf8_prev_char (format);
+ break;
+
+ case 'A': case 'a':
+ if (*(format + 1) == 'm' || *(format + 1) == 'M') {
+ if (*(format + 2) == '/') {
+ if (*(format + 3) == 'P' || *(format + 3) == 'p') {
+ if (*(format + 4) == 'm' || *(format + 4) == 'M') {
+ format++;
+ }
+ format++;
+ }
+ format++;
+ }
+ format++;
+ }
+ g_string_append (regexp, "([Aa]|[Pp])[Mm]?");
+ append_type (MATCH_AMPM);
+ break;
+
+ case '"':
+ /* Matches a string */
+ format++;
+ while (*format != '"') {
+ if (*format == 0)
+ goto error;
+ format = gnumeric_regexp_quote1 (regexp, format);
+ }
+ break;
+
+ case '@':
+ g_string_append (regexp, "(.*)");
+ append_type (MATCH_STRING_CONSTANT);
+ break;
+
+ case '_':
+ if (format[1]) {
+ g_string_append (regexp, "[ ]?");
+ format++;
+ }
+ break;
+
+ case '/':
+ g_string_append_c (regexp, '/');
+ if (number_seen) {
+ fraction = TRUE;
+ /* Fraction. Ick. */
+ if (strncmp (regexp->str, "^([-+]?[0-9]+) ", 15) == 0) {
+ g_string_erase (regexp, 14, 1);
+ g_string_insert (regexp, 13, " +|");
+ /* FIXME: The final regexp won't match a plain digit sequence. */
+ }
+
+ while (format[1] == '?' || g_ascii_isdigit (format[1]))
+ format++;
+
+ g_string_append (regexp, "([0-9]+) *");
+ append_type (MATCH_DENOMINATOR);
+ }
+ break;
+
+#if 0
+ /* these were here explicitly before adding default.
+ * Leave them explicit for now as documentation.
+ */
+ /* Default appears fine for this. */
+ case 0x00a3: /* GBP sign. */
+ case 0x00a5: /* JPY sign. */
+ case 0x20ac: /* EUR sign. */
+ case '^':
+ case '|':
+ case ']':
+ case '$':
+ case ':':
+ case '-':
+ case ' ':
+ case '(':
+ case ')':
+
+#endif
+ default :
+ gnumeric_regexp_quote1 (regexp, format);
+ }
+ }
+
+ g_string_append_c (regexp, '$');
+
+ str = g_string_free (regexp, FALSE);
+ *dest = match_types;
+
+#ifdef DEBUG_NUMBER_MATCH
+ printf ("'%s'\n",str);
+#endif
+ return str;
+
+ error:
+ g_string_free (regexp, TRUE);
+ g_byte_array_free (match_types, TRUE);
+ return NULL;
+}
+
+static void
+print_regex_error (int ret)
+{
+ switch (ret) {
+ case REG_BADBR:
+ fprintf (stderr,
+ "There was an invalid `\\{...\\}' construct in the regular\n"
+ "expression. A valid `\\{...\\}' construct must contain either a\n"
+ "single number, or two numbers in increasing order separated by a\n"
+ "comma.\n");
+ break;
+
+ case REG_BADPAT:
+ fprintf (stderr,
+ "There was a syntax error in the regular expression.\n");
+ break;
+
+ case REG_BADRPT:
+ fprintf (stderr,
+ "A repetition operator such as `?' or `*' appeared in a bad\n"
+ "position (with no preceding subexpression to act on).\n");
+ break;
+
+ case REG_ECOLLATE:
+ fprintf (stderr,
+ "The regular expression referred to an invalid collating element\n"
+ "(one not defined in the current locale for string collation).\n");
+ break;
+
+ case REG_ECTYPE:
+ fprintf (stderr,
+ "The regular expression referred to an invalid character class name.\n");
+ break;
+
+#if REG_EESCAPE != REG_BADPAT
+ case REG_EESCAPE:
+ fprintf (stderr,
+ "The regular expression ended with `\\'.\n");
+ break;
+#endif
+
+ case REG_ESUBREG:
+ fprintf (stderr,
+ "There was an invalid number in the `\\DIGIT' construct.\n");
+ break;
+
+ case REG_EBRACK:
+ fprintf (stderr,
+ "There were unbalanced square brackets in the regular expression.\n");
+ break;
+
+#if REG_EPAREN != REG_BADPAT
+ case REG_EPAREN:
+ fprintf (stderr,
+ "An extended regular expression had unbalanced parentheses, or a\n"
+ "basic regular expression had unbalanced `\\(' and `\\)'.\n");
+ break;
+#endif
+
+#if REG_EBRACE != REG_BADPAT
+ case REG_EBRACE:
+ fprintf (stderr,
+ "The regular expression had unbalanced `\\{' and `\\}'.\n");
+ break;
+#endif
+
+#ifdef REG_EBOL
+ case REG_EBOL:
+ fprintf (stderr, "Found ^ not at the beginning.\n");
+ break;
+#endif
+
+#ifdef REG_EEOL
+ case REG_EEOL:
+ fprintf (stderr, "Found $ not at the end.\n");
+ break;
+#endif
+
+ case REG_ERANGE:
+ fprintf (stderr,
+ "One of the endpoints in a range expression was invalid.\n");
+ break;
+
+ case REG_ESPACE:
+ fprintf (stderr,
+ "`regcomp' ran out of memory.\n");
+ break;
+
+ default:
+ fprintf (stderr, "regexp error %d\n", ret);
+ }
+}
+
+static GSList *format_match_list = NULL;
+static GSList *format_dup_match_list = NULL;
+static GSList *format_failed_match_list = NULL;
+
+void
+format_match_release (GnmFormat *fmt)
+{
+ if (fmt->regexp_str != NULL) {
+ g_free (fmt->regexp_str);
+ go_regfree (&fmt->regexp);
+ g_byte_array_free (fmt->match_tags, TRUE);
+ }
+}
+
+gboolean
+format_match_create (GnmFormat *fmt)
+{
+ GByteArray *match_tags;
+ char *regexp;
+ go_regex_t r;
+ int ret;
+
+ g_return_val_if_fail (fmt != NULL, FALSE);
+ g_return_val_if_fail (fmt->regexp_str == NULL, FALSE);
+ g_return_val_if_fail (fmt->match_tags == NULL, FALSE);
+ g_return_val_if_fail (strcmp (fmt->format, "General"), FALSE);
+
+ regexp = format_create_regexp (fmt->format, &match_tags);
+ if (!regexp) {
+ fmt->regexp_str = NULL;
+ fmt->match_tags = NULL;
+ return FALSE;
+ }
+
+ ret = go_regcomp (&r, regexp, REG_EXTENDED | REG_ICASE);
+ if (ret != 0) {
+ g_warning ("expression [%s] produced [%s]", fmt->format, regexp);
+ print_regex_error (ret);
+ g_free (regexp);
+ return FALSE;
+ }
+
+ fmt->regexp_str = regexp;
+ fmt->regexp = r;
+ fmt->match_tags = match_tags;
+
+ return TRUE;
+}
+
+/*
+ * value_is_error : Check to see if a string begins with one of the magic
+ * error strings.
+ *
+ * @str : The string to test
+ *
+ * returns : an error if there is one, or NULL.
+ */
+static GnmValue *
+value_is_error (char const *str)
+{
+ GnmStdError e;
+
+ for (e = (GnmStdError)0; e < GNM_ERROR_UNKNOWN; e++)
+ if (0 == strcmp (str, value_error_name (e, TRUE)))
+ return value_new_error_std (NULL, e);
+
+ return NULL;
+}
+
+/*
+ * Loads the initial formats that we will recognize
+ */
+void
+format_match_init (void)
+{
+ int i;
+ GnmFormat *fmt;
+ GHashTable *hash;
+
+ currency_date_format_init ();
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (i = 0; cell_formats[i]; i++) {
+ char const * const * p = cell_formats[i];
+
+ for (; *p; p++) {
+ /* do not include text formats in the standard set */
+ if (!strcmp ("@", *p))
+ continue;
+
+ fmt = style_format_new_XL (*p, FALSE);
+ if (fmt->regexp_str != NULL) {
+ /* TODO : * We could keep track of the regexps
+ * that General would match. and avoid putting
+ * them in the list. */
+ if (g_hash_table_lookup (hash, fmt->regexp_str) == NULL) {
+ format_match_list = g_slist_append (format_match_list, fmt);
+ g_hash_table_insert (hash, fmt->regexp_str, fmt);
+ } else
+ format_dup_match_list = g_slist_append (format_dup_match_list, fmt);
+ } else
+ format_failed_match_list = g_slist_append (format_failed_match_list, fmt);
+ }
+ }
+ g_hash_table_destroy (hash);
+}
+
+void
+format_match_finish (void)
+{
+ GSList *l;
+
+ for (l = format_match_list; l; l = l->next)
+ style_format_unref (l->data);
+ g_slist_free (format_match_list);
+
+ for (l = format_dup_match_list; l; l = l->next)
+ style_format_unref (l->data);
+ g_slist_free (format_dup_match_list);
+
+ for (l = format_failed_match_list; l; l = l->next)
+ style_format_unref (l->data);
+ g_slist_free (format_failed_match_list);
+
+ currency_date_format_shutdown ();
+}
+
+/*
+ * table_lookup:
+ *
+ * Looks the string in the table passed
+ */
+static int
+table_lookup (char const *str, char const *const *table)
+{
+ char const *const *p = table;
+ int i = 0;
+
+ for (p = table; *p; p++, i++) {
+ char const *v = *p;
+ char const *iv = _(*p);
+
+ if (*v == '*') {
+ v++;
+ iv++;
+ }
+
+ if (g_ascii_strcasecmp (str, v) == 0)
+ return i;
+
+ if (g_ascii_strcasecmp (str, iv) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+/*
+ * extract_text:
+ *
+ * Returns a newly allocated string which is a region from
+ * STR. The ranges are defined in the regmatch_t variable MP
+ * in the fields rm_so and rm_eo
+ */
+static char *
+extract_text (char const *str, const regmatch_t *mp)
+{
+ char *p;
+
+ p = g_malloc (mp->rm_eo - mp->rm_so + 1);
+ strncpy (p, &str[mp->rm_so], mp->rm_eo - mp->rm_so);
+ p[mp->rm_eo - mp->rm_so] = 0;
+
+ return p;
+}
+
+/*
+ * Given a number of matches described by MP on S,
+ * compute the number based on the information on ARRAY
+ *
+ * Currently the code cannot mix a MATCH_NUMBER with any
+ * of the date/time matching.
+ */
+static GnmValue *
+compute_value (char const *s, const regmatch_t *mp,
+ GByteArray *array, GnmDateConventions const *date_conv)
+{
+ int const len = array->len;
+ gnm_float number = 0.0;
+ guchar *data = array->data;
+ gboolean percentify = FALSE;
+ gboolean is_number = FALSE;
+ gboolean is_pm = FALSE;
+ gboolean is_explicit_am = FALSE;
+ gboolean is_neg = FALSE;
+ gboolean hours_are_cummulative = FALSE;
+ gboolean minutes_are_cummulative = FALSE;
+ gboolean seconds_are_cummulative = FALSE;
+ gboolean hours_set = FALSE;
+ gboolean minutes_set = FALSE;
+ gboolean seconds_set = FALSE;
+ int i;
+ int month, day, year, year_short;
+ int hours, minutes;
+ gnm_float seconds;
+ int numerator = 0, denominator = 1;
+
+ GString const *thousands_sep = format_get_thousand ();
+ GString const *decimal = format_get_decimal ();
+
+ month = day = year = year_short = -1;
+ hours = minutes = -1;
+ seconds = -1.;
+
+ for (i = 0; i < len; ) {
+ MatchType type = *(data++);
+ char *str;
+
+ str = extract_text (s, &mp[++i]);
+
+#ifdef DEBUG_NUMBER_MATCH
+ printf ("Item[%d] = \'%s\' is a %d\n", i, str, type);
+#endif
+ switch (type) {
+ case MATCH_MONTH_FULL:
+ month = table_lookup (str, month_long);
+ if (month == -1) {
+ g_free (str);
+ return NULL;
+ }
+ month++;
+ break;
+
+ case MATCH_MONTH_NUMBER:
+ month = atoi (str);
+ break;
+
+ case MATCH_MONTH_SHORT:
+ month = table_lookup (str, month_short);
+ if (month == -1) {
+ g_free (str);
+ return NULL;
+ }
+ month++;
+ break;
+
+ case MATCH_DAY_FULL:
+ /* FIXME: handle weekday */
+ break;
+
+ case MATCH_DAY_NUMBER:
+ day = atoi (str);
+ break;
+
+ case MATCH_NUMERATOR:
+ numerator = atoi (str);
+ break;
+
+ case MATCH_DENOMINATOR:
+ denominator = atoi (str);
+ if (denominator <= 0)
+ return NULL;
+ if (is_neg && numerator < 0)
+ return NULL;
+
+ is_number = TRUE;
+ if (is_neg)
+ number -= numerator / (gnm_float)denominator;
+ else
+ number += numerator / (gnm_float)denominator;
+ break;
+
+ case MATCH_NUMBER:
+ if (*str != '\0') {
+ char *ptr = str;
+
+ switch (*ptr) {
+ case '-':
+ is_neg = TRUE;
+ ptr++;
+ break;
+ case '+':
+ ptr++;
+ /* Fall through. */
+ default:
+ is_neg = FALSE;
+ }
+
+ number = 0.;
+ /* FIXME: this loop is bogus. */
+ while (1) {
+ int thisnumber;
+ if (number > DBL_MAX / 1000.0) {
+ g_free (str);
+ return NULL;
+ }
+
+ number *= 1000.0;
+
+ errno = 0; /* strtol sets errno, but does not clear it. */
+ thisnumber = strtoul (ptr, &ptr, 10);
+ if (errno == ERANGE) {
+ g_free (str);
+ return NULL;
+ }
+
+ number += thisnumber;
+
+ if (strncmp (ptr, thousands_sep->str, thousands_sep->len) != 0)
+ break;
+
+ ptr += thousands_sep->len;
+ }
+ is_number = TRUE;
+ if (is_neg) number = -number;
+ }
+ break;
+
+ case MATCH_NUMBER_DECIMALS: {
+ char *exppart = NULL;
+ if (strncmp (str, decimal->str, decimal->len) == 0) {
+ char *end;
+ errno = 0; /* strtognum sets errno, but does not clear it. */
+ if (seconds < 0) {
+ gnm_float fraction;
+
+ for (end = str; *end && *end != 'e' && *end != 'E'; )
+ end++;
+ if (*end) {
+ exppart = end + 1;
+ *end = 0;
+ }
+
+ fraction = strtognum (str, &end);
+ if (is_neg)
+ number -= fraction;
+ else
+ number += fraction;
+ is_number = TRUE;
+ } else
+ seconds += strtognum (str, &end);
+ }
+ if (exppart) {
+ char *end;
+ int exponent;
+
+ errno = 0; /* strtol sets errno, but does not clear it. */
+ exponent = strtol (exppart, &end, 10);
+ number *= gpow10 (exponent);
+ }
+ break;
+ }
+
+ case MATCH_CUMMULATIVE_HOURS:
+ hours_are_cummulative = TRUE;
+ if (str[0] == '-') is_neg = TRUE;
+ case MATCH_HOUR:
+ hours_set = TRUE;
+ hours = abs (atoi (str));
+ break;
+
+ case MATCH_CUMMULATIVE_MINUTES:
+ minutes_are_cummulative = TRUE;
+ if (str[0] == '-') is_neg = TRUE;
+ case MATCH_MINUTE:
+ minutes_set = TRUE;
+ minutes = abs (atoi (str));
+ break;
+
+ case MATCH_CUMMULATIVE_SECONDS :
+ seconds_are_cummulative = TRUE;
+ if (str[0] == '-') is_neg = TRUE;
+ case MATCH_SECOND:
+ seconds_set = TRUE;
+ seconds = abs (atoi (str));
+ break;
+
+ case MATCH_PERCENT:
+ percentify = TRUE;
+ break;
+
+ case MATCH_YEAR_FULL:
+ year = atoi (str);
+ break;
+
+ case MATCH_YEAR_SHORT:
+ year_short = atoi (str);
+ break;
+
+ case MATCH_AMPM:
+ if (*str == 'p' || *str == 'P')
+ is_pm = TRUE;
+ else
+ is_explicit_am = TRUE;
+ break;
+
+ case MATCH_SKIP:
+ break;
+
+ case MATCH_STRING_CONSTANT:
+ return value_new_string_str (gnm_string_get_nocopy (str));
+
+ default :
+ g_warning ("compute_value: This should not happen.");
+ break;
+ }
+
+ g_free (str);
+ }
+
+ if (is_number) {
+ if (percentify)
+ number *= 0.01;
+ return value_new_float (number);
+ }
+
+ if (!(year == -1 && month == -1 && day == -1)) {
+ time_t t = time (NULL);
+ static time_t lastt;
+ static struct tm tm;
+ GDate *date;
+
+ if (t != lastt) {
+ /*
+ * Since localtime is moderately expensive, do
+ * at most one call per second. One per day
+ * would be enough but is harder to check for.
+ */
+ lastt = t;
+ tm = *localtime (&t);
+ }
+
+ if (year == -1) {
+ if (year_short != -1) {
+ /* Window of -75 thru +24 years. */
+ /* (TODO: See what current
+ * version of MS Excel uses.) */
+ /* Earliest allowable interpretation
+ * is 75 years ago. */
+ int earliest_ccyy
+ = tm.tm_year + 1900 - 75;
+ int earliest_cc = earliest_ccyy / 100;
+
+ g_return_val_if_fail (year_short >= 0 &&
+ year_short <= 99,
+ NULL);
+ year = earliest_cc * 100 + year_short;
+ /*
+ * Our first guess at year has the same
+ * cc part as EARLIEST_CCYY, so is
+ * guaranteed to be in [earliest_ccyy -
+ * 99, earliest_ccyy + 99]. The yy
+ * part is of course year_short.
+ */
+ if (year < earliest_ccyy)
+ year += 100;
+ /*
+ * year is now guaranteed to be in
+ * [earliest_ccyy, earliest_ccyy + 99],
+ * i.e. -75 thru +24 years from current
+ * year; and year % 100 == short_year.
+ */
+ } else if (month != -1) {
+ /* Window of -6 thru +5 months. */
+ /* (TODO: See what current
+ * version of MS Excel uses.) */
+ int earliest_yyymm
+ = (tm.tm_year * 12 +
+ tm.tm_mon - 6);
+ year = earliest_yyymm / 12;
+ /* First estimate of yyy (i.e. years
+ * since 1900) is the yyy part of
+ * earliest_yyymm. year*12+month-1 is
+ * guaranteed to be in [earliest_yyymm
+ * - 11, earliest_yyymm + 11].
+ */
+ year += (year * 12 + month <=
+ earliest_yyymm);
+ /* year*12+month-1 is now guaranteed
+ * to be in [earliest_yyymm,
+ * earliest_yyymm + 11], i.e.
+ * representing -6 thru +5 months
+ * from now.
+ */
+ year += 1900;
+ /* Finally convert from years since
+ * 1900 (yyy) to a proper 4-digit
+ * year.
+ */
+ } else
+ year = 1900 + tm.tm_year;
+ }
+ if (month == -1)
+ month = tm.tm_mon + 1;
+ if (day == -1)
+ day = tm.tm_mday;
+
+ if (year < 1900 || !g_date_valid_dmy (day, month, year))
+ return NULL;
+
+ date = g_date_new_dmy (day, month, year);
+ number = datetime_g_to_serial (date, date_conv);
+ g_date_free (date);
+ }
+
+ if (!seconds_set && !minutes_set && !hours_set)
+ return value_new_int (number);
+
+ if (!seconds_set)
+ seconds = 0;
+
+ if (!minutes_set)
+ minutes = 0;
+
+ if (!hours_set)
+ hours = 0;
+
+ if (!hours_are_cummulative) {
+ if (is_pm) {
+ if (hours < 12)
+ hours += 12;
+ } else if (is_explicit_am && hours == 12)
+ hours = 0;
+ }
+
+ if ((hours < 0 || hours > 23) && !hours_are_cummulative)
+ return NULL;
+
+ if ((minutes < 0 || minutes > 59) && !minutes_are_cummulative)
+ return NULL;
+
+ if ((seconds < 0 || seconds > 59) && !seconds_are_cummulative)
+ return NULL;
+
+ if (hours == 0 && minutes == 0 && seconds == 0)
+ return value_new_int (number);
+
+ number += (hours * 3600 + minutes * 60 + seconds) / (3600*24.0);
+ if (is_neg) number = -number;
+
+ return value_new_float (number);
+}
+
+/**
+ * format_match_simple :
+ * @s : A String to match against.
+ *
+ * Attempt to match the the supplied string as a simple value.
+ *
+ * WARNING WARNING WARNING : This routine should NEVER be changed to match
+ * VALUE_STRING that will break the parsers
+ * handling of named expressions.
+ */
+GnmValue *
+format_match_simple (char const *text)
+{
+ /* Is it a boolean? */
+ if (0 == g_ascii_strcasecmp (text, format_boolean (TRUE)))
+ return value_new_bool (TRUE);
+ if (0 == g_ascii_strcasecmp (text, format_boolean (FALSE)))
+ return value_new_bool (FALSE);
+
+ /* Is it an error? */
+ if (*text == '#') {
+ GnmValue *err = value_is_error (text);
+ if (err != NULL)
+ return err;
+ }
+
+ /* Is it an integer? */
+ {
+ char *end;
+ long l;
+
+ errno = 0; /* strtol sets errno, but does not clear it. */
+ l = strtol (text, &end, 10);
+ if (text != end && errno != ERANGE && l == (int)l) {
+ /* Allow and ignore spaces at the end. */
+ while (*end == ' ')
+ end++;
+ if (*end == '\0')
+ return value_new_int ((int)l);
+ }
+ }
+
+ /* Is it a double? */
+ {
+ char *end;
+ gnm_float d;
+
+ errno = 0; /* strtognum sets errno, but does not clear it. */
+ d = strtognum (text, &end);
+ if (text != end && errno != ERANGE) {
+ /* Allow and ignore spaces at the end. */
+ while (*end == ' ')
+ end++;
+ if (*end == '\0')
+ return value_new_float (d);
+ }
+ }
+
+ return NULL;
+}
+
+#define NM 40
+
+/**
+ * format_match :
+ *
+ * @text : The text to parse
+ * @cur_fmt : The current format for the value (potentially NULL)
+ * @date_conv: optional date convention
+ *
+ * Attempts to parse the supplied string to see if it matches a known value
+ * format. The caller is responsible for releasing the resulting value.
+ **/
+GnmValue *
+format_match (char const *text, GnmFormat *cur_fmt,
+ GnmDateConventions const *date_conv)
+{
+ GnmValue *v;
+ GSList *l;
+ regmatch_t mp[NM + 1];
+
+ if (text[0] == '\0')
+ return value_new_empty ();
+
+ /* If it begins with a '\'' it is a string */
+ if (text[0] == '\'')
+ return value_new_string (text + 1);
+
+ if (cur_fmt) {
+ switch (cur_fmt->family) {
+ case FMT_TEXT:
+ return value_new_string (text);
+
+ default:
+ if (cur_fmt->regexp_str != NULL &&
+ go_regexec (&cur_fmt->regexp, text, NM, mp, 0) != REG_NOMATCH &&
+ NULL != (v = compute_value (text, mp, cur_fmt->match_tags,
+ date_conv))) {
+#ifdef DEBUG_NUMBER_MATCH
+ int i;
+ g_print ("matches expression: %s %s\n", cur_fmt->format, cur_fmt->regexp_str);
+ for (i = 0; i < NM; i++) {
+ char *p;
+
+ if (mp[i].rm_so == -1)
+ break;
+
+ p = extract_text (text, &mp[i]);
+ g_print ("%d %d->%s\n", mp[i].rm_so, mp[i].rm_eo, p);
+ }
+#endif
+
+ value_set_fmt (v, cur_fmt);
+ return v;
+ } else {
+#ifdef DEBUG_NUMBER_MATCH
+ g_print ("does not match expression: %s %s\n",
+ cur_fmt->format,
+ cur_fmt->regexp_str ? cur_fmt->regexp_str : "(null)");
+#endif
+ }
+ }
+ }
+
+ /* Check basic types */
+ v = format_match_simple (text);
+ if (v != NULL)
+ return v;
+
+ /* Fall back to checking the set of canned formats */
+ for (l = format_match_list; l; l = l->next) {
+ GnmFormat *fmt = l->data;
+#ifdef DEBUG_NUMBER_MATCH
+ printf ("test: %s \'%s\'\n", fmt->format, fmt->regexp_str);
+#endif
+ if (go_regexec (&fmt->regexp, text, NM, mp, 0) == REG_NOMATCH)
+ continue;
+
+#ifdef DEBUG_NUMBER_MATCH
+ {
+ int i;
+ printf ("matches expression: %s %s\n", fmt->format, fmt->regexp_str);
+ for (i = 0; i < NM; i++) {
+ char *p;
+
+ if (mp[i].rm_so == -1)
+ break;
+
+ p = extract_text (text, &mp[i]);
+ printf ("%d %d->%s\n", mp[i].rm_so, mp[i].rm_eo, p);
+ }
+ }
+#endif
+
+ v = compute_value (text, mp, fmt->match_tags, date_conv);
+
+#ifdef DEBUG_NUMBER_MATCH
+ if (v) {
+ printf ("value = ");
+ value_dump (v);
+ } else
+ printf ("unable to compute value\n");
+#endif
+ if (v != NULL) {
+ value_set_fmt (v, fmt);
+ return v;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * format_match_number :
+ *
+ * @text : The text to parse
+ * @cur_fmt : The current format for the value (potentially NULL)
+ * @date_conv: optional date convention
+ *
+ * Attempts to parse the supplied string to see if it matches a known value format.
+ * Will eventually use the current cell format in preference to canned formats.
+ * If @format is supplied it will get a copy of the matching format with no
+ * additional references. The caller is responsible for releasing the
+ * resulting value. Will ONLY return numbers.
+ */
+GnmValue *
+format_match_number (char const *text, GnmFormat *cur_fmt,
+ GnmDateConventions const *date_conv)
+{
+ GnmValue *res = format_match (text, cur_fmt, date_conv);
+
+ if (res != NULL) {
+ if (VALUE_IS_NUMBER (res))
+ return res;
+ value_release (res);
+ }
+ return NULL;
+}
--- /dev/null
+++ lib/goffice/split/gnumeric-gconf.h
@@ -0,0 +1,214 @@
+#ifndef GNM_CONF_H
+#define GNM_CONF_H
+
+#include <numbers.h>
+#include <gnumeric.h>
+//#include <print-info.h>
+
+typedef struct {
+ struct {
+ GSList const *extra_dirs;
+ char *sys_dir;
+ char *usr_dir;
+ } autoformat;
+
+ struct {
+ char const *name;
+ float size;
+ gboolean is_bold, is_italic;
+ } default_font;
+
+ gint file_history_max;
+ GSList const *file_history_files;
+ guint num_of_recent_funcs;
+ GSList const *recent_funcs;
+
+ GSList const *plugin_file_states;
+ GSList const *plugin_extra_dirs;
+ GSList const *active_plugins;
+ gboolean activate_new_plugins;
+
+ gboolean show_sheet_name;
+ guint max_descriptor_width;
+ gint undo_size;
+ gint undo_max_number;
+
+ gint initial_sheet_number;
+ float horizontal_window_fraction;
+ float vertical_window_fraction;
+ float zoom;
+
+ gint xml_compression_level;
+ gboolean file_overwrite_default_answer;
+ gboolean file_ask_single_sheet_save;
+
+ gboolean sort_default_by_case;
+ gboolean sort_default_retain_formats;
+ gboolean sort_default_ascending;
+ gint sort_max_initial_clauses;
+
+ gboolean print_all_sheets; /* vs print only selected */
+ gchar *printer_config;
+ GSList const *printer_header;
+ GSList const *printer_footer;
+ GSList const *printer_header_formats_left;
+ GSList const *printer_header_formats_middle;
+ GSList const *printer_header_formats_right;
+ GnmStyle *printer_decoration_font;
+ gboolean print_center_horizontally;
+ gboolean print_center_vertically;
+ gboolean print_grid_lines;
+ gboolean print_even_if_only_styles;
+ gboolean print_black_and_white;
+ gboolean print_titles;
+ gboolean print_order_right_then_down;
+ gboolean print_scale_percentage;
+ float print_scale_percentage_value;
+ gint print_scale_width;
+ gint print_scale_height;
+ gchar *print_repeat_top;
+ gchar *print_repeat_left;
+ //PrintMargins print_tb_margins;
+
+ float horizontal_dpi;
+ float vertical_dpi;
+ gboolean auto_complete;
+ gboolean transition_keys;
+ gboolean live_scrolling;
+ gint recalc_lag;
+ gboolean unfocused_range_selection;
+ gboolean prefer_clipboard_selection; /* As opposed to "primary". */
+ gboolean latex_use_utf8;
+} GnmAppPrefs;
+extern GnmAppPrefs const *gnm_app_prefs;
+
+void gnm_conf_init (gboolean fast);
+void gnm_conf_shutdown (void);
+void gnm_conf_sync (void);
+
+/* autocorrect */
+void gnm_gconf_set_autocorrect_init_caps (gboolean val);
+void gnm_gconf_set_autocorrect_first_letter (gboolean val);
+void gnm_gconf_set_autocorrect_names_of_days (gboolean val);
+void gnm_gconf_set_autocorrect_replace (gboolean val);
+
+/* autocomplete */
+void gnm_gconf_set_autocomplete (gboolean val);
+
+/* autoformat */
+void gnm_gconf_set_autoformat_sys_dirs (char const * string);
+void gnm_gconf_set_autoformat_usr_dirs (char const * string);
+
+/* file history */
+void gnm_gconf_set_file_history_files (GSList *list);
+void gnm_gconf_set_file_history_number (gint value);
+
+/* plugins */
+void gnm_gconf_set_plugin_file_states (GSList *list);
+void gnm_gconf_set_plugin_extra_dirs (GSList *list);
+void gnm_gconf_set_active_plugins (GSList *list);
+void gnm_gconf_set_activate_new_plugins (gboolean val);
+
+/* undo */
+void gnm_gconf_set_show_sheet_name (gboolean val);
+void gnm_gconf_set_max_descriptor_width (gint val);
+void gnm_gconf_set_undo_size (gint val);
+void gnm_gconf_set_undo_max_number (gint val);
+
+/* xml/files */
+void gnm_gconf_set_recent_funcs (GSList *list);
+void gnm_gconf_set_xml_compression (gint value);
+void gnm_gconf_set_file_overwrite (gboolean value);
+void gnm_gconf_set_file_single_sheet_save (gboolean value);
+
+/* print-setup & printing */
+void gnm_gconf_set_all_sheets (gboolean val);
+void gnm_gconf_set_printer_config (gchar *str);
+void gnm_gconf_set_printer_header (gchar const *left, gchar const *middle,
+ gchar const *right);
+void gnm_gconf_set_printer_footer (gchar const *left, gchar const *middle,
+ gchar const *right);
+void gnm_gconf_set_print_center_horizontally (gboolean val);
+void gnm_gconf_set_print_center_vertically (gboolean val);
+void gnm_gconf_set_print_grid_lines (gboolean val);
+void gnm_gconf_set_print_even_if_only_styles (gboolean val);
+void gnm_gconf_set_print_black_and_white (gboolean val);
+void gnm_gconf_set_print_titles (gboolean val);
+void gnm_gconf_set_print_order_right_then_down (gboolean val);
+void gnm_gconf_set_print_scale_percentage (gboolean val);
+void gnm_gconf_set_print_scale_percentage_value (gnm_float val);
+//void gnm_gconf_set_print_tb_margins (PrintMargins const *pm);
+void gnm_gconf_set_print_header_formats (GSList *left, GSList *middle,
+ GSList *right);
+
+/* gui */
+void gnm_gconf_set_gui_window_x (gnm_float val);
+void gnm_gconf_set_gui_window_y (gnm_float val);
+void gnm_gconf_set_gui_zoom (gnm_float val);
+void gnm_gconf_set_gui_transition_keys (gboolean value);
+void gnm_gconf_set_gui_livescrolling (gboolean value);
+void gnm_gconf_set_gui_resolution_h (gnm_float val);
+void gnm_gconf_set_gui_resolution_v (gnm_float val);
+
+/* default font */
+void gnm_gconf_set_default_font_size (gnm_float val);
+void gnm_gconf_set_default_font_name (char const *str);
+void gnm_gconf_set_default_font_bold (gboolean val);
+void gnm_gconf_set_default_font_italic (gboolean val);
+
+/* hf font */
+void gnm_gconf_set_hf_font (GnmStyle const *mstyle);
+
+/* sorting */
+void gnm_gconf_set_sort_dialog_max_initial (gint value);
+void gnm_gconf_set_sort_retain_form (gboolean value);
+void gnm_gconf_set_sort_by_case (gboolean value);
+void gnm_gconf_set_sort_ascending (gboolean value);
+
+/* workbook */
+void gnm_gconf_set_workbook_nsheets (gint value);
+void gnm_gconf_set_unfocused_rs (gboolean value);
+
+/* function selector and formula guru */
+void gnm_gconf_set_num_recent_functions (gint value);
+
+/* standard plugins */
+void gnm_gconf_set_latex_use_utf8 (gboolean value);
+
+/* application interface */
+void gnm_gconf_set_prefer_clipboard (gboolean value);
+
+/**************************************************************/
+
+char *go_conf_get_short_desc (char const *key);
+char *go_conf_get_long_desc (char const *key);
+GType go_conf_get_type (char const *key);
+char *go_conf_get_value_as_str (char const *key);
+gboolean go_conf_set_value_from_str (char const *key, char const *val_str);
+
+gboolean go_conf_get_bool (char const *key);
+int go_conf_get_int (char const *key);
+double go_conf_get_double (char const *key);
+char *go_conf_get_string (char const *key);
+GSList *go_conf_get_str_list (char const *key);
+
+gboolean go_conf_load_bool (char const *key, gboolean default_val);
+int go_conf_load_int (char const *key, int minima, int maxima, int default_val);
+double go_conf_load_double (char const *key, double minima, double maxima, double default_val);
+char *go_conf_load_string (char const *key);
+GSList *go_conf_load_str_list (char const *key);
+
+void go_conf_set_bool (char const *key, gboolean val);
+void go_conf_set_int (char const *key, gint val);
+void go_conf_set_double (char const *key, gnm_float val);
+void go_conf_set_string (char const *key, char const *str);
+void go_conf_set_str_list (char const *key, GSList *list);
+
+void go_conf_sync (void);
+
+typedef void (*GOConfMonitorFunc) (char const *key, gpointer data);
+void go_conf_remove_monitor (guint monitor_id);
+guint go_conf_add_monitor (char const *key,
+ GOConfMonitorFunc monitor, gpointer data);
+
+#endif /* GNM_CONF_H */
--- /dev/null
+++ lib/goffice/split/position.h
@@ -0,0 +1,77 @@
+#ifndef GNUMERIC_POSITION_H
+#define GNUMERIC_POSITION_H
+
+#include "gnumeric.h"
+
+struct _GnmEvalPos {
+ GnmCellPos eval;
+ Sheet *sheet;
+ GnmDependent *dep; /* optionally NULL */
+};
+
+struct _GnmParsePos {
+ GnmCellPos eval;
+ Sheet *sheet;
+ Workbook *wb;
+};
+
+/**
+ * Used for getting a valid Sheet *from a GnmCellRef
+ * Syntax is GnmCellRef, valid Sheet *
+ */
+#define eval_sheet(a,b) (((a) != NULL) ? (a) : (b))
+
+/* Initialization routines for Evaluation Positions */
+GnmEvalPos *eval_pos_init (GnmEvalPos *ep, Sheet *s, GnmCellPos const *pos);
+GnmEvalPos *eval_pos_init_dep (GnmEvalPos *ep, GnmDependent const *dep);
+GnmEvalPos *eval_pos_init_cell (GnmEvalPos *ep, GnmCell const *cell);
+GnmEvalPos *eval_pos_init_sheet (GnmEvalPos *ep, Sheet *sheet);
+
+/* Initialization routines for Parse Positions */
+GnmParsePos *parse_pos_init (GnmParsePos *pp, Workbook *wb,
+ Sheet *sheet, int col, int row);
+GnmParsePos *parse_pos_init_dep (GnmParsePos *pp, GnmDependent const *dep);
+GnmParsePos *parse_pos_init_cell (GnmParsePos *pp, GnmCell const *cell);
+GnmParsePos *parse_pos_init_evalpos (GnmParsePos *pp, GnmEvalPos const *pos);
+GnmParsePos *parse_pos_init_editpos (GnmParsePos *pp, SheetView const *sv);
+GnmParsePos *parse_pos_init_sheet (GnmParsePos *pp, Sheet *sheet);
+
+/*****************************************************************************/
+
+struct _GnmCellRef {
+ Sheet *sheet;
+ int col, row;
+
+ unsigned char col_relative;
+ unsigned char row_relative;
+};
+struct _GnmRangeRef {
+ GnmCellRef a, b;
+};
+
+GnmCellRef *cellref_init (GnmCellRef *ref, Sheet *sheet, int col, int row,
+ gboolean rel);
+gboolean cellref_equal (GnmCellRef const *a, GnmCellRef const *b);
+guint cellref_hash (GnmCellRef const *cr);
+void cellref_make_abs (GnmCellRef *dest,
+ GnmCellRef const *src,
+ GnmEvalPos const *ep);
+int cellref_get_abs_col (GnmCellRef const *ref,
+ GnmEvalPos const *pos);
+int cellref_get_abs_row (GnmCellRef const *cell_ref,
+ GnmEvalPos const *src_fp);
+void cellref_get_abs_pos (GnmCellRef const *cell_ref,
+ GnmCellPos const *pos,
+ GnmCellPos *res);
+
+gboolean rangeref_equal (GnmRangeRef const *a, GnmRangeRef const *b);
+guint rangeref_hash (GnmRangeRef const *cr);
+GnmRangeRef *rangeref_dup (GnmRangeRef const *cr);
+void rangeref_normalize (GnmRangeRef const *ref, GnmEvalPos const *ep,
+ Sheet **start_sheet, Sheet **end_sheet,
+ GnmRange *dest);
+
+guint cellpos_hash (GnmCellPos const *key);
+gint cellpos_equal (GnmCellPos const *a, GnmCellPos const *b);
+
+#endif /* GNUMERIC_POSITION_H */
--- /dev/null
+++ lib/goffice/split/style-border.h
@@ -0,0 +1,99 @@
+#ifndef GNUMERIC_STYLE_BORDER_H
+#define GNUMERIC_STYLE_BORDER_H
+
+#include "gnumeric.h"
+#include <gdk/gdkgc.h>
+#include <libgnomeprint/gnome-print.h>
+
+typedef enum {
+ STYLE_BORDER_HORIZONTAL,
+ STYLE_BORDER_VERTICAL,
+ STYLE_BORDER_DIAGONAL
+} StyleBorderOrientation;
+
+typedef enum {
+ STYLE_BORDER_NONE = 0x0,
+ STYLE_BORDER_THIN = 0x1,
+ STYLE_BORDER_MEDIUM = 0x2,
+ STYLE_BORDER_DASHED = 0x3,
+ STYLE_BORDER_DOTTED = 0x4,
+ STYLE_BORDER_THICK = 0x5,
+ STYLE_BORDER_DOUBLE = 0x6,
+ STYLE_BORDER_HAIR = 0x7,
+ STYLE_BORDER_MEDIUM_DASH = 0x8,
+ STYLE_BORDER_DASH_DOT = 0x9,
+ STYLE_BORDER_MEDIUM_DASH_DOT = 0xa,
+ STYLE_BORDER_DASH_DOT_DOT = 0xb,
+ STYLE_BORDER_MEDIUM_DASH_DOT_DOT = 0xc,
+ STYLE_BORDER_SLANTED_DASH_DOT = 0xd,
+
+ /* ONLY for internal use */
+ STYLE_BORDER_INCONSISTENT = 0xe,
+
+ STYLE_BORDER_MAX
+} StyleBorderType;
+
+/* The order corresponds to the border_buttons name list
+ * in dialog_cell_format_impl */
+typedef enum _StyleBorderLocation {
+ STYLE_BORDER_TOP, STYLE_BORDER_BOTTOM,
+ STYLE_BORDER_LEFT, STYLE_BORDER_RIGHT,
+ STYLE_BORDER_REV_DIAG, STYLE_BORDER_DIAG,
+
+ /* These are special.
+ * They are logical rather than actual borders, however, they
+ * require extra lines to be drawn so they need to be here.
+ */
+ STYLE_BORDER_HORIZ, STYLE_BORDER_VERT,
+
+ STYLE_BORDER_EDGE_MAX
+} StyleBorderLocation;
+
+struct _GnmBorder {
+ /* Key elements */
+ StyleBorderType line_type;
+ GnmColor *color;
+ int begin_margin, end_margin, width;
+
+ /* Private */
+ GdkGC *gc;
+ GdkScreen *gc_screen;
+ gint ref_count;
+};
+
+void style_border_unref (GnmBorder *border);
+GnmBorder *style_border_ref (GnmBorder *border);
+
+#define style_border_is_blank(b) ((b) == NULL || (b)->line_type == STYLE_BORDER_NONE)
+GnmBorder *style_border_none (void);
+void style_border_none_set_color (GnmColor *color);
+
+GnmBorder *style_border_fetch (StyleBorderType const line_type,
+ GnmColor *color,
+ StyleBorderOrientation orientation);
+gboolean style_border_visible_in_blank (GnmBorder const *border);
+
+StyleBorderOrientation style_border_get_orientation (StyleBorderLocation type);
+
+gint style_border_get_width (StyleBorderType const line_type);
+void style_border_set_gc_dash (GdkGC *gc, StyleBorderType const line_type);
+
+void style_borders_row_draw (GnmBorder const * const * prev_vert,
+ GnmRow const *sr,
+ GdkDrawable *drawable,
+ int x, int y1, int y2,
+ int *colwidths, gboolean draw_vertical);
+void style_border_draw_diag (GnmStyle const *style,
+ GdkDrawable *drawable,
+ int x1, int y1, int x2, int y2);
+
+void style_borders_row_print (GnmBorder const * const * prev_vert,
+ GnmRow const *sr,
+ GnomePrintContext *context,
+ float x, float y1, float y2,
+ Sheet const *sheet, gboolean draw_vertical);
+void style_border_print_diag (GnmStyle const *style,
+ GnomePrintContext *context,
+ float x1, float y1, float x2, float y2);
+
+#endif /* GNUMERIC_STYLE_BORDER_H */
--- /dev/null
+++ lib/goffice/split/number-match.h
@@ -0,0 +1,18 @@
+#ifndef GNUMERIC_NUMBER_MATCH_H
+#define GNUMERIC_NUMBER_MATCH_H
+
+#include "gnumeric.h"
+
+gboolean format_match_create (GnmFormat *fmt);
+void format_match_release (GnmFormat *fmt);
+
+GnmValue *format_match_simple (char const *s);
+GnmValue *format_match (char const *s, GnmFormat *cur_fmt,
+ GnmDateConventions const *date_conv);
+GnmValue *format_match_number (char const *s, GnmFormat *cur_fmt,
+ GnmDateConventions const *date_conv);
+
+void format_match_init (void);
+void format_match_finish (void);
+
+#endif /* GNUMERIC_NUMBER_MATCH_H */
--- /dev/null
+++ lib/goffice/split/ranges.h
@@ -0,0 +1,116 @@
+#ifndef GNUMERIC_RANGES_H
+#define GNUMERIC_RANGES_H
+
+#include "gnumeric.h"
+
+/**
+ * range_equal:
+ * @a: First range
+ * @b: Second range
+ *
+ * NB. commutative, symmetric, and transitive.
+ *
+ * Returns: True if both ranges are equal.
+ **/
+#define range_equal(a,b) ((a)->start.row == (b)->start.row && \
+ (a)->end.row == (b)->end.row && \
+ (a)->start.col == (b)->start.col && \
+ (a)->end.col == (b)->end.col)
+
+/**
+ * range_overlap:
+ * @a: First range
+ * @b: Second range
+ *
+ * NB. commutative, symmetric, but not transitive.
+ *
+ * Returns: True if the ranges overlap at all.
+ **/
+#define range_overlap(a,b) (((a)->end.row >= (b)->start.row) && \
+ ((b)->end.row >= (a)->start.row) && \
+ ((a)->end.col >= (b)->start.col) && \
+ ((b)->end.col >= (a)->start.col))
+
+/**
+ * range_contains:
+ * @r: range to operate on
+ * @x: column,
+ * @y: row co-ordinate
+ *
+ * Determine if a range contains a col,row co-ordinate.
+ *
+ * Return value: TRUE if co-ordinate contained.
+ **/
+#define range_contains(r,x,y) (((y) <= (r)->end.row) && \
+ ((y) >= (r)->start.row) && \
+ ((x) >= (r)->start.col) && \
+ ((x) <= (r)->end.col))
+
+/*
+ * Quickly Test if a range is valid
+ */
+#define range_valid(r) ((r)->start.col <= (r)->end.col && \
+ (r)->start.row <= (r)->end.row)
+
+GnmRange *range_init_full_sheet (GnmRange *r);
+GnmRange *range_init_rangeref (GnmRange *range, GnmRangeRef const *rr);
+GnmRange *range_init_value (GnmRange *range, GnmValue const *v);
+GnmRange *range_init_cellpos (GnmRange *r, GnmCellPos const *start, GnmCellPos const *end);
+
+GnmRange *range_init (GnmRange *r, int start_col, int start_row,
+ int end_col, int end_row);
+GnmValue *range_parse (Sheet *sheet, char const *range, gboolean strict);
+gboolean parse_range (char const *text, GnmRange *r);
+void range_list_destroy (GSList *ranges);
+
+int range_width (GnmRange const *r);
+int range_height (GnmRange const *r);
+gboolean range_is_singleton (GnmRange const *r);
+gboolean range_is_infinite (GnmRange const *r);
+gboolean range_is_full (GnmRange const *r, gboolean is_cols);
+void range_clip_to_finite(GnmRange *range, Sheet *sheet);
+gboolean range_contained (GnmRange const *a, GnmRange const *b);
+gboolean range_adjacent (GnmRange const *a, GnmRange const *b);
+GnmRange range_merge (GnmRange const *a, GnmRange const *b);
+gboolean range_intersection (GnmRange *r,
+ GnmRange const *a,
+ GnmRange const *b);
+void range_normalize (GnmRange *src);
+GnmRange range_union (GnmRange const *a, GnmRange const *b);
+void range_ensure_sanity (GnmRange *range);
+gboolean range_is_sane (GnmRange const *range);
+gboolean range_translate (GnmRange *range, int col_offset, int row_offset);
+gboolean range_transpose (GnmRange *range, GnmCellPos const *origin);
+
+/* TODO : Do these 2 belong here ? or in sheet.h
+ * Probably sheet.h but that is overfull.
+ */
+gboolean range_trim (Sheet const *sheet, GnmRange *r,
+ gboolean cols);
+gboolean range_has_header (Sheet const *sheet, GnmRange const *src,
+ gboolean top, gboolean ignore_styles);
+
+char const *range_name (GnmRange const *src);
+void range_dump (GnmRange const *src, char const *suffix);
+GnmRange *range_dup (GnmRange const *src);
+
+GSList *range_split_ranges (GnmRange const *hard, GnmRange const *soft);
+GSList *range_fragment (GnmRange const *a, GnmRange const *b);
+void range_fragment_free (GSList *fragments);
+
+GnmSheetRange *global_range_new (Sheet *sheet, GnmRange const *r);
+GnmSheetRange *global_range_dup (GnmSheetRange const *src);
+gboolean value_to_global_range (GnmValue const *v, GnmSheetRange *res);
+void global_range_free (GnmSheetRange *gr);
+gboolean global_range_overlap (GnmSheetRange const *a, GnmSheetRange const *b);
+GnmValue *global_range_parse (Sheet *sheet, char const *range);
+char *global_range_name (Sheet *sheet, GnmRange const *r);
+gboolean global_range_contained (Sheet const *sheet,
+ GnmValue const *a, GnmValue const *b);
+GSList *global_range_list_parse (Sheet *sheet, char const *str);
+GnmValue *global_range_list_foreach (GSList *gr_list, GnmEvalPos const *ep,
+ CellIterFlags flags,
+ CellIterFunc handler,
+ gpointer closure);
+
+#endif /* GNUMERIC_RANGES_H */
--- /dev/null
+++ lib/goffice/split/style.c
@@ -0,0 +1,685 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Style.c: Style resource management
+ *
+ * Author:
+ * Miguel de Icaza (miguel at gnu.org)
+ * (C) 1998-2002 Miguel de Icaza
+ */
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "style.h"
+#include "mstyle.h"
+
+#include "format.h"
+#include "style-color.h"
+#include "global-gnome-font.h"
+#include "application.h"
+//#include "sheet.h"
+//#include "cell.h"
+#include "value.h"
+
+#include "gui-util.h"
+#include "mathfunc.h"
+#include "gnumeric-gconf.h"
+
+#include <pango/pangoft2.h>
+#include <gtk/gtkmain.h>
+#include <string.h>
+
+#undef DEBUG_REF_COUNT
+#undef DEBUG_FONTS
+
+static GHashTable *style_font_hash;
+static GHashTable *style_font_negative_hash;
+
+double gnumeric_default_font_width;
+static char *gnumeric_default_font_name;
+static double gnumeric_default_font_size;
+static PangoFontFamily **pango_families;
+static GStringChunk *size_names;
+
+/**
+ * get_substitute_font
+ * @fontname The font name
+ *
+ * Tries to find a gnome font which matches the Excel font.
+ * Returns the name of the substitute font if found. Otherwise returns NULL
+ */
+/* This is very ad hoc - throw it away when something better comes along */
+static gchar const *
+get_substitute_font (gchar const *fontname)
+{
+ int i;
+
+ static char const *map[][2] = {
+ { "Times New Roman", "Times"},
+ { "Tms Rmn", "Times"},
+ { "Arial", "Sans"},
+ { "Albany", "Sans"},
+ { "Helvetica", "Sans"},
+ { "Courier New", "Courier"},
+ { "£Í£Ó £Ð¥´¥·¥Ã¥¯", "Kochi Gothic"},
+ { "£Í£Ó ¥´¥·¥Ã¥¯", "Kochi Gothic"},
+ { "¥´¥·¥Ã¥¯", "Kochi Gothic"},
+ { "MS UI Gothic", "Kochi Gothic"},
+ { "£Í£Ó £ÐÌÀÄ«", "Kochi Mincho"},
+ { "£Í£Ó ÌÀÄ«", "Kochi Mincho"},
+ { "ÌÀÄ«", "Kochi Mincho"},
+ { NULL }
+ };
+ for (i = 0; map[i][0]; i++)
+ if (g_ascii_strcasecmp (map[i][0], fontname) == 0)
+ return map[i][1];
+
+ return NULL;
+}
+
+static int
+style_font_string_width (GnmFont const *font, char const *str)
+{
+ int w;
+ pango_layout_set_text (font->pango.layout, str, -1);
+ pango_layout_get_pixel_size (font->pango.layout, &w, NULL);
+ return w;
+}
+
+static double
+calc_font_width (GnmFont const *font, char const *teststr)
+{
+ char const *p1, *p2;
+ int w = 0, w1, w2, dw;
+ char buf[3];
+
+ for (p1 = teststr; *p1; p1++) {
+ buf[0] = *p1;
+ buf[1] = 0;
+ w1 = style_font_string_width (font, buf);
+ for (p2 = teststr; *p2; p2++) {
+ buf[1] = *p2;
+ buf[2] = 0;
+ w2 = style_font_string_width (font, buf);
+ dw = w2 - w1;
+ if (dw > w) {
+ w = dw;
+#ifdef DEBUG_FONT_WIDTH
+ fprintf (stderr, " %s = %d", buf, w);
+#endif
+ }
+ }
+ }
+
+ return w;
+}
+
+
+static GnmFont *
+style_font_new_simple (PangoContext *context,
+ char const *font_name, double size_pts, double scale,
+ gboolean bold, gboolean italic)
+{
+ GnmFont *font;
+ GnmFont key;
+ int height;
+
+ if (font_name == NULL) {
+ g_warning ("font_name == NULL, using %s", DEFAULT_FONT);
+ font_name = DEFAULT_FONT;
+ }
+ if (size_pts <= 0) {
+ g_warning ("font_size <= 0, using %f", DEFAULT_SIZE);
+ size_pts = DEFAULT_SIZE;
+ }
+
+ /* This cast does not mean we will change the name. */
+ key.font_name = (char *)font_name;
+ key.size_pts = size_pts;
+ key.is_bold = bold;
+ key.is_italic = italic;
+ key.scale = scale;
+
+ font = (GnmFont *) g_hash_table_lookup (style_font_hash, &key);
+ if (font == NULL) {
+ PangoFontDescription *desc;
+ double pts_scale;
+
+ if (g_hash_table_lookup (style_font_negative_hash, &key))
+ return NULL;
+
+ font = g_new0 (GnmFont, 1);
+ font->font_name = g_strdup (font_name);
+ font->size_pts = size_pts;
+ font->scale = scale;
+ font->is_bold = bold;
+ font->is_italic = italic;
+ /* One reference for the cache, one for the caller. */
+ font->ref_count = 2;
+
+ g_object_ref (context);
+ font->pango.context = context;
+ desc = pango_context_get_font_description (context);
+ pango_font_description_set_family (desc, font_name);
+ pango_font_description_set_weight (desc,
+ bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
+ pango_font_description_set_style (desc,
+ italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
+ /* FIXME: set scale separately? */
+ pango_font_description_set_size (desc,
+ size_pts * scale *
+ PANGO_SCALE);
+
+ font->pango.font = pango_context_load_font (context, desc);
+ if (font->pango.font == NULL) {
+ /* if we fail, try to be smart and map to something similar */
+ char const *sub = get_substitute_font (font_name);
+ if (sub != NULL) {
+ pango_font_description_set_family (desc, font_name);
+ font->pango.font = pango_context_load_font (context,
+ desc);
+ }
+
+ if (font->pango.font == NULL) {
+ g_object_unref (context);
+ font->pango.context = NULL;
+ font->pango.font_descr = NULL;
+ g_hash_table_insert (style_font_negative_hash,
+ font, font);
+ return NULL;
+ }
+ }
+
+ font->gnome_print_font = gnm_font_find_closest_from_weight_slant (font_name,
+ bold ? GNOME_FONT_BOLD : GNOME_FONT_REGULAR, italic, size_pts);
+
+ font->pango.font_descr = pango_font_describe (font->pango.font);
+
+ font->pango.layout = pango_layout_new (context);
+ pango_layout_set_font_description (font->pango.layout,
+ font->pango.font_descr);
+
+ font->pango.metrics = pango_font_get_metrics (font->pango.font,
+ gtk_get_default_language ());
+
+ height = pango_font_metrics_get_ascent (font->pango.metrics) +
+ pango_font_metrics_get_descent (font->pango.metrics);
+ font->height = PANGO_PIXELS (height);
+ font->approx_width.pixels.digit = calc_font_width (font, "0123456789");
+ font->approx_width.pixels.decimal = calc_font_width (font, ".,");
+ font->approx_width.pixels.hash = calc_font_width (font, "#");
+ font->approx_width.pixels.sign = calc_font_width (font, "-+");
+ font->approx_width.pixels.E = calc_font_width (font, "E");
+ font->approx_width.pixels.e = calc_font_width (font, "e");
+
+ pts_scale = 72. / (gnm_app_display_dpi_get (TRUE) * scale);
+ font->approx_width.pts.digit =
+ font->approx_width.pixels.digit * pts_scale;
+ font->approx_width.pts.decimal =
+ font->approx_width.pixels.decimal * pts_scale;
+ font->approx_width.pts.sign =
+ font->approx_width.pixels.sign * pts_scale;
+ font->approx_width.pts.E =
+ font->approx_width.pixels.E * pts_scale;
+ font->approx_width.pts.e =
+ font->approx_width.pixels.e * pts_scale;
+
+ g_hash_table_insert (style_font_hash, font, font);
+ } else
+ font->ref_count++;
+
+#ifdef DEBUG_REF_COUNT
+ g_message (__FUNCTION__ " font=%p name=%s%s%s ref_count=%d\n",
+ font, font->font_name,
+ font->is_bold ? " bold" : "",
+ font->is_italic ? " italic" : "",
+ font->ref_count);
+#endif
+ return font;
+}
+
+GnmFont *
+style_font_new (PangoContext *context,
+ char const *font_name, double size_pts, double scale,
+ gboolean bold, gboolean italic)
+{
+ GnmFont *font;
+
+ g_return_val_if_fail (font_name != NULL, NULL);
+ g_return_val_if_fail (size_pts > 0, NULL);
+
+ font = style_font_new_simple (context, font_name, size_pts,
+ scale, bold, italic);
+ if (font) return font;
+
+ font_name = gnumeric_default_font_name;
+ font = style_font_new_simple (context, font_name, size_pts,
+ scale, bold, italic);
+ if (font) return font;
+
+ size_pts = gnumeric_default_font_size;
+ font = style_font_new_simple (context, font_name, size_pts,
+ scale, bold, italic);
+ if (font) return font;
+
+ bold = FALSE;
+ font = style_font_new_simple (context, font_name, size_pts,
+ scale, bold, italic);
+ if (font) return font;
+
+ italic = FALSE;
+ font = style_font_new_simple (context, font_name, size_pts,
+ scale, bold, italic);
+ if (font) return font;
+
+ /*
+ * This should not be possible to reach as we have reverted all the way
+ * back to the default font.
+ */
+ g_assert_not_reached ();
+ abort ();
+}
+
+void
+style_font_ref (GnmFont *sf)
+{
+ g_return_if_fail (sf != NULL);
+
+ sf->ref_count++;
+#ifdef DEBUG_REF_COUNT
+ g_message (__FUNCTION__ " font=%p name=%s%s%s ref_count=%d\n",
+ sf, sf->font_name,
+ sf->is_bold ? " bold" : "",
+ sf->is_italic ? " italic" : "",
+ sf->ref_count);
+#endif
+}
+
+void
+style_font_unref (GnmFont *sf)
+{
+ g_return_if_fail (sf != NULL);
+ g_return_if_fail (sf->ref_count > 0);
+
+ sf->ref_count--;
+#ifdef DEBUG_REF_COUNT
+ g_message (__FUNCTION__ " font=%p name=%s%s%s ref_count=%d\n",
+ sf, sf->font_name,
+ sf->is_bold ? " bold" : "",
+ sf->is_italic ? " italic" : "",
+ sf->ref_count);
+#endif
+ if (sf->ref_count != 0)
+ return;
+
+ if (sf->pango.context != NULL) {
+ g_object_unref (G_OBJECT (sf->pango.context));
+ sf->pango.context = NULL;
+ }
+ if (sf->pango.layout != NULL) {
+ g_object_unref (G_OBJECT (sf->pango.layout));
+ sf->pango.layout = NULL;
+ }
+ if (sf->pango.font != NULL) {
+ g_object_unref (G_OBJECT (sf->pango.font));
+ sf->pango.font = NULL;
+ }
+ if (sf->pango.font_descr != NULL) {
+ pango_font_description_free (sf->pango.font_descr);
+ sf->pango.font_descr = NULL;
+ }
+ if (sf->pango.metrics != NULL) {
+ pango_font_metrics_unref (sf->pango.metrics);
+ sf->pango.metrics = NULL;
+ }
+ if (sf->gnome_print_font != NULL) {
+ gnome_font_unref (sf->gnome_print_font);
+ sf->gnome_print_font = NULL;
+ }
+ g_hash_table_remove (style_font_hash, sf);
+ g_free (sf->font_name);
+ g_free (sf);
+}
+
+/*
+ * The routines used to hash and compare the different styles
+ */
+gint
+style_font_equal (gconstpointer v, gconstpointer v2)
+{
+ GnmFont const *k1 = (GnmFont const *) v;
+ GnmFont const *k2 = (GnmFont const *) v2;
+
+ if (k1->size_pts != k2->size_pts)
+ return 0;
+
+ if (k1->is_bold != k2->is_bold)
+ return 0;
+ if (k1->is_italic != k2->is_italic)
+ return 0;
+ if (k1->scale != k2->scale)
+ return 0;
+
+ return !strcmp (k1->font_name, k2->font_name);
+}
+
+guint
+style_font_hash_func (gconstpointer v)
+{
+ GnmFont const *k = (GnmFont const *) v;
+
+ return k->size_pts + g_str_hash (k->font_name);
+}
+
+static int
+compare_family_pointers_by_name (gconstpointer a, gconstpointer b)
+{
+ PangoFontFamily * const * const fa = a;
+ PangoFontFamily * const * const fb = b;
+ return g_utf8_collate (pango_font_family_get_name (*fa),
+ pango_font_family_get_name (*fb));
+}
+
+/**
+ * gnm_pango_context_get :
+ *
+ * Simple wrapper to handle windowless operation
+ **/
+PangoContext *
+gnm_pango_context_get (void)
+{
+ PangoContext *context;
+ GdkScreen *screen = gdk_screen_get_default ();
+
+ if (screen != NULL) {
+ context = gdk_pango_context_get_for_screen (screen);
+ /* FIXME: barf! */
+ gdk_pango_context_set_colormap (context,
+ gdk_screen_get_default_colormap (screen));
+ } else {
+ PangoFontMap *fontmap = pango_ft2_font_map_new ();
+ pango_ft2_font_map_set_resolution (PANGO_FT2_FONT_MAP (fontmap), 96, 96);
+ context = pango_ft2_font_map_create_context (PANGO_FT2_FONT_MAP (fontmap));
+ }
+ pango_context_set_language (context, gtk_get_default_language ());
+ pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
+
+ return context;
+}
+
+static void
+font_init (void)
+{
+ PangoContext *context;
+ GnmFont *gnumeric_default_font = NULL;
+ int n_families, i;
+
+ gnumeric_default_font_name = g_strdup (gnm_app_prefs->default_font.name);
+ gnumeric_default_font_size = gnm_app_prefs->default_font.size;
+
+ context = gnm_pango_context_get ();
+ if (gnumeric_default_font_name && gnumeric_default_font_size >= 1)
+ gnumeric_default_font = style_font_new_simple (context,
+ gnumeric_default_font_name, gnumeric_default_font_size,
+ 1., FALSE, FALSE);
+ if (gnumeric_default_font == NULL) {
+ g_warning ("Configured default font '%s %f' not available, trying fallback...",
+ gnumeric_default_font_name, gnumeric_default_font_size);
+ gnumeric_default_font = style_font_new_simple (context,
+ DEFAULT_FONT, DEFAULT_SIZE, 1., FALSE, FALSE);
+ if (gnumeric_default_font != NULL) {
+ g_free (gnumeric_default_font_name);
+ gnumeric_default_font_name = g_strdup (DEFAULT_FONT);
+ gnumeric_default_font_size = DEFAULT_SIZE;
+ } else {
+ g_warning ("Fallback font '%s %f' not available, trying 'fixed'...",
+ DEFAULT_FONT, DEFAULT_SIZE);
+ gnumeric_default_font = style_font_new_simple (context,
+ "fixed", 10, 1., FALSE, FALSE);
+ if (gnumeric_default_font != NULL) {
+ g_free (gnumeric_default_font_name);
+ gnumeric_default_font_name = g_strdup ("fixed");
+ gnumeric_default_font_size = 10;
+ } else {
+ g_warning ("Even 'fixed 10' failed ?? We're going to exit now,"
+ "there is something wrong with your font configuration");
+ exit (1);
+ }
+ }
+ }
+
+ gnumeric_default_font_width = gnumeric_default_font->approx_width.pts.digit;
+ style_font_unref (gnumeric_default_font);
+
+ size_names = g_string_chunk_new (128);
+
+ pango_context_list_families (context,
+ &pango_families, &n_families);
+ qsort (pango_families, n_families, sizeof (*pango_families),
+ compare_family_pointers_by_name);
+
+ for (i = 0 ; i < n_families ; i++)
+ gnumeric_font_family_list = g_list_prepend (
+ gnumeric_font_family_list,
+ (gpointer) pango_font_family_get_name (pango_families[i]));
+
+ gnumeric_font_family_list = g_list_reverse (gnumeric_font_family_list);
+
+ for (i = 0; gnumeric_point_sizes [i] != 0; i++){
+ char buffer[4 * sizeof (int)];
+ sprintf (buffer, "%d", gnumeric_point_sizes [i]);
+ gnumeric_point_size_list = g_list_prepend (
+ gnumeric_point_size_list,
+ g_string_chunk_insert (size_names, buffer));
+ }
+
+ g_object_unref (G_OBJECT (context));
+}
+
+static void
+font_shutdown (void)
+{
+ g_free (gnumeric_default_font_name);
+ gnumeric_default_font_name = NULL;
+
+ g_free (pango_families);
+ pango_families = NULL;
+ g_list_free (gnumeric_font_family_list);
+ gnumeric_font_family_list = NULL;
+ g_list_free (gnumeric_point_size_list);
+ gnumeric_point_size_list = NULL;
+ g_string_chunk_free (size_names);
+ size_names = NULL;
+}
+
+void
+style_init (void)
+{
+ number_format_init ();
+ style_font_hash = g_hash_table_new (style_font_hash_func,
+ style_font_equal);
+ style_font_negative_hash = g_hash_table_new (style_font_hash_func,
+ style_font_equal);
+
+ font_init ();
+}
+
+static void
+delete_neg_font (GnmFont *sf, gpointer value, gpointer user_data)
+{
+ g_free (sf->font_name);
+ g_free (sf);
+}
+
+static void
+list_cached_fonts (GnmFont *font, gpointer value, GSList **lp)
+{
+ *lp = g_slist_prepend (*lp, font);
+}
+
+/*
+ * Release all resources allocated by style_init.
+ */
+void
+style_shutdown (void)
+{
+ font_shutdown ();
+ number_format_shutdown ();
+ {
+ /* Make a list of the fonts, then unref them. */
+ GSList *fonts = NULL, *tmp;
+ g_hash_table_foreach (style_font_hash, (GHFunc) list_cached_fonts, &fonts);
+ for (tmp = fonts; tmp; tmp = tmp->next) {
+ GnmFont *sf = tmp->data;
+ if (sf->ref_count != 1)
+ g_warning ("Font %s has %d references instead of the expected single.",
+ sf->font_name, sf->ref_count);
+ style_font_unref (sf);
+ }
+ g_slist_free (fonts);
+ }
+ g_hash_table_destroy (style_font_hash);
+ style_font_hash = NULL;
+
+ g_hash_table_foreach (style_font_negative_hash, (GHFunc) delete_neg_font, NULL);
+ g_hash_table_destroy (style_font_negative_hash);
+ style_font_negative_hash = NULL;
+}
+
+/**
+ * required_updates_for_style
+ * @style: the style
+ *
+ * What changes are required after applying the supplied style.
+ */
+SpanCalcFlags
+required_updates_for_style (GnmStyle const *style)
+{
+ SpanCalcFlags res = SPANCALC_SIMPLE;
+
+ gboolean const row_height =
+ mstyle_is_element_set (style, MSTYLE_FONT_SIZE) ||
+ mstyle_is_element_set (style, MSTYLE_WRAP_TEXT) ||
+ mstyle_is_element_set (style, MSTYLE_ROTATION);
+ gboolean const size_change = row_height ||
+ mstyle_is_element_set (style, MSTYLE_FONT_NAME) ||
+ mstyle_is_element_set (style, MSTYLE_FONT_BOLD) ||
+ mstyle_is_element_set (style, MSTYLE_FONT_ITALIC);
+ gboolean const format_change =
+ (mstyle_is_element_set (style, MSTYLE_FORMAT) ||
+ mstyle_is_element_set (style, MSTYLE_INDENT) ||
+ mstyle_is_element_set (style, MSTYLE_ALIGN_H) ||
+ mstyle_is_element_set (style, MSTYLE_ALIGN_V) ||
+ mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH) ||
+ mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
+ mstyle_is_element_set (style, MSTYLE_COLOR_FORE) ||
+ mstyle_is_element_set (style, MSTYLE_ROTATION));
+
+ if (row_height)
+ res |= SPANCALC_ROW_HEIGHT;
+ if (format_change || size_change)
+ res |= SPANCALC_RE_RENDER | SPANCALC_RESIZE;
+ return res;
+}
+
+#if 0 // gnumeric
+
+/**
+ * style_default_halign :
+ * @style :
+ * @cell :
+ *
+ * Select the appropriate horizontal alignment depending on the style and cell
+ * value.
+ */
+StyleHAlignFlags
+style_default_halign (GnmStyle const *mstyle, GnmCell const *c)
+{
+ StyleHAlignFlags align = mstyle_get_align_h (mstyle);
+ GnmValue *v;
+
+ if (align != HALIGN_GENERAL)
+ return align;
+
+ if (mstyle_get_rotation (mstyle) != 0)
+ return HALIGN_LEFT;
+
+ g_return_val_if_fail (c != NULL, HALIGN_RIGHT);
+
+ if (c->base.sheet && c->base.sheet->display_formulas &&
+ cell_has_expr (c))
+ return HALIGN_LEFT;
+
+ for (v = c->value; v != NULL ; )
+ switch (v->type) {
+ case VALUE_BOOLEAN :
+ case VALUE_ERROR :
+ return HALIGN_CENTER;
+
+ case VALUE_INTEGER :
+ case VALUE_FLOAT :
+ return HALIGN_RIGHT;
+
+ case VALUE_ARRAY :
+ /* Tail recurse into the array */
+ if (v->v_array.x > 0 && v->v_array.y > 0) {
+ v = v->v_array.vals [0][0];
+ continue;
+ }
+
+ default :
+ return HALIGN_LEFT;
+ }
+ return HALIGN_RIGHT;
+}
+
+#endif // 0
+
+/**
+ * gnm_font_find_closest_from_weight_slant :
+ *
+ * A wrapper around gnome-print because it is stupid.
+ * At least this will warn us when it is stupid
+ **/
+GnomeFont *
+gnm_font_find_closest_from_weight_slant (const guchar *family,
+ GnomeFontWeight weight,
+ gboolean italic, gdouble size)
+{
+ GnomeFont *font;
+ guchar const *fam;
+ guchar *name;
+
+ g_return_val_if_fail (family != NULL, NULL);
+
+ while (1) {
+ font = gnome_font_find_closest_from_weight_slant
+ (family, weight, italic, size);
+ fam = gnome_font_get_family_name (font);
+ if (fam != NULL && g_ascii_strcasecmp (family, fam) == 0)
+ return font;
+
+ name = gnome_font_get_full_name (font);
+ g_warning ("GnomePrint: Requested %s but using %s (%s)",
+ family, fam, name);
+ g_free (name);
+
+ /* put in some fallbacks */
+ if (!g_ascii_strcasecmp (family, "Sans"))
+ family = "Sans Regular";
+ else if (!g_ascii_strcasecmp (family, "Helvetica"))
+ family = "Sans";
+ else if (!g_ascii_strcasecmp (family, "Albany"))
+ family = "Arial";
+ /* one of the arials */
+ else if (!g_ascii_strncasecmp (family, "Arial ", 6))
+ family = "Arial";
+ else if (!g_ascii_strcasecmp (family, "Arial"))
+ family = "Sans";
+ else
+ return font;
+
+ g_warning ("Trying to fallback to '%s'", family);
+ }
+ /* notreached */
+ return font;
+}
--- /dev/null
+++ lib/goffice/split/mstyle.c
@@ -0,0 +1,1786 @@
+/* vim: set sw=8: */
+/*
+ * GnmStyle.c: The guts of the style engine.
+ *
+ * Authors:
+ * Michael Meeks <mmeeks at gnu.org>
+ * Almer S. Tigelaar <almer at gnome.org>
+ * Jody Goldberg <jody at gnome.org>
+ * Morten Welinder <terra at gnome.org>
+ */
+#include <config.h>
+#include "gnumeric.h"
+#include "mstyle.h"
+
+#include "str.h"
+#include "style-border.h"
+#include "style-color.h"
+//#include "validation.h"
+//#include "pattern.h"
+#include "format.h"
+//#include "sheet-style.h"
+#include "application.h"
+#include "gutils.h"
+#include "gnumeric-gconf.h"
+
+#include <stdio.h>
+
+#ifndef USE_MSTYLE_POOL
+#define USE_MSTYLE_POOL 1
+#endif
+
+#if USE_MSTYLE_POOL
+/* Memory pool for mstyles. */
+static GnmMemChunk *mstyle_pool;
+#define CHUNK_ALLOC(T,p) ((T*)gnm_mem_chunk_alloc (p))
+#define CHUNK_ALLOC0(T,p) ((T*)gnm_mem_chunk_alloc0 (p))
+#define CHUNK_FREE(p,v) gnm_mem_chunk_free ((p), (v))
+#else
+#define CHUNK_ALLOC(T,c) g_new (T,1)
+#define CHUNK_ALLOC0(T,c) g_new0 (T,1)
+#define CHUNK_FREE(p,v) g_free ((v))
+#endif
+
+typedef struct {
+ MStyleElementType type;
+ union {
+ union {
+ GnmColor *any;
+ GnmColor *fore;
+ GnmColor *back;
+ GnmColor *pattern;
+ } color;
+ union {
+ GnmBorder *top;
+ GnmBorder *bottom;
+ GnmBorder *left;
+ GnmBorder *right;
+ GnmBorder *diagonal;
+ GnmBorder *rev_diagonal;
+
+ /* Used for loading */
+ GnmBorder *any;
+ } border;
+ guint32 pattern;
+
+ union {
+ GnmString *name;
+ gboolean bold;
+ gboolean italic;
+ StyleUnderlineType underline;
+ gboolean strikethrough;
+ float size;
+ } font;
+ GnmFormat *format;
+ union {
+ guint16 v;
+ guint16 h;
+ } align;
+ int indent;
+ int rotation;
+ gboolean wrap_text;
+ gboolean shrink_to_fit;
+ gboolean content_locked;
+ gboolean content_hidden;
+
+ GnmValidation *validation;
+ GnmHLink *hlink;
+ GnmInputMsg *input_msg;
+
+ /* Convenience members */
+ gpointer any_pointer;
+ gboolean any_boolean;
+ float any_float;
+ guint16 any_guint16;
+ guint32 any_guint32;
+ } u;
+} MStyleElement;
+
+struct _GnmStyle {
+ guint32 ref_count;
+ guint32 link_count;
+ Sheet *linked_sheet;
+ MStyleElement elements[MSTYLE_ELEMENT_MAX];
+ PangoAttrList *pango_attrs;
+ double pango_attrs_zoom;
+ GnmFont *font;
+ double font_zoom;
+};
+
+#define MSTYLE_ANY_COLOR MSTYLE_COLOR_FORE: \
+ case MSTYLE_COLOR_BACK: \
+ case MSTYLE_COLOR_PATTERN
+
+#define MSTYLE_ANY_BORDER MSTYLE_BORDER_TOP: \
+ case MSTYLE_BORDER_BOTTOM: \
+ case MSTYLE_BORDER_LEFT: \
+ case MSTYLE_BORDER_RIGHT: \
+ case MSTYLE_BORDER_DIAGONAL: \
+ case MSTYLE_BORDER_REV_DIAGONAL
+
+#define MSTYLE_ANY_POINTER MSTYLE_FONT_NAME: \
+ case MSTYLE_FORMAT: \
+ case MSTYLE_VALIDATION: \
+ case MSTYLE_HLINK: \
+ case MSTYLE_INPUT_MSG
+
+#define MSTYLE_ANY_BOOLEAN MSTYLE_FONT_BOLD: \
+ case MSTYLE_FONT_ITALIC: \
+ case MSTYLE_FONT_STRIKETHROUGH: \
+ case MSTYLE_WRAP_TEXT:\
+ case MSTYLE_SHRINK_TO_FIT:\
+ case MSTYLE_CONTENT_LOCKED:\
+ case MSTYLE_CONTENT_HIDDEN
+
+#define MSTYLE_ANY_GUINT16 MSTYLE_ALIGN_V: \
+ case MSTYLE_ALIGN_H
+
+#define MSTYLE_ANY_GUINT32 MSTYLE_PATTERN: \
+ case MSTYLE_ROTATION
+
+#define MSTYLE_ANY_FLOAT MSTYLE_FONT_SIZE
+
+
+static const char *
+mstyle_names[MSTYLE_ELEMENT_MAX] = {
+ "--UnSet--",
+ "--Conflict--",
+ "Color.Back",
+ "Color.Pattern",
+ "Border.Top",
+ "Border.Bottom",
+ "Border.Left",
+ "Border.Right",
+ "Border.RevDiagonal",
+ "Border.Diagonal",
+ "Pattern",
+ "--MaxBlank--",
+ "Color.Fore",
+ "Font.Name",
+ "Font.Bold",
+ "Font.Italic",
+ "Font.Underline",
+ "Font.Strikethrough",
+ "Font.Size",
+ "Format",
+ "Align.v",
+ "Align.h",
+ "Indent",
+ "Rotation",
+ "WrapText",
+ "ShrinkToFit",
+ "Content.Locked",
+ "Content.Hidden",
+ "Validation",
+ "Hyper Link",
+ "Input Msg"
+};
+
+/* Some ref/link count debugging */
+#if 0
+#define d(arg) printf arg
+#else
+#define d(arg) do { } while (0)
+#endif
+
+static guint
+mstyle_hash_internal (gconstpointer st, int i)
+{
+ const GnmStyle *mstyle = (const GnmStyle *)st;
+ guint32 hash = 0;
+
+ while (i-- > (MSTYLE_ELEMENT_CONFLICT + 1)) {
+ const MStyleElement *e = &mstyle->elements[i];
+ hash = (hash << 7) ^ (hash >> (sizeof (hash) * 8 - 7));
+ switch (i) {
+ case MSTYLE_ANY_COLOR:
+ /* auto colours break things */
+ if (!e->u.color.any->is_auto)
+ hash = hash ^ GPOINTER_TO_UINT (e->u.color.any);
+ break;
+ case MSTYLE_ANY_BORDER:
+ hash = hash ^ GPOINTER_TO_UINT (e->u.border.any);
+ break;
+ case MSTYLE_ANY_POINTER:
+ /*
+ * FIXME FIXME FIXME
+ * Will someone please convince me that it is safe
+ * to use the raw pointers here? -- MW.
+ */
+ hash = hash ^ GPOINTER_TO_UINT (e->u.any_pointer);
+ break;
+ case MSTYLE_ELEMENT_MAX_BLANK: /* A dummy element */
+ break;
+ case MSTYLE_ANY_BOOLEAN:
+ if (e->u.any_boolean)
+ hash = hash ^ 0x1379;
+ break;
+ case MSTYLE_ANY_FLOAT:
+ hash = hash ^ ((int)(e->u.any_float * 97));
+ break;
+ case MSTYLE_ANY_GUINT16:
+ hash = hash ^ e->u.any_guint16;
+ break;
+ case MSTYLE_ANY_GUINT32:
+ hash = hash ^ e->u.any_guint32;
+ break;
+ case MSTYLE_INDENT:
+ hash = hash ^ e->u.indent;
+ break;
+ case MSTYLE_FONT_UNDERLINE:
+ hash = hash ^ e->u.font.underline;
+ break;
+
+#ifndef DEBUG_SWITCH_ENUM
+ default:
+ g_assert_not_reached ();
+ break;
+#endif
+ }
+ }
+
+ return hash;
+}
+
+guint
+mstyle_hash_XL (gconstpointer st)
+{
+ return mstyle_hash_internal (st, MSTYLE_VALIDATION);
+}
+
+guint
+mstyle_hash (gconstpointer st)
+{
+ return mstyle_hash_internal (st, MSTYLE_ELEMENT_MAX);
+}
+
+
+static char *
+mstyle_element_dump (const MStyleElement *e)
+{
+ GString *ans = g_string_new (NULL);
+ char *txt_ans;
+
+ /* This leaks ans from above. Let's consider that a feature. */
+ g_return_val_if_fail (e != NULL, g_strdup ("Duff element"));
+
+ switch (e->type) {
+ case MSTYLE_ELEMENT_UNSET:
+ g_string_printf (ans, "\tUnset\n");
+ break;
+ case MSTYLE_COLOR_BACK:
+ g_string_printf (ans, "\tbackground col %hx:%hx:%hx\n",
+ e->u.color.any->color.red,
+ e->u.color.any->color.green,
+ e->u.color.any->color.blue);
+ break;
+ case MSTYLE_COLOR_PATTERN:
+ g_string_printf (ans, "\tpattern col %hx:%hx:%hx\n",
+ e->u.color.any->color.red,
+ e->u.color.any->color.green,
+ e->u.color.any->color.blue);
+ break;
+ case MSTYLE_BORDER_TOP:
+ case MSTYLE_BORDER_BOTTOM:
+ case MSTYLE_BORDER_LEFT:
+ case MSTYLE_BORDER_RIGHT:
+ case MSTYLE_BORDER_DIAGONAL:
+ case MSTYLE_BORDER_REV_DIAGONAL:
+ if (e->u.border.any)
+ g_string_printf (ans, "\t%s %d\n", mstyle_names[e->type], e->u.border.any->line_type);
+ else
+ g_string_printf (ans, "\t%s blank\n", mstyle_names[e->type]);
+ break;
+
+ case MSTYLE_PATTERN :
+ g_string_printf (ans, "\tpattern %d\n", e->u.pattern);
+ break;
+
+ case MSTYLE_COLOR_FORE:
+ g_string_printf (ans, "\tforegnd col %hx:%hx:%hx\n",
+ e->u.color.any->color.red,
+ e->u.color.any->color.green,
+ e->u.color.any->color.blue);
+ break;
+ case MSTYLE_FONT_NAME:
+ g_string_printf (ans, "\tname '%s'\n", e->u.font.name->str);
+ break;
+ case MSTYLE_FONT_BOLD:
+ if (e->u.font.bold)
+ g_string_printf (ans, "\tbold\n");
+ else
+ g_string_printf (ans, "\tnot bold\n");
+ break;
+ case MSTYLE_FONT_ITALIC:
+ if (e->u.font.italic)
+ g_string_printf (ans, "\titalic\n");
+ else
+ g_string_printf (ans, "\tnot italic\n");
+ break;
+ case MSTYLE_FONT_UNDERLINE:
+ switch (e->u.font.underline) {
+ default :
+ case UNDERLINE_NONE :
+ g_string_printf (ans, "\tnot underline\n");
+ case UNDERLINE_SINGLE :
+ g_string_printf (ans, "\tsingle underline\n");
+ case UNDERLINE_DOUBLE :
+ g_string_printf (ans, "\tdouble underline\n");
+ };
+ break;
+ case MSTYLE_FONT_STRIKETHROUGH:
+ if (e->u.font.strikethrough)
+ g_string_printf (ans, "\tstrikethrough\n");
+ else
+ g_string_printf (ans, "\tnot strikethrough\n");
+ break;
+ case MSTYLE_FONT_SIZE:
+ g_string_printf (ans, "\tsize %f\n", e->u.font.size);
+ break;
+
+ case MSTYLE_FORMAT: {
+ char *fmt = style_format_as_XL (e->u.format, TRUE);
+ g_string_printf (ans, "\tformat '%s'\n", fmt);
+ g_free (fmt);
+ break;
+ }
+
+ case MSTYLE_ALIGN_V:
+ g_string_printf (ans, "\tvalign %hd\n", e->u.align.v);
+ break;
+ case MSTYLE_ALIGN_H:
+ g_string_printf (ans, "\thalign %hd\n", e->u.align.h);
+ break;
+ case MSTYLE_INDENT:
+ g_string_printf (ans, "\tindent %d\n", e->u.indent);
+ break;
+ case MSTYLE_ROTATION:
+ g_string_printf (ans, "\trotation %d\n", e->u.rotation);
+ break;
+
+ case MSTYLE_WRAP_TEXT :
+ g_string_printf (ans, "\twrap text %d\n", e->u.wrap_text);
+ break;
+ case MSTYLE_SHRINK_TO_FIT :
+ g_string_printf (ans, "\tshrink to fit %d\n", e->u.shrink_to_fit);
+ break;
+ case MSTYLE_CONTENT_LOCKED :
+ g_string_printf (ans, "\tlocked %d\n", e->u.content_locked);
+ break;
+ case MSTYLE_CONTENT_HIDDEN :
+ g_string_printf (ans, "\thidden %d\n", e->u.content_hidden);
+ break;
+ case MSTYLE_VALIDATION :
+ g_string_printf (ans, "\tvalidation %p\n", e->u.validation);
+ break;
+
+ case MSTYLE_HLINK :
+ g_string_printf (ans, "\thlink %p\n", e->u.hlink);
+ break;
+
+ case MSTYLE_INPUT_MSG :
+ g_string_printf (ans, "\tinput msg %p\n", e->u.input_msg);
+ break;
+
+ default:
+ g_string_printf (ans, "\t%s\n", mstyle_names[e->type]);
+ break;
+ }
+
+ txt_ans = ans->str;
+ g_string_free (ans, FALSE);
+
+ return txt_ans;
+}
+
+static gboolean
+mstyle_element_equal (MStyleElement const *a,
+ MStyleElement const *b)
+{
+ if ((a->type == MSTYLE_ELEMENT_UNSET ||
+ b->type == MSTYLE_ELEMENT_UNSET) && a->type != b->type)
+ return FALSE;
+
+ g_return_val_if_fail (a->type == b->type, FALSE);
+
+ switch (a->type) {
+ case MSTYLE_ANY_COLOR:
+ return (a->u.color.any == b->u.color.any ||
+ (a->u.color.any->is_auto && b->u.color.any->is_auto));
+ case MSTYLE_ANY_BORDER:
+ return (a->u.border.any == b->u.border.any);
+ case MSTYLE_PATTERN:
+ return (a->u.pattern == b->u.pattern);
+ case MSTYLE_FONT_NAME:
+ return (a->u.font.name == b->u.font.name);
+ case MSTYLE_FONT_BOLD:
+ return (a->u.font.bold == b->u.font.bold);
+ case MSTYLE_FONT_ITALIC:
+ return (a->u.font.italic == b->u.font.italic);
+ case MSTYLE_FONT_UNDERLINE:
+ return (a->u.font.underline == b->u.font.underline);
+ case MSTYLE_FONT_STRIKETHROUGH:
+ return (a->u.font.strikethrough == b->u.font.strikethrough);
+ case MSTYLE_FONT_SIZE:
+ return (a->u.font.size == b->u.font.size);
+ case MSTYLE_FORMAT:
+ return (a->u.format == b->u.format);
+ case MSTYLE_ALIGN_V:
+ return (a->u.align.v == b->u.align.v);
+ case MSTYLE_ALIGN_H:
+ return (a->u.align.h == b->u.align.h);
+ case MSTYLE_INDENT:
+ return (a->u.indent == b->u.indent);
+ case MSTYLE_ROTATION:
+ return (a->u.rotation == b->u.rotation);
+ case MSTYLE_WRAP_TEXT:
+ return (a->u.wrap_text == b->u.wrap_text);
+ case MSTYLE_SHRINK_TO_FIT:
+ return (a->u.shrink_to_fit == b->u.shrink_to_fit);
+ case MSTYLE_CONTENT_LOCKED:
+ return (a->u.content_locked == b->u.content_locked);
+ case MSTYLE_CONTENT_HIDDEN:
+ return (a->u.content_hidden == b->u.content_hidden);
+ case MSTYLE_VALIDATION:
+ return (a->u.validation == b->u.validation);
+ case MSTYLE_HLINK:
+ return (a->u.hlink == b->u.hlink);
+ case MSTYLE_INPUT_MSG:
+ return (a->u.input_msg == b->u.input_msg);
+ default:
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static inline MStyleElement
+mstyle_element_ref (const MStyleElement *e)
+{
+ switch (e->type) {
+ case MSTYLE_ANY_COLOR:
+ style_color_ref (e->u.color.any);
+ break;
+ case MSTYLE_ANY_BORDER:
+ style_border_ref (e->u.border.any);
+ break;
+ case MSTYLE_FONT_NAME:
+ gnm_string_ref (e->u.font.name);
+ break;
+ case MSTYLE_FORMAT:
+ style_format_ref (e->u.format);
+ break;
+#if 0
+ case MSTYLE_VALIDATION:
+ if (e->u.validation)
+ validation_ref (e->u.validation);
+ break;
+#endif //0
+ case MSTYLE_HLINK:
+ if (e->u.hlink)
+ g_object_ref (G_OBJECT (e->u.hlink));
+ break;
+ case MSTYLE_INPUT_MSG:
+ if (e->u.input_msg)
+ g_object_ref (G_OBJECT (e->u.input_msg));
+ break;
+ default:
+ break;
+ }
+ return *e;
+}
+
+static inline void
+mstyle_element_unref (MStyleElement e)
+{
+ switch (e.type) {
+ case MSTYLE_ANY_COLOR:
+ style_color_unref (e.u.color.any);
+ break;
+ case MSTYLE_ANY_BORDER:
+ style_border_unref (e.u.border.any);
+ break;
+ case MSTYLE_FONT_NAME:
+ gnm_string_unref (e.u.font.name);
+ break;
+ case MSTYLE_FORMAT:
+ style_format_unref (e.u.format);
+ break;
+#if 0
+ case MSTYLE_VALIDATION:
+ if (e.u.validation)
+ validation_unref (e.u.validation);
+ break;
+#endif // 0
+ case MSTYLE_HLINK:
+ if (e.u.hlink)
+ g_object_unref (G_OBJECT (e.u.hlink));
+ break;
+ case MSTYLE_INPUT_MSG:
+ if (e.u.input_msg)
+ g_object_unref (G_OBJECT (e.u.input_msg));
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * mstyle_elements_compare:
+ * @a: style to be tagged
+ * @b: style to compare.
+ *
+ * Compares styles and tags conflicts into a.
+ **/
+static inline void
+mstyle_elements_compare (MStyleElement *a,
+ const MStyleElement *b)
+{
+ int i;
+
+ g_return_if_fail (a != NULL);
+ g_return_if_fail (b != NULL);
+
+ for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
+ if (b[i].type == MSTYLE_ELEMENT_UNSET ||
+ b[i].type == MSTYLE_ELEMENT_CONFLICT ||
+ a[i].type == MSTYLE_ELEMENT_CONFLICT)
+ continue;
+ if (a[i].type == MSTYLE_ELEMENT_UNSET) {
+ mstyle_element_ref (&b[i]);
+ a[i] = b[i];
+ } else if (!mstyle_element_equal (a+i, b+i)) {
+ mstyle_element_unref (a[i]);
+ a[i].type = MSTYLE_ELEMENT_CONFLICT;
+ }
+ }
+
+}
+
+void
+mstyle_compare (GnmStyle *a, const GnmStyle *b)
+{
+ mstyle_elements_compare (a->elements,
+ b->elements);
+}
+
+static void
+mstyle_elements_unref (MStyleElement *e)
+{
+ int i;
+
+ if (e)
+ for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
+ mstyle_element_unref (e[i]);
+ e[i].type = MSTYLE_ELEMENT_UNSET;
+ }
+}
+
+static void
+mstyle_elements_copy (GnmStyle *new_style, const GnmStyle *old_style)
+{
+ int i;
+ MStyleElement *ans;
+ const MStyleElement *e;
+
+ e = old_style->elements;
+ ans = new_style->elements;
+
+ for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
+ mstyle_element_ref (&e[i]);
+ ans[i] = e[i];
+ }
+}
+
+static inline void
+mstyle_pango_clear (GnmStyle *mstyle)
+{
+ if (mstyle->pango_attrs) {
+ pango_attr_list_unref (mstyle->pango_attrs);
+ mstyle->pango_attrs = NULL;
+ }
+}
+
+
+static inline void
+mstyle_font_clear (GnmStyle *mstyle)
+{
+ if (mstyle->font) {
+ style_font_unref (mstyle->font);
+ mstyle->font = NULL;
+ }
+}
+
+
+GnmStyle *
+mstyle_new (void)
+{
+ GnmStyle *style = CHUNK_ALLOC0 (GnmStyle, mstyle_pool);
+
+ style->ref_count = 1;
+ style->link_count = 0;
+ style->linked_sheet = NULL;
+ style->pango_attrs = NULL;
+ style->font = NULL;
+ d(("new %p\n", style));
+
+ return style;
+}
+
+GnmStyle *
+mstyle_copy (const GnmStyle *style)
+{
+ GnmStyle *new_style = CHUNK_ALLOC (GnmStyle, mstyle_pool);
+
+ new_style->ref_count = 1;
+ new_style->link_count = 0;
+ new_style->linked_sheet = NULL;
+ mstyle_elements_copy (new_style, style);
+
+ if ((new_style->pango_attrs = style->pango_attrs))
+ pango_attr_list_ref (new_style->pango_attrs);
+ if ((new_style->font = style->font)) {
+ style_font_ref (new_style->font);
+ new_style->font_zoom = style->font_zoom;
+ }
+
+ d(("copy %p\n", new_style));
+ return new_style;
+}
+
+GnmStyle *
+mstyle_copy_merge (const GnmStyle *orig, const GnmStyle *overlay)
+{
+ int i;
+ GnmStyle *res = CHUNK_ALLOC0 (GnmStyle, mstyle_pool);
+
+ MStyleElement *res_e;
+ const MStyleElement *orig_e;
+ const MStyleElement *overlay_e;
+
+ res->ref_count = 1;
+ res->link_count = 0;
+ res->linked_sheet = NULL;
+ res_e = res->elements;
+ orig_e = orig->elements;
+ overlay_e = overlay->elements;
+
+ for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
+ res_e[i] = mstyle_element_ref (
+ (overlay_e[i].type ? overlay_e : orig_e) + i);
+
+ d(("copy merge %p\n", res));
+ return res;
+}
+
+/**
+ * mstyle_new_default:
+ *
+ * Return the default style,
+ * this should _never_ _ever_ have any of its elements
+ * set.
+ *
+ * Return value: the default style.
+ **/
+
+GnmStyle *
+mstyle_new_default (void)
+{
+ GnmStyle *mstyle = mstyle_new ();
+
+ mstyle_set_font_name (mstyle, gnm_app_prefs->default_font.name);
+ mstyle_set_font_size (mstyle, gnm_app_prefs->default_font.size);
+ mstyle_set_font_bold (mstyle, gnm_app_prefs->default_font.is_bold);
+ mstyle_set_font_italic (mstyle, gnm_app_prefs->default_font.is_italic);
+
+ mstyle_set_format_text (mstyle, "General");
+ mstyle_set_align_v (mstyle, VALIGN_BOTTOM);
+ mstyle_set_align_h (mstyle, HALIGN_GENERAL);
+ mstyle_set_indent (mstyle, 0);
+ mstyle_set_rotation (mstyle, 0);
+ mstyle_set_wrap_text (mstyle, FALSE);
+ mstyle_set_shrink_to_fit (mstyle, FALSE);
+ mstyle_set_content_locked (mstyle, TRUE);
+ mstyle_set_content_hidden (mstyle, FALSE);
+ mstyle_set_font_uline (mstyle, UNDERLINE_NONE);
+ mstyle_set_font_strike (mstyle, FALSE);
+
+ mstyle_set_hlink (mstyle, NULL);
+ mstyle_set_input_msg (mstyle, NULL);
+ mstyle_set_validation (mstyle, NULL);
+
+ mstyle_set_color (mstyle, MSTYLE_COLOR_FORE,
+ style_color_black ());
+ mstyle_set_color (mstyle, MSTYLE_COLOR_BACK,
+ style_color_white ());
+ mstyle_set_color (mstyle, MSTYLE_COLOR_PATTERN,
+ style_color_black ());
+
+ /* To negate borders */
+ mstyle_set_border (mstyle, MSTYLE_BORDER_TOP,
+ style_border_ref (style_border_none ()));
+ mstyle_set_border (mstyle, MSTYLE_BORDER_LEFT,
+ style_border_ref (style_border_none ()));
+ mstyle_set_border (mstyle, MSTYLE_BORDER_BOTTOM,
+ style_border_ref (style_border_none ()));
+ mstyle_set_border (mstyle, MSTYLE_BORDER_RIGHT,
+ style_border_ref (style_border_none ()));
+ mstyle_set_border (mstyle, MSTYLE_BORDER_DIAGONAL,
+ style_border_ref (style_border_none ()));
+ mstyle_set_border (mstyle, MSTYLE_BORDER_REV_DIAGONAL,
+ style_border_ref (style_border_none ()));
+
+ /* This negates the back and pattern colors */
+ mstyle_set_pattern (mstyle, 0);
+
+ return mstyle;
+}
+
+void
+mstyle_ref (GnmStyle *style)
+{
+ g_return_if_fail (style->ref_count > 0);
+
+ style->ref_count++;
+ d(("ref %p = %d\n", style, style->ref_count));
+}
+
+void
+mstyle_unref (GnmStyle *style)
+{
+ g_return_if_fail (style->ref_count > 0);
+
+ d(("unref %p = %d\n", style, style->ref_count-1));
+ if (style->ref_count-- <= 1) {
+ g_return_if_fail (style->link_count == 0);
+ g_return_if_fail (style->linked_sheet == NULL);
+
+ if (style->elements)
+ mstyle_elements_unref (style->elements);
+ mstyle_pango_clear (style);
+ mstyle_font_clear (style);
+
+ CHUNK_FREE (mstyle_pool, style);
+ }
+}
+
+/**
+ * Replace auto pattern color in style with sheet's auto pattern color.
+ * make_copy tells if we are allowed to modify the style in place or we must
+ * make a copy first.
+ */
+static GnmStyle *
+link_pattern_color (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
+{
+ MStyleElementType etype = MSTYLE_COLOR_PATTERN;
+ GnmColor *pattern_color = style->elements[etype].u.color.any;
+
+ if (pattern_color->is_auto && auto_color != pattern_color) {
+ style_color_ref (auto_color);
+ if (make_copy) {
+ GnmStyle *orig = style;
+ style = mstyle_copy (style);
+ mstyle_unref (orig);
+ }
+ mstyle_set_color (style, etype, auto_color);
+ }
+ return style;
+}
+
+/**
+ * Replace auto border colors in style with sheet's auto pattern
+ * color. (pattern is *not* a typo.)
+ * make_copy tells if we are allowed to modify the style in place or we must
+ * make a copy first.
+ *
+ * FIXME: We conjecture that XL color 64 in border should change with the
+ * pattern, but not color 127. That distinction is not yet represented in
+ * our data structures.
+ */
+static GnmStyle *
+link_border_colors (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
+{
+ GnmBorder *border;
+ GnmColor *color;
+ int i;
+
+ for (i = MSTYLE_BORDER_TOP ; i <= MSTYLE_BORDER_DIAGONAL ; ++i) {
+ if (mstyle_is_element_set (style, i)) {
+ border = style->elements[i].u.border.any;
+ color = border->color;
+ if (color->is_auto && auto_color != color) {
+ GnmBorder *new_border;
+ StyleBorderOrientation orientation;
+
+ switch (i) {
+ case MSTYLE_BORDER_LEFT:
+ case MSTYLE_BORDER_RIGHT:
+ orientation = STYLE_BORDER_VERTICAL;
+ break;
+ case MSTYLE_BORDER_REV_DIAGONAL:
+ case MSTYLE_BORDER_DIAGONAL:
+ orientation = STYLE_BORDER_DIAGONAL;
+ break;
+ case MSTYLE_BORDER_TOP:
+ case MSTYLE_BORDER_BOTTOM:
+ default:
+ orientation = STYLE_BORDER_HORIZONTAL;
+ break;
+ }
+ style_color_ref (auto_color);
+ new_border = style_border_fetch (
+ border->line_type, auto_color,
+ orientation);
+
+ if (make_copy) {
+ GnmStyle *orig = style;
+ style = mstyle_copy (style);
+ mstyle_unref (orig);
+ make_copy = FALSE;
+ }
+ mstyle_set_border (style, i, new_border);
+ }
+ }
+ }
+ return style;
+}
+
+/**
+ * mstyle_link_sheet :
+ * @style :
+ * @sheet :
+ *
+ * ABSORBS a reference to the style and sets the link count to 1.
+ *
+ * Where auto pattern color occurs in the style (it may for pattern and
+ * borders), it is replaced with the sheet's auto pattern color. We make
+ * sure that we do not modify the style which was passed in to us, but also
+ * that we don't copy more than once. The final argument to the
+ * link_xxxxx_color functions tell whether or not to copy.
+ */
+GnmStyle *
+mstyle_link_sheet (GnmStyle *style, Sheet *sheet)
+{
+ GnmColor *auto_color;
+ gboolean style_is_orig = TRUE;
+
+ if (style->linked_sheet != NULL) {
+ GnmStyle *orig = style;
+ style = mstyle_copy (style);
+ mstyle_unref (orig);
+ style_is_orig = FALSE;
+
+ /* safety test */
+ g_return_val_if_fail (style->linked_sheet != sheet, style);
+ }
+
+ g_return_val_if_fail (style->link_count == 0, style);
+ g_return_val_if_fail (style->linked_sheet == NULL, style);
+
+ //auto_color = sheet_style_get_auto_pattern_color (sheet);
+ auto_color = style_color_black();
+ if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN))
+ style = link_pattern_color (style, auto_color, style_is_orig);
+ style = link_border_colors (style, auto_color, style_is_orig);
+ style_color_unref (auto_color);
+
+ style->linked_sheet = sheet;
+ style->link_count = 1;
+
+#if 0
+ /* Not needed for validation anymore, leave it as template for conditionals */
+ if (mstyle_is_element_set (style, MSTYLE_VALIDATION))
+ validation_link (style->elements[MSTYLE_VALIDATION].u.validation, sheet);
+#endif
+
+ d(("link sheet %p = 1\n", style));
+ return style;
+}
+
+void
+mstyle_link (GnmStyle *style)
+{
+ g_return_if_fail (style->link_count > 0);
+
+ style->link_count++;
+ d(("link %p = %d\n", style, style->link_count));
+}
+
+void
+mstyle_link_multiple (GnmStyle *style, int count)
+{
+ g_return_if_fail (style->link_count > 0);
+
+ style->link_count += count;
+ d(("multiple link %p + %d = %d\n", style, count, style->link_count));
+}
+
+void
+mstyle_unlink (GnmStyle *style)
+{
+ g_return_if_fail (style->link_count > 0);
+
+ d(("unlink %p = %d\n", style, style->link_count-1));
+ if (style->link_count-- == 1) {
+#if 0
+ /* Not needed for validation anymore, leave it as template for conditionals */
+ if (mstyle_is_element_set (style, MSTYLE_VALIDATION))
+ validation_unlink (style->elements[MSTYLE_VALIDATION].u.validation);
+#endif
+ //sheet_style_unlink (style->linked_sheet, style);
+ style->linked_sheet = NULL;
+ mstyle_unref (style);
+ }
+}
+
+char *
+mstyle_to_string (const GnmStyle *style)
+{
+ guint i;
+ GString *ans;
+ char *txt_ans;
+
+ g_return_val_if_fail (style != NULL, g_strdup ("(null)"));
+
+ ans = g_string_new ("Elements : ");
+ for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
+ char *txt;
+
+ if (style->elements[i].type) {
+ txt = mstyle_element_dump (&style->elements[i]);
+ g_string_append_printf (ans, "%s ", txt);
+ g_free (txt);
+ } else
+ g_string_append_printf (ans, ".\n");
+ }
+ txt_ans = ans->str;
+ g_string_free (ans, FALSE);
+
+ return txt_ans;
+}
+
+void
+mstyle_dump (const GnmStyle *style)
+{
+ char *txt;
+
+ fprintf (stderr, "Style Refs %d\n",
+ style->ref_count);
+ txt = mstyle_to_string (style);
+ fprintf (stderr, "%s\n", txt);
+ g_free (txt);
+}
+
+gboolean
+mstyle_equal (const GnmStyle *a, const GnmStyle *b)
+{
+ int i;
+ MStyleElement const *ea, *eb;
+
+ g_return_val_if_fail (a != NULL, FALSE);
+ g_return_val_if_fail (b != NULL, FALSE);
+
+ if (a == b)
+ return TRUE;
+
+ ea = a->elements;
+ eb = b->elements;
+ for (i = 1; i < MSTYLE_ELEMENT_MAX; i++) {
+ /* Elements in the same position should have the same types */
+ if (ea[i].type != eb[i].type) {
+ if (ea[i].type != MSTYLE_ELEMENT_UNSET &&
+ eb[i].type != MSTYLE_ELEMENT_UNSET)
+ g_warning ("%s mismatched types.", mstyle_names[i]);
+ return FALSE;
+ }
+
+ if (!mstyle_element_equal (ea+i, eb+i))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+mstyle_equal_XL (const GnmStyle *a, const GnmStyle *b)
+{
+ int i;
+ MStyleElement const *ea, *eb;
+
+ g_return_val_if_fail (a != NULL, FALSE);
+ g_return_val_if_fail (b != NULL, FALSE);
+
+ if (a == b)
+ return TRUE;
+
+ ea = a->elements;
+ eb = b->elements;
+ for (i = 1; i < MSTYLE_VALIDATION; i++) {
+ /* Elements in the same position should have the same types */
+ if (ea[i].type != eb[i].type) {
+ if (ea[i].type != MSTYLE_ELEMENT_UNSET &&
+ eb[i].type != MSTYLE_ELEMENT_UNSET)
+ g_warning ("%s mismatched types.", mstyle_names[i]);
+ return FALSE;
+ }
+
+ if (!mstyle_element_equal (ea+i, eb+i))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+mstyle_empty (const GnmStyle *style)
+{
+ int i;
+
+ g_return_val_if_fail (style != NULL, FALSE);
+
+ for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
+ if (style->elements[i].type)
+ return FALSE;
+ return TRUE;
+}
+
+gboolean
+mstyle_verify (const GnmStyle *style)
+{
+ int j;
+
+ for (j = 0; j < MSTYLE_ELEMENT_MAX; j++) {
+ MStyleElement e = style->elements[j];
+
+ g_return_val_if_fail (e.type < MSTYLE_ELEMENT_MAX, FALSE);
+ g_return_val_if_fail (e.type != MSTYLE_ELEMENT_CONFLICT, FALSE);
+ }
+ return TRUE;
+}
+
+gboolean
+mstyle_is_element_set (const GnmStyle *st, MStyleElementType t)
+{
+ g_return_val_if_fail (st != NULL, FALSE);
+ g_return_val_if_fail (t > 0 && t < MSTYLE_ELEMENT_MAX, FALSE);
+
+ return st->elements[t].type != MSTYLE_ELEMENT_UNSET &&
+ st->elements[t].type != MSTYLE_ELEMENT_CONFLICT;
+}
+
+gboolean
+mstyle_is_element_conflict (const GnmStyle *st, MStyleElementType t)
+{
+ g_return_val_if_fail (st != NULL, FALSE);
+ g_return_val_if_fail (t > 0 && t < MSTYLE_ELEMENT_MAX, FALSE);
+
+ return st->elements[t].type == MSTYLE_ELEMENT_CONFLICT;
+}
+
+void
+mstyle_unset_element (GnmStyle *st, MStyleElementType t)
+{
+ g_return_if_fail (st != NULL);
+ g_return_if_fail (t > 0 && t < MSTYLE_ELEMENT_MAX);
+
+ mstyle_element_unref (st->elements[t]);
+ st->elements[t].type = MSTYLE_ELEMENT_UNSET;
+}
+
+/**
+ * mstyle_replace_element:
+ * @src: Source mstyle
+ * @dst: Destination mstyle
+ * @t: Element to replace
+ *
+ * This function replaces element 't' in mstyle 'dst' with element 't'
+ * in mstyle 'src'. (If element 't' was already set in mstyle 'dst' then
+ * the element will first be unset)
+ **/
+void
+mstyle_replace_element (GnmStyle *src, GnmStyle *dst, MStyleElementType t)
+{
+ g_return_if_fail (src != NULL);
+ g_return_if_fail (dst != NULL);
+
+ mstyle_element_ref (&src->elements[t]);
+
+ if (mstyle_is_element_set (dst, t))
+ mstyle_unset_element (dst, t);
+
+ dst->elements[t] = src->elements[t];
+}
+
+void
+mstyle_set_color (GnmStyle *st, MStyleElementType t,
+ GnmColor *col)
+{
+ g_return_if_fail (st != NULL);
+ g_return_if_fail (col != NULL);
+
+ switch (t) {
+ case MSTYLE_ANY_COLOR:
+ mstyle_element_unref (st->elements[t]);
+ st->elements[t].type = t;
+ st->elements[t].u.color.any = col;
+ mstyle_pango_clear (st);
+ break;
+ default:
+ g_warning ("Not a color element");
+ break;
+ }
+}
+
+GnmColor *
+mstyle_get_color (GnmStyle const *st, MStyleElementType t)
+{
+ g_return_val_if_fail (mstyle_is_element_set (st, t), NULL);
+
+ switch (t) {
+ case MSTYLE_ANY_COLOR:
+ return st->elements[t].u.color.any;
+
+ default:
+ g_warning ("Not a color element");
+ return NULL;
+ }
+}
+
+void
+mstyle_set_border (GnmStyle *st, MStyleElementType t,
+ GnmBorder *border)
+{
+ g_return_if_fail (st != NULL);
+
+ /* NOTE : It is legal for border to be NULL */
+ switch (t) {
+ case MSTYLE_ANY_BORDER:
+ mstyle_element_unref (st->elements[t]);
+ st->elements[t].type = t;
+ st->elements[t].u.border.any = border;
+ break;
+ default:
+ g_warning ("Not a border element");
+ break;
+ }
+
+}
+
+GnmBorder *
+mstyle_get_border (const GnmStyle *st, MStyleElementType t)
+{
+ switch (t) {
+ case MSTYLE_ANY_BORDER:
+ return st->elements[t].u.border.any;
+
+ default:
+ g_warning ("Not a border element");
+ return NULL;
+ }
+}
+
+void
+mstyle_set_pattern (GnmStyle *st, int pattern)
+{
+ g_return_if_fail (st != NULL);
+ g_return_if_fail (pattern >= 0);
+ //g_return_if_fail (pattern <= GNUMERIC_SHEET_PATTERNS);
+
+ st->elements[MSTYLE_PATTERN].type = MSTYLE_PATTERN;
+ st->elements[MSTYLE_PATTERN].u.pattern = pattern;
+}
+
+int
+mstyle_get_pattern (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_PATTERN), 0);
+
+ return style->elements[MSTYLE_PATTERN].u.pattern;
+}
+
+GnmFont *
+mstyle_get_font (const GnmStyle *style, PangoContext *context, double zoom)
+{
+ g_return_val_if_fail (style != NULL, NULL);
+
+ if (!style->font || style->font_zoom != zoom) {
+ const gchar *name;
+ gboolean bold, italic;
+ double size;
+
+ mstyle_font_clear ((GnmStyle *)style);
+
+ if (mstyle_is_element_set (style, MSTYLE_FONT_NAME))
+ name = mstyle_get_font_name (style);
+ else
+ name = DEFAULT_FONT;
+
+ if (mstyle_is_element_set (style, MSTYLE_FONT_BOLD))
+ bold = mstyle_get_font_bold (style);
+ else
+ bold = FALSE;
+
+ if (mstyle_is_element_set (style, MSTYLE_FONT_ITALIC))
+ italic = mstyle_get_font_italic (style);
+ else
+ italic = FALSE;
+
+ if (mstyle_is_element_set (style, MSTYLE_FONT_SIZE))
+ size = mstyle_get_font_size (style);
+ else
+ size = DEFAULT_SIZE;
+
+ ((GnmStyle *)style)->font =
+ style_font_new (context, name, size,
+ zoom, bold, italic);
+ ((GnmStyle *)style)->font_zoom = zoom;
+ }
+
+ style_font_ref (style->font);
+ return style->font;
+}
+
+void
+mstyle_set_font_name (GnmStyle *style, const char *name)
+{
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (style != NULL);
+
+ mstyle_element_unref (style->elements[MSTYLE_FONT_NAME]);
+ style->elements[MSTYLE_FONT_NAME].type = MSTYLE_FONT_NAME;
+ style->elements[MSTYLE_FONT_NAME].u.font.name = gnm_string_get (name);
+ mstyle_font_clear (style);
+ mstyle_pango_clear (style);
+}
+
+const char *
+mstyle_get_font_name (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_FONT_NAME), NULL);
+
+ return style->elements[MSTYLE_FONT_NAME].u.font.name->str;
+}
+
+void
+mstyle_set_font_bold (GnmStyle *style, gboolean bold)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_FONT_BOLD].type = MSTYLE_FONT_BOLD;
+ style->elements[MSTYLE_FONT_BOLD].u.font.bold = bold;
+ mstyle_font_clear (style);
+ mstyle_pango_clear (style);
+}
+
+gboolean
+mstyle_get_font_bold (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_FONT_BOLD), FALSE);
+
+ return style->elements[MSTYLE_FONT_BOLD].u.font.bold;
+}
+
+void
+mstyle_set_font_italic (GnmStyle *style, gboolean italic)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_FONT_ITALIC].type = MSTYLE_FONT_ITALIC;
+ style->elements[MSTYLE_FONT_ITALIC].u.font.italic = italic;
+ mstyle_font_clear (style);
+ mstyle_pango_clear (style);
+}
+
+gboolean
+mstyle_get_font_italic (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_FONT_ITALIC), FALSE);
+
+ return style->elements[MSTYLE_FONT_ITALIC].u.font.italic;
+}
+
+void
+mstyle_set_font_uline (GnmStyle *style, StyleUnderlineType const underline)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_FONT_UNDERLINE].type = MSTYLE_FONT_UNDERLINE;
+ style->elements[MSTYLE_FONT_UNDERLINE].u.font.underline = underline;
+ mstyle_pango_clear (style);
+}
+
+StyleUnderlineType
+mstyle_get_font_uline (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE), FALSE);
+
+ return style->elements[MSTYLE_FONT_UNDERLINE].u.font.underline;
+}
+
+void
+mstyle_set_font_strike (GnmStyle *style, gboolean const strikethrough)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_FONT_STRIKETHROUGH].type = MSTYLE_FONT_STRIKETHROUGH;
+ style->elements[MSTYLE_FONT_STRIKETHROUGH].u.font.strikethrough = strikethrough;
+ mstyle_pango_clear (style);
+}
+
+gboolean
+mstyle_get_font_strike (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH), FALSE);
+
+ return style->elements[MSTYLE_FONT_STRIKETHROUGH].u.font.strikethrough;
+}
+void
+mstyle_set_font_size (GnmStyle *style, double size)
+{
+ g_return_if_fail (style != NULL);
+ g_return_if_fail (size >= 1.);
+
+ style->elements[MSTYLE_FONT_SIZE].type = MSTYLE_FONT_SIZE;
+ style->elements[MSTYLE_FONT_SIZE].u.font.size = size;
+ mstyle_font_clear (style);
+ mstyle_pango_clear (style);
+}
+
+double
+mstyle_get_font_size (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_FONT_SIZE), 12.0);
+
+ return style->elements[MSTYLE_FONT_SIZE].u.font.size;
+}
+
+void
+mstyle_set_format (GnmStyle *style, GnmFormat *format)
+{
+ g_return_if_fail (style != NULL);
+ g_return_if_fail (format != NULL);
+
+ style_format_ref (format);
+ mstyle_element_unref (style->elements[MSTYLE_FORMAT]);
+ style->elements[MSTYLE_FORMAT].type = MSTYLE_FORMAT;
+ style->elements[MSTYLE_FORMAT].u.format = format;
+}
+
+void
+mstyle_set_format_text (GnmStyle *style, const char *format)
+{
+ GnmFormat *sf;
+
+ g_return_if_fail (style != NULL);
+ g_return_if_fail (format != NULL);
+
+ /* FIXME FIXME FIXME : This is a potential problem
+ * I am not sure people are feeding us only translated formats.
+ * This entire function should be deleted.
+ */
+ sf = style_format_new_XL (format, FALSE);
+ mstyle_set_format (style, sf);
+ style_format_unref (sf);
+}
+
+GnmFormat *
+mstyle_get_format (GnmStyle const *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_FORMAT), NULL);
+
+ return style->elements[MSTYLE_FORMAT].u.format;
+}
+
+void
+mstyle_set_align_h (GnmStyle *style, StyleHAlignFlags a)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_ALIGN_H].type = MSTYLE_ALIGN_H;
+ style->elements[MSTYLE_ALIGN_H].u.align.h = a;
+}
+
+StyleHAlignFlags
+mstyle_get_align_h (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_ALIGN_H), 0);
+
+ return style->elements[MSTYLE_ALIGN_H].u.align.h;
+}
+
+void
+mstyle_set_align_v (GnmStyle *style, StyleVAlignFlags a)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_ALIGN_V].type = MSTYLE_ALIGN_V;
+ style->elements[MSTYLE_ALIGN_V].u.align.v = a;
+}
+
+StyleVAlignFlags
+mstyle_get_align_v (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_ALIGN_V), 0);
+
+ return style->elements[MSTYLE_ALIGN_V].u.align.v;
+}
+
+void
+mstyle_set_indent (GnmStyle *style, int i)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_INDENT].type = MSTYLE_INDENT;
+ style->elements[MSTYLE_INDENT].u.indent = i;
+}
+
+int
+mstyle_get_indent (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_INDENT), 0);
+
+ return style->elements[MSTYLE_INDENT].u.indent;
+}
+
+void
+mstyle_set_rotation (GnmStyle *style, int rot_deg)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_ROTATION].type = MSTYLE_ROTATION;
+ style->elements[MSTYLE_ROTATION].u.rotation = rot_deg;
+}
+
+int
+mstyle_get_rotation (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_ROTATION), 0);
+
+ return style->elements[MSTYLE_ROTATION].u.rotation;
+}
+
+void
+mstyle_set_wrap_text (GnmStyle *style, gboolean f)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_WRAP_TEXT].type = MSTYLE_WRAP_TEXT;
+ style->elements[MSTYLE_WRAP_TEXT].u.wrap_text = f;
+}
+
+gboolean
+mstyle_get_wrap_text (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_WRAP_TEXT), FALSE);
+
+ return style->elements[MSTYLE_WRAP_TEXT].u.wrap_text;
+}
+
+/*
+ * Same as mstyle_get_wrap_text except that if either halign or valign
+ * is _JUSTIFY, the result will be TRUE.
+ */
+gboolean
+mstyle_get_effective_wrap_text (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_WRAP_TEXT), FALSE);
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_ALIGN_V), FALSE);
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_ALIGN_H), FALSE);
+
+ /* Note: HALIGN_GENERAL never expands to HALIGN_JUSTIFY. */
+ return (style->elements[MSTYLE_WRAP_TEXT].u.wrap_text ||
+ style->elements[MSTYLE_ALIGN_V].u.align.v == VALIGN_JUSTIFY ||
+ style->elements[MSTYLE_ALIGN_H].u.align.h == HALIGN_JUSTIFY);
+}
+
+void
+mstyle_set_shrink_to_fit (GnmStyle *style, gboolean f)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_SHRINK_TO_FIT].type = MSTYLE_SHRINK_TO_FIT;
+ style->elements[MSTYLE_SHRINK_TO_FIT].u.wrap_text = f;
+}
+
+gboolean
+mstyle_get_shrink_to_fit (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_SHRINK_TO_FIT), FALSE);
+
+ return style->elements[MSTYLE_SHRINK_TO_FIT].u.wrap_text;
+}
+void
+mstyle_set_content_locked (GnmStyle *style, gboolean f)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_CONTENT_LOCKED].type = MSTYLE_CONTENT_LOCKED;
+ style->elements[MSTYLE_CONTENT_LOCKED].u.content_locked = f;
+}
+
+gboolean
+mstyle_get_content_locked (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_CONTENT_LOCKED), FALSE);
+
+ return style->elements[MSTYLE_CONTENT_LOCKED].u.content_locked;
+}
+void
+mstyle_set_content_hidden (GnmStyle *style, gboolean f)
+{
+ g_return_if_fail (style != NULL);
+
+ style->elements[MSTYLE_CONTENT_HIDDEN].type = MSTYLE_CONTENT_HIDDEN;
+ style->elements[MSTYLE_CONTENT_HIDDEN].u.content_hidden = f;
+}
+
+gboolean
+mstyle_get_content_hidden (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_CONTENT_HIDDEN), FALSE);
+
+ return style->elements[MSTYLE_CONTENT_HIDDEN].u.content_hidden;
+}
+
+void
+mstyle_set_validation (GnmStyle *style, GnmValidation *v)
+{
+ g_return_if_fail (style != NULL);
+
+ mstyle_element_unref (style->elements[MSTYLE_VALIDATION]);
+ style->elements[MSTYLE_VALIDATION].type = MSTYLE_VALIDATION;
+ style->elements[MSTYLE_VALIDATION].u.validation = v;
+}
+
+GnmValidation *
+mstyle_get_validation (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_VALIDATION), NULL);
+
+ return style->elements[MSTYLE_VALIDATION].u.validation;
+}
+
+void
+mstyle_set_hlink (GnmStyle *style, GnmHLink *link)
+{
+ g_return_if_fail (style != NULL);
+
+ mstyle_element_unref (style->elements[MSTYLE_HLINK]);
+ style->elements[MSTYLE_HLINK].type = MSTYLE_HLINK;
+ style->elements[MSTYLE_HLINK].u.hlink = link;
+}
+
+GnmHLink *
+mstyle_get_hlink (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_HLINK), NULL);
+
+ return style->elements[MSTYLE_HLINK].u.hlink;
+}
+
+void
+mstyle_set_input_msg (GnmStyle *style, GnmInputMsg *msg)
+{
+ g_return_if_fail (style != NULL);
+
+ mstyle_element_unref (style->elements[MSTYLE_INPUT_MSG]);
+ style->elements[MSTYLE_INPUT_MSG].type = MSTYLE_INPUT_MSG;
+ style->elements[MSTYLE_INPUT_MSG].u.input_msg = msg;
+}
+
+GnmInputMsg *
+mstyle_get_input_msg (const GnmStyle *style)
+{
+ g_return_val_if_fail (mstyle_is_element_set (style, MSTYLE_INPUT_MSG), NULL);
+
+ return style->elements[MSTYLE_INPUT_MSG].u.input_msg;
+}
+
+gboolean
+mstyle_visible_in_blank (const GnmStyle *st)
+{
+ MStyleElementType i;
+
+ if (mstyle_is_element_set (st, MSTYLE_PATTERN) &&
+ mstyle_get_pattern (st) > 0)
+ return TRUE;
+
+ for (i = MSTYLE_BORDER_TOP ; i <= MSTYLE_BORDER_DIAGONAL ; ++i)
+ if (mstyle_is_element_set (st, i) &&
+ style_border_visible_in_blank (mstyle_get_border (st, i)))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+add_attr (PangoAttrList *attrs, PangoAttribute *attr)
+{
+ attr->start_index = 0;
+ attr->end_index = G_MAXINT;
+ pango_attr_list_insert (attrs, attr);
+}
+
+/**
+ * mstyle_get_pango_attrs :
+ * @style : #GnmStyle
+ **/
+PangoAttrList *
+mstyle_get_pango_attrs (const GnmStyle *mstyle,
+ PangoContext *context,
+ double zoom)
+{
+ PangoAttrList *l;
+
+ if (mstyle->pango_attrs) {
+ if (zoom == mstyle->pango_attrs_zoom) {
+ pango_attr_list_ref (mstyle->pango_attrs);
+ return mstyle->pango_attrs;
+ }
+ pango_attr_list_unref (((GnmStyle *)mstyle)->pango_attrs);
+ }
+
+ ((GnmStyle *)mstyle)->pango_attrs = l = pango_attr_list_new ();
+ ((GnmStyle *)mstyle)->pango_attrs_zoom = zoom;
+
+ /* Foreground colour. */
+ /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
+ if (0) {
+ const GnmColor *fore = mstyle_get_color (mstyle, MSTYLE_COLOR_FORE);
+ add_attr (l, pango_attr_foreground_new (
+ fore->color.red, fore->color.green, fore->color.blue));
+ }
+
+ /* Handle underlining. */
+ switch (mstyle_get_font_uline (mstyle)) {
+ case UNDERLINE_SINGLE :
+ add_attr (l, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE));
+ break;
+ case UNDERLINE_DOUBLE :
+ add_attr (l, pango_attr_underline_new (PANGO_UNDERLINE_DOUBLE));
+ break;
+ default :
+ break;
+ }
+
+ if (mstyle_get_font_strike (mstyle))
+ add_attr (l, pango_attr_strikethrough_new (TRUE));
+
+ {
+ GnmFont *font = mstyle_get_font (mstyle, context, zoom);
+ add_attr (l, pango_attr_font_desc_new (font->pango.font_descr));
+ style_font_unref (font);
+ }
+
+ pango_attr_list_ref (l);
+ return l;
+}
+
+PangoAttrList *
+mstyle_generate_attrs_full (GnmStyle const *st)
+{
+ GnmColor *fore = mstyle_get_color (st, MSTYLE_COLOR_FORE);
+ PangoAttrList *l = pango_attr_list_new ();
+
+ add_attr (l, pango_attr_family_new (mstyle_get_font_name (st)));
+ add_attr (l, pango_attr_size_new (mstyle_get_font_size (st) * PANGO_SCALE));
+ add_attr (l, pango_attr_style_new (mstyle_get_font_italic (st)
+ ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
+ add_attr (l, pango_attr_weight_new (mstyle_get_font_bold (st)
+ ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
+ add_attr (l, pango_attr_foreground_new (
+ fore->color.red, fore->color.green, fore->color.blue));
+ add_attr (l, pango_attr_strikethrough_new (mstyle_get_font_strike (st)));
+ switch (mstyle_get_font_uline (st)) {
+ case UNDERLINE_SINGLE :
+ add_attr (l, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE));
+ break;
+ case UNDERLINE_DOUBLE :
+ add_attr (l, pango_attr_underline_new (PANGO_UNDERLINE_DOUBLE));
+ break;
+ default :
+ add_attr (l, pango_attr_underline_new (PANGO_UNDERLINE_NONE));
+ break;
+ }
+
+ return l;
+}
+
+void
+mstyle_set_from_pango_attribute (GnmStyle *style, PangoAttribute const *attr)
+{
+ switch (attr->klass->type) {
+ case PANGO_ATTR_FAMILY :
+ mstyle_set_font_name (style, ((PangoAttrString *)attr)->value);
+ break;
+ case PANGO_ATTR_SIZE :
+ mstyle_set_font_size (style,
+ (double )(((PangoAttrInt *)attr)->value) / PANGO_SCALE);
+ break;
+ case PANGO_ATTR_STYLE :
+ mstyle_set_font_italic (style,
+ ((PangoAttrInt *)attr)->value == PANGO_STYLE_ITALIC);
+ break;
+ case PANGO_ATTR_WEIGHT :
+ mstyle_set_font_bold (style,
+ ((PangoAttrInt *)attr)->value >= PANGO_WEIGHT_BOLD);
+ break;
+ case PANGO_ATTR_FOREGROUND :
+ mstyle_set_color (style, MSTYLE_COLOR_FORE,
+ style_color_new_pango (
+ &((PangoAttrColor *)attr)->color));
+ break;
+ case PANGO_ATTR_UNDERLINE :
+ switch (((PangoAttrInt *)attr)->value) {
+ case PANGO_UNDERLINE_NONE :
+ mstyle_set_font_uline (style, UNDERLINE_NONE);
+ break;
+ case PANGO_UNDERLINE_SINGLE :
+ mstyle_set_font_uline (style, UNDERLINE_SINGLE);
+ break;
+ case PANGO_UNDERLINE_DOUBLE :
+ mstyle_set_font_uline (style, UNDERLINE_DOUBLE);
+ break;
+ }
+ break;
+ case PANGO_ATTR_STRIKETHROUGH :
+ mstyle_set_font_strike (style,
+ ((PangoAttrInt *)attr)->value != 0);
+ break;
+ default :
+ break; /* ignored */
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+void
+mstyle_init (void)
+{
+#if USE_MSTYLE_POOL
+ mstyle_pool =
+ gnm_mem_chunk_new ("mstyle pool",
+ sizeof (GnmStyle),
+ 16 * 1024 - 128);
+#endif
+}
+
+#if USE_MSTYLE_POOL
+static void
+cb_mstyle_pool_leak (gpointer data, gpointer user)
+{
+ GnmStyle *mstyle = data;
+ fprintf (stderr, "Leaking mstyle at %p.\n", mstyle);
+ mstyle_dump (mstyle);
+}
+#endif
+
+void
+mstyle_shutdown (void)
+{
+#if USE_MSTYLE_POOL
+ gnm_mem_chunk_foreach_leak (mstyle_pool, cb_mstyle_pool_leak, NULL);
+ gnm_mem_chunk_destroy (mstyle_pool, FALSE);
+ mstyle_pool = NULL;
+#endif
+}
--- /dev/null
+++ lib/goffice/split/gui-util.c
@@ -0,0 +1,1445 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gnumeric-util.c: Various GUI utility functions.
+ *
+ * Author:
+ * Miguel de Icaza (miguel at gnu.org)
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "gui-util.h"
+
+//#include "workbook-control-gui-priv.h"
+#include "gutils.h"
+//#include "parse-util.h"
+#include "style.h"
+#include "style-color.h"
+#include "error-info.h"
+#include "value.h"
+#include "number-match.h"
+#include "format.h"
+#include "application.h"
+//#include "workbook.h"
+//#include "libgnumeric.h"
+
+#include <goffice/gui-utils/go-combo-color.h>
+#include <glade/glade.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkfilechooser.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtkeventbox.h>
+#include <gtk/gtkradiobutton.h>
+#include <gtk/gtkalignment.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkframe.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkbbox.h>
+#include <gtk/gtkhbox.h>
+#include <atk/atkrelation.h>
+#include <atk/atkrelationset.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <string.h>
+
+gboolean
+gnumeric_dialog_question_yes_no (GtkWindow *parent,
+ gchar const *message,
+ gboolean default_answer)
+{
+ GtkWidget *dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_YES_NO,
+ message);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ default_answer ? GTK_RESPONSE_YES : GTK_RESPONSE_NO);
+ return gnumeric_dialog_run (parent,
+ GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
+}
+/*
+ * TODO:
+ * Get rid of trailing newlines /whitespace.
+ */
+void
+gnumeric_notice (GtkWindow *parent, GtkMessageType type, char const *str)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT, type,
+ GTK_BUTTONS_OK, str);
+ gtk_label_set_use_markup (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE);
+
+ gnumeric_dialog_run (parent, GTK_DIALOG (dialog));
+}
+
+void
+gnumeric_notice_nonmodal (GtkWindow *parent, GtkWidget **ref, GtkMessageType type, char const *str)
+{
+ GtkWidget *dialog;
+
+ if (*ref != NULL)
+ gtk_widget_destroy (*ref);
+
+ *ref = dialog = gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT, type,
+ GTK_BUTTONS_OK, str);
+
+ g_signal_connect_object (G_OBJECT (dialog),
+ "response",
+ G_CALLBACK (gtk_widget_destroy), G_OBJECT (dialog), 0);
+ g_signal_connect (G_OBJECT (dialog),
+ "destroy",
+ G_CALLBACK (gtk_widget_destroyed), ref);
+
+ gtk_widget_show (dialog);
+
+ return;
+}
+
+static void
+fsel_response_cb (GtkFileChooser *dialog,
+ gint response_id,
+ gboolean *result)
+{
+ if (response_id == GTK_RESPONSE_OK) {
+ char *uri = gtk_file_chooser_get_uri (dialog);
+
+ if (uri) {
+ g_free (uri);
+ *result = TRUE;
+ }
+ }
+
+ gtk_main_quit ();
+}
+
+static gint
+gu_delete_handler (GtkDialog *dialog,
+ GdkEventAny *event,
+ gpointer data)
+{
+ gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+ return TRUE; /* Do not destroy */
+}
+
+#if 0
+gboolean
+gnumeric_dialog_file_selection (WorkbookControlGUI *wbcg, GtkWidget *w)
+{
+ /* Note: wbcg will be NULL if called (indirectly) from gog-style.c */
+ gboolean result = FALSE;
+ gulong delete_handler;
+
+ g_return_val_if_fail (GTK_IS_FILE_CHOOSER (w), FALSE);
+
+ gtk_window_set_modal (GTK_WINDOW (w), TRUE);
+ if (wbcg)
+ gnumeric_set_transient (wbcg_toplevel (wbcg),
+ GTK_WINDOW (w));
+ g_signal_connect (w, "response",
+ G_CALLBACK (fsel_response_cb), &result);
+ delete_handler =
+ g_signal_connect (w,
+ "delete_event",
+ G_CALLBACK (gu_delete_handler),
+ NULL);
+
+ gtk_widget_show_all (w);
+ gtk_grab_add (w);
+ gtk_main ();
+
+ g_signal_handler_disconnect (w, delete_handler);
+
+ return result;
+}
+#endif // 0
+
+
+static gint
+cb_modal_dialog_keypress (GtkWidget *w, GdkEventKey *e)
+{
+ if(e->keyval == GDK_Escape) {
+ gtk_dialog_response (GTK_DIALOG (w), GTK_RESPONSE_CANCEL);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gnumeric_dialog_run
+ *
+ * Pop up a dialog as child of a window.
+ */
+gint
+gnumeric_dialog_run (GtkWindow *parent, GtkDialog *dialog)
+{
+ gint result;
+
+ g_return_val_if_fail (GTK_IS_DIALOG (dialog), GTK_RESPONSE_NONE);
+
+ if (parent) {
+ g_return_val_if_fail (GTK_IS_WINDOW (parent), GTK_RESPONSE_NONE);
+
+ gnumeric_set_transient (parent, GTK_WINDOW (dialog));
+ }
+
+ g_signal_connect (G_OBJECT (dialog),
+ "key-press-event",
+ G_CALLBACK (cb_modal_dialog_keypress), NULL);
+
+ while ((result = gtk_dialog_run (dialog)) >= 0)
+ ;
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ return result;
+}
+
+#define ERROR_INFO_MAX_LEVEL 9
+#define ERROR_INFO_TAG_NAME "errorinfotag%i"
+
+static void
+insert_error_info (GtkTextBuffer* text, ErrorInfo *error, gint level)
+{
+ gchar *message = (gchar *) error_info_peek_message (error);
+ GSList *details_list, *l;
+ GtkTextIter start, last;
+ gchar *tag_name = g_strdup_printf (ERROR_INFO_TAG_NAME,
+ MIN (level, ERROR_INFO_MAX_LEVEL));
+ if (message == NULL)
+ message = g_strdup (_("Multiple errors\n"));
+ else
+ message = g_strdup_printf ("%s\n", message);
+ gtk_text_buffer_get_bounds (text, &start, &last);
+ gtk_text_buffer_insert_with_tags_by_name (text, &last,
+ message, -1,
+ tag_name, NULL);
+ g_free (tag_name);
+ g_free (message);
+ details_list = error_info_peek_details (error);
+ for (l = details_list; l != NULL; l = l->next) {
+ ErrorInfo *detail_error = l->data;
+ insert_error_info (text, detail_error, level + 1);
+ }
+ return;
+}
+
+/**
+ * gnumeric_error_info_dialog_new
+ *
+ */
+GtkWidget *
+gnumeric_error_info_dialog_new (ErrorInfo *error)
+{
+ GtkWidget *dialog;
+ GtkWidget *scrolled_window;
+ GtkTextView *view;
+ GtkTextBuffer *text;
+ GtkMessageType mtype;
+ gchar *message;
+ gint bf_lim = 1;
+ gint i;
+ GdkScreen *screen;
+
+ g_return_val_if_fail (error != NULL, NULL);
+
+ message = (gchar *) error_info_peek_message (error);
+ if (message == NULL)
+ bf_lim++;
+
+ mtype = GTK_MESSAGE_ERROR;
+ if (error_info_peek_severity (error) < GNM_ERROR)
+ mtype = GTK_MESSAGE_WARNING;
+ dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
+ mtype, GTK_BUTTONS_CLOSE, " ");
+ screen = gtk_widget_get_screen (dialog);
+ gtk_widget_set_size_request (dialog,
+ gdk_screen_get_width (screen) / 3,
+ gdk_screen_get_width (screen) / 4);
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type
+ (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_ETCHED_IN);
+ view = GTK_TEXT_VIEW (gtk_text_view_new ());
+ gtk_text_view_set_wrap_mode (view, GTK_WRAP_WORD);
+ gtk_text_view_set_editable (view, FALSE);
+ gtk_text_view_set_cursor_visible (view, FALSE);
+
+ gtk_text_view_set_pixels_below_lines
+ (view, gtk_text_view_get_pixels_inside_wrap (view) + 3);
+ text = gtk_text_view_get_buffer (view);
+ for (i = ERROR_INFO_MAX_LEVEL; i-- > 0;) {
+ gchar *tag_name = g_strdup_printf (ERROR_INFO_TAG_NAME, i);
+ gtk_text_buffer_create_tag
+ (text, tag_name,
+ "left_margin", i * 12,
+ "right_margin", i * 12,
+ "weight", ((i < bf_lim)
+ ? PANGO_WEIGHT_BOLD
+ : PANGO_WEIGHT_NORMAL),
+ NULL);
+ g_free (tag_name);
+ }
+ insert_error_info (text, error, 0);
+
+ gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (view));
+ gtk_widget_show_all (GTK_WIDGET (scrolled_window));
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), scrolled_window, TRUE, TRUE, 0);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+ return dialog;
+}
+
+/**
+ * gnumeric_error_info_dialog_show
+ *
+ */
+void
+gnumeric_error_info_dialog_show (GtkWindow *parent, ErrorInfo *error)
+{
+ GtkWidget *dialog = gnumeric_error_info_dialog_new (error);
+ gnumeric_dialog_run (parent, GTK_DIALOG (dialog));
+}
+
+static void
+cb_parent_mapped (GtkWidget *parent, GtkWindow *window)
+{
+ if (GTK_WIDGET_MAPPED (window)) {
+ gtk_window_present (window);
+ g_signal_handlers_disconnect_by_func (G_OBJECT (parent),
+ G_CALLBACK (cb_parent_mapped), window);
+ }
+}
+
+/**
+ * gnumeric_set_transient
+ * @wbcg : The calling window
+ * @window : the transient window
+ *
+ * Make the window a child of the workbook in the command context, if there is
+ * one.
+ * The function duplicates the positioning functionality in
+ * gnome_dialog_set_parent, but does not require the transient window to be
+ * a GnomeDialog.
+ */
+void
+gnumeric_set_transient (GtkWindow *toplevel, GtkWindow *window)
+{
+/* FIXME: */
+/* GtkWindowPosition position = gnome_preferences_get_dialog_position(); */
+ GtkWindowPosition position = GTK_WIN_POS_CENTER_ON_PARENT;
+
+ g_return_if_fail (GTK_IS_WINDOW (toplevel));
+ g_return_if_fail (GTK_IS_WINDOW (window));
+
+ gtk_window_set_transient_for (window, toplevel);
+
+ if (position == GTK_WIN_POS_NONE)
+ position = GTK_WIN_POS_CENTER_ON_PARENT;
+ gtk_window_set_position (window, position);
+
+ if (!GTK_WIDGET_MAPPED (toplevel))
+ g_signal_connect_after (G_OBJECT (toplevel),
+ "map",
+ G_CALLBACK (cb_parent_mapped), window);
+}
+
+typedef struct {
+ WorkbookControlGUI *wbcg;
+ GtkWidget *dialog;
+ char const *key;
+ gboolean freed;
+} KeyedDialogContext;
+
+static void
+cb_free_keyed_dialog_context (KeyedDialogContext *ctxt)
+{
+ if (ctxt->freed)
+ return;
+ ctxt->freed = TRUE;
+
+ /*
+ * One of these causes a recursive call which will do nothing due to
+ * ->freed.
+ */
+ g_object_set_data (G_OBJECT (ctxt->wbcg), ctxt->key, NULL);
+ g_object_set_data (G_OBJECT (ctxt->dialog), "KeyedDialog", NULL);
+ g_free (ctxt);
+}
+
+static gint
+cb_keyed_dialog_keypress (GtkWidget *dialog, GdkEventKey *event,
+ G_GNUC_UNUSED gpointer user)
+{
+ if (event->keyval == GDK_Escape) {
+ gtk_object_destroy (GTK_OBJECT (dialog));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#if 0
+/**
+ * gnumeric_keyed_dialog
+ *
+ * @wbcg A WorkbookControlGUI
+ * @dialog A transient window
+ * @key A key to identify the dialog
+ *
+ * Make dialog a transient child of wbcg, attaching to wbcg object data to
+ * identify the dialog. The object data makes it possible to ensure that
+ * only one dialog of a kind can be displayed for a wbcg. Deallocation of
+ * the object data is managed here.
+ **/
+void
+gnumeric_keyed_dialog (WorkbookControlGUI *wbcg, GtkWindow *dialog, const char *key)
+{
+ KeyedDialogContext *ctxt;
+
+ g_return_if_fail (IS_WORKBOOK_CONTROL_GUI (wbcg));
+ g_return_if_fail (GTK_IS_WINDOW (dialog));
+ g_return_if_fail (key != NULL);
+
+ wbcg_set_transient (wbcg, dialog);
+
+ ctxt = g_new (KeyedDialogContext, 1);
+ ctxt->wbcg = wbcg;
+ ctxt->dialog = GTK_WIDGET (dialog);
+ ctxt->key = key;
+ ctxt->freed = FALSE;
+ g_object_set_data_full (G_OBJECT (wbcg),
+ key, ctxt, (GDestroyNotify) cb_free_keyed_dialog_context);
+ g_object_set_data_full (G_OBJECT (dialog),
+ "KeyedDialog", ctxt, (GDestroyNotify) cb_free_keyed_dialog_context);
+ g_signal_connect (G_OBJECT (dialog),
+ "key_press_event",
+ G_CALLBACK (cb_keyed_dialog_keypress), NULL);
+}
+#endif // 0
+
+/**
+ * gnumeric_dialog_raise_if_exists
+ *
+ * @wbcg A WorkbookControlGUI
+ * @key A key to identify the dialog
+ *
+ * Raise the dialog identified by key if it is registered on the wbcg.
+ * Returns TRUE if dialog found, FALSE if not.
+ **/
+gpointer
+gnumeric_dialog_raise_if_exists (WorkbookControlGUI *wbcg, const char *key)
+{
+ KeyedDialogContext *ctxt;
+
+ g_return_val_if_fail (wbcg != NULL, NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+
+ /* Ensure we only pop up one copy per workbook */
+ ctxt = g_object_get_data (G_OBJECT (wbcg), key);
+ if (ctxt && GTK_IS_WINDOW (ctxt->dialog)) {
+ gdk_window_raise (ctxt->dialog->window);
+ return ctxt->dialog;
+ } else
+ return NULL;
+}
+
+static gboolean
+cb_activate_default (GtkWindow *window)
+{
+ /*
+ * gtk_window_activate_default has a bad habit of trying
+ * to activate the focus widget.
+ */
+ return window->default_widget &&
+ GTK_WIDGET_IS_SENSITIVE (window->default_widget) &&
+ gtk_window_activate_default (window);
+}
+
+
+/**
+ * gnumeric_editable_enters: Make the "activate" signal of an editable click
+ * the default dialog button.
+ * @window: dialog to affect.
+ * @editable: Editable to affect.
+ *
+ * This is a literal copy of gnome_dialog_editable_enters, but not restricted
+ * to GnomeDialogs.
+ *
+ * Normally if there's an editable widget (such as #GtkEntry) in your
+ * dialog, pressing Enter will activate the editable rather than the
+ * default dialog button. However, in most cases, the user expects to
+ * type something in and then press enter to close the dialog. This
+ * function enables that behavior.
+ *
+ **/
+void
+gnumeric_editable_enters (GtkWindow *window, GtkWidget *w)
+{
+ g_return_if_fail (GTK_IS_WINDOW(window));
+
+#if 0
+ /* because I really do not feel like changing all the calls to this routine */
+ if (IS_GNM_EXPR_ENTRY (w))
+ w = GTK_WIDGET (gnm_expr_entry_get_entry (GNM_EXPR_ENTRY (w)));
+#endif // 0
+
+ g_signal_connect_swapped (G_OBJECT (w),
+ "activate",
+ G_CALLBACK (cb_activate_default), window);
+}
+
+int
+gtk_radio_group_get_selected (GSList *radio_group)
+{
+ GSList *l;
+ int i, c;
+
+ g_return_val_if_fail (radio_group != NULL, 0);
+
+ c = g_slist_length (radio_group);
+
+ for (i = 0, l = radio_group; l; l = l->next, i++){
+ GtkRadioButton *button = l->data;
+
+ if (GTK_TOGGLE_BUTTON (button)->active)
+ return c - i - 1;
+ }
+
+ return 0;
+}
+
+
+int
+gnumeric_glade_group_value (GladeXML *gui, const char *group[])
+{
+ int i;
+ for (i = 0; group[i]; i++) {
+ GtkWidget *w = glade_xml_get_widget (gui, group[i]);
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
+ return i;
+ }
+ return -1;
+}
+
+static void
+kill_popup_menu (GtkWidget *widget, GtkMenu *menu)
+{
+ g_return_if_fail (menu != NULL);
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ g_object_unref (G_OBJECT (menu));
+}
+
+void
+gnumeric_popup_menu (GtkMenu *menu, GdkEventButton *event)
+{
+ g_return_if_fail (menu != NULL);
+ g_return_if_fail (GTK_IS_MENU (menu));
+
+ g_object_ref (menu);
+ gtk_object_sink (GTK_OBJECT (menu));
+
+ g_signal_connect (G_OBJECT (menu),
+ "hide",
+ G_CALLBACK (kill_popup_menu), menu);
+
+ /* Do NOT pass the button used to create the menu.
+ * instead pass 0. Otherwise bringing up a menu with
+ * the right button will disable clicking on the menu with the left.
+ */
+ gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0,
+ (event != NULL) ? event->time
+ : gtk_get_current_event_time());
+}
+
+
+GtkWidget *
+gnumeric_create_tooltip (void)
+{
+ GtkWidget *tip, *label, *frame;
+ static GtkRcStyle*rc_style = NULL;
+
+ if (rc_style == NULL) {
+ int i;
+ rc_style = gtk_rc_style_new ();
+
+ for (i = 5; --i >= 0 ; ) {
+ rc_style->color_flags[i] = GTK_RC_BG;
+ rc_style->bg[i] = gs_yellow;
+ }
+ }
+
+ tip = gtk_window_new (GTK_WINDOW_POPUP);
+ if (rc_style != NULL)
+ gtk_widget_modify_style (tip, rc_style);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
+ label = gtk_label_new ("");
+
+ gtk_container_add (GTK_CONTAINER (tip), frame);
+ gtk_container_add (GTK_CONTAINER (frame), label);
+
+ return label;
+}
+
+void
+gnumeric_position_tooltip (GtkWidget *tip, int horizontal)
+{
+ GtkRequisition req;
+ int x, y;
+
+ gtk_widget_size_request (tip, &req);
+ gdk_window_get_pointer (NULL, &x, &y, NULL);
+
+ if (horizontal){
+ x -= req.width / 2;
+ y -= req.height + 20;
+ } else {
+ x -= req.width + 20;
+ y -= req.height / 2;
+ }
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+
+ gtk_window_move (GTK_WINDOW (gtk_widget_get_toplevel (tip)), x, y);
+}
+
+/**
+ * gnm_glade_xml_new :
+ * @cc : #GnmCmdContext
+ * @gladefile :
+ *
+ * Simple utility to open glade files
+ **/
+GladeXML *
+gnm_glade_xml_new (GnmCmdContext *cc, char const *gladefile,
+ char const *root, char const *domain)
+{
+ GladeXML *gui;
+ char *f;
+
+ g_return_val_if_fail (gladefile != NULL, NULL);
+
+ if (!g_path_is_absolute (gladefile)) {
+ char *d = gnm_sys_glade_dir ();
+ f = g_build_filename (d, gladefile, NULL);
+ g_free (d);
+ } else
+ f = g_strdup (gladefile);
+
+ gui = glade_xml_new (f, root, domain);
+ if (gui == NULL && cc != NULL) {
+ char *msg = g_strdup_printf (_("Unable to open file '%s'"), f);
+ gnm_cmd_context_error_system (cc, msg);
+ g_free (msg);
+ }
+ g_free (f);
+
+ return gui;
+}
+
+static gint
+cb_non_modal_dialog_keypress (GtkWidget *w, GdkEventKey *e)
+{
+ if(e->keyval == GDK_Escape) {
+ gtk_widget_destroy (w);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+gnumeric_non_modal_dialog (GtkWindow *toplevel, GtkWindow *dialog)
+{
+ gnumeric_set_transient (toplevel, dialog);
+ g_signal_connect (G_OBJECT (dialog),
+ "key-press-event",
+ G_CALLBACK (cb_non_modal_dialog_keypress), NULL);
+}
+
+static void
+popup_item_activate (GtkWidget *item, gpointer *user_data)
+{
+ GnumericPopupMenuElement const *elem =
+ g_object_get_data (G_OBJECT (item), "descriptor");
+ GnumericPopupMenuHandler handler =
+ g_object_get_data (G_OBJECT (item), "handler");
+
+ g_return_if_fail (elem != NULL);
+ g_return_if_fail (handler != NULL);
+
+ if (handler (elem, user_data))
+ gtk_widget_destroy (gtk_widget_get_toplevel (item));
+}
+
+static void
+gnumeric_create_popup_menu_list (GSList *elements,
+ GnumericPopupMenuHandler handler,
+ gpointer user_data,
+ int display_filter,
+ int sensitive_filter,
+ GdkEventButton *event)
+{
+ GtkWidget *menu, *item;
+ char const *trans;
+
+ menu = gtk_menu_new ();
+
+ for (; elements != NULL ; elements = elements->next) {
+ GnumericPopupMenuElement const *element = elements->data;
+ char const * const name = element->name;
+ char const * const pix_name = element->pixmap;
+
+ item = NULL;
+
+ if (element->display_filter != 0 &&
+ !(element->display_filter & display_filter))
+ continue;
+
+ if (name != NULL && *name != '\0') {
+ trans = _(name);
+ item = gtk_image_menu_item_new_with_mnemonic (trans);
+ if (element->sensitive_filter != 0 &&
+ (element->sensitive_filter & sensitive_filter))
+ gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
+ if (pix_name != NULL) {
+ GtkWidget *image = gtk_image_new_from_stock (pix_name,
+ GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image);
+ gtk_image_menu_item_set_image (
+ GTK_IMAGE_MENU_ITEM (item),
+ image);
+ }
+ } else {
+ /* separator */
+ item = gtk_menu_item_new ();
+ gtk_widget_set_sensitive (item, FALSE);
+ }
+
+ if (element->index != 0) {
+ g_signal_connect (G_OBJECT (item),
+ "activate",
+ G_CALLBACK (&popup_item_activate), user_data);
+ g_object_set_data (
+ G_OBJECT (item), "descriptor", (gpointer)(element));
+ g_object_set_data (
+ G_OBJECT (item), "handler", (gpointer)handler);
+ }
+
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ }
+
+ gnumeric_popup_menu (GTK_MENU (menu), event);
+}
+
+void
+gnumeric_create_popup_menu (GnumericPopupMenuElement const *elements,
+ GnumericPopupMenuHandler handler,
+ gpointer user_data,
+ int display_filter, int sensitive_filter,
+ GdkEventButton *event)
+{
+ int i;
+ GSList *tmp = NULL;
+
+ for (i = 0; elements [i].name != NULL; i++)
+ tmp = g_slist_prepend (tmp, (gpointer)(elements + i));
+
+ tmp = g_slist_reverse (tmp);
+ gnumeric_create_popup_menu_list (tmp, handler, user_data,
+ display_filter, sensitive_filter, event);
+ g_slist_free (tmp);
+}
+
+/**
+ * go_combo_color_get_style_color :
+ *
+ * A utility wrapper to map between gal's colour combo and gnumeric's StyleColors.
+ */
+GnmColor *
+go_combo_color_get_style_color (GtkWidget *go_combo_color)
+{
+ GnmColor *sc = NULL;
+ guint16 r, g, b;
+ GOColor color = go_combo_color_get_color (GO_COMBO_COLOR (go_combo_color), NULL);
+ if (UINT_RGBA_A (color) >= 0x80) {
+ r = UINT_RGBA_R (color); r |= (r << 8);
+ g = UINT_RGBA_G (color); g |= (g << 8);
+ b = UINT_RGBA_B (color); b |= (b << 8);
+ sc = style_color_new (r, g, b);
+ }
+ return sc;
+}
+
+#ifdef WITH_GNOME
+#include <libgnome/gnome-help.h>
+#endif
+void
+gnumeric_help_display (char const *link)
+{
+ g_return_if_fail (link != NULL);
+#ifdef WITH_GNOME
+ gnome_help_display ("gnumeric", link, NULL);
+#else
+ g_warning ("TODO : launch help browser for %s", link);
+#endif
+}
+
+static void
+cb_help (GtkWidget *button, char const *link)
+{
+ gnumeric_help_display (link);
+}
+
+void
+gnumeric_init_help_button (GtkWidget *w, char const *link)
+{
+ GtkWidget *parent = gtk_widget_get_parent (w);
+ if (GTK_IS_BUTTON_BOX (parent))
+ gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (parent),
+ w, TRUE);
+
+ g_signal_connect (G_OBJECT (w),
+ "clicked",
+ G_CALLBACK (cb_help), (gpointer) link);
+}
+
+static void
+gnumeric_help_pbox_goto (void *ignore, int ignore2, char const *link)
+{
+ gnumeric_help_display (link);
+}
+
+void
+gnumeric_pbox_init_help (GtkWidget *dialog, char const *link)
+{
+ g_signal_connect (G_OBJECT (dialog),
+ "help",
+ G_CALLBACK (gnumeric_help_pbox_goto), (gpointer)link);
+}
+
+char *
+gnumeric_textview_get_text (GtkTextView *text_view)
+{
+ GtkTextIter start, end;
+ GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+
+ g_return_val_if_fail (buf != NULL, NULL);
+
+ gtk_text_buffer_get_start_iter (buf, &start);
+ gtk_text_buffer_get_end_iter (buf, &end);
+ return gtk_text_buffer_get_text (buf, &start, &end, FALSE);
+}
+
+void
+gnumeric_textview_set_text (GtkTextView *text_view, char const *txt)
+{
+ gtk_text_buffer_set_text (
+ gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)),
+ txt, -1);
+}
+
+void
+focus_on_entry (GtkEntry *entry)
+{
+ if (entry == NULL)
+ return;
+ gtk_widget_grab_focus (GTK_WIDGET(entry));
+ gtk_editable_set_position (GTK_EDITABLE (entry), 0);
+ gtk_editable_select_region (GTK_EDITABLE (entry), 0, entry->text_length);
+}
+
+gboolean
+entry_to_float_with_format_default (GtkEntry *entry, gnm_float *the_float, gboolean update,
+ GnmFormat *format, gnm_float num)
+{
+ char const *text = gtk_entry_get_text (entry);
+ gboolean need_default = (text == NULL);
+
+ if (!need_default) {
+ char *new_text = g_strdup (text);
+ need_default = (0 == strlen (g_strstrip(new_text)));
+ g_free (new_text);
+ }
+
+ if (need_default && !update) {
+ *the_float = num;
+ return FALSE;
+ }
+
+ if (need_default)
+ float_to_entry (entry, num);
+
+ return entry_to_float_with_format (entry, the_float, update, format);
+}
+
+gboolean
+entry_to_float_with_format (GtkEntry *entry, gnm_float *the_float, gboolean update,
+ GnmFormat *format)
+{
+ GnmValue *value = format_match_number (gtk_entry_get_text (entry), format, NULL);
+
+ *the_float = 0.0;
+ if (!value)
+ return TRUE;
+
+ if (!VALUE_IS_NUMBER (value)) {
+ value_release (value);
+ return TRUE;
+ }
+
+ *the_float = value_get_as_float (value);
+ if (update) {
+ char *tmp = format_value (format, value, NULL, 16, NULL);
+ gtk_entry_set_text (entry, tmp);
+ g_free (tmp);
+ }
+
+ value_release (value);
+ return FALSE;
+}
+
+/**
+ * entry_to_int:
+ * @entry:
+ * @the_int:
+ * @update:
+ *
+ * retrieve an int from an entry field parsing all reasonable formats
+ *
+ **/
+gboolean
+entry_to_int (GtkEntry *entry, gint *the_int, gboolean update)
+{
+ GnmValue *value = format_match_number (gtk_entry_get_text (entry), NULL, NULL);
+
+ *the_int = 0;
+ if (!value)
+ return TRUE;
+
+ if (value->type != VALUE_INTEGER) {
+ value_release (value);
+ return TRUE;
+ }
+
+ *the_int = value_get_as_int (value);
+ if (update) {
+ char *tmp = format_value (NULL, value, NULL, 16, NULL);
+ gtk_entry_set_text (entry, tmp);
+ g_free (tmp);
+ }
+
+ value_release (value);
+ return FALSE;
+}
+
+/**
+ * float_to_entry:
+ * @entry:
+ * @the_float:
+ *
+ **/
+void
+float_to_entry (GtkEntry *entry, gnm_float the_float)
+{
+ GnmValue *val = value_new_float (the_float);
+ char *text = format_value (NULL, val, NULL, 16, NULL);
+ value_release(val);
+ if (text != NULL) {
+ gtk_entry_set_text (entry, text);
+ g_free (text);
+ }
+}
+
+/**
+ * int_to_entry:
+ * @entry:
+ * @the_float:
+ *
+ *
+ **/
+void
+int_to_entry (GtkEntry *entry, gint the_int)
+{
+ GnmValue *val = value_new_int (the_int);
+ char *text = format_value (NULL, val, NULL, 16, NULL);
+ value_release(val);
+ if (text != NULL) {
+ gtk_entry_set_text (entry, text);
+ g_free (text);
+ }
+}
+
+char *
+gnumeric_icondir (char const *filename)
+{
+ //return g_build_filename (gnumeric_icon_dir, filename, NULL);
+ return g_build_filename( "FIXME-icondir", filename, NULL );
+}
+
+GtkWidget *
+gnumeric_load_image (char const *filename)
+{
+ char *path = gnumeric_icondir (filename);
+ GtkWidget *image = gtk_image_new_from_file (path);
+ g_free (path);
+
+ if (image)
+ gtk_widget_show (image);
+
+ return image;
+}
+
+/**
+ * gnumeric_load_pixbuf : utility routine to create pixbufs from file named @name.
+ * looking in the gnumeric icondir.
+ **/
+GdkPixbuf *
+gnumeric_load_pixbuf (char const *filename)
+{
+ char *path = gnumeric_icondir (filename);
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+ g_free (path);
+ return pixbuf;
+}
+
+
+/**
+ * gnm_pixbuf_tile: created a pixbuf consistent of the source pixbuf tiled
+ * enough times to fill out the space needed.
+ *
+ * The fractional tiles are spead evenly left-right and top-bottom.
+ */
+GdkPixbuf *
+gnm_pixbuf_tile (const GdkPixbuf *src, int w, int h)
+{
+ int src_w = gdk_pixbuf_get_width (src);
+ int src_h = gdk_pixbuf_get_height (src);
+
+ int tile_x = w / src_w; /* Number of full tiles */
+ int tile_y = h / src_h;
+
+ int left_x = w - tile_x * src_w;
+ int left_y = h - tile_y * src_h;
+
+ int dst_y = 0;
+ int stripe_y;
+ GdkPixbuf *dst = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
+ gdk_pixbuf_get_has_alpha (src),
+ gdk_pixbuf_get_bits_per_sample (src),
+ w, h);
+
+ for (stripe_y = -1; stripe_y <= tile_y; stripe_y++) {
+ int dst_x = 0;
+ int stripe_x;
+ int this_h, start_y = 0;
+
+ if (stripe_y == -1) {
+ this_h = (left_y + 1) / 2;
+ start_y = src_h - this_h;
+ } else if (stripe_y == tile_y)
+ this_h = left_y / 2;
+ else
+ this_h = src_h;
+
+ if (this_h == 0)
+ continue;
+
+ for (stripe_x = -1; stripe_x <= tile_x; stripe_x++) {
+ int this_w, start_x = 0;
+
+ if (stripe_x == -1) {
+ this_w = (left_x + 1) / 2;
+ start_x = src_w - this_w;
+ } else if (stripe_x == tile_x)
+ this_w = left_x / 2;
+ else
+ this_w = src_w;
+
+ if (this_w == 0)
+ continue;
+
+ gdk_pixbuf_copy_area (src, start_x, start_y,
+ this_w, this_h,
+ dst,
+ dst_x, dst_y);
+
+ dst_x += this_w;
+ }
+
+ dst_y += this_h;
+ }
+
+ return dst;
+}
+
+
+static void
+add_atk_relation (GtkWidget *w0, GtkWidget *w1, AtkRelationType type)
+{
+ AtkObject *atk0 = gtk_widget_get_accessible(w0);
+ AtkObject *atk1 = gtk_widget_get_accessible(w1);
+ AtkRelationSet *relation_set = atk_object_ref_relation_set (atk0);
+ AtkRelation *relation = atk_relation_new (&atk1, 1, type);
+ atk_relation_set_add (relation_set, relation);
+ g_object_unref (relation_set);
+ g_object_unref (relation);
+}
+
+/**
+ * gnm_setup_label_atk :
+ * @label : #GtkWidget
+ * @target : #GtkWidget
+ *
+ * A convenience routine to setup label-for/labeled-by relationship between a
+ * pair of widgets
+ **/
+void
+gnm_setup_label_atk (GtkWidget *label, GtkWidget *target)
+{
+ add_atk_relation (label, target, ATK_RELATION_LABEL_FOR);
+ add_atk_relation (target, label, ATK_RELATION_LABELLED_BY);
+}
+
+
+int
+gnm_measure_string (PangoContext *context, const PangoFontDescription *font_desc, const char *str)
+{
+ PangoLayout *layout = pango_layout_new (context);
+ int width;
+
+ pango_layout_set_text (layout, str, -1);
+ pango_layout_set_font_description (layout, font_desc);
+ pango_layout_get_pixel_size (layout, &width, NULL);
+
+ g_object_unref (layout);
+
+ return width;
+}
+
+static void
+cb_focus_to_entry (GtkWidget *button, GtkWidget *entry)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
+ gtk_widget_grab_focus (entry);
+}
+
+static gboolean
+cb_activate_button (GtkWidget *button)
+{
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+ return FALSE;
+}
+
+void
+gnm_link_button_and_entry (GtkWidget *button, GtkWidget *entry)
+{
+ g_signal_connect (G_OBJECT (button),
+ "clicked", G_CALLBACK (cb_focus_to_entry),
+ entry);
+ g_signal_connect_swapped (G_OBJECT (entry),
+ "focus_in_event",
+ G_CALLBACK (cb_activate_button),
+ button);
+}
+
+/* ------------------------------------------------------------------------- */
+
+void
+gnm_widget_set_cursor (GtkWidget *w, GdkCursor *cursor)
+{
+ gdk_window_set_cursor (w->window, cursor);
+}
+
+void
+gnm_widget_set_cursor_type (GtkWidget *w, GdkCursorType ct)
+{
+ GdkDisplay *display = gdk_drawable_get_display (w->window);
+ GdkCursor *cursor = gdk_cursor_new_for_display (display, ct);
+ gnm_widget_set_cursor (w, cursor);
+ gdk_cursor_unref (cursor);
+}
+
+GdkCursor *
+gnm_fat_cross_cursor (GdkDisplay *display)
+{
+ /* We don't actually own a ref, but that's ok. */
+ static GdkPixbuf *pixbuf = NULL;
+
+ if (!pixbuf)
+ pixbuf = gnm_app_get_pixbuf ("cursor_cross");
+
+ return gdk_cursor_new_from_pixbuf (display, pixbuf, 17, 17);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * gnumeric_button_new_with_stock_image
+ *
+ * Code from gedit
+ *
+ * Creates a new GtkButton with custom label and stock image.
+ *
+ * text : button label
+ * sotck_id : id for stock icon
+ *
+ * return : newly created button
+ *
+ **/
+
+GtkWidget*
+gnumeric_button_new_with_stock_image (const gchar* text, const gchar* stock_id)
+{
+ GtkWidget *button;
+ GtkStockItem item;
+ GtkWidget *label;
+ GtkWidget *image;
+ GtkWidget *hbox;
+ GtkWidget *align;
+
+ button = gtk_button_new ();
+
+ if (GTK_BIN (button)->child)
+ gtk_container_remove (GTK_CONTAINER (button),
+ GTK_BIN (button)->child);
+
+ if (gtk_stock_lookup (stock_id, &item)) {
+ label = gtk_label_new_with_mnemonic (text);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+ hbox = gtk_hbox_new (FALSE, 2);
+
+ align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ gtk_container_add (GTK_CONTAINER (button), align);
+ gtk_container_add (GTK_CONTAINER (align), hbox);
+ gtk_widget_show_all (align);
+
+ return button;
+ }
+
+ label = gtk_label_new_with_mnemonic (text);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
+
+ gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
+
+ gtk_widget_show (label);
+ gtk_container_add (GTK_CONTAINER (button), label);
+
+ return button;
+}
+
+/**
+ * gnumeric_dialog_add_button
+ *
+ * Code from gedit
+ *
+ * Creates and adds a button with stock image to the action area of an existing dialog.
+ *
+ * dialog : dialog you want to add a button
+ * text : button label
+ * sotck_id : stock icon id
+ * response_id : respond id when button clicked
+ *
+ * return : newly created button
+ *
+ **/
+
+GtkWidget*
+gnumeric_dialog_add_button (GtkDialog *dialog, const gchar* text, const gchar* stock_id,
+ gint response_id)
+{
+ GtkWidget *button;
+
+ g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
+ g_return_val_if_fail (text != NULL, NULL);
+ g_return_val_if_fail (stock_id != NULL, NULL);
+
+ button = gnumeric_button_new_with_stock_image (text, stock_id);
+ g_return_val_if_fail (button != NULL, NULL);
+
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+
+ gtk_widget_show (button);
+
+ gtk_dialog_add_action_widget (dialog, button, response_id);
+
+ return button;
+}
+
+/**
+ * gnumeric_message_dialog_new :
+ *
+ * A convenience fonction to build HIG compliant message dialogs.
+ *
+ * parent : transient parent, or NULL for none.
+ * flags
+ * type : type of dialog
+ * primary_message : message displayed in bold
+ * secondary_message : message displayed below
+ *
+ * return : a GtkDialog, without buttons.
+ **/
+
+GtkWidget *
+gnumeric_message_dialog_new (GtkWindow * parent,
+ GtkDialogFlags flags,
+ GtkMessageType type,
+ gchar const * primary_message,
+ gchar const * secondary_message)
+{
+ GtkWidget * dialog;
+ GtkWidget * label;
+ GtkWidget * image;
+ GtkWidget * hbox;
+ gchar * message;
+ const gchar *stock_id = NULL;
+ GtkStockItem item;
+
+ dialog = gtk_dialog_new_with_buttons ("", parent, flags, NULL);
+
+ if (dialog) {
+ image = gtk_image_new ();
+
+ switch (type) {
+ case GTK_MESSAGE_INFO:
+ stock_id = GTK_STOCK_DIALOG_INFO;
+ break;
+
+ case GTK_MESSAGE_QUESTION:
+ stock_id = GTK_STOCK_DIALOG_QUESTION;
+ break;
+
+ case GTK_MESSAGE_WARNING:
+ stock_id = GTK_STOCK_DIALOG_WARNING;
+ break;
+
+ case GTK_MESSAGE_ERROR:
+ stock_id = GTK_STOCK_DIALOG_ERROR;
+ break;
+
+ default:
+ g_warning ("Unknown GtkMessageType %d", type);
+ break;
+ }
+
+ if (stock_id == NULL)
+ stock_id = GTK_STOCK_DIALOG_INFO;
+
+ if (gtk_stock_lookup (stock_id, &item)) {
+ gtk_image_set_from_stock (GTK_IMAGE (image), stock_id,
+ GTK_ICON_SIZE_DIALOG);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), item.label);
+ } else
+ g_warning ("Stock dialog ID doesn't exist?");
+
+ if (primary_message) {
+ if (secondary_message) {
+ message = g_strdup_printf ("<b>%s</b>\n\n%s",
+ primary_message,
+ secondary_message);
+ } else {
+ message = g_strdup_printf ("<b>%s</b>",
+ primary_message);
+ }
+ } else {
+ message = g_strdup_printf (secondary_message);
+ }
+ label = gtk_label_new (message);
+ g_free (message);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
+ gtk_box_pack_start_defaults (GTK_BOX (hbox),
+ label);
+ gtk_box_pack_start_defaults (GTK_BOX (GTK_DIALOG (dialog)->vbox),
+ hbox);
+
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0 , 0.0);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0 , 0.0);
+ gtk_box_set_spacing (GTK_BOX (hbox), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+ gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_window_set_resizable (GTK_WINDOW(dialog), FALSE);
+ gtk_widget_show_all (GTK_WIDGET (GTK_DIALOG (dialog)->vbox));
+ }
+
+ return dialog;
+}
+
+GdkPixbuf*
+gnm_pixbuf_intelligent_scale (GdkPixbuf *buf, guint width, guint height)
+{
+ GdkPixbuf *scaled;
+ int w, h;
+ unsigned long int ow = gdk_pixbuf_get_width (buf);
+ unsigned long int oh = gdk_pixbuf_get_height (buf);
+
+ if (ow <= width && oh <= height)
+ scaled = g_object_ref (buf);
+ else
+ {
+ if (ow * height > oh * width)
+ {
+ w = width;
+ h = width * (((double)oh)/(double)ow);
+ }
+ else
+ {
+ h = height;
+ w = height * (((double)ow)/(double)oh);
+ }
+
+ scaled = gdk_pixbuf_scale_simple (buf, w, h, GDK_INTERP_BILINEAR);
+ }
+
+ return scaled;
+}
+
+void
+gnm_widget_disable_focus (GtkWidget *w)
+{
+ if (GTK_IS_CONTAINER (w))
+ gtk_container_foreach (GTK_CONTAINER (w),
+ (GtkCallback) gnm_widget_disable_focus, NULL);
+ GTK_WIDGET_UNSET_FLAGS (w, GTK_CAN_FOCUS);
+}
+
+
+gboolean
+gnm_tree_model_iter_prev (GtkTreeModel *model, GtkTreeIter* iter)
+{
+ GtkTreePath *path = gtk_tree_model_get_path (model, iter);
+
+ if (gtk_tree_path_prev (path) &&
+ gtk_tree_model_get_iter (model, iter, path)) {
+ gtk_tree_path_free (path);
+ return TRUE;
+ }
+ gtk_tree_path_free (path);
+ return FALSE;
+}
--- /dev/null
+++ lib/goffice/split/mstyle.h
@@ -0,0 +1,158 @@
+#ifndef GNUMERIC_MSTYLE_H
+#define GNUMERIC_MSTYLE_H
+
+#include "gnumeric.h"
+#include "style.h"
+
+/*
+ * Keep element_size up to date.
+ */
+typedef enum {
+ /* Delimiter */
+ MSTYLE_ELEMENT_UNSET = 0,
+ /* When there is a conflict in a merge */
+ MSTYLE_ELEMENT_CONFLICT,
+ /* Types that are visible in blank cells */
+ MSTYLE_COLOR_BACK,
+ MSTYLE_COLOR_PATTERN,
+
+ MSTYLE_BORDER_TOP,
+ MSTYLE_BORDER_BOTTOM,
+ MSTYLE_BORDER_LEFT,
+ MSTYLE_BORDER_RIGHT,
+ MSTYLE_BORDER_REV_DIAGONAL,
+ MSTYLE_BORDER_DIAGONAL,
+
+ MSTYLE_PATTERN,
+ /* Delimiter */
+ MSTYLE_ELEMENT_MAX_BLANK,
+ /* Normal types */
+ MSTYLE_COLOR_FORE,
+ MSTYLE_FONT_NAME,
+ MSTYLE_FONT_BOLD,
+ MSTYLE_FONT_ITALIC,
+ MSTYLE_FONT_UNDERLINE,
+ MSTYLE_FONT_STRIKETHROUGH,
+ MSTYLE_FONT_SIZE,
+
+ MSTYLE_FORMAT,
+
+ MSTYLE_ALIGN_V,
+ MSTYLE_ALIGN_H,
+ MSTYLE_INDENT,
+ MSTYLE_ROTATION,
+ MSTYLE_WRAP_TEXT,
+ MSTYLE_SHRINK_TO_FIT,
+
+ MSTYLE_CONTENT_LOCKED,
+ MSTYLE_CONTENT_HIDDEN,
+
+ /* Things not in MS Excel's Style */
+ MSTYLE_VALIDATION,
+ MSTYLE_HLINK, /* patch equal_XL if this is changed */
+ MSTYLE_INPUT_MSG, /* patch equal_XL if this is changed */
+ /* Delimiter */
+ MSTYLE_ELEMENT_MAX
+} MStyleElementType;
+
+GnmStyle *mstyle_new (void);
+GnmStyle *mstyle_new_default (void);
+GnmStyle *mstyle_copy (const GnmStyle *st);
+GnmStyle *mstyle_copy_merge (const GnmStyle *orig, const GnmStyle *overlay);
+void mstyle_ref (GnmStyle *st);
+void mstyle_unref (GnmStyle *st);
+
+GnmStyle *mstyle_link_sheet (GnmStyle *st, Sheet *sheet);
+void mstyle_link (GnmStyle *st);
+void mstyle_link_multiple (GnmStyle *st, int count);
+void mstyle_unlink (GnmStyle *st);
+
+gboolean mstyle_equal (GnmStyle const *a, GnmStyle const *b);
+gboolean mstyle_equal_XL (GnmStyle const *a, GnmStyle const *b);
+gboolean mstyle_verify (GnmStyle const *st);
+guint mstyle_hash (gconstpointer st);
+guint mstyle_hash_XL (gconstpointer st);
+gboolean mstyle_empty (const GnmStyle *st);
+
+/*
+ * Wafer thin element access functions.
+ */
+gboolean mstyle_is_element_set (const GnmStyle *st, MStyleElementType t);
+gboolean mstyle_is_element_conflict (const GnmStyle *st, MStyleElementType t);
+void mstyle_compare (GnmStyle *a, const GnmStyle *b);
+void mstyle_unset_element (GnmStyle *st, MStyleElementType t);
+void mstyle_replace_element (GnmStyle *src, GnmStyle *dst, MStyleElementType t);
+void mstyle_set_color (GnmStyle *st, MStyleElementType t,
+ GnmColor *col);
+GnmColor *mstyle_get_color (const GnmStyle *st, MStyleElementType t);
+void mstyle_set_border (GnmStyle *st, MStyleElementType t,
+ GnmBorder *border);
+GnmBorder *mstyle_get_border (const GnmStyle *st, MStyleElementType t);
+void mstyle_set_pattern (GnmStyle *st, int pattern);
+int mstyle_get_pattern (const GnmStyle *st);
+void mstyle_set_font_name (GnmStyle *st, const char *name);
+const char *mstyle_get_font_name (const GnmStyle *st);
+void mstyle_set_font_bold (GnmStyle *st, gboolean bold);
+gboolean mstyle_get_font_bold (const GnmStyle *st);
+void mstyle_set_font_italic (GnmStyle *st, gboolean italic);
+gboolean mstyle_get_font_italic (const GnmStyle *st);
+void mstyle_set_font_uline (GnmStyle *st, StyleUnderlineType const t);
+StyleUnderlineType mstyle_get_font_uline (const GnmStyle *st);
+void mstyle_set_font_strike (GnmStyle *st, gboolean strikethrough);
+gboolean mstyle_get_font_strike (const GnmStyle *st);
+void mstyle_set_font_size (GnmStyle *st, double size);
+double mstyle_get_font_size (const GnmStyle *st);
+
+/* this font must be unrefd after use */
+GnmFont *mstyle_get_font (const GnmStyle *st,
+ PangoContext *context,
+ double zoom);
+void mstyle_set_format (GnmStyle *st, GnmFormat *);
+void mstyle_set_format_text (GnmStyle *st, const char *format);
+GnmFormat *mstyle_get_format (const GnmStyle *st);
+void mstyle_set_align_h (GnmStyle *st, StyleHAlignFlags a);
+StyleHAlignFlags mstyle_get_align_h (const GnmStyle *st);
+void mstyle_set_align_v (GnmStyle *st, StyleVAlignFlags a);
+StyleVAlignFlags mstyle_get_align_v (const GnmStyle *st);
+void mstyle_set_indent (GnmStyle *st, int i);
+int mstyle_get_indent (const GnmStyle *st);
+
+void mstyle_set_rotation (GnmStyle *st, int r);
+int mstyle_get_rotation (const GnmStyle *st);
+
+void mstyle_set_wrap_text (GnmStyle *st, gboolean f);
+gboolean mstyle_get_wrap_text (const GnmStyle *st);
+gboolean mstyle_get_effective_wrap_text (const GnmStyle *st);
+void mstyle_set_shrink_to_fit (GnmStyle *st, gboolean f);
+gboolean mstyle_get_shrink_to_fit (const GnmStyle *st);
+
+void mstyle_set_content_locked (GnmStyle *st, gboolean f);
+gboolean mstyle_get_content_locked (const GnmStyle *st);
+void mstyle_set_content_hidden (GnmStyle *st, gboolean f);
+gboolean mstyle_get_content_hidden (const GnmStyle *st);
+
+void mstyle_set_validation (GnmStyle *st, GnmValidation *v);
+GnmValidation *mstyle_get_validation (const GnmStyle *st);
+
+void mstyle_set_hlink (GnmStyle *st, GnmHLink *link);
+GnmHLink *mstyle_get_hlink (const GnmStyle *st);
+
+void mstyle_set_input_msg (GnmStyle *st, GnmInputMsg *msg);
+GnmInputMsg *mstyle_get_input_msg (const GnmStyle *st);
+
+gboolean mstyle_visible_in_blank (const GnmStyle *st);
+
+PangoAttrList *mstyle_generate_attrs_full (const GnmStyle *st);
+PangoAttrList *mstyle_get_pango_attrs (const GnmStyle *st,
+ PangoContext *context,
+ double zoom);
+void mstyle_set_from_pango_attribute (GnmStyle *style,
+ PangoAttribute const *attr);
+
+char *mstyle_to_string (const GnmStyle *st); /* Debug only ! leaks like a sieve */
+void mstyle_dump (const GnmStyle *st);
+
+void mstyle_init (void);
+void mstyle_shutdown (void);
+
+#endif /* GNUMERIC_MSTYLE_H */
--- /dev/null
+++ lib/goffice/split/str.c
@@ -0,0 +1,138 @@
+/*
+ * String management for the Gnumeric Spreadsheet
+ *
+ * Author:
+ * Miguel de Icaza (miguel at kernel.org)
+ */
+#include <config.h>
+#include "gnumeric.h"
+#include "str.h"
+#include "gutils.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifndef USE_STRING_POOLS
+#define USE_STRING_POOLS 1
+#endif
+
+#if USE_STRING_POOLS
+/* Memory pool for strings. */
+static GnmMemChunk *string_pool;
+#define CHUNK_ALLOC(T,p) ((T*)gnm_mem_chunk_alloc (p))
+#define CHUNK_FREE(p,v) gnm_mem_chunk_free ((p), (v))
+#else
+#define CHUNK_ALLOC(T,c) g_new (T,1)
+#define CHUNK_FREE(p,v) g_free ((v))
+#endif
+
+static GHashTable *string_hash_table;
+
+static GnmString *
+gnm_string_lookup (char const *s)
+{
+ g_return_val_if_fail (s != NULL, NULL);
+ return g_hash_table_lookup (string_hash_table, s);
+}
+
+GnmString *
+gnm_string_get (char const *s)
+{
+ GnmString *string = gnm_string_lookup (s);
+ if (string){
+ gnm_string_ref (string);
+ return string;
+ }
+
+ /* If non-existant, create */
+ string = CHUNK_ALLOC (GnmString, string_pool);
+ string->ref_count = 1;
+ string->str = g_strdup (s);
+
+ g_hash_table_insert (string_hash_table, string->str, string);
+
+ return string;
+}
+
+/*
+ * gnm_string_get_nocopy :
+ *
+ * Take control of the supplied string.
+ * delete it if it is already available.
+ */
+GnmString *
+gnm_string_get_nocopy (char *s)
+{
+ GnmString *string = gnm_string_lookup (s);
+ if (string) {
+ gnm_string_ref (string);
+ g_free (s);
+ return string;
+ }
+
+ /* If non-existant, create */
+ string = CHUNK_ALLOC (GnmString, string_pool);
+ string->ref_count = 1;
+ string->str = s;
+
+ g_hash_table_insert (string_hash_table, string->str, string);
+
+ return string;
+}
+
+GnmString *
+gnm_string_ref (GnmString *string)
+{
+ g_return_val_if_fail (string != NULL, NULL);
+
+ string->ref_count++;
+
+ return string;
+}
+
+void
+gnm_string_unref (GnmString *string)
+{
+ g_return_if_fail (string != NULL);
+ g_return_if_fail (string->ref_count > 0);
+
+ if (--(string->ref_count) == 0){
+ g_hash_table_remove (string_hash_table, string->str);
+ g_free (string->str);
+ CHUNK_FREE (string_pool, string);
+ }
+}
+
+void
+gnm_string_init (void)
+{
+ string_hash_table = g_hash_table_new (g_str_hash, g_str_equal);
+#if USE_STRING_POOLS
+ string_pool =
+ gnm_mem_chunk_new ("string pool",
+ sizeof (GnmString),
+ 16 * 1024 - 128);
+#endif
+}
+
+#if USE_STRING_POOLS
+static void
+cb_string_pool_leak (gpointer data, gpointer user)
+{
+ GnmString *string = data;
+ fprintf (stderr, "Leaking string [%s] with ref_count=%d.\n",
+ string->str, string->ref_count);
+}
+#endif
+
+void
+gnm_string_shutdown (void)
+{
+ g_hash_table_destroy (string_hash_table);
+ string_hash_table = NULL;
+#if USE_STRING_POOLS
+ gnm_mem_chunk_foreach_leak (string_pool, cb_string_pool_leak, NULL);
+ gnm_mem_chunk_destroy (string_pool, FALSE);
+ string_pool = NULL;
+#endif
+}
--- /dev/null
+++ lib/goffice/split/io-context-priv.h
@@ -0,0 +1,70 @@
+#ifndef GNUMERIC_IO_CONTEXT_PRIV_H
+#define GNUMERIC_IO_CONTEXT_PRIV_H
+
+#include "gnumeric.h"
+#include "io-context.h"
+#include "error-info.h"
+#include "command-context-priv.h"
+#include <stdio.h>
+
+#define IO_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_IO_CONTEXT, IOContextClass))
+#define IS_IO_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_IO_CONTEXT))
+
+typedef enum {
+ GNM_PROGRESS_HELPER_NONE,
+ GNM_PROGRESS_HELPER_FILE,
+ GNM_PROGRESS_HELPER_MEM,
+ GNM_PROGRESS_HELPER_COUNT,
+ GNM_PROGRESS_HELPER_VALUE,
+ GNM_PROGRESS_HELPER_WORKBOOK,
+ GNM_PROGRESS_HELPER_LAST
+} GnmProgressHelperType;
+
+typedef struct {
+ GnmProgressHelperType helper_type;
+ union {
+ struct {
+ gchar *start;
+ gint size;
+ } mem;
+ struct {
+ gint total, last, current;
+ gint step;
+ } count;
+ struct {
+ gint total, last;
+ gint step;
+ } value;
+ struct {
+ gint n_elements, last, current;
+ gint step;
+ } workbook;
+ } v;
+} GnmProgressHelper;
+
+typedef struct {
+ gfloat min, max;
+} ProgressRange;
+
+struct _IOContext {
+ GObject base;
+
+ GnmCmdContext *impl;
+ ErrorInfo *info;
+ gboolean error_occurred;
+ gboolean warning_occurred;
+
+ GList *progress_ranges;
+ gfloat progress_min, progress_max;
+ gdouble last_progress;
+ gdouble last_time;
+ GnmProgressHelper helper;
+};
+
+struct _IOContextClass {
+ GObjectClass base;
+ void (*set_num_files) (IOContext *ioc, guint count);
+ void (*processing_file) (IOContext *ioc, char const *name);
+};
+
+#endif /* GNUMERIC_IO_CONTEXT_PRIV_H */
--- /dev/null
+++ lib/goffice/split/mathfunc.c
@@ -0,0 +1,7327 @@
+/*
+ * mathfunc.c: Mathematical functions.
+ *
+ * Authors:
+ * Ross Ihaka. (See note 1.)
+ * The R Development Core Team. (See note 1.)
+ * Morten Welinder <terra at gnome.org>
+ * Miguel de Icaza (miguel at gnu.org)
+ * Jukka-Pekka Iivonen (iivonen at iki.fi)
+ * James Theiler. (See note 2.)
+ * Brian Gough. (See note 2.)
+ * Makoto Matsumoto and Takuji Nishimura (Mersenne Twister, see note in code)
+ * Ian Smith (iandjmsmith at aol.com). (See note 3.)
+ */
+
+/*
+ * NOTE 1: most of this file comes from the "R" package, notably version 2
+ * or newer (we re-sync from time to time).
+ * "R" is distributed under GPL licence, see file COPYING.
+ * The relevant parts are copyright (C) 1998 Ross Ihaka and
+ * 2000-2004 The R Development Core Team.
+ *
+ * Thank you!
+ */
+
+/*
+ * NOTE 2: most of the random distribution code comes from the GNU Scientific
+ * Library (GSL), notably version 1.1.1. GSL is distributed under GPL licence,
+ * see COPYING. The relevant parts are copyright (C) 1996, 1997, 1998, 1999,
+ * 2000 James Theiler and Brian Gough.
+ *
+ * Thank you!
+ */
+
+/*
+ * NOTE 3: the pbeta (and support) code comes from Ian Smith. (Translated
+ * into C, adapted to Gnumeric naming convensions, and R's API conventions
+ * by Morten Welinder. Blame me for problems.)
+ *
+ * Copyright © Ian Smith 2002-2003
+ * Version 1.0.24
+ * Thanks to Jerry W. Lewis for help with testing of and improvements to the code.
+ *
+ * Thank you!
+ */
+
+
+/* for random() */
+#define _SVID_SOURCE 1
+#define _BSD_SOURCE 1
+
+#include <config.h>
+#include "gnumeric.h"
+#include "mathfunc.h"
+
+#include <math.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <float.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <locale.h>
+#include <string.h>
+#include <goffice/utils/go-math.h>
+
+#if defined (HAVE_IEEEFP_H) || defined (HAVE_IEEE754_H)
+/* Make sure we have this symbol defined, since the existance of either
+ header file implies it. */
+#ifndef IEEE_754
+#define IEEE_754
+#endif
+#endif
+
+#define M_LN_SQRT_2PI GNM_const(0.918938533204672741780329736406) /* log(sqrt(2*pi)) */
+#define M_SQRT_32 GNM_const(5.656854249492380195206754896838) /* sqrt(32) */
+#define M_1_SQRT_2PI GNM_const(0.398942280401432677939946059934) /* 1/sqrt(2pi) */
+#define M_SQRT_2dPI GNM_const(0.797884560802865355879892119869) /* sqrt(2/pi) */
+#define M_2PIgnum (2 * M_PIgnum)
+#define M_Egnum GNM_const(2.718281828459045235360287471352662497757247)
+
+#define ML_UNDERFLOW (GNUM_EPSILON * GNUM_EPSILON)
+#define ML_ERROR(cause) /* Nothing */
+#define MATHLIB_ERROR g_error
+#define MATHLIB_WARNING g_warning
+#define MATHLIB_WARNING2 g_warning
+#define MATHLIB_WARNING4 g_warning
+
+static inline gnm_float fmin2 (gnm_float x, gnm_float y) { return MIN (x, y); }
+static inline gnm_float fmax2 (gnm_float x, gnm_float y) { return MAX (x, y); }
+static inline int imin2 (int x, int y) { return MIN (x, y); }
+static inline int imax2 (int x, int y) { return MAX (x, y); }
+
+#define MATHLIB_STANDALONE
+#define ML_ERR_return_NAN { return gnm_nan; }
+static void pnorm_both (gnm_float x, gnm_float *cum, gnm_float *ccum, int i_tail, gboolean log_p);
+
+#define SQR(x) ((x)*(x))
+/* Scale factor for continued fractions. ==2^256. */
+static const gnm_float scalefactor = SQR(SQR(SQR(GNM_const(4294967296.0))));
+#undef SQR
+
+/* MW ---------------------------------------------------------------------- */
+
+gnm_float gnm_nan;
+gnm_float gnm_pinf;
+gnm_float gnm_ninf;
+
+void
+mathfunc_init (void)
+{
+ const char *bug_url = "http://bugzilla.gnome.org/enter_bug.cgi?product=gnumeric";
+
+ gnm_pinf = go_pinf;
+ if (finitegnum (gnm_pinf) || !(gnm_pinf > 0)) {
+ g_error ("Failed to generate +Inf. Please report at %s",
+ bug_url);
+ abort ();
+ }
+
+ gnm_ninf = go_ninf;
+ if (finitegnum (gnm_ninf) || !(gnm_ninf < 0)) {
+ g_error ("Failed to generate -Inf. Please report at %s",
+ bug_url);
+ abort ();
+ }
+
+ gnm_nan = go_nan;
+ if (!isnangnum (gnm_nan)) {
+ g_error ("Failed to generate NaN. Please report at %s",
+ bug_url);
+ abort ();
+ }
+}
+
+/*
+ * In preparation for truncation, make the value a tiny bit larger (seen
+ * absolutely). This makes ROUND (etc.) behave a little closer to what
+ * people want, even if it is a bit bogus.
+ */
+gnm_float
+gnumeric_add_epsilon (gnm_float x)
+{
+ if (!finitegnum (x) || x == 0)
+ return x;
+ else {
+ int exp;
+ gnm_float mant = frexpgnum (gnumabs (x), &exp);
+ gnm_float absres = ldexpgnum (mant + GNUM_EPSILON, exp);
+ return (x < 0) ? -absres : absres;
+ }
+}
+
+gnm_float
+gnumeric_sub_epsilon (gnm_float x)
+{
+ if (!finitegnum (x) || x == 0)
+ return x;
+ else {
+ int exp;
+ gnm_float mant = frexpgnum (gnumabs (x), &exp);
+ gnm_float absres = ldexpgnum (mant - GNUM_EPSILON, exp);
+ return (x < 0) ? -absres : absres;
+ }
+}
+
+gnm_float
+gnumeric_fake_floor (gnm_float x)
+{
+ return floorgnum (gnumeric_add_epsilon (x));
+}
+
+gnm_float
+gnumeric_fake_ceil (gnm_float x)
+{
+ return ceilgnum (gnumeric_sub_epsilon (x));
+}
+
+gnm_float
+gnumeric_fake_round (gnm_float x)
+{
+ return (x >= 0)
+ ? gnumeric_fake_floor (x + 0.5)
+ : -gnumeric_fake_floor (-x + 0.5);
+}
+
+gnm_float
+gnumeric_fake_trunc (gnm_float x)
+{
+ return (x >= 0)
+ ? gnumeric_fake_floor (x)
+ : -gnumeric_fake_floor (-x);
+}
+
+/* ------------------------------------------------------------------------- */
+/* --- BEGIN MAGIC R SOURCE MARKER --- */
+
+/* The following source code was imported from the R project. */
+/* It was automatically transformed by tools/import-R. */
+
+/* Imported src/nmath/dpq.h from R. */
+ /* Utilities for `dpq' handling (density/probability/quantile) */
+
+/* give_log in "d"; log_p in "p" & "q" : */
+#define give_log log_p
+ /* "DEFAULT" */
+ /* --------- */
+#define R_D__0 (log_p ? gnm_ninf : 0.) /* 0 */
+#define R_D__1 (log_p ? 0. : 1.) /* 1 */
+#define R_DT_0 (lower_tail ? R_D__0 : R_D__1) /* 0 */
+#define R_DT_1 (lower_tail ? R_D__1 : R_D__0) /* 1 */
+
+#define R_D_Lval(p) (lower_tail ? (p) : (1 - (p))) /* p */
+#define R_D_Cval(p) (lower_tail ? (1 - (p)) : (p)) /* 1 - p */
+
+#define R_D_val(x) (log_p ? loggnum(x) : (x)) /* x in pF(x,..) */
+#define R_D_qIv(p) (log_p ? expgnum(p) : (p)) /* p in qF(p,..) */
+#define R_D_exp(x) (log_p ? (x) : expgnum(x)) /* expgnum(x) */
+#define R_D_log(p) (log_p ? (p) : loggnum(p)) /* loggnum(p) */
+#define R_D_Clog(p) (log_p ? log1pgnum(-(p)) : (1 - (p)))/* [log](1-p) */
+
+/* loggnum(1-expgnum(x)): R_D_LExp(x) == (log1pgnum(- R_D_qIv(x))) but even more stable:*/
+#define R_D_LExp(x) (log_p ? swap_log_tail(x) : log1pgnum(-x))
+
+/*till 1.8.x:
+ * #define R_DT_val(x) R_D_val(R_D_Lval(x))
+ * #define R_DT_Cval(x) R_D_val(R_D_Cval(x)) */
+#define R_DT_val(x) (lower_tail ? R_D_val(x) : R_D_Clog(x))
+#define R_DT_Cval(x) (lower_tail ? R_D_Clog(x) : R_D_val(x))
+
+/*#define R_DT_qIv(p) R_D_Lval(R_D_qIv(p)) * p in qF ! */
+#define R_DT_qIv(p) (log_p ? (lower_tail ? expgnum(p) : - expm1gnum(p)) \
+ : R_D_Lval(p))
+
+/*#define R_DT_CIv(p) R_D_Cval(R_D_qIv(p)) * 1 - p in qF */
+#define R_DT_CIv(p) (log_p ? (lower_tail ? -expm1gnum(p) : expgnum(p)) \
+ : R_D_Cval(p))
+
+#define R_DT_exp(x) R_D_exp(R_D_Lval(x)) /* expgnum(x) */
+#define R_DT_Cexp(x) R_D_exp(R_D_Cval(x)) /* expgnum(1 - x) */
+
+#define R_DT_log(p) (lower_tail? R_D_log(p) : R_D_LExp(p))/* loggnum(p) in qF */
+#define R_DT_Clog(p) (lower_tail? R_D_LExp(p): R_D_log(p))/* log1pgnum (-p) in qF*/
+#define R_DT_Log(p) (lower_tail? (p) : swap_log_tail(p))
+/* == R_DT_log when we already "know" log_p == TRUE :*/
+
+
+#define R_Q_P01_check(p) \
+ if ((log_p && p > 0) || \
+ (!log_p && (p < 0 || p > 1)) ) \
+ ML_ERR_return_NAN
+
+
+/* additions for density functions (C.Loader) */
+#define R_D_fexp(f,x) (give_log ? -0.5*loggnum(f)+(x) : expgnum(x)/sqrtgnum(f))
+#define R_D_forceint(x) floorgnum((x) + 0.5)
+#define R_D_nonint(x) (gnumabs((x) - floorgnum((x)+0.5)) > 1e-7)
+/* [neg]ative or [non int]eger : */
+#define R_D_negInonint(x) (x < 0. || R_D_nonint(x))
+
+#define R_D_nonint_check(x) \
+ if(R_D_nonint(x)) { \
+ MATHLIB_WARNING("non-integer x = %" GNUM_FORMAT_f "", x); \
+ return R_D__0; \
+ }
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/ftrunc.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * SYNOPSIS
+ *
+ * #include <Rmath.h>
+ * double ftrunc(double x);
+ *
+ * DESCRIPTION
+ *
+ * Truncation toward zero.
+ */
+
+
+gnm_float gnm_trunc(gnm_float x)
+{
+ if(x >= 0) return floorgnum(x);
+ else return ceilgnum(x);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dnorm.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ * Copyright (C) 2003 The R Foundation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * SYNOPSIS
+ *
+ * double dnorm4(double x, double mu, double sigma, int give_log)
+ * {dnorm (..) is synonymous and preferred inside R}
+ *
+ * DESCRIPTION
+ *
+ * Compute the density of the normal distribution.
+ */
+
+
+gnm_float dnorm(gnm_float x, gnm_float mu, gnm_float sigma, gboolean give_log)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(mu) || isnangnum(sigma))
+ return x + mu + sigma;
+#endif
+ if(!finitegnum(sigma)) return R_D__0;
+ if(!finitegnum(x) && mu == x) return gnm_nan;/* x-mu is NaN */
+ if (sigma <= 0) {
+ if (sigma < 0) ML_ERR_return_NAN;
+ /* sigma == 0 */
+ return (x == mu) ? gnm_pinf : R_D__0;
+ }
+ x = (x - mu) / sigma;
+
+ if(!finitegnum(x)) return R_D__0;
+ return (give_log ?
+ -(M_LN_SQRT_2PI + 0.5 * x * x + loggnum(sigma)) :
+ M_1_SQRT_2PI * expgnum(-0.5 * x * x) / sigma);
+ /* M_1_SQRT_2PI = 1 / sqrtgnum(2 * pi) */
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/pnorm.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000-2002 The R Development Core Team
+ * Copyright (C) 2003 The R Foundation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * SYNOPSIS
+ *
+ * #include <Rmath.h>
+ *
+ * double pnorm5(double x, double mu, double sigma, int lower_tail,int log_p);
+ * {pnorm (..) is synonymous and preferred inside R}
+ *
+ * void pnorm_both(double x, double *cum, double *ccum,
+ * int i_tail, int log_p);
+ *
+ * DESCRIPTION
+ *
+ * The main computation evaluates near-minimax approximations derived
+ * from those in "Rational Chebyshev approximations for the error
+ * function" by W. J. Cody, Math. Comp., 1969, 631-637. This
+ * transportable program uses rational functions that theoretically
+ * approximate the normal distribution function to at least 18
+ * significant decimal digits. The accuracy achieved depends on the
+ * arithmetic system, the compiler, the intrinsic functions, and
+ * proper selection of the machine-dependent constants.
+ *
+ * REFERENCE
+ *
+ * Cody, W. D. (1993).
+ * ALGORITHM 715: SPECFUN - A Portable FORTRAN Package of
+ * Special Function Routines and Test Drivers".
+ * ACM Transactions on Mathematical Software. 19, 22-32.
+ *
+ * EXTENSIONS
+ *
+ * The "_both" , lower, upper, and log_p variants were added by
+ * Martin Maechler, Jan.2000;
+ * as well as log1p() and similar improvements later on.
+ *
+ * James M. Rath contributed bug report PR#699 and patches correcting SIXTEN
+ * and if() clauses {with a bug: "|| instead of &&" -> PR #2883) more in line
+ * with the original Cody code.
+ */
+
+gnm_float pnorm(gnm_float x, gnm_float mu, gnm_float sigma, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float p, cp;
+
+ /* Note: The structure of these checks has been carefully thought through.
+ * For example, if x == mu and sigma == 0, we get the correct answer 1.
+ */
+#ifdef IEEE_754
+ if(isnangnum(x) || isnangnum(mu) || isnangnum(sigma))
+ return x + mu + sigma;
+#endif
+ if(!finitegnum(x) && mu == x) return gnm_nan;/* x-mu is NaN */
+ if (sigma <= 0) {
+ if(sigma < 0) ML_ERR_return_NAN;
+ /* sigma = 0 : */
+ return (x < mu) ? R_DT_0 : R_DT_1;
+ }
+ p = (x - mu) / sigma;
+ if(!finitegnum(p))
+ return (x < mu) ? R_DT_0 : R_DT_1;
+ x = p;
+
+ pnorm_both(x, &p, &cp, (lower_tail ? 0 : 1), log_p);
+
+ return(lower_tail ? p : cp);
+}
+
+#define SIXTEN 16 /* Cutoff allowing exact "*" and "/" */
+
+void pnorm_both(gnm_float x, gnm_float *cum, gnm_float *ccum, int i_tail, gboolean log_p)
+{
+/* i_tail in {0,1,2} means: "lower", "upper", or "both" :
+ if(lower) return *cum := P[X <= x]
+ if(upper) return *ccum := P[X > x] = 1 - P[X <= x]
+*/
+ static const gnm_float a[5] = {
+ GNM_const(2.2352520354606839287),
+ GNM_const(161.02823106855587881),
+ GNM_const(1067.6894854603709582),
+ GNM_const(18154.981253343561249),
+ GNM_const(0.065682337918207449113)
+ };
+ static const gnm_float b[4] = {
+ GNM_const(47.20258190468824187),
+ GNM_const(976.09855173777669322),
+ GNM_const(10260.932208618978205),
+ GNM_const(45507.789335026729956)
+ };
+ static const gnm_float c[9] = {
+ GNM_const(0.39894151208813466764),
+ GNM_const(8.8831497943883759412),
+ GNM_const(93.506656132177855979),
+ GNM_const(597.27027639480026226),
+ GNM_const(2494.5375852903726711),
+ GNM_const(6848.1904505362823326),
+ GNM_const(11602.651437647350124),
+ GNM_const(9842.7148383839780218),
+ GNM_const(1.0765576773720192317e-8)
+ };
+ static const gnm_float d[8] = {
+ GNM_const(22.266688044328115691),
+ GNM_const(235.38790178262499861),
+ GNM_const(1519.377599407554805),
+ GNM_const(6485.558298266760755),
+ GNM_const(18615.571640885098091),
+ GNM_const(34900.952721145977266),
+ GNM_const(38912.003286093271411),
+ GNM_const(19685.429676859990727)
+ };
+ static const gnm_float p[6] = {
+ GNM_const(0.21589853405795699),
+ GNM_const(0.1274011611602473639),
+ GNM_const(0.022235277870649807),
+ GNM_const(0.001421619193227893466),
+ GNM_const(2.9112874951168792e-5),
+ GNM_const(0.02307344176494017303)
+ };
+ static const gnm_float q[5] = {
+ GNM_const(1.28426009614491121),
+ GNM_const(0.468238212480865118),
+ GNM_const(0.0659881378689285515),
+ GNM_const(0.00378239633202758244),
+ GNM_const(7.29751555083966205e-5)
+ };
+
+ gnm_float xden, xnum, temp, del, eps, xsq, y;
+#ifdef NO_DENORMS
+ gnm_float min = GNUM_MIN;
+#endif
+ int i, lower, upper;
+
+#ifdef IEEE_754
+ if(isnangnum(x)) { *cum = *ccum = x; return; }
+#endif
+
+ /* Consider changing these : */
+ eps = GNUM_EPSILON * 0.5;
+
+ /* i_tail in {0,1,2} =^= {lower, upper, both} */
+ lower = i_tail != 1;
+ upper = i_tail != 0;
+
+ y = gnumabs(x);
+ if (y <= 0.67448975) { /* qnorm(3/4) = .6744.... -- earlier had 0.66291 */
+ if (y > eps) {
+ xsq = x * x;
+ xnum = a[4] * xsq;
+ xden = xsq;
+ for (i = 0; i < 3; ++i) {
+ xnum = (xnum + a[i]) * xsq;
+ xden = (xden + b[i]) * xsq;
+ }
+ } else xnum = xden = 0.0;
+
+ temp = x * (xnum + a[3]) / (xden + b[3]);
+ if(lower) *cum = 0.5 + temp;
+ if(upper) *ccum = 0.5 - temp;
+ if(log_p) {
+ if(lower) *cum = loggnum(*cum);
+ if(upper) *ccum = loggnum(*ccum);
+ }
+ }
+ else if (y <= M_SQRT_32) {
+
+ /* Evaluate pnorm for 0.674.. = qnorm(3/4) < |x| <= sqrtgnum(32) ~= 5.657 */
+
+ xnum = c[8] * y;
+ xden = y;
+ for (i = 0; i < 7; ++i) {
+ xnum = (xnum + c[i]) * y;
+ xden = (xden + d[i]) * y;
+ }
+ temp = (xnum + c[7]) / (xden + d[7]);
+
+#define do_del(X) \
+ xsq = gnm_trunc(X * SIXTEN) / SIXTEN; \
+ del = (X - xsq) * (X + xsq); \
+ if(log_p) { \
+ *cum = (-xsq * xsq * 0.5) + (-del * 0.5) + loggnum(temp); \
+ if((lower && x > 0.) || (upper && x <= 0.)) \
+ *ccum = log1pgnum(-expgnum(-xsq * xsq * 0.5) * \
+ expgnum(-del * 0.5) * temp); \
+ } \
+ else { \
+ *cum = expgnum(-xsq * xsq * 0.5) * expgnum(-del * 0.5) * temp; \
+ *ccum = 1.0 - *cum; \
+ }
+
+#define swap_tail \
+ if (x > 0.) {/* swap ccum <--> cum */ \
+ temp = *cum; if(lower) *cum = *ccum; *ccum = temp; \
+ }
+
+ do_del(y);
+ swap_tail;
+ }
+
+/* else |x| > sqrtgnum(32) = 5.657 :
+ * the next two case differentiations were really for lower=T, log=F
+ * Particularly *not* for log_p !
+
+ * Cody had (-37.5193 < x && x < 8.2924) ; R originally had y < 50
+ *
+ * Note that we do want symmetry(0), lower/upper -> hence use y
+ */
+ else if(log_p
+ /* ^^^^^ MM FIXME: can speedup for log_p and much larger |x| !
+ * Then, make use of Abramowitz & Stegun, 26.2.13, something like
+
+ xsq = x*x;
+
+ if(xsq * GNUM_EPSILON < 1.)
+ del = (1. - (1. - 5./(xsq+6.)) / (xsq+4.)) / (xsq+2.);
+ else
+ del = 0.;
+ *cum = -.5*xsq - M_LN_SQRT_2PI - loggnum(x) + log1pgnum(-del);
+ *ccum = log1pgnum(-expgnum(*cum)); /.* ~ loggnum(1) = 0 *./
+
+ swap_tail;
+
+ */
+ || (lower && -37.5193 < x && x < 8.2924)
+ || (upper && -8.2924 < x && x < 37.5193)
+ ) {
+
+ /* Evaluate pnorm for x in (-37.5, -5.657) union (5.657, 37.5) */
+ xsq = 1.0 / (x * x);
+ xnum = p[5] * xsq;
+ xden = xsq;
+ for (i = 0; i < 4; ++i) {
+ xnum = (xnum + p[i]) * xsq;
+ xden = (xden + q[i]) * xsq;
+ }
+ temp = xsq * (xnum + p[4]) / (xden + q[4]);
+ temp = (M_1_SQRT_2PI - temp) / y;
+
+ do_del(x);
+ swap_tail;
+ }
+ else { /* no log_p , large x such that probs are 0 or 1 */
+ if(x > 0) { *cum = 1.; *ccum = 0.; }
+ else { *cum = 0.; *ccum = 1.; }
+ }
+
+
+#ifdef NO_DENORMS
+ /* do not return "denormalized" -- we do in R */
+ if(log_p) {
+ if(*cum > -min) *cum = -0.;
+ if(*ccum > -min)*ccum = -0.;
+ }
+ else {
+ if(*cum < min) *cum = 0.;
+ if(*ccum < min) *ccum = 0.;
+ }
+#endif
+ return;
+}
+/* Cleaning up done by tools/import-R: */
+#undef SIXTEN
+#undef do_del
+#undef swap_tail
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/qnorm.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ * based on AS 111 (C) 1977 Royal Statistical Society
+ * and on AS 241 (C) 1988 Royal Statistical Society
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * SYNOPSIS
+ *
+ * double qnorm5(double p, double mu, double sigma,
+ * int lower_tail, int log_p)
+ * {qnorm (..) is synonymous and preferred inside R}
+ *
+ * DESCRIPTION
+ *
+ * Compute the quantile function for the normal distribution.
+ *
+ * For small to moderate probabilities, algorithm referenced
+ * below is used to obtain an initial approximation which is
+ * polished with a final Newton step.
+ *
+ * For very large arguments, an algorithm of Wichura is used.
+ *
+ * REFERENCE
+ *
+ * Beasley, J. D. and S. G. Springer (1977).
+ * Algorithm AS 111: The percentage points of the normal distribution,
+ * Applied Statistics, 26, 118-121.
+ *
+ * Wichura, M.J. (1988).
+ * Algorithm AS 241: The Percentage Points of the Normal Distribution.
+ * Applied Statistics, 37, 477-484.
+ */
+
+
+gnm_float qnorm(gnm_float p, gnm_float mu, gnm_float sigma, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float p_, q, r, val;
+
+#ifdef IEEE_754
+ if (isnangnum(p) || isnangnum(mu) || isnangnum(sigma))
+ return p + mu + sigma;
+#endif
+ if (p == R_DT_0) return gnm_ninf;
+ if (p == R_DT_1) return gnm_pinf;
+ R_Q_P01_check(p);
+
+ if(sigma < 0) ML_ERR_return_NAN;
+ if(sigma == 0) return mu;
+
+ p_ = R_DT_qIv(p);/* real lower_tail prob. p */
+ q = p_ - 0.5;
+
+#ifdef DEBUG_qnorm
+ REprintf("qnorm(p=%10.7" GNUM_FORMAT_g ", m=%" GNUM_FORMAT_g ", s=%" GNUM_FORMAT_g ", l.t.= %d, log= %d): q = %" GNUM_FORMAT_g "\n",
+ p,mu,sigma, lower_tail, log_p, q);
+#endif
+
+
+/*-- use AS 241 --- */
+/* gnm_float ppnd16_(gnm_float *p, long *ifault)*/
+/* ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3
+
+ Produces the normal deviate Z corresponding to a given lower
+ tail area of P; Z is accurate to about 1 part in 10**16.
+
+ (original fortran code used PARAMETER(..) for the coefficients
+ and provided hash codes for checking them...)
+*/
+ if (gnumabs(q) <= .425) {/* 0.075 <= p <= 0.925 */
+ r = .180625 - q * q;
+ val =
+ q * (((((((r * GNM_const(2509.0809287301226727) +
+ GNM_const(33430.575583588128105)) * r + GNM_const(67265.770927008700853)) * r +
+ GNM_const(45921.953931549871457)) * r + GNM_const(13731.693765509461125)) * r +
+ GNM_const(1971.5909503065514427)) * r + GNM_const(133.14166789178437745)) * r +
+ GNM_const(3.387132872796366608))
+ / (((((((r * GNM_const(5226.495278852854561) +
+ GNM_const(28729.085735721942674)) * r + GNM_const(39307.89580009271061)) * r +
+ GNM_const(21213.794301586595867)) * r + GNM_const(5394.1960214247511077)) * r +
+ GNM_const(687.1870074920579083)) * r + GNM_const(42.313330701600911252)) * r + 1.);
+ }
+ else { /* closer than 0.075 from {0,1} boundary */
+
+ /* r = min(p, 1-p) < 0.075 */
+ if (q > 0)
+ r = R_DT_CIv(p);/* 1-p */
+ else
+ r = p_;/* = R_DT_Iv(p) ^= p */
+
+ r = sqrtgnum(- ((log_p &&
+ ((lower_tail && q <= 0) || (!lower_tail && q > 0))) ?
+ p : /* else */ loggnum(r)));
+ /* r = sqrtgnum(-loggnum(r)) <==> min(p, 1-p) = expgnum( - r^2 ) */
+#ifdef DEBUG_qnorm
+ REprintf("\t close to 0 or 1: r = %7" GNUM_FORMAT_g "\n", r);
+#endif
+
+ if (r <= 5.) { /* <==> min(p,1-p) >= expgnum(-25) ~= 1.3888e-11 */
+ r += -1.6;
+ val = (((((((r * GNM_const(7.7454501427834140764e-4) +
+ GNM_const(.0227238449892691845833)) * r + GNM_const(.24178072517745061177)) *
+ r + GNM_const(1.27045825245236838258)) * r +
+ GNM_const(3.64784832476320460504)) * r + GNM_const(5.7694972214606914055)) *
+ r + GNM_const(4.6303378461565452959)) * r +
+ GNM_const(1.42343711074968357734))
+ / (((((((r *
+ GNM_const(1.05075007164441684324e-9) + GNM_const(5.475938084995344946e-4)) *
+ r + GNM_const(.0151986665636164571966)) * r +
+ GNM_const(.14810397642748007459)) * r + GNM_const(.68976733498510000455)) *
+ r + GNM_const(1.6763848301838038494)) * r +
+ GNM_const(2.05319162663775882187)) * r + 1.);
+ }
+ else { /* very close to 0 or 1 */
+ r += -5.;
+ val = (((((((r * GNM_const(2.01033439929228813265e-7) +
+ GNM_const(2.71155556874348757815e-5)) * r +
+ GNM_const(.0012426609473880784386)) * r + GNM_const(.026532189526576123093)) *
+ r + GNM_const(.29656057182850489123)) * r +
+ GNM_const(1.7848265399172913358)) * r + GNM_const(5.4637849111641143699)) *
+ r + GNM_const(6.6579046435011037772))
+ / (((((((r *
+ GNM_const(2.04426310338993978564e-15) + GNM_const(1.4215117583164458887e-7))*
+ r + GNM_const(1.8463183175100546818e-5)) * r +
+ GNM_const(7.868691311456132591e-4)) * r + GNM_const(.0148753612908506148525))
+ * r + GNM_const(.13692988092273580531)) * r +
+ GNM_const(.59983220655588793769)) * r + 1.);
+ }
+
+ if(q < 0.0)
+ val = -val;
+ /* return (q >= 0.)? r : -r ;*/
+ }
+ return mu + sigma * val;
+}
+
+
+
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/plnorm.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The lognormal distribution function.
+ */
+
+
+gnm_float plnorm(gnm_float x, gnm_float logmean, gnm_float logsd, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(logmean) || isnangnum(logsd))
+ return x + logmean + logsd;
+#endif
+ if (logsd <= 0) ML_ERR_return_NAN;
+
+ if (x > 0)
+ return pnorm(loggnum(x), logmean, logsd, lower_tail, log_p);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/qlnorm.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * This the lognormal quantile function.
+ */
+
+
+gnm_float qlnorm(gnm_float p, gnm_float logmean, gnm_float logsd, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(p) || isnangnum(logmean) || isnangnum(logsd))
+ return p + logmean + logsd;
+#endif
+ R_Q_P01_check(p);
+
+ if (p == R_DT_1) return gnm_pinf;
+ if (p == R_DT_0) return 0;
+ return expgnum(qnorm(p, logmean, logsd, lower_tail, log_p));
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/ppois.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The distribution function of the Poisson distribution.
+ */
+
+
+gnm_float ppois(gnm_float x, gnm_float lambda, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(lambda))
+ return x + lambda;
+#endif
+ if(lambda < 0.) ML_ERR_return_NAN;
+
+ x = floorgnum(x + 1e-7);
+ if (x < 0) return R_DT_0;
+ if (lambda == 0.) return R_DT_1;
+ if (!finitegnum(x)) return R_DT_1;
+
+ return pgamma(lambda, x + 1, 1., !lower_tail, log_p);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/qpois.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The quantile function of the Poisson distribution.
+ *
+ * METHOD
+ *
+ * Uses the Cornish-Fisher Expansion to include a skewness
+ * correction to a normal approximation. This gives an
+ * initial value which never seems to be off by more than
+ * 1 or 2. A search is then conducted of values close to
+ * this initial start point.
+ */
+
+
+gnm_float qpois(gnm_float p, gnm_float lambda, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float mu, sigma, gamma, z, y;
+#ifdef IEEE_754
+ if (isnangnum(p) || isnangnum(lambda))
+ return p + lambda;
+#endif
+ if(!finitegnum(lambda))
+ ML_ERR_return_NAN;
+ R_Q_P01_check(p);
+ if(lambda < 0) ML_ERR_return_NAN;
+
+ if (p == R_DT_0) return 0;
+ if (p == R_DT_1) return gnm_pinf;
+
+ if(lambda == 0) return 0;
+
+ mu = lambda;
+ sigma = sqrtgnum(lambda);
+ gamma = sigma;
+
+ /* Note : "same" code in qpois.c, qbinom.c, qnbinom.c --
+ * FIXME: This is far from optimal [cancellation for p ~= 1, etc]: */
+ if(!lower_tail || log_p) {
+ p = R_DT_qIv(p); /* need check again (cancellation!): */
+ if (p == 0.) return 0;
+ if (p == 1.) return gnm_pinf;
+ }
+ /* temporary hack --- FIXME --- */
+ if (p + 1.01*GNUM_EPSILON >= 1.) return gnm_pinf;
+
+ /* y := approx.value (Cornish-Fisher expansion) : */
+ z = qnorm(p, 0., 1., /*lower_tail*/TRUE, /*log_p*/FALSE);
+ y = floorgnum(mu + sigma * (z + gamma * (z*z - 1) / 6) + 0.5);
+
+ z = ppois(y, lambda, /*lower_tail*/TRUE, /*log_p*/FALSE);
+
+ /* fuzz to ensure left continuity; 1 - 1e-7 may lose too much : */
+ p *= 1 - 64*GNUM_EPSILON;
+
+/*-- Fixme, here y can be way off --
+ should use interval search instead of primitive stepping down or up */
+
+#ifdef maybe_future
+ if((lower_tail && z >= p) || (!lower_tail && z <= p)) {
+#else
+ if(z >= p) {
+#endif
+ /* search to the left */
+ for(;;) {
+ if(y == 0 ||
+ (z = ppois(y - 1, lambda, /*l._t.*/TRUE, /*log_p*/FALSE)) < p)
+ return y;
+ y = y - 1;
+ }
+ }
+ else { /* search to the right */
+ for(;;) {
+ y = y + 1;
+ if((z = ppois(y, lambda, /*l._t.*/TRUE, /*log_p*/FALSE)) >= p)
+ return y;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/stirlerr.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, The R Core Development Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ *
+ * Computes the log of the error term in Stirling's formula.
+ * For n > 15, uses the series 1/12n - 1/360n^3 + ...
+ * For n <=15, integers or half-integers, uses stored values.
+ * For other n < 15, uses lgamma directly (don't use this to
+ * write lgamma!)
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, The R Core Development Team
+ * R has lgammafn, and lgamma is not part of ISO C
+ */
+
+
+/* stirlerr(n) = loggnum(n!) - loggnum( sqrtgnum(2*pi*n)*(n/e)^n )
+ * = loggnum Gamma(n+1) - 1/2 * [loggnum(2*pi) + loggnum(n)] - n*[loggnum(n) - 1]
+ * = loggnum Gamma(n+1) - (n + 1/2) * loggnum(n) + n - loggnum(2*pi)/2
+ *
+ * see also lgammacor() in ./lgammacor.c which computes almost the same!
+ */
+
+static gnm_float stirlerr(gnm_float n)
+{
+
+#define S0 GNM_const(0.083333333333333333333) /* 1/12 */
+#define S1 GNM_const(0.00277777777777777777778) /* 1/360 */
+#define S2 GNM_const(0.00079365079365079365079365) /* 1/1260 */
+#define S3 GNM_const(0.000595238095238095238095238) /* 1/1680 */
+#define S4 GNM_const(0.0008417508417508417508417508)/* 1/1188 */
+
+/*
+ error for 0, 0.5, 1.0, 1.5, ..., 14.5, 15.0.
+*/
+ static const gnm_float sferr_halves[31] = {
+ 0.0, /* n=0 - wrong, place holder only */
+ GNM_const(0.1534264097200273452913848), /* 0.5 */
+ GNM_const(0.0810614667953272582196702), /* 1.0 */
+ GNM_const(0.0548141210519176538961390), /* 1.5 */
+ GNM_const(0.0413406959554092940938221), /* 2.0 */
+ GNM_const(0.03316287351993628748511048), /* 2.5 */
+ GNM_const(0.02767792568499833914878929), /* 3.0 */
+ GNM_const(0.02374616365629749597132920), /* 3.5 */
+ GNM_const(0.02079067210376509311152277), /* 4.0 */
+ GNM_const(0.01848845053267318523077934), /* 4.5 */
+ GNM_const(0.01664469118982119216319487), /* 5.0 */
+ GNM_const(0.01513497322191737887351255), /* 5.5 */
+ GNM_const(0.01387612882307074799874573), /* 6.0 */
+ GNM_const(0.01281046524292022692424986), /* 6.5 */
+ GNM_const(0.01189670994589177009505572), /* 7.0 */
+ GNM_const(0.01110455975820691732662991), /* 7.5 */
+ GNM_const(0.010411265261972096497478567), /* 8.0 */
+ GNM_const(0.009799416126158803298389475), /* 8.5 */
+ GNM_const(0.009255462182712732917728637), /* 9.0 */
+ GNM_const(0.008768700134139385462952823), /* 9.5 */
+ GNM_const(0.008330563433362871256469318), /* 10.0 */
+ GNM_const(0.007934114564314020547248100), /* 10.5 */
+ GNM_const(0.007573675487951840794972024), /* 11.0 */
+ GNM_const(0.007244554301320383179543912), /* 11.5 */
+ GNM_const(0.006942840107209529865664152), /* 12.0 */
+ GNM_const(0.006665247032707682442354394), /* 12.5 */
+ GNM_const(0.006408994188004207068439631), /* 13.0 */
+ GNM_const(0.006171712263039457647532867), /* 13.5 */
+ GNM_const(0.005951370112758847735624416), /* 14.0 */
+ GNM_const(0.005746216513010115682023589), /* 14.5 */
+ GNM_const(0.005554733551962801371038690) /* 15.0 */
+ };
+ gnm_float nn;
+
+ if (n <= 15.0) {
+ nn = n + n;
+ if (nn == (int)nn) return(sferr_halves[(int)nn]);
+ return(lgamma1p (n) - (n + 0.5)*loggnum(n) + n - M_LN_SQRT_2PI);
+ }
+
+ nn = n*n;
+ if (n>500) return((S0-S1/nn)/n);
+ if (n> 80) return((S0-(S1-S2/nn)/nn)/n);
+ if (n> 35) return((S0-(S1-(S2-S3/nn)/nn)/nn)/n);
+ /* 15 < n <= 35 : */
+ return((S0-(S1-(S2-(S3-S4/nn)/nn)/nn)/nn)/n);
+}
+/* Cleaning up done by tools/import-R: */
+#undef S0
+#undef S1
+#undef S2
+#undef S3
+#undef S4
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/bd0.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, The R Core Development Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ * Evaluates the "deviance part"
+ * bd0(x,M) := M * D0(x/M) = M*[ x/M * log(x/M) + 1 - (x/M) ] =
+ * = x * log(x/M) + M - x
+ * where M = E[X] = n*p (or = lambda), for x, M > 0
+ *
+ * in a manner that should be stable (with small relative error)
+ * for all x and np. In particular for x/np close to 1, direct
+ * evaluation fails, and evaluation is based on the Taylor series
+ * of log((1+v)/(1-v)) with v = (x-np)/(x+np).
+ */
+
+static gnm_float bd0(gnm_float x, gnm_float np)
+{
+ gnm_float ej, s, s1, v;
+ int j;
+
+ if (gnumabs(x-np) < 0.1*(x+np)) {
+ v = (x-np)/(x+np);
+ s = (x-np)*v;/* s using v -- change by MM */
+ ej = 2*x*v;
+ v = v*v;
+ for (j=1; ; j++) { /* Taylor series */
+ ej *= v;
+ s1 = s+ej/((j<<1)+1);
+ if (s1==s) /* last term was effectively 0 */
+ return(s1);
+ s = s1;
+ }
+ }
+ /* else: | x - np | is not too small */
+ return(x*loggnum(x/np)+np-x);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dpois.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, The R Core Development Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ *
+ * dpois() checks argument validity and calls dpois_raw().
+ *
+ * dpois_raw() computes the Poisson probability lb^x exp(-lb) / x!.
+ * This does not check that x is an integer, since dgamma() may
+ * call this with a fractional x argument. Any necessary argument
+ * checks should be done in the calling function.
+ *
+ */
+
+
+static gnm_float dpois_raw(gnm_float x, gnm_float lambda, gboolean give_log)
+{
+ if (lambda == 0) return( (x == 0) ? R_D__1 : R_D__0 );
+ if (x == 0) return( R_D_exp(-lambda) );
+ if (x < 0) return( R_D__0 );
+
+ return(R_D_fexp( M_2PIgnum*x, -stirlerr(x)-bd0(x,lambda) ));
+}
+
+gnm_float dpois(gnm_float x, gnm_float lambda, gboolean give_log)
+{
+#ifdef IEEE_754
+ if(isnangnum(x) || isnangnum(lambda))
+ return x + lambda;
+#endif
+
+ if (lambda < 0) ML_ERR_return_NAN;
+ R_D_nonint_check(x);
+ if (x < 0 || !finitegnum(x)) return R_D__0;
+ x = R_D_forceint(x);
+
+ return( dpois_raw(x,lambda,give_log) );
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dgamma.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000 The R Core Development Team
+ * Copyright (C) 2004 The R Foundation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ *
+ * Computes the density of the gamma distribution,
+ *
+ * 1/s (x/s)^{a-1} exp(-x/s)
+ * p(x;a,s) = -----------------------
+ * (a-1)!
+ *
+ * where `s' is the scale (= 1/lambda in other parametrizations)
+ * and `a' is the shape parameter ( = alpha in other contexts).
+ *
+ * The old (R 1.1.1) version of the code is available via `#define D_non_pois'
+ */
+
+
+gnm_float dgamma(gnm_float x, gnm_float shape, gnm_float scale, gboolean give_log)
+{
+ gnm_float pr;
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(shape) || isnangnum(scale))
+ return x + shape + scale;
+#endif
+ if (shape <= 0 || scale <= 0) ML_ERR_return_NAN;
+ if (x < 0)
+ return R_D__0;
+ if (x == 0) {
+ if (shape < 1) return gnm_pinf;
+ if (shape > 1) return R_D__0;
+ /* else */
+ return give_log ? -loggnum(scale) : 1 / scale;
+ }
+
+ if (shape < 1) {
+ pr = dpois_raw(shape, x/scale, give_log);
+ return give_log ? pr + loggnum(shape/x) : pr*shape/x;
+ }
+ /* else shape >= 1 */
+ pr = dpois_raw(shape-1, x/scale, give_log);
+ return give_log ? pr - loggnum(scale) : pr/scale;
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/pgamma.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 1999-2000 The R Development Core Team
+ * Copyright (C) 2003-2004 The R Foundation
+ * Copyright (C) 2004 Morten Welinder
+ * Copyright (C) 2002-2003 Ian Smith
+ *
+ * Formerly based on AS 239 (C) 1988 Royal Statistical Society
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * SYNOPSIS
+ *
+ * #include <Rmath.h>
+ * double pgamma(double x, double alph, double scale,
+ * int lower_tail, int log_p)
+ *
+ * DESCRIPTION
+ *
+ * This function computes the distribution function for the
+ * gamma distribution with shape parameter alph and scale parameter
+ * scale. This is also known as the incomplete gamma function.
+ * See Abramowitz and Stegun (6.5.1) for example.
+ *
+ * NOTES
+ *
+ * This function is an adaptation of Algorithm 239 from the
+ * Applied Statistics Series. The algorithm is faster than
+ * those by W. Fullerton in the FNLIB library and also the
+ * TOMS 542 alorithm of W. Gautschi. It provides comparable
+ * accuracy to those algorithms and is considerably simpler.
+ *
+ * REFERENCES
+ *
+ * Algorithm AS 239, Incomplete Gamma Function
+ * Applied Statistics 37, 1988.
+ */
+
+
+
+
+/*
+ * Compute the log of a sum from logs of terms, i.e.,
+ *
+ * log (exp (logx) + exp (logy))
+ *
+ * without causing overflows and without throwing away large handfuls
+ * of accuracy.
+ */
+static gnm_float
+logspace_add (gnm_float logx, gnm_float logy)
+{
+ return fmax2 (logx, logy) + log1pgnum (expgnum (-gnumabs (logx - logy)));
+}
+
+
+/*
+ * Compute the log of a difference from logs of terms, i.e.,
+ *
+ * log (exp (logx) - exp (logy))
+ *
+ * without causing overflows and without throwing away large handfuls
+ * of accuracy.
+ */
+static gnm_float
+logspace_sub (gnm_float logx, gnm_float logy)
+{
+ return logx + log1pgnum (-expgnum (logy - logx));
+}
+
+
+static gnm_float
+dpois_wrap (gnm_float x_plus_1, gnm_float lambda, gboolean give_log)
+{
+#if 0
+ printf ("x+1=%.14" GNUM_FORMAT_g " lambda=%.14" GNUM_FORMAT_g "\n", x_plus_1, lambda);
+#endif
+
+ if (x_plus_1 > 1)
+ return dpois_raw (x_plus_1 - 1, lambda, give_log);
+ else {
+ gnm_float d = dpois_raw (x_plus_1, lambda, give_log);
+ return give_log
+ ? d + loggnum (x_plus_1 / lambda)
+ : d * (x_plus_1 / lambda);
+ }
+}
+
+/*
+ * Abramowitz and Stegun 6.5.29 [right]
+ */
+static gnm_float
+pgamma_smallx (gnm_float x, gnm_float alph, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float sum = 0, c = alph, n = 0, term;
+
+#if 0
+ printf ("x:%.14" GNUM_FORMAT_g " alph:%.14" GNUM_FORMAT_g "\n", x, alph);
+#endif
+
+ /*
+ * Relative to 6.5.29 all terms have been multiplied by alph
+ * and the first, thus being 1, is omitted.
+ */
+
+ do {
+ n++;
+ c *= -x / n;
+ term = c / (alph + n);
+ sum += term;
+ } while (gnumabs (term) > GNUM_EPSILON * gnumabs (sum));
+
+ if (lower_tail) {
+ gnm_float f1 = log_p ? log1pgnum (sum) : 1 + sum;
+ gnm_float f2;
+ if (alph > 1) {
+ f2 = dpois_raw (alph, x, log_p);
+ f2 = log_p ? f2 + x : f2 * expgnum (x);
+ } else if (log_p)
+ f2 = alph * loggnum (x) - lgamma1p (alph);
+ else
+ f2 = powgnum (x, alph) / expgnum (lgamma1p (alph));
+
+ return log_p ? f1 + f2 : f1 * f2;
+ } else {
+ gnm_float lf2 = alph * loggnum (x) - lgamma1p (alph);
+#if 0
+ printf ("1:%.14" GNUM_FORMAT_g " 2:%.14" GNUM_FORMAT_g "\n", alph * loggnum (x), lgamma1p (alph));
+ printf ("sum=%.14" GNUM_FORMAT_g " log1pgnum (sum)=%.14" GNUM_FORMAT_g " lf2=%.14" GNUM_FORMAT_g "\n", sum, log1pgnum (sum), lf2);
+#endif
+ if (log_p)
+ return swap_log_tail (log1pgnum (sum) + lf2);
+ else {
+ gnm_float f1m1 = sum;
+ gnm_float f2m1 = expm1gnum (lf2);
+ return -(f1m1 + f2m1 + f1m1 * f2m1);
+ }
+ }
+}
+
+static gnm_float
+pd_upper_series (gnm_float x, gnm_float y, gboolean log_p)
+{
+ gnm_float term = x / y;
+ gnm_float sum = term;
+
+ do {
+ y++;
+ term *= x / y;
+ sum += term;
+ } while (term > sum * GNUM_EPSILON);
+
+ return log_p ? loggnum (sum) : sum;
+}
+
+static gnm_float
+pd_lower_cf (gnm_float i, gnm_float d)
+{
+ gnm_float f = 0, of;
+
+ gnm_float a1 = 0;
+ gnm_float b1 = 1;
+ gnm_float a2 = i;
+ gnm_float b2 = d;
+ gnm_float c1 = 0;
+ gnm_float c2 = a2;
+ gnm_float c3;
+ gnm_float c4 = b2;
+
+ while (1) {
+ c1++;
+ c2--;
+ c3 = c1 * c2;
+ c4 += 2;
+ a1 = c4 * a2 + c3 * a1;
+ b1 = c4 * b2 + c3 * b1;
+
+ c1++;
+ c2--;
+ c3 = c1 * c2;
+ c4 += 2;
+ a2 = c4 * a1 + c3 * a2;
+ b2 = c4 * b1 + c3 * b2;
+
+ if (b2 > scalefactor) {
+ a1 = a1 / scalefactor;
+ b1 = b1 / scalefactor;
+ a2 = a2 / scalefactor;
+ b2 = b2 / scalefactor;
+ }
+
+ if (b2 != 0) {
+ of = f;
+ f = a2 / b2;
+ if (gnumabs (f - of) <= GNUM_EPSILON * fmin2 (1.0, gnumabs (f)))
+ return f;
+ }
+ }
+}
+
+static gnm_float
+pd_lower_series (gnm_float lambda, gnm_float y)
+{
+ gnm_float term = 1, sum = 0;
+
+ while (y >= 1 && term > sum * GNUM_EPSILON) {
+ term *= y / lambda;
+ sum += term;
+ y--;
+ }
+
+ if (y != floorgnum (y)) {
+ /*
+ * The series does not converge as the terms start getting
+ * bigger (besides flipping sign) for y < -lambda.
+ */
+ gnm_float f = pd_lower_cf (y, lambda + 1 - y);
+ sum += term * f;
+ }
+
+ return sum;
+}
+
+/*
+ * Asymptotic expansion to calculate the probability that poisson variate
+ * has value <= x.
+ */
+static gnm_float
+ppois_asymp (gnm_float x, gnm_float lambda,
+ gboolean lower_tail, gboolean log_p)
+{
+ static const gnm_float coef15 = 1 / GNM_const(12.0);
+ static const gnm_float coef25 = 1 / GNM_const(288.0);
+ static const gnm_float coef35 = -139 / GNM_const(51840.0);
+ static const gnm_float coef45 = -571 / GNM_const(2488320.0);
+ static const gnm_float coef55 = 163879 / GNM_const(209018880.0);
+ static const gnm_float coef65 = 5246819 / GNM_const(75246796800.0);
+ static const gnm_float coef75 = -534703531 / GNM_const(902961561600.0);
+ static const gnm_float coef1 = 2 / GNM_const(3.0);
+ static const gnm_float coef2 = -4 / GNM_const(135.0);
+ static const gnm_float coef3 = 8 / GNM_const(2835.0);
+ static const gnm_float coef4 = 16 / GNM_const(8505.0);
+ static const gnm_float coef5 = -8992 / GNM_const(12629925.0);
+ static const gnm_float coef6 = -334144 / GNM_const(492567075.0);
+ static const gnm_float coef7 = 698752 / GNM_const(1477701225.0);
+ static const gnm_float two = 2;
+
+ gnm_float dfm, pt,s2pt,res1,res2,elfb,term;
+ gnm_float ig2,ig3,ig4,ig5,ig6,ig7,ig25,ig35,ig45,ig55,ig65,ig75;
+ gnm_float f, np, nd;
+
+ dfm = lambda - x;
+ pt = -x * log1pmx (dfm / x);
+ s2pt = sqrtgnum (2 * pt);
+ if (dfm < 0) s2pt = -s2pt;
+
+ ig2 = 1.0 + pt;
+ term = pt * pt * 0.5;
+ ig3 = ig2 + term;
+ term *= pt / 3;
+ ig4 = ig3 + term;
+ term *= pt / 4;
+ ig5 = ig4 + term;
+ term *= pt / 5;
+ ig6 = ig5 + term;
+ term *= pt / 6;
+ ig7 = ig6 + term;
+
+ term = pt * (two / 3);
+ ig25 = 1.0 + term;
+ term *= pt * (two / 5);
+ ig35 = ig25 + term;
+ term *= pt * (two / 7);
+ ig45 = ig35 + term;
+ term *= pt * (two / 9);
+ ig55 = ig45 + term;
+ term *= pt * (two / 11);
+ ig65 = ig55 + term;
+ term *= pt * (two / 13);
+ ig75 = ig65 + term;
+
+ elfb = ((((((coef75/x + coef65)/x + coef55)/x + coef45)/x + coef35)/x + coef25)/x + coef15) + x;
+ res1 = ((((((ig7*coef7/x + ig6*coef6)/x + ig5*coef5)/x + ig4*coef4)/x + ig3*coef3)/x + ig2*coef2)/x + coef1)*sqrtgnum(x);
+ res2 = ((((((ig75*coef75/x + ig65*coef65)/x + ig55*coef55)/x + ig45*coef45)/x + ig35*coef35)/x + ig25*coef25)/x + coef15)*s2pt;
+
+ if (!lower_tail) elfb = -elfb;
+ f = (res1 + res2) / elfb;
+
+ np = pnorm (s2pt, 0.0, 1.0, !lower_tail, log_p);
+ nd = dnorm (s2pt, 0.0, 1.0, log_p);
+
+#if 0
+ printf ("f=%.14" GNUM_FORMAT_g " np=%.14" GNUM_FORMAT_g " nd=%.14" GNUM_FORMAT_g " f*nd=%.14" GNUM_FORMAT_g "\n", f, np, nd, f * nd);
+#endif
+
+ if (log_p)
+ return (f >= 0)
+ ? logspace_add (np, loggnum (gnumabs (f)) + nd)
+ : logspace_sub (np, loggnum (gnumabs (f)) + nd);
+ else
+ return np + f * nd;
+}
+
+
+static gnm_float
+pgamma_raw (gnm_float x, gnm_float alph, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float res;
+
+#if 0
+ printf ("x=%.14" GNUM_FORMAT_g " alph=%.14" GNUM_FORMAT_g " low=%d log=%d\n", x, alph, lower_tail, log_p);
+#endif
+
+ if (x < 1) {
+ res = pgamma_smallx (x, alph, lower_tail, log_p);
+ } else if (x <= alph - 1 && x < 0.8 * (alph + 50)) {
+ gnm_float sum = pd_upper_series (x, alph, log_p);
+ gnm_float d = dpois_wrap (alph, x, log_p);
+
+ if (!lower_tail)
+ res = log_p
+ ? swap_log_tail (d + sum)
+ : 1 - d * sum;
+ else
+ res = log_p ? sum + d : sum * d;
+ } else if (alph - 1 < x && alph < 0.8 * (x + 50)) {
+ gnm_float sum;
+ gnm_float d = dpois_wrap (alph, x, log_p);
+
+ if (alph < 1) {
+ gnm_float f = pd_lower_cf (alph, x - (alph - 1))
+ * x / alph;
+ sum = log_p ? loggnum (f) : f;
+ } else {
+ sum = pd_lower_series (x, alph - 1);
+ sum = log_p ? log1pgnum (sum) : 1 + sum;
+ }
+
+ if (!lower_tail)
+ res = log_p ? sum + d : sum * d;
+ else
+ res = log_p
+ ? swap_log_tail (d + sum)
+ : 1 - d * sum;
+ } else {
+ res = ppois_asymp (alph - 1, x, !lower_tail, log_p);
+ }
+
+ /*
+ * We lose a fair amount of accuracy to underflow in the cases
+ * where the final result is very close to DBL_MIN. In those
+ * cases, simply redo via log space.
+ */
+ if (!log_p && res < GNUM_MIN / GNUM_EPSILON)
+ return expgnum (pgamma_raw (x, alph, lower_tail, 1));
+ else
+ return res;
+}
+
+
+gnm_float pgamma(gnm_float x, gnm_float alph, gnm_float scale, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(alph) || isnangnum(scale))
+ return x + alph + scale;
+#endif
+ if(alph <= 0. || scale <= 0.)
+ ML_ERR_return_NAN;
+ if (x <= 0.)
+ return R_DT_0;
+ x /= scale;
+#ifdef IEEE_754
+ if (isnangnum(x)) /* eg. original x = scale = +Inf */
+ return x;
+#endif
+
+ return pgamma_raw (x, alph, lower_tail, log_p);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/chebyshev.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * SYNOPSIS
+ *
+ * int chebyshev_init(double *dos, int nos, double eta)
+ * double chebyshev_eval(double x, double *a, int n)
+ *
+ * DESCRIPTION
+ *
+ * "chebyshev_init" determines the number of terms for the
+ * double precision orthogonal series "dos" needed to insure
+ * the error is no larger than "eta". Ordinarily eta will be
+ * chosen to be one-tenth machine precision.
+ *
+ * "chebyshev_eval" evaluates the n-term Chebyshev series
+ * "a" at "x".
+ *
+ * NOTES
+ *
+ * These routines are translations into C of Fortran routines
+ * by W. Fullerton of Los Alamos Scientific Laboratory.
+ *
+ * Based on the Fortran routine dcsevl by W. Fullerton.
+ * Adapted from R. Broucke, Algorithm 446, CACM., 16, 254 (1973).
+ */
+
+
+/* NaNs propagated correctly */
+
+
+static int chebyshev_init(gnm_float *dos, int nos, gnm_float eta)
+{
+ int i, ii;
+ gnm_float err;
+
+ if (nos < 1)
+ return 0;
+
+ err = 0.0;
+ i = 0; /* just to avoid compiler warnings */
+ for (ii=1; ii<=nos; ii++) {
+ i = nos - ii;
+ err += gnumabs(dos[i]);
+ if (err > eta) {
+ return i;
+ }
+ }
+ return i;
+}
+
+
+static gnm_float chebyshev_eval(gnm_float x, const gnm_float *a, const int n)
+{
+ gnm_float b0, b1, b2, twox;
+ int i;
+
+ if (n < 1 || n > 1000) ML_ERR_return_NAN;
+
+ if (x < -1.1 || x > 1.1) ML_ERR_return_NAN;
+
+ twox = x * 2;
+ b2 = b1 = 0;
+ b0 = 0;
+ for (i = 1; i <= n; i++) {
+ b2 = b1;
+ b1 = b0;
+ b0 = twox * b1 - b2 + a[n - i];
+ }
+ return (b0 - b2) * 0.5;
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/lgammacor.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000-2001 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * SYNOPSIS
+ *
+ * #include <Rmath.h>
+ * double lgammacor(double x);
+ *
+ * DESCRIPTION
+ *
+ * Compute the log gamma correction factor for x >= 10 so that
+ *
+ * log(gamma(x)) = .5*log(2*pi) + (x-.5)*log(x) -x + lgammacor(x)
+ *
+ * [ lgammacor(x) is called Del(x) in other contexts (e.g. dcdflib)]
+ *
+ * NOTES
+ *
+ * This routine is a translation into C of a Fortran subroutine
+ * written by W. Fullerton of Los Alamos Scientific Laboratory.
+ *
+ * SEE ALSO
+ *
+ * Loader(1999)'s stirlerr() {in ./stirlerr.c} is *very* similar in spirit,
+ * is faster and cleaner, but is only defined "fast" for half integers.
+ */
+
+
+static gnm_float lgammacor(gnm_float x)
+{
+ static const gnm_float algmcs[15] = {
+ GNM_const(+.1666389480451863247205729650822e+0),
+ GNM_const(-.1384948176067563840732986059135e-4),
+ GNM_const(+.9810825646924729426157171547487e-8),
+ GNM_const(-.1809129475572494194263306266719e-10),
+ GNM_const(+.6221098041892605227126015543416e-13),
+ GNM_const(-.3399615005417721944303330599666e-15),
+ GNM_const(+.2683181998482698748957538846666e-17),
+ GNM_const(-.2868042435334643284144622399999e-19),
+ GNM_const(+.3962837061046434803679306666666e-21),
+ GNM_const(-.6831888753985766870111999999999e-23),
+ GNM_const(+.1429227355942498147573333333333e-24),
+ GNM_const(-.3547598158101070547199999999999e-26),
+ GNM_const(+.1025680058010470912000000000000e-27),
+ GNM_const(-.3401102254316748799999999999999e-29),
+ GNM_const(+.1276642195630062933333333333333e-30)
+ };
+
+ gnm_float tmp;
+
+#ifdef NOMORE_FOR_THREADS
+ static int nalgm = 0;
+ static gnm_float xbig = 0, xmax = 0;
+
+ /* Initialize machine dependent constants, the first time gamma() is called.
+ FIXME for threads ! */
+ if (nalgm == 0) {
+ /* For IEEE gnm_float precision : nalgm = 5 */
+ nalgm = chebyshev_init(algmcs, 15, GNUM_EPSILON/2);/*was d1mach(3)*/
+ xbig = 1 / sqrtgnum(GNUM_EPSILON/2); /* ~ 94906265.6 for IEEE gnm_float */
+ xmax = expgnum(fmin2(loggnum(GNUM_MAX / 12), -loggnum(12 * GNUM_MIN)));
+ /* = GNUM_MAX / 48 ~= 3.745e306 for IEEE gnm_float */
+ }
+#else
+/* For IEEE gnm_float precision GNUM_EPSILON = 2^-52 = GNM_const(2.220446049250313e-16) :
+ * xbig = 2 ^ 26.5
+ * xmax = GNUM_MAX / 48 = 2^1020 / 3 */
+# define nalgm 5
+# define xbig 94906265.62425156
+# define xmax GNM_const(3.745194030963158e306)
+#endif
+
+ if (x < 10)
+ ML_ERR_return_NAN
+ else if (x >= xmax) {
+ ML_ERROR(ME_UNDERFLOW);
+ return ML_UNDERFLOW;
+ }
+ else if (x < xbig) {
+ tmp = 10 / x;
+ return chebyshev_eval(tmp * tmp * 2 - 1, algmcs, nalgm) / x;
+ }
+ else return 1 / (x * 12);
+}
+/* Cleaning up done by tools/import-R: */
+#undef nalgm
+#undef xbig
+#undef xmax
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/lbeta.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ * Copyright (C) 2003 The R Foundation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * SYNOPSIS
+ *
+ * #include <Rmath.h>
+ * double lbeta(double a, double b);
+ *
+ * DESCRIPTION
+ *
+ * This function returns the value of the log beta function.
+ *
+ * NOTES
+ *
+ * This routine is a translation into C of a Fortran subroutine
+ * by W. Fullerton of Los Alamos Scientific Laboratory.
+ */
+
+
+static gnm_float lbeta(gnm_float a, gnm_float b)
+{
+ gnm_float corr, p, q;
+
+ p = q = a;
+ if(b < p) p = b;/* := min(a,b) */
+ if(b > q) q = b;/* := max(a,b) */
+
+#ifdef IEEE_754
+ if(isnangnum(a) || isnangnum(b))
+ return a + b;
+#endif
+
+ /* both arguments must be >= 0 */
+
+ if (p < 0)
+ ML_ERR_return_NAN
+ else if (p == 0) {
+ return gnm_pinf;
+ }
+ else if (!finitegnum(q)) {
+ return gnm_ninf;
+ }
+
+ if (p >= 10) {
+ /* p and q are big. */
+ corr = lgammacor(p) + lgammacor(q) - lgammacor(p + q);
+ return loggnum(q) * -0.5 + M_LN_SQRT_2PI + corr
+ + (p - 0.5) * loggnum(p / (p + q)) + q * log1pgnum(-p / (p + q));
+ }
+ else if (q >= 10) {
+ /* p is small, but q is big. */
+ corr = lgammacor(q) - lgammacor(p + q);
+ return lgammagnum(p) + corr + p - p * loggnum(p + q)
+ + (q - 0.5) * log1pgnum(-p / (p + q));
+ }
+ else
+ /* p and q are small: p <= q < 10. */
+ return lgammagnum(p) + lgammagnum(q) - lgammagnum(p + q);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dt.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, The R Core Development Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ *
+ * The t density is evaluated as
+ * sqrt(n/2) / ((n+1)/2) * Gamma((n+3)/2) / Gamma((n+2)/2).
+ * * (1+x^2/n)^(-n/2)
+ * / sqrt( 2 pi (1+x^2/n) )
+ *
+ * This form leads to a stable computation for all
+ * values of n, including n -> 0 and n -> infinity.
+ */
+
+
+gnm_float dt(gnm_float x, gnm_float n, gboolean give_log)
+{
+ gnm_float t, u;
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(n))
+ return x + n;
+#endif
+
+ if (n <= 0) ML_ERR_return_NAN;
+ if(!finitegnum(x))
+ return R_D__0;
+ if(!finitegnum(n))
+ return dnorm(x, 0., 1., give_log);
+
+ t = -bd0(n/2.,(n+1)/2.) + stirlerr((n+1)/2.) - stirlerr(n/2.);
+ if ( x*x > 0.2*n )
+ u = log1pgnum (x*x/n ) * n/2;
+ else
+ u = -bd0(n/2.,(n+x*x)/2.) + x*x/2.;
+
+ return R_D_fexp(M_2PIgnum*(1+x*x/n), t-u);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/pt.c from R. */
+/*
+ * R : A Computer Language for Statistical Data Analysis
+ * Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+
+gnm_float pt(gnm_float x, gnm_float n, gboolean lower_tail, gboolean log_p)
+{
+/* return P[ T <= x ] where
+ * T ~ t_{n} (t distrib. with n degrees of freedom).
+
+ * --> ./pnt.c for NON-central
+ */
+ gnm_float val;
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(n))
+ return x + n;
+#endif
+ if (n <= 0.0) ML_ERR_return_NAN;
+
+ if(!finitegnum(x))
+ return (x < 0) ? R_DT_0 : R_DT_1;
+ if(!finitegnum(n))
+ return pnorm(x, 0.0, 1.0, lower_tail, log_p);
+ if (0 && n > 4e5) { /*-- Fixme(?): test should depend on `n' AND `x' ! */
+ /* Approx. from Abramowitz & Stegun 26.7.8 (p.949) */
+ val = 1./(4.*n);
+ return pnorm(x*(1. - val)/sqrtgnum(1. + x*x*2.*val), 0.0, 1.0,
+ lower_tail, log_p);
+ }
+
+ val = (n > x * x)
+ ? pbeta (x * x / (n + x * x), 0.5, n / 2, /*lower_tail*/0, log_p)
+ : pbeta (n / (n + x * x), n / 2.0, 0.5, /*lower_tail*/1, log_p);
+
+ /* Use "1 - v" if lower_tail and x > 0 (but not both):*/
+ if(x <= 0.)
+ lower_tail = !lower_tail;
+
+ if(log_p) {
+ if(lower_tail) return log1pgnum(-0.5*expgnum(val));
+ else return val - M_LN2gnum; /* = loggnum(.5* pbeta(....)) */
+ }
+ else {
+ val /= 2.;
+ return R_D_Cval(val);
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/qt.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000-2002 The R Development Core Team
+ * Copyright (C) 2003 The R Foundation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The "Student" t distribution quantile function.
+ *
+ * NOTES
+ *
+ * This is a C translation of the Fortran routine given in:
+ * Hill, G.W (1970) "Algorithm 396: Student's t-quantiles"
+ * CACM 13(10), 619-620.
+ *
+ * ADDITIONS:
+ * - lower_tail, log_p
+ * - using expm1() : takes care of Lozy (1979) "Remark on Algo.", TOMS
+ * - Apply 2-term Taylor expansion as in
+ * Hill, G.W (1981) "Remark on Algo.396", ACM TOMS 7, 250-1
+ * - Improve the formula decision for 1 < df < 2
+ */
+
+
+gnm_float qt(gnm_float p, gnm_float ndf, gboolean lower_tail, gboolean log_p)
+{
+ const gnm_float eps = 1.e-12;
+
+ gnm_float a, b, c, d, p_, P, q, x, y;
+ gboolean neg;
+
+#ifdef IEEE_754
+ if (isnangnum(p) || isnangnum(ndf))
+ return p + ndf;
+#endif
+ if (p == R_DT_0) return gnm_ninf;
+ if (p == R_DT_1) return gnm_pinf;
+ R_Q_P01_check(p);
+
+ if (ndf < 1) /* FIXME: not yet treated here */
+ ML_ERR_return_NAN;
+
+ /* FIXME: This test should depend on ndf AND p !!
+ * ----- and in fact should be replaced by
+ * something like Abramowitz & Stegun 26.7.5 (p.949)
+ */
+ if (ndf > 1e20) return qnorm(p, 0., 1., lower_tail, log_p);
+
+ p_ = R_D_qIv(p); /* note: expgnum(p) may underflow to 0; fix later */
+
+ if((lower_tail && p_ > 0.5) || (!lower_tail && p_ < 0.5)) {
+ neg = FALSE; P = 2 * R_D_Cval(p_);
+ } else {
+ neg = TRUE; P = 2 * R_D_Lval(p_);
+ } /* 0 <= P <= 1 ; P = 2*min(p_, 1 - p_) in all cases */
+
+ if (gnumabs(ndf - 2) < eps) { /* df ~= 2 */
+ if(P > 0)
+ q = sqrtgnum(2 / (P * (2 - P)) - 2);
+ else { /* P = 0, but maybe = expgnum(p) ! */
+ if(log_p) q = M_SQRT2gnum * expgnum(- .5 * R_D_Lval(p));
+ else q = gnm_pinf;
+ }
+ }
+ else if (ndf < 1 + eps) { /* df ~= 1 (df < 1 excluded above): Cauchy */
+ if(P > 0)
+ q = - tangnum((P+1) * M_PI_2gnum);
+
+ else { /* P = 0, but maybe p_ = expgnum(p) ! */
+ if(log_p) q = M_1_PI * expgnum(-R_D_Lval(p));/* cot(e) ~ 1/e */
+ else q = gnm_pinf;
+ }
+ }
+ else { /*-- usual case; including, e.g., df = 1.1 */
+ a = 1 / (ndf - 0.5);
+ b = 48 / (a * a);
+ c = ((20700 * a / b - 98) * a - 16) * a + 96.36;
+ d = ((94.5 / (b + c) - 3) / b + 1) * sqrtgnum(a * M_PI_2gnum) * ndf;
+ if(P > 0 || !log_p)
+ y = powgnum(d * P, 2 / ndf);
+ else /* P = 0 && log_p; P = 2*expgnum(p) */
+ y = expgnum(2 / ndf * (loggnum(d) + M_LN2gnum + R_D_Lval(p)));
+
+ if ((ndf < 2.1 && P > 0.5) || y > 0.05 + a) { /* P > P0(df) */
+ /* Asymptotic inverse expansion about normal */
+ if(P > 0 || !log_p)
+ x = qnorm(0.5 * P, 0., 1., /*lower_tail*/TRUE, /*log_p*/FALSE);
+ else /* P = 0 && log_p; P = 2*expgnum(p') */
+ x = qnorm( p, 0., 1., lower_tail, /*log_p*/TRUE);
+
+ y = x * x;
+ if (ndf < 5)
+ c += 0.3 * (ndf - 4.5) * (x + 0.6);
+ c = (((0.05 * d * x - 5) * x - 7) * x - 2) * x + b + c;
+ y = (((((0.4 * y + 6.3) * y + 36) * y + 94.5) / c
+ - y - 3) / b + 1) * x;
+ y = expm1gnum(a * y * y);
+ } else {
+ y = ((1 / (((ndf + 6) / (ndf * y) - 0.089 * d - 0.822)
+ * (ndf + 2) * 3) + 0.5 / (ndf + 4))
+ * y - 1) * (ndf + 1) / (ndf + 2) + 1 / y;
+ }
+ q = sqrtgnum(ndf * y);
+
+ /* Now apply 2-term Taylor expansion improvement (1-term = Newton):
+ * as by Hill (1981) [ref.above] */
+
+ /* FIXME: This is can be far from optimal when log_p = TRUE !
+ * and probably also improvable when lower_tail = FALSE */
+ x = (pt(q, ndf, /*lower_tail = */FALSE, /*log_p = */FALSE) - P/2) /
+ dt(q, ndf, /* give_log = */FALSE);
+ /* Newton (=Taylor 1 term):
+ * q += x;
+ * Taylor 2-term : */
+ q += x * (1. + x * q * (ndf + 1) / (2 * (q * q + ndf)));
+ }
+ if(neg) q = -q;
+ return q;
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/qf.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The quantile function of the F distribution.
+*/
+
+
+gnm_float qf(gnm_float p, gnm_float n1, gnm_float n2, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(p) || isnangnum(n1) || isnangnum(n2))
+ return p + n1 + n2;
+#endif
+ if (n1 <= 0. || n2 <= 0.) ML_ERR_return_NAN;
+
+ R_Q_P01_check(p);
+ if (p == R_DT_0)
+ return 0;
+
+ /* fudge the extreme DF cases -- qbeta doesn't do this well */
+
+ if (n2 > 4e5)
+ return qchisq(p, n1, lower_tail, log_p) / n1;
+
+ if (n1 > 4e5)
+ return 1/qchisq(p, n2, !lower_tail, log_p) * n2;
+
+ p = (1. / qbeta(R_DT_CIv(p), n2/2, n1/2, TRUE, FALSE) - 1.) * (n2 / n1);
+ return !isnangnum(p) ? p : gnm_nan;
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/pchisq.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA.
+ *
+ * DESCRIPTION
+ *
+ * The distribution function of the chi-squared distribution.
+ */
+
+
+gnm_float pchisq(gnm_float x, gnm_float df, gboolean lower_tail, gboolean log_p)
+{
+ return pgamma(x, df/2., 2., lower_tail, log_p);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/qchisq.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The quantile function of the chi-squared distribution.
+ */
+
+
+gnm_float qchisq(gnm_float p, gnm_float df, gboolean lower_tail, gboolean log_p)
+{
+ return qgamma(p, 0.5 * df, 2.0, lower_tail, log_p);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dweibull.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The density function of the Weibull distribution.
+ */
+
+
+gnm_float dweibull(gnm_float x, gnm_float shape, gnm_float scale, gboolean give_log)
+{
+ gnm_float tmp1, tmp2;
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(shape) || isnangnum(scale))
+ return x + shape + scale;
+#endif
+ if (shape <= 0 || scale <= 0) ML_ERR_return_NAN;
+
+ if (x < 0) return R_D__0;
+ if (!finitegnum(x)) return R_D__0;
+ tmp1 = powgnum(x / scale, shape - 1);
+ tmp2 = tmp1 * (x / scale);
+ return give_log ?
+ -tmp2 + loggnum(shape * tmp1 / scale) :
+ shape * tmp1 * expgnum(-tmp2) / scale;
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/pweibull.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000-2002 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The distribution function of the Weibull distribution.
+ */
+
+
+gnm_float pweibull(gnm_float x, gnm_float shape, gnm_float scale, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(shape) || isnangnum(scale))
+ return x + shape + scale;
+#endif
+ if(shape <= 0 || scale <= 0) ML_ERR_return_NAN;
+
+ if (x <= 0)
+ return R_DT_0;
+ x = -powgnum(x / scale, shape);
+ if (lower_tail)
+ return (log_p
+ /* loggnum(1 - expgnum(x)) for x < 0 : */
+ ? (x > -M_LN2gnum ? loggnum(-expm1gnum(x)) : log1pgnum(-expgnum(x)))
+ : -expm1gnum(x));
+ /* else: !lower_tail */
+ return R_D_exp(x);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/pbinom.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000, 2002 The R Development Core Team
+ * Copyright (C) 2004 The R Foundation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The distribution function of the binomial distribution.
+ */
+
+gnm_float pbinom(gnm_float x, gnm_float n, gnm_float p, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(n) || isnangnum(p))
+ return x + n + p;
+ if (!finitegnum(n) || !finitegnum(p)) ML_ERR_return_NAN;
+
+#endif
+ if(R_D_nonint(n)) ML_ERR_return_NAN;
+ n = R_D_forceint(n);
+ if(n <= 0 || p < 0 || p > 1) ML_ERR_return_NAN;
+
+ x = floorgnum(x + 1e-7);
+ if (x < 0.0) return R_DT_0;
+ if (n <= x) return R_DT_1;
+ return pbeta(p, x + 1, n - x, !lower_tail, log_p);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dbinom.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, The R Core Development Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ *
+ * To compute the binomial probability, call dbinom(x,n,p).
+ * This checks for argument validity, and calls dbinom_raw().
+ *
+ * dbinom_raw() does the actual computation; note this is called by
+ * other functions in addition to dbinom()).
+ * (1) dbinom_raw() has both p and q arguments, when one may be represented
+ * more accurately than the other (in particular, in df()).
+ * (2) dbinom_raw() does NOT check that inputs x and n are integers. This
+ * should be done in the calling function, where necessary.
+ * (3) Also does not check for 0 <= p <= 1 and 0 <= q <= 1 or NaN's.
+ * Do this in the calling function.
+ */
+
+
+static gnm_float dbinom_raw(gnm_float x, gnm_float n, gnm_float p, gnm_float q, gboolean give_log)
+{
+ gnm_float f, lc;
+
+ if (p == 0) return((x == 0) ? R_D__1 : R_D__0);
+ if (q == 0) return((x == n) ? R_D__1 : R_D__0);
+
+ if (x == 0) {
+ if(n == 0) return R_D__1;
+ lc = (p < 0.1) ? -bd0(n,n*q) - n*p : n*loggnum(q);
+ return( R_D_exp(lc) );
+ }
+ if (x == n) {
+ lc = (q < 0.1) ? -bd0(n,n*p) - n*q : n*loggnum(p);
+ return( R_D_exp(lc) );
+ }
+ if (x < 0 || x > n) return( R_D__0 );
+
+ lc = stirlerr(n) - stirlerr(x) - stirlerr(n-x) - bd0(x,n*p) - bd0(n-x,n*q);
+ f = (M_2PIgnum*x*(n-x))/n;
+
+ return R_D_fexp(f,lc);
+}
+
+gnm_float dbinom(gnm_float x, gnm_float n, gnm_float p, gboolean give_log)
+{
+#ifdef IEEE_754
+ /* NaNs propagated correctly */
+ if (isnangnum(x) || isnangnum(n) || isnangnum(p)) return x + n + p;
+#endif
+
+ if (p < 0 || p > 1 || R_D_negInonint(n))
+ ML_ERR_return_NAN;
+ R_D_nonint_check(x);
+
+ n = R_D_forceint(n);
+ x = R_D_forceint(x);
+
+ return dbinom_raw(x,n,p,1-p,give_log);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/qbinom.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000, 2002 The R Development Core Team
+ * Copyright (C) 2003--2004 The R Foundation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The quantile function of the binomial distribution.
+ *
+ * METHOD
+ *
+ * Uses the Cornish-Fisher Expansion to include a skewness
+ * correction to a normal approximation. This gives an
+ * initial value which never seems to be off by more than
+ * 1 or 2. A search is then conducted of values close to
+ * this initial start point.
+ */
+
+
+gnm_float qbinom(gnm_float p, gnm_float n, gnm_float pr, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float q, mu, sigma, gamma, z, y;
+
+#ifdef IEEE_754
+ if (isnangnum(p) || isnangnum(n) || isnangnum(pr))
+ return p + n + pr;
+#endif
+ if(!finitegnum(p) || !finitegnum(n) || !finitegnum(pr))
+ ML_ERR_return_NAN;
+ R_Q_P01_check(p);
+
+ if(n != floorgnum(n + 0.5)) ML_ERR_return_NAN;
+ if (pr < 0 || pr > 1 || n < 0)
+ ML_ERR_return_NAN;
+
+ if (pr == 0. || n == 0) return 0.;
+ if (p == R_DT_0) return 0.;
+ if (p == R_DT_1) return n;
+
+ q = 1 - pr;
+ if(q == 0.) return n; /* covers the full range of the distribution */
+ mu = n * pr;
+ sigma = sqrtgnum(n * pr * q);
+ gamma = (q - pr) / sigma;
+
+#ifdef DEBUG_qbinom
+ REprintf("qbinom(p=%7" GNUM_FORMAT_g ", n=%" GNUM_FORMAT_g ", pr=%7" GNUM_FORMAT_g ", l.t.=%d, log=%d): sigm=%" GNUM_FORMAT_g ", gam=%" GNUM_FORMAT_g "\n",
+ p,n,pr, lower_tail, log_p, sigma, gamma);
+#endif
+ /* Note : "same" code in qpois.c, qbinom.c, qnbinom.c --
+ * FIXME: This is far from optimal [cancellation for p ~= 1, etc]: */
+ if(!lower_tail || log_p) {
+ p = R_DT_qIv(p); /* need check again (cancellation!): */
+ if (p == 0.) return 0.;
+ if (p == 1.) return n;
+ }
+ /* temporary hack --- FIXME --- */
+ if (p + 1.01*GNUM_EPSILON >= 1.) return n;
+
+ /* y := approx.value (Cornish-Fisher expansion) : */
+ z = qnorm(p, 0., 1., /*lower_tail*/TRUE, /*log_p*/FALSE);
+ y = floorgnum(mu + sigma * (z + gamma * (z*z - 1) / 6) + 0.5);
+ if(y > n) /* way off */ y = n;
+
+#ifdef DEBUG_qbinom
+ REprintf(" new (p,1-p)=(%7" GNUM_FORMAT_g ",%7" GNUM_FORMAT_g "), z=qnorm(..)=%7" GNUM_FORMAT_g ", y=%5" GNUM_FORMAT_g "\n", p, 1-p, z, y);
+#endif
+ z = pbinom(y, n, pr, /*lower_tail*/TRUE, /*log_p*/FALSE);
+
+ /* fuzz to ensure left continuity: */
+ p *= 1 - 64*GNUM_EPSILON;
+
+/*-- Fixme, here y can be way off --
+ should use interval search instead of primitive stepping down or up */
+
+#ifdef maybe_future
+ if((lower_tail && z >= p) || (!lower_tail && z <= p)) {
+#else
+ if(z >= p) {
+#endif
+ /* search to the left */
+#ifdef DEBUG_qbinom
+ REprintf("\tnew z=%7" GNUM_FORMAT_g " >= p = %7" GNUM_FORMAT_g " --> search to left (y--) ..\n", z,p);
+#endif
+ for(;;) {
+ if(y == 0 ||
+ (z = pbinom(y - 1, n, pr, /*l._t.*/TRUE, /*log_p*/FALSE)) < p)
+ return y;
+ y = y - 1;
+ }
+ }
+ else { /* search to the right */
+#ifdef DEBUG_qbinom
+ REprintf("\tnew z=%7" GNUM_FORMAT_g " < p = %7" GNUM_FORMAT_g " --> search to right (y++) ..\n", z,p);
+#endif
+ for(;;) {
+ y = y + 1;
+ if(y == n ||
+ (z = pbinom(y, n, pr, /*l._t.*/TRUE, /*log_p*/FALSE)) >= p)
+ return y;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dnbinom.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000 and Feb, 2001.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000--2001, The R Core Development Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ *
+ * Computes the negative binomial distribution. For integer n,
+ * this is probability of x failures before the nth success in a
+ * sequence of Bernoulli trials. We do not enforce integer n, since
+ * the distribution is well defined for non-integers,
+ * and this can be useful for e.g. overdispersed discrete survival times.
+ */
+
+
+gnm_float dnbinom(gnm_float x, gnm_float n, gnm_float p, gboolean give_log)
+{
+ gnm_float prob;
+
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(n) || isnangnum(p))
+ return x + n + p;
+#endif
+
+ if (p < 0 || p > 1 || n <= 0) ML_ERR_return_NAN;
+ R_D_nonint_check(x);
+ if (x < 0 || !finitegnum(x)) return R_D__0;
+ x = R_D_forceint(x);
+
+ prob = dbinom_raw(n, x+n, p, 1-p, give_log);
+ p = ((gnm_float)n)/(n+x);
+ return((give_log) ? loggnum(p) + prob : p * prob);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/pnbinom.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The distribution function of the negative binomial distribution.
+ *
+ * NOTES
+ *
+ * x = the number of failures before the n-th success
+ */
+
+
+gnm_float pnbinom(gnm_float x, gnm_float n, gnm_float p, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(n) || isnangnum(p))
+ return x + n + p;
+ if(!finitegnum(n) || !finitegnum(p)) ML_ERR_return_NAN;
+#endif
+ if (n <= 0 || p <= 0 || p >= 1) ML_ERR_return_NAN;
+
+ x = floorgnum(x + 1e-7);
+ if (x < 0) return R_DT_0;
+ if (!finitegnum(x)) return R_DT_1;
+ return pbeta(p, n, x + 1, lower_tail, log_p);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/qnbinom.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * SYNOPSIS
+ *
+ * #include <Rmath.h>
+ * double qnbinom(double p, double n, double pr, int lower_tail, int log_p)
+ *
+ * DESCRIPTION
+ *
+ * The quantile function of the negative binomial distribution.
+ *
+ * NOTES
+ *
+ * x = the number of failures before the n-th success
+ *
+ * METHOD
+ *
+ * Uses the Cornish-Fisher Expansion to include a skewness
+ * correction to a normal approximation. This gives an
+ * initial value which never seems to be off by more than
+ * 1 or 2. A search is then conducted of values close to
+ * this initial start point.
+ */
+
+
+gnm_float qnbinom(gnm_float p, gnm_float n, gnm_float pr, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float P, Q, mu, sigma, gamma, z, y;
+
+#ifdef IEEE_754
+ if (isnangnum(p) || isnangnum(n) || isnangnum(pr))
+ return p + n + pr;
+#endif
+ R_Q_P01_check(p);
+ if (pr <= 0 || pr >= 1 || n <= 0) ML_ERR_return_NAN;
+
+ if (p == R_DT_0) return 0;
+ if (p == R_DT_1) return gnm_pinf;
+ Q = 1.0 / pr;
+ P = (1.0 - pr) * Q;
+ mu = n * P;
+ sigma = sqrtgnum(n * P * Q);
+ gamma = (Q + P)/sigma;
+
+ /* Note : "same" code in qpois.c, qbinom.c, qnbinom.c --
+ * FIXME: This is far from optimal [cancellation for p ~= 1, etc]: */
+ if(!lower_tail || log_p) {
+ p = R_DT_qIv(p); /* need check again (cancellation!): */
+ if (p == R_DT_0) return 0;
+ if (p == R_DT_1) return gnm_pinf;
+ }
+ /* temporary hack --- FIXME --- */
+ if (p + 1.01*GNUM_EPSILON >= 1.) return gnm_pinf;
+
+ /* y := approx.value (Cornish-Fisher expansion) : */
+ z = qnorm(p, 0., 1., /*lower_tail*/TRUE, /*log_p*/FALSE);
+ y = floorgnum(mu + sigma * (z + gamma * (z*z - 1) / 6) + 0.5);
+
+ z = pnbinom(y, n, pr, /*lower_tail*/TRUE, /*log_p*/FALSE);
+
+ /* fuzz to ensure left continuity: */
+ p *= 1 - 64*GNUM_EPSILON;
+
+/*-- Fixme, here y can be way off --
+ should use interval search instead of primitive stepping down or up */
+
+#ifdef maybe_future
+ if((lower_tail && z >= p) || (!lower_tail && z <= p)) {
+#else
+ if(z >= p) {
+#endif
+ /* search to the left */
+ for(;;) {
+ if(y == 0 ||
+ (z = pnbinom(y - 1, n, pr, /*l._t.*/TRUE, /*log_p*/FALSE)) < p)
+ return y;
+ y = y - 1;
+ }
+ }
+ else { /* search to the right */
+
+ for(;;) {
+ y = y + 1;
+ if((z = pnbinom(y, n, pr, /*l._t.*/TRUE, /*log_p*/FALSE)) >= p)
+ return y;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dbeta.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, The R Core Development Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ * Beta density,
+ * (a+b-1)! a-1 b-1
+ * p(x;a,b) = ------------ x (1-x)
+ * (a-1)!(b-1)!
+ *
+ * = (a+b-1) dbinom(a-1; a+b-2,x)
+ *
+ * We must modify this when a<1 or b<1, to avoid passing negative
+ * arguments to dbinom_raw. Note that the modifications require
+ * division by x and/or 1-x, so cannot be used except where necessary.
+ */
+
+
+gnm_float dbeta(gnm_float x, gnm_float a, gnm_float b, gboolean give_log)
+{
+ gnm_float f, p;
+ volatile gnm_float am1, bm1; /* prevent roundoff trouble on some
+ platforms */
+
+#ifdef IEEE_754
+ /* NaNs propagated correctly */
+ if (isnangnum(x) || isnangnum(a) || isnangnum(b)) return x + a + b;
+#endif
+
+ if (a <= 0 || b <= 0) ML_ERR_return_NAN;
+ if (x < 0 || x > 1) return(R_D__0);
+ if (x == 0) {
+ if(a > 1) return(R_D__0);
+ if(a < 1) return(gnm_pinf);
+ /* a == 1 : */ return(R_D_val(b));
+ }
+ if (x == 1) {
+ if(b > 1) return(R_D__0);
+ if(b < 1) return(gnm_pinf);
+ /* b == 1 : */ return(R_D_val(a));
+ }
+ if (a < 1) {
+ if (b < 1) { /* a,b < 1 */
+ f = a*b/((a+b)*x*(1-x));
+ p = dbinom_raw(a,a+b, x,1-x, give_log);
+ }
+ else { /* a < 1 <= b */
+ f = a/x;
+ bm1 = b - 1;
+ p = dbinom_raw(a,a+bm1, x,1-x, give_log);
+ }
+ }
+ else {
+ if (b < 1) { /* a >= 1 > b */
+ f = b/(1-x);
+ am1 = a - 1;
+ p = dbinom_raw(am1,am1+b, x,1-x, give_log);
+ }
+ else { /* a,b >= 1 */
+ f = a+b-1;
+ am1 = a - 1;
+ bm1 = b - 1;
+ p = dbinom_raw(am1,am1+bm1, x,1-x, give_log);
+ }
+ }
+ return( (give_log) ? p + loggnum(f) : p*f );
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dhyper.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, 2001 The R Core Development Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ *
+ * Given a sequence of r successes and b failures, we sample n (\le b+r)
+ * items without replacement. The hypergeometric probability is the
+ * probability of x successes:
+ *
+ * choose(r, x) * choose(b, n-x)
+ * p(x; r,b,n) = ----------------------------- =
+ * choose(r+b, n)
+ *
+ * dbinom(x,r,p) * dbinom(n-x,b,p)
+ * = --------------------------------
+ * dbinom(n,r+b,p)
+ *
+ * for any p. For numerical stability, we take p=n/(r+b); with this choice,
+ * the denominator is not exponentially small.
+ */
+
+
+gnm_float dhyper(gnm_float x, gnm_float r, gnm_float b, gnm_float n, gboolean give_log)
+{
+ gnm_float p, q, p1, p2, p3;
+
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(r) || isnangnum(b) || isnangnum(n))
+ return x + r + b + n;
+#endif
+
+ if (R_D_negInonint(r) || R_D_negInonint(b) || R_D_negInonint(n) || n > r+b)
+ ML_ERR_return_NAN;
+ if (R_D_negInonint(x))
+ return(R_D__0);
+
+ x = R_D_forceint(x);
+ r = R_D_forceint(r);
+ b = R_D_forceint(b);
+ n = R_D_forceint(n);
+
+ if (n < x || r < x || n - x > b) return(R_D__0);
+ if (n == 0) return((x == 0) ? R_D__1 : R_D__0);
+
+ p = ((gnm_float)n)/((gnm_float)(r+b));
+ q = ((gnm_float)(r+b-n))/((gnm_float)(r+b));
+
+ p1 = dbinom_raw(x, r, p,q,give_log);
+ p2 = dbinom_raw(n-x,b, p,q,give_log);
+ p3 = dbinom_raw(n,r+b, p,q,give_log);
+
+ return( (give_log) ? p1 + p2 - p3 : p1*p2/p3 );
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dexp.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * DESCRIPTION
+ *
+ * The density of the exponential distribution.
+ */
+
+
+gnm_float dexp(gnm_float x, gnm_float scale, gboolean give_log)
+{
+#ifdef IEEE_754
+ /* NaNs propagated correctly */
+ if (isnangnum(x) || isnangnum(scale)) return x + scale;
+#endif
+ if (scale <= 0.0) ML_ERR_return_NAN;
+
+ if (x < 0.)
+ return R_D__0;
+ return (give_log ?
+ (-x / scale) - loggnum(scale) :
+ expgnum(-x / scale) / scale);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/pexp.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000-2002 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The distribution function of the exponential distribution.
+ */
+
+gnm_float pexp(gnm_float x, gnm_float scale, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(scale))
+ return x + scale;
+ if (scale < 0) ML_ERR_return_NAN;
+#else
+ if (scale <= 0) ML_ERR_return_NAN;
+#endif
+
+ if (x <= 0.)
+ return R_DT_0;
+ /* same as weibull( shape = 1): */
+ x = -(x / scale);
+ if (lower_tail)
+ return (log_p
+ /* loggnum(1 - expgnum(x)) for x < 0 : */
+ ? (x > -M_LN2gnum ? loggnum(-expm1gnum(x)) : log1pgnum(-expgnum(x)))
+ : -expm1gnum(x));
+ /* else: !lower_tail */
+ return R_D_exp(x);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dgeom.c from R. */
+/*
+ * AUTHOR
+ * Catherine Loader, catherine at research.bell-labs.com.
+ * October 23, 2000.
+ *
+ * Merge in to R:
+ * Copyright (C) 2000, 2001 The R Core Development Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ *
+ * DESCRIPTION
+ *
+ * Computes the geometric probabilities, Pr(X=x) = p(1-p)^x.
+ */
+
+
+gnm_float dgeom(gnm_float x, gnm_float p, gboolean give_log)
+{
+ gnm_float prob;
+
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(p)) return x + p;
+#endif
+
+ if (p < 0 || p > 1) ML_ERR_return_NAN;
+
+ R_D_nonint_check(x);
+ if (x < 0 || !finitegnum(x) || p == 0) return R_D__0;
+ x = R_D_forceint(x);
+
+ /* prob = (1-p)^x, stable for small p */
+ prob = dbinom_raw(0.,x, p,1-p, give_log);
+
+ return((give_log) ? loggnum(p) + prob : p*prob);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/pgeom.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000, 2001 The R Development Core Team
+ * Copyright (C) 2004 The R Foundation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * DESCRIPTION
+ *
+ * The distribution function of the geometric distribution.
+ */
+
+
+gnm_float pgeom(gnm_float x, gnm_float p, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(p))
+ return x + p;
+#endif
+ x = floorgnum(x +1e-7);
+ if(p < 0 || p > 1) ML_ERR_return_NAN;
+
+ if (x < 0. || p == 0.) return R_DT_0;
+ if (!finitegnum(x)) return R_DT_1;
+
+ if(p == 1.) { /* we cannot assume IEEE */
+ x = lower_tail ? 1: 0;
+ return log_p ? loggnum(x) : x;
+ }
+ x = log1pgnum(-p) * (x + 1);
+ if (log_p)
+ return R_DT_Clog(x);
+ else
+ return lower_tail ? -expm1gnum(x) : expgnum(x);
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/dcauchy.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (C) 2000 The R Development Core Team
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * DESCRIPTION
+ *
+ * The density of the Cauchy distribution.
+ */
+
+
+gnm_float dcauchy(gnm_float x, gnm_float location, gnm_float scale, gboolean give_log)
+{
+ gnm_float y;
+#ifdef IEEE_754
+ /* NaNs propagated correctly */
+ if (isnangnum(x) || isnangnum(location) || isnangnum(scale))
+ return x + location + scale;
+#endif
+ if (scale <= 0) ML_ERR_return_NAN;
+
+ y = (x - location) / scale;
+ return give_log ?
+ - loggnum(M_PIgnum * scale * (1. + y * y)) :
+ 1. / (M_PIgnum * scale * (1. + y * y));
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/bessel.h from R. */
+
+/* Constants und Documentation that apply to several of the
+ * ./bessel_[ijky].c files */
+
+/* *******************************************************************
+
+ Explanation of machine-dependent constants
+
+ beta = Radix for the floating-point system
+ minexp = Smallest representable power of beta
+ maxexp = Smallest power of beta that overflows
+ it = p = Number of bits (base-beta digits) in the mantissa
+ (significand) of a working precision (floating-point) variable
+ NSIG = Decimal significance desired. Should be set to
+ INT(LOG10(2)*it+1). Setting NSIG lower will result
+ in decreased accuracy while setting NSIG higher will
+ increase CPU time without increasing accuracy. The
+ truncation error is limited to a relative error of
+ T=.5*10^(-NSIG).
+ ENTEN = 10 ^ K, where K is the largest long such that
+ ENTEN is machine-representable in working precision
+ ENSIG = 10 ^ NSIG
+ RTNSIG = 10 ^ (-K) for the smallest long K such that
+ K >= NSIG/4
+ ENMTEN = Smallest ABS(X) such that X/4 does not underflow
+ XINF = Largest positive machine number; approximately beta ^ maxexp
+ == GNUM_MAX (defined in #include <float.h>)
+ SQXMIN = Square root of beta ^ minexp = sqrtgnum(GNUM_MIN)
+
+ EPS = The smallest positive floating-point number such that 1.0+EPS > 1.0
+ = beta ^ (-p) == GNUM_EPSILON
+
+
+ For I :
+
+ EXPARG = Largest working precision argument that the library
+ EXP routine can handle and upper limit on the
+ magnitude of X when IZE=1; approximately LOG(beta ^ maxexp)
+
+ For I and J :
+
+ xlrg_IJ = (was = XLARGE). Upper limit on the magnitude of X (when
+ IZE=2 for I()). Bear in mind that if ABS(X)=N, then at least
+ N iterations of the backward recursion will be executed.
+ The value of 10 ^ 4 is used on every machine.
+
+ For j :
+ XMIN_J = Smallest acceptable argument for RBESY; approximately
+ max(2*beta ^ minexp, 2/XINF), rounded up
+
+ For Y :
+
+ xlrg_Y = (was = XLARGE). Upper bound on X;
+ approximately 1/DEL, because the sine and cosine functions
+ have lost about half of their precision at that point.
+
+ EPS_SINC = Machine number below which singnum(x)/x = 1; approximately SQRT(EPS).
+ THRESH = Lower bound for use of the asymptotic form;
+ approximately AINT(-LOG10(EPS/2.0))+1.0
+
+
+ For K :
+
+ xmax_k = (was = XMAX). Upper limit on the magnitude of X when ize = 1;
+ i.e. maximal x for UNscaled answer.
+
+ Solution to equation:
+ W(X) * (1 -1/8 X + 9/128 X^2) = beta ^ minexp
+ where W(X) = EXP(-X)*SQRT(PI/2X)
+
+ --------------------------------------------------------------------
+
+ Approximate values for some important machines are:
+
+ beta minexp maxexp it NSIG ENTEN ENSIG RTNSIG ENMTEN EXPARG
+ IEEE (IBM/XT,
+ SUN, etc.) (S.P.) 2 -126 128 24 8 1e38 1e8 1e-2 4.70e-38 88
+ IEEE (...) (D.P.) 2 -1022 1024 53 16 1e308 1e16 1e-4 8.90e-308 709
+ CRAY-1 (S.P.) 2 -8193 8191 48 15 1e2465 1e15 1e-4 1.84e-2466 5677
+ Cyber 180/855
+ under NOS (S.P.) 2 -975 1070 48 15 1e322 1e15 1e-4 1.25e-293 741
+ IBM 3033 (D.P.) 16 -65 63 14 5 1e75 1e5 1e-2 2.16e-78 174
+ VAX (S.P.) 2 -128 127 24 8 1e38 1e8 1e-2 1.17e-38 88
+ VAX D-Format (D.P.) 2 -128 127 56 17 1e38 1e17 1e-5 1.17e-38 88
+ VAX G-Format (D.P.) 2 -1024 1023 53 16 1e307 1e16 1e-4 2.22e-308 709
+
+
+And routine specific :
+
+ xlrg_IJ xlrg_Y xmax_k EPS_SINC XMIN_J XINF THRESH
+ IEEE (IBM/XT,
+ SUN, etc.) (S.P.) 1e4 1e4 85.337 1e-4 2.36e-38 3.40e38 8.
+ IEEE (...) (D.P.) 1e4 1e8 705.342 1e-8 4.46e-308 1.79e308 16.
+ CRAY-1 (S.P.) 1e4 2e7 5674.858 5e-8 3.67e-2466 5.45e2465 15.
+ Cyber 180/855
+ under NOS (S.P.) 1e4 2e7 672.788 5e-8 6.28e-294 1.26e322 15.
+ IBM 3033 (D.P.) 1e4 1e8 177.852 1e-8 2.77e-76 7.23e75 17.
+ VAX (S.P.) 1e4 1e4 86.715 1e-4 1.18e-38 1.70e38 8.
+ VAX e-Format (D.P.) 1e4 1e9 86.715 1e-9 1.18e-38 1.70e38 17.
+ VAX G-Format (D.P.) 1e4 1e8 706.728 1e-8 2.23e-308 8.98e307 16.
+
+*/
+#define nsig_BESS 16
+#define ensig_BESS 1e16
+#define rtnsig_BESS 1e-4
+#define enmten_BESS 8.9e-308
+#define enten_BESS 1e308
+
+#define exparg_BESS 709.
+#define xlrg_BESS_IJ 1e4
+#define xlrg_BESS_Y 1e8
+#define thresh_BESS_Y 16.
+
+#define xmax_BESS_K 705.342/* maximal x for UNscaled answer */
+
+
+/* sqrtgnum(GNUM_MIN) = 1.491668e-154 */
+#define sqxmin_BESS_K 1.49e-154
+
+/* x < eps_sinc <==> singnum(x)/x == 1 (particularly "==>");
+ Linux (around 2001-02) gives GNM_const(2.14946906753213e-08)
+ Solaris 2.5.1 gives GNM_const(2.14911933289084e-08)
+*/
+#define M_eps_sinc 2.149e-8
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/bessel_i.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998-2001 Ross Ihaka and the R Development Core team.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* DESCRIPTION --> see below */
+
+
+/* From http://www.netlib.org/specfun/ribesl Fortran translated by f2c,...
+ * ------------------------------=#---- Martin Maechler, ETH Zurich
+ */
+
+#ifndef MATHLIB_STANDALONE
+#endif
+
+static void I_bessel(gnm_float *x, gnm_float *alpha, long *nb,
+ long *ize, gnm_float *bi, long *ncalc);
+
+gnm_float bessel_i(gnm_float x, gnm_float alpha, gnm_float expo)
+{
+ long nb, ncalc, ize;
+ gnm_float *bi;
+#ifndef MATHLIB_STANDALONE
+ char *vmax;
+#endif
+
+#ifdef IEEE_754
+ /* NaNs propagated correctly */
+ if (isnangnum(x) || isnangnum(alpha)) return x + alpha;
+#endif
+ if (x < 0) {
+ ML_ERROR(ME_RANGE);
+ return gnm_nan;
+ }
+ ize = (long)expo;
+ if (alpha < 0) {
+ /* Using Abramowitz & Stegun 9.6.2
+ * this may not be quite optimal (CPU and accuracy wise) */
+ return(bessel_i(x, -alpha, expo) +
+ bessel_k(x, -alpha, expo) * ((ize == 1)? 2. : 2.*expgnum(-x))/M_PIgnum
+ * singnum(-M_PIgnum * alpha));
+ }
+ nb = 1+ (long)floorgnum(alpha);/* nb-1 <= alpha < nb */
+ alpha -= (nb-1);
+#ifdef MATHLIB_STANDALONE
+ bi = (gnm_float *) calloc(nb, sizeof(gnm_float));
+ if (!bi) MATHLIB_ERROR("%s", "bessel_i allocation error");
+#else
+ vmax = vmaxget();
+ bi = (gnm_float *) R_alloc(nb, sizeof(gnm_float));
+#endif
+ I_bessel(&x, &alpha, &nb, &ize, bi, &ncalc);
+ if(ncalc != nb) {/* error input */
+ if(ncalc < 0)
+ MATHLIB_WARNING4("bessel_i(%" GNUM_FORMAT_g "): ncalc (=%ld) != nb (=%ld); alpha=%" GNUM_FORMAT_g "."
+ " Arg. out of range?\n",
+ x, ncalc, nb, alpha);
+ else
+ MATHLIB_WARNING2("bessel_i(%" GNUM_FORMAT_g ",nu=%" GNUM_FORMAT_g "): precision lost in result\n",
+ x, alpha+nb-1);
+ }
+ x = bi[nb-1];
+#ifdef MATHLIB_STANDALONE
+ free(bi);
+#else
+ vmaxset(vmax);
+#endif
+ return x;
+}
+
+static void I_bessel(gnm_float *x, gnm_float *alpha, long *nb,
+ long *ize, gnm_float *bi, long *ncalc)
+{
+/* -------------------------------------------------------------------
+
+ This routine calculates Bessel functions I_(N+ALPHA) (X)
+ for non-negative argument X, and non-negative order N+ALPHA,
+ with or without exponential scaling.
+
+
+ Explanation of variables in the calling sequence
+
+ X - Non-negative argument for which
+ I's or exponentially scaled I's (I*EXP(-X))
+ are to be calculated. If I's are to be calculated,
+ X must be less than EXPARG_BESS (see bessel.h).
+ ALPHA - Fractional part of order for which
+ I's or exponentially scaled I's (I*EXP(-X)) are
+ to be calculated. 0 <= ALPHA < 1.0.
+ NB - Number of functions to be calculated, NB > 0.
+ The first function calculated is of order ALPHA, and the
+ last is of order (NB - 1 + ALPHA).
+ IZE - Type. IZE = 1 if unscaled I's are to be calculated,
+ = 2 if exponentially scaled I's are to be calculated.
+ BI - Output vector of length NB. If the routine
+ terminates normally (NCALC=NB), the vector BI contains the
+ functions I(ALPHA,X) through I(NB-1+ALPHA,X), or the
+ corresponding exponentially scaled functions.
+ NCALC - Output variable indicating possible errors.
+ Before using the vector BI, the user should check that
+ NCALC=NB, i.e., all orders have been calculated to
+ the desired accuracy. See error returns below.
+
+
+ *******************************************************************
+ *******************************************************************
+
+ Error returns
+
+ In case of an error, NCALC != NB, and not all I's are
+ calculated to the desired accuracy.
+
+ NCALC < 0: An argument is out of range. For example,
+ NB <= 0, IZE is not 1 or 2, or IZE=1 and ABS(X) >= EXPARG_BESS.
+ In this case, the BI-vector is not calculated, and NCALC is
+ set to MIN0(NB,0)-1 so that NCALC != NB.
+
+ NB > NCALC > 0: Not all requested function values could
+ be calculated accurately. This usually occurs because NB is
+ much larger than ABS(X). In this case, BI[N] is calculated
+ to the desired accuracy for N <= NCALC, but precision
+ is lost for NCALC < N <= NB. If BI[N] does not vanish
+ for N > NCALC (because it is too small to be represented),
+ and BI[N]/BI[NCALC] = 10**(-K), then only the first NSIG-K
+ significant figures of BI[N] can be trusted.
+
+
+ Intrinsic functions required are:
+
+ DBLE, EXP, gamma_cody, INT, MAX, MIN, REAL, SQRT
+
+
+ Acknowledgement
+
+ This program is based on a program written by David J.
+ Sookne (2) that computes values of the Bessel functions J or
+ I of float argument and long order. Modifications include
+ the restriction of the computation to the I Bessel function
+ of non-negative float argument, the extension of the computation
+ to arbitrary positive order, the inclusion of optional
+ exponential scaling, and the elimination of most underflow.
+ An earlier version was published in (3).
+
+ References: "A Note on Backward Recurrence Algorithms," Olver,
+ F. W. J., and Sookne, D. J., Math. Comp. 26, 1972,
+ pp 941-947.
+
+ "Bessel Functions of Real Argument and Integer Order,"
+ Sookne, D. J., NBS Jour. of Res. B. 77B, 1973, pp
+ 125-132.
+
+ "ALGORITHM 597, Sequence of Modified Bessel Functions
+ of the First Kind," Cody, W. J., Trans. Math. Soft.,
+ 1983, pp. 242-245.
+
+ Latest modification: May 30, 1989
+
+ Modified by: W. J. Cody and L. Stoltz
+ Applied Mathematics Division
+ Argonne National Laboratory
+ Argonne, IL 60439
+*/
+
+ /*-------------------------------------------------------------------
+ Mathematical constants
+ -------------------------------------------------------------------*/
+ const gnm_float const__ = 1.585;
+
+ /* Local variables */
+ long nend, intx, nbmx, k, l, n, nstart;
+ gnm_float pold, test, p, em, en, empal, emp2al, halfx,
+ aa, bb, cc, psave, plast, tover, psavel, sum, nu, twonu;
+
+ /*Parameter adjustments */
+ --bi;
+ nu = *alpha;
+ twonu = nu + nu;
+
+ /*-------------------------------------------------------------------
+ Check for X, NB, OR IZE out of range.
+ ------------------------------------------------------------------- */
+ if (*nb > 0 && *x >= 0. && (0. <= nu && nu < 1.) &&
+ (1 <= *ize && *ize <= 2) ) {
+
+ *ncalc = *nb;
+ if((*ize == 1 && *x > exparg_BESS) ||
+ (*ize == 2 && *x > xlrg_BESS_IJ)) {
+ ML_ERROR(ME_RANGE);
+ for(k=1; k <= *nb; k++)
+ bi[k]=gnm_pinf;
+ return;
+ }
+ intx = (long) (*x);/* --> we will probably fail when *x > LONG_MAX */
+ if (*x >= rtnsig_BESS) { /* "non-small" x */
+/* -------------------------------------------------------------------
+ Initialize the forward sweep, the P-sequence of Olver
+ ------------------------------------------------------------------- */
+ nbmx = *nb - intx;
+ n = intx + 1;
+ en = (gnm_float) (n + n) + twonu;
+ plast = 1.;
+ p = en / *x;
+ /* ------------------------------------------------
+ Calculate general significance test
+ ------------------------------------------------ */
+ test = ensig_BESS + ensig_BESS;
+ if (intx << 1 > nsig_BESS * 5) {
+ test = sqrtgnum(test * p);
+ } else {
+ test /= powgnum(const__, (gnm_float)intx);
+ }
+ if (nbmx >= 3) {
+ /* --------------------------------------------------
+ Calculate P-sequence until N = NB-1
+ Check for possible overflow.
+ ------------------------------------------------ */
+ tover = enten_BESS / ensig_BESS;
+ nstart = intx + 2;
+ nend = *nb - 1;
+ for (k = nstart; k <= nend; ++k) {
+ n = k;
+ en += 2.;
+ pold = plast;
+ plast = p;
+ p = en * plast / *x + pold;
+ if (p > tover) {
+ /* ------------------------------------------------
+ To avoid overflow, divide P-sequence by TOVER.
+ Calculate P-sequence until ABS(P) > 1.
+ ---------------------------------------------- */
+ tover = enten_BESS;
+ p /= tover;
+ plast /= tover;
+ psave = p;
+ psavel = plast;
+ nstart = n + 1;
+ do {
+ ++n;
+ en += 2.;
+ pold = plast;
+ plast = p;
+ p = en * plast / *x + pold;
+ }
+ while (p <= 1.);
+
+ bb = en / *x;
+ /* ------------------------------------------------
+ Calculate backward test, and find NCALC,
+ the highest N such that the test is passed.
+ ------------------------------------------------ */
+ test = pold * plast / ensig_BESS;
+ test *= .5 - .5 / (bb * bb);
+ p = plast * tover;
+ --n;
+ en -= 2.;
+ nend = imin2(*nb,n);
+ for (l = nstart; l <= nend; ++l) {
+ *ncalc = l;
+ pold = psavel;
+ psavel = psave;
+ psave = en * psavel / *x + pold;
+ if (psave * psavel > test) {
+ goto L90;
+ }
+ }
+ *ncalc = nend + 1;
+L90:
+ --(*ncalc);
+ goto L120;
+ }
+ }
+ n = nend;
+ en = (gnm_float)(n + n) + twonu;
+ /*---------------------------------------------------
+ Calculate special significance test for NBMX > 2.
+ --------------------------------------------------- */
+ test = fmax2(test,sqrtgnum(plast * ensig_BESS) * sqrtgnum(p + p));
+ }
+ /* --------------------------------------------------------
+ Calculate P-sequence until significance test passed.
+ -------------------------------------------------------- */
+ do {
+ ++n;
+ en += 2.;
+ pold = plast;
+ plast = p;
+ p = en * plast / *x + pold;
+ } while (p < test);
+
+L120:
+/* -------------------------------------------------------------------
+ Initialize the backward recursion and the normalization sum.
+ ------------------------------------------------------------------- */
+ ++n;
+ en += 2.;
+ bb = 0.;
+ aa = 1. / p;
+ em = (gnm_float) n - 1.;
+ empal = em + nu;
+ emp2al = em - 1. + twonu;
+ sum = aa * empal * emp2al / em;
+ nend = n - *nb;
+ if (nend < 0) {
+ /* -----------------------------------------------------
+ N < NB, so store BI[N] and set higher orders to 0..
+ ----------------------------------------------------- */
+ bi[n] = aa;
+ nend = -nend;
+ for (l = 1; l <= nend; ++l) {
+ bi[n + l] = 0.;
+ }
+ } else {
+ if (nend > 0) {
+ /* -----------------------------------------------------
+ Recur backward via difference equation,
+ calculating (but not storing) BI[N], until N = NB.
+ --------------------------------------------------- */
+ for (l = 1; l <= nend; ++l) {
+ --n;
+ en -= 2.;
+ cc = bb;
+ bb = aa;
+ aa = en * bb / *x + cc;
+ em -= 1.;
+ emp2al -= 1.;
+ if (n == 1) {
+ break;
+ }
+ if (n == 2) {
+ emp2al = 1.;
+ }
+ empal -= 1.;
+ sum = (sum + aa * empal) * emp2al / em;
+ }
+ }
+ /* ---------------------------------------------------
+ Store BI[NB]
+ --------------------------------------------------- */
+ bi[n] = aa;
+ if (*nb <= 1) {
+ sum = sum + sum + aa;
+ goto L230;
+ }
+ /* -------------------------------------------------
+ Calculate and Store BI[NB-1]
+ ------------------------------------------------- */
+ --n;
+ en -= 2.;
+ bi[n] = en * aa / *x + bb;
+ if (n == 1) {
+ goto L220;
+ }
+ em -= 1.;
+ if (n == 2)
+ emp2al = 1.;
+ else
+ emp2al -= 1.;
+
+ empal -= 1.;
+ sum = (sum + bi[n] * empal) * emp2al / em;
+ }
+ nend = n - 2;
+ if (nend > 0) {
+ /* --------------------------------------------
+ Calculate via difference equation
+ and store BI[N], until N = 2.
+ ------------------------------------------ */
+ for (l = 1; l <= nend; ++l) {
+ --n;
+ en -= 2.;
+ bi[n] = en * bi[n + 1] / *x + bi[n + 2];
+ em -= 1.;
+ if (n == 2)
+ emp2al = 1.;
+ else
+ emp2al -= 1.;
+ empal -= 1.;
+ sum = (sum + bi[n] * empal) * emp2al / em;
+ }
+ }
+ /* ----------------------------------------------
+ Calculate BI[1]
+ -------------------------------------------- */
+ bi[1] = 2. * empal * bi[2] / *x + bi[3];
+L220:
+ sum = sum + sum + bi[1];
+
+L230:
+ /* ---------------------------------------------------------
+ Normalize. Divide all BI[N] by sum.
+ --------------------------------------------------------- */
+ if (nu != 0.)
+ sum *= (expgnum(lgamma1p (nu)) * powgnum(*x * .5, -nu));
+ if (*ize == 1)
+ sum *= expgnum(-(*x));
+ aa = enmten_BESS;
+ if (sum > 1.)
+ aa *= sum;
+ for (n = 1; n <= *nb; ++n) {
+ if (bi[n] < aa)
+ bi[n] = 0.;
+ else
+ bi[n] /= sum;
+ }
+ return;
+ } else {
+ /* -----------------------------------------------------------
+ Two-term ascending series for small X.
+ -----------------------------------------------------------*/
+ aa = 1.;
+ empal = 1. + nu;
+ if (*x > enmten_BESS)
+ halfx = .5 * *x;
+ else
+ halfx = 0.;
+ if (nu != 0.)
+ aa = powgnum(halfx, nu) / expgnum(lgamma1p(nu));
+ if (*ize == 2)
+ aa *= expgnum(-(*x));
+ if (*x + 1. > 1.)
+ bb = halfx * halfx;
+ else
+ bb = 0.;
+
+ bi[1] = aa + aa * bb / empal;
+ if (*x != 0. && bi[1] == 0.)
+ *ncalc = 0;
+ if (*nb > 1) {
+ if (*x == 0.) {
+ for (n = 2; n <= *nb; ++n) {
+ bi[n] = 0.;
+ }
+ } else {
+ /* -------------------------------------------------
+ Calculate higher-order functions.
+ ------------------------------------------------- */
+ cc = halfx;
+ tover = (enmten_BESS + enmten_BESS) / *x;
+ if (bb != 0.)
+ tover = enmten_BESS / bb;
+ for (n = 2; n <= *nb; ++n) {
+ aa /= empal;
+ empal += 1.;
+ aa *= cc;
+ if (aa <= tover * empal)
+ bi[n] = aa = 0.;
+ else
+ bi[n] = aa + aa * bb / empal;
+ if (bi[n] == 0. && *ncalc > n)
+ *ncalc = n - 1;
+ }
+ }
+ }
+ }
+ } else {
+ *ncalc = imin2(*nb,0) - 1;
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* Imported src/nmath/bessel_k.c from R. */
+/*
+ * Mathlib : A C Library of Special Functions
+ * Copyright (C) 1998-2001 Ross Ihaka and the R Development Core team.
+ * Copyright (C) 2002-3 The R Foundation
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* DESCRIPTION --> see below */
+
+
+/* From http://www.netlib.org/specfun/rkbesl Fortran translated by f2c,...
+ * ------------------------------=#---- Martin Maechler, ETH Zurich
+ */
+
+#ifndef MATHLIB_STANDALONE
+#endif
+
+static void K_bessel(gnm_float *x, gnm_float *alpha, long *nb,
+ long *ize, gnm_float *bk, long *ncalc);
+
+gnm_float bessel_k(gnm_float x, gnm_float alpha, gnm_float expo)
+{
+ long nb, ncalc, ize;
+ gnm_float *bk;
+#ifndef MATHLIB_STANDALONE
+ char *vmax;
+#endif
+
+#ifdef IEEE_754
+ /* NaNs propagated correctly */
+ if (isnangnum(x) || isnangnum(alpha)) return x + alpha;
+#endif
+ if (x < 0) {
+ ML_ERROR(ME_RANGE);
+ return gnm_nan;
+ }
+ ize = (long)expo;
+ if(alpha < 0)
+ alpha = -alpha;
+ nb = 1+ (long)floorgnum(alpha);/* nb-1 <= |alpha| < nb */
+ alpha -= (nb-1);
+#ifdef MATHLIB_STANDALONE
+ bk = (gnm_float *) calloc(nb, sizeof(gnm_float));
+ if (!bk) MATHLIB_ERROR("%s", "bessel_k allocation error");
+#else
+ vmax = vmaxget();
+ bk = (gnm_float *) R_alloc(nb, sizeof(gnm_float));
+#endif
+ K_bessel(&x, &alpha, &nb, &ize, bk, &ncalc);
+ if(ncalc != nb) {/* error input */
+ if(ncalc < 0)
+ MATHLIB_WARNING4("bessel_k(%" GNUM_FORMAT_g "): ncalc (=%ld) != nb (=%ld); alpha=%" GNUM_FORMAT_g ". Arg. out of range?\n",
+ x, ncalc, nb, alpha);
+ else
+ MATHLIB_WARNING2("bessel_k(%" GNUM_FORMAT_g ",nu=%" GNUM_FORMAT_g "): precision lost in result\n",
+ x, alpha+nb-1);
+ }
+ x = bk[nb-1];
+#ifdef MATHLIB_STANDALONE
+ free(bk);
+#else
+ vmaxset(vmax);
+#endif
+ return x;
+}
+
+static void K_bessel(gnm_float *x, gnm_float *alpha, long *nb,
+ long *ize, gnm_float *bk, long *ncalc)
+{
+/*-------------------------------------------------------------------
+
+ This routine calculates modified Bessel functions
+ of the third kind, K_(N+ALPHA) (X), for non-negative
+ argument X, and non-negative order N+ALPHA, with or without
+ exponential scaling.
+
+ Explanation of variables in the calling sequence
+
+ X - Non-negative argument for which
+ K's or exponentially scaled K's (K*EXP(X))
+ are to be calculated. If K's are to be calculated,
+ X must not be greater than XMAX_BESS_K.
+ ALPHA - Fractional part of order for which
+ K's or exponentially scaled K's (K*EXP(X)) are
+ to be calculated. 0 <= ALPHA < 1.0.
+ NB - Number of functions to be calculated, NB > 0.
+ The first function calculated is of order ALPHA, and the
+ last is of order (NB - 1 + ALPHA).
+ IZE - Type. IZE = 1 if unscaled K's are to be calculated,
+ = 2 if exponentially scaled K's are to be calculated.
+ BK - Output vector of length NB. If the
+ routine terminates normally (NCALC=NB), the vector BK
+ contains the functions K(ALPHA,X), ... , K(NB-1+ALPHA,X),
+ or the corresponding exponentially scaled functions.
+ If (0 < NCALC < NB), BK(I) contains correct function
+ values for I <= NCALC, and contains the ratios
+ K(ALPHA+I-1,X)/K(ALPHA+I-2,X) for the rest of the array.
+ NCALC - Output variable indicating possible errors.
+ Before using the vector BK, the user should check that
+ NCALC=NB, i.e., all orders have been calculated to
+ the desired accuracy. See error returns below.
+
+
+ *******************************************************************
+
+ Error returns
+
+ In case of an error, NCALC != NB, and not all K's are
+ calculated to the desired accuracy.
+
+ NCALC < -1: An argument is out of range. For example,
+ NB <= 0, IZE is not 1 or 2, or IZE=1 and ABS(X) >= XMAX_BESS_K.
+ In this case, the B-vector is not calculated,
+ and NCALC is set to MIN0(NB,0)-2 so that NCALC != NB.
+ NCALC = -1: Either K(ALPHA,X) >= XINF or
+ K(ALPHA+NB-1,X)/K(ALPHA+NB-2,X) >= XINF. In this case,
+ the B-vector is not calculated. Note that again
+ NCALC != NB.
+
+ 0 < NCALC < NB: Not all requested function values could
+ be calculated accurately. BK(I) contains correct function
+ values for I <= NCALC, and contains the ratios
+ K(ALPHA+I-1,X)/K(ALPHA+I-2,X) for the rest of the array.
+
+
+ Intrinsic functions required are:
+
+ ABS, AINT, EXP, INT, LOG, MAX, MIN, SINH, SQRT
+
+
+ Acknowledgement
+
+ This program is based on a program written by J. B. Campbell
+ (2) that computes values of the Bessel functions K of float
+ argument and float order. Modifications include the addition
+ of non-scaled functions, parameterization of machine
+ dependencies, and the use of more accurate approximations
+ for SINH and SIN.
+
+ References: "On Temme's Algorithm for the Modified Bessel
+ Functions of the Third Kind," Campbell, J. B.,
+ TOMS 6(4), Dec. 1980, pp. 581-586.
+
+ "A FORTRAN IV Subroutine for the Modified Bessel
+ Functions of the Third Kind of Real Order and Real
+ Argument," Campbell, J. B., Report NRC/ERB-925,
+ National Research Council, Canada.
+
+ Latest modification: May 30, 1989
+
+ Modified by: W. J. Cody and L. Stoltz
+ Applied Mathematics Division
+ Argonne National Laboratory
+ Argonne, IL 60439
+
+ -------------------------------------------------------------------
+*/
+ /*---------------------------------------------------------------------
+ * Mathematical constants
+ * A = LOG(2) - Euler's constant
+ * D = SQRT(2/PI)
+ ---------------------------------------------------------------------*/
+ const gnm_float a = GNM_const(.11593151565841244881);
+
+ /*---------------------------------------------------------------------
+ P, Q - Approximation for LOG(GAMMA(1+ALPHA))/ALPHA + Euler's constant
+ Coefficients converted from hex to decimal and modified
+ by W. J. Cody, 2/26/82 */
+ static const gnm_float p[8] = { GNM_const(.805629875690432845),GNM_const(20.4045500205365151),
+ GNM_const(157.705605106676174),GNM_const(536.671116469207504),GNM_const(900.382759291288778),
+ GNM_const(730.923886650660393),GNM_const(229.299301509425145),GNM_const(.822467033424113231) };
+ static const gnm_float q[7] = { GNM_const(29.4601986247850434),GNM_const(277.577868510221208),
+ GNM_const(1206.70325591027438),GNM_const(2762.91444159791519),GNM_const(3443.74050506564618),
+ GNM_const(2210.63190113378647),GNM_const(572.267338359892221) };
+ /* R, S - Approximation for (1-ALPHA*PI/SIN(ALPHA*PI))/(2.D0*ALPHA) */
+ static const gnm_float r[5] = { GNM_const(-.48672575865218401848),GNM_const(13.079485869097804016),
+ GNM_const(-101.96490580880537526),GNM_const(347.65409106507813131),
+ GNM_const(3.495898124521934782e-4) };
+ static const gnm_float s[4] = { GNM_const(-25.579105509976461286),GNM_const(212.57260432226544008),
+ GNM_const(-610.69018684944109624),GNM_const(422.69668805777760407) };
+ /* T - Approximation for SINH(Y)/Y */
+ static const gnm_float t[6] = { GNM_const(1.6125990452916363814e-10),
+ GNM_const(2.5051878502858255354e-8),GNM_const(2.7557319615147964774e-6),
+ GNM_const(1.9841269840928373686e-4),GNM_const(.0083333333333334751799),
+ GNM_const(.16666666666666666446) };
+ /*---------------------------------------------------------------------*/
+ static const gnm_float estm[6] = { 52.0583,5.7607,2.7782,14.4303,185.3004, 9.3715 };
+ static const gnm_float estf[7] = { 41.8341,7.1075,6.4306,42.511,1.35633,84.5096,20.};
+
+ /* Local variables */
+ long iend, i, j, k, m, ii, mplus1;
+ gnm_float x2by4, twox, c, blpha, ratio, wminf;
+ gnm_float d1, d2, d3, f0, f1, f2, p0, q0, t1, t2, twonu;
+ gnm_float dm, ex, bk1, bk2, nu;
+
+ ii = 0; /* -Wall */
+
+ ex = *x;
+ nu = *alpha;
+ *ncalc = imin2(*nb,0) - 2;
+ if (*nb > 0 && (0. <= nu && nu < 1.) && (1 <= *ize && *ize <= 2)) {
+ if(ex <= 0 || (*ize == 1 && ex > xmax_BESS_K)) {
+ if(ex <= 0) {
+ ML_ERROR(ME_RANGE);
+ for(i=0; i < *nb; i++)
+ bk[i] = gnm_pinf;
+ } else /* would only have underflow */
+ for(i=0; i < *nb; i++)
+ bk[i] = 0.;
+ *ncalc = *nb;
+ return;
+ }
+ k = 0;
+ if (nu < sqxmin_BESS_K) {
+ nu = 0.;
+ } else if (nu > .5) {
+ k = 1;
+ nu -= 1.;
+ }
+ twonu = nu + nu;
+ iend = *nb + k - 1;
+ c = nu * nu;
+ d3 = -c;
+ if (ex <= 1.) {
+ /* ------------------------------------------------------------
+ Calculation of P0 = GAMMA(1+ALPHA) * (2/X)**ALPHA
+ Q0 = GAMMA(1-ALPHA) * (X/2)**ALPHA
+ ------------------------------------------------------------ */
+ d1 = 0.; d2 = p[0];
+ t1 = 1.; t2 = q[0];
+ for (i = 2; i <= 7; i += 2) {
+ d1 = c * d1 + p[i - 1];
+ d2 = c * d2 + p[i];
+ t1 = c * t1 + q[i - 1];
+ t2 = c * t2 + q[i];
+ }
+ d1 = nu * d1;
+ t1 = nu * t1;
+ f1 = loggnum(ex);
+ f0 = a + nu * (p[7] - nu * (d1 + d2) / (t1 + t2)) - f1;
+ q0 = expgnum(-nu * (a - nu * (p[7] + nu * (d1-d2) / (t1-t2)) - f1));
+ f1 = nu * f0;
+ p0 = expgnum(f1);
+ /* -----------------------------------------------------------
+ Calculation of F0 =
+ ----------------------------------------------------------- */
+ d1 = r[4];
+ t1 = 1.;
+ for (i = 0; i < 4; ++i) {
+ d1 = c * d1 + r[i];
+ t1 = c * t1 + s[i];
+ }
+ /* d2 := sinhgnum(f1)/ nu = sinhgnum(f1)/(f1/f0)
+ * = f0 * sinhgnum(f1)/f1 */
+ if (gnumabs(f1) <= .5) {
+ f1 *= f1;
+ d2 = 0.;
+ for (i = 0; i < 6; ++i) {
+ d2 = f1 * d2 + t[i];
+ }
+ d2 = f0 + f0 * f1 * d2;
+ } else {
+ d2 = sinhgnum(f1) / nu;
+ }
+ f0 = d2 - nu * d1 / (t1 * p0);
+ if (ex <= 1e-10) {
+ /* ---------------------------------------------------------
+ X <= 1.0E-10
+ Calculation of K(ALPHA,X) and X*K(ALPHA+1,X)/K(ALPHA,X)
+ --------------------------------------------------------- */
+ bk[0] = f0 + ex * f0;
+ if (*ize == 1) {
+ bk[0] -= ex * bk[0];
+ }
+ ratio = p0 / f0;
+ c = ex * GNUM_MAX;
+ if (k != 0) {
+ /* ---------------------------------------------------
+ Calculation of K(ALPHA,X)
+ and X*K(ALPHA+1,X)/K(ALPHA,X), ALPHA >= 1/2
+ --------------------------------------------------- */
+ *ncalc = -1;
+ if (bk[0] >= c / ratio) {
+ return;
+ }
+ bk[0] = ratio * bk[0] / ex;
+ twonu += 2.;
+ ratio = twonu;
+ }
+ *ncalc = 1;
+ if (*nb == 1)
+ return;
+
+ /* -----------------------------------------------------
+ Calculate K(ALPHA+L,X)/K(ALPHA+L-1,X),
+ L = 1, 2, ... , NB-1
+ ----------------------------------------------------- */
+ *ncalc = -1;
+ for (i = 1; i < *nb; ++i) {
+ if (ratio >= c)
+ return;
+
+ bk[i] = ratio / ex;
+ twonu += 2.;
+ ratio = twonu;
+ }
+ *ncalc = 1;
+ goto L420;
+ } else {
+ /* ------------------------------------------------------
+ 10^-10 < X <= 1.0
+ ------------------------------------------------------ */
+ c = 1.;
+ x2by4 = ex * ex / 4.;
+ p0 = .5 * p0;
+ q0 = .5 * q0;
+ d1 = -1.;
+ d2 = 0.;
+ bk1 = 0.;
+ bk2 = 0.;
+ f1 = f0;
+ f2 = p0;
+ do {
+ d1 += 2.;
+ d2 += 1.;
+ d3 = d1 + d3;
+ c = x2by4 * c / d2;
+ f0 = (d2 * f0 + p0 + q0) / d3;
+ p0 /= d2 - nu;
+ q0 /= d2 + nu;
+ t1 = c * f0;
+ t2 = c * (p0 - d2 * f0);
+ bk1 += t1;
+ bk2 += t2;
+ } while (gnumabs(t1 / (f1 + bk1)) > GNUM_EPSILON ||
+ gnumabs(t2 / (f2 + bk2)) > GNUM_EPSILON);
+ bk1 = f1 + bk1;
+ bk2 = 2. * (f2 + bk2) / ex;
+ if (*ize == 2) {
+ d1 = expgnum(ex);
+ bk1 *= d1;
+ bk2 *= d1;
+ }
+ wminf = estf[0] * ex + estf[1];
+ }
+ } else if (GNUM_EPSILON * ex > 1.) {
+ /* -------------------------------------------------
+ X > 1./EPS
+ ------------------------------------------------- */
+ *ncalc = *nb;
+ bk1 = 1. / (M_SQRT_2dPI * sqrtgnum(ex));
+ for (i = 0; i < *nb; ++i)
+ bk[i] = bk1;
+ return;
+
+ } else {
+ /* -------------------------------------------------------
+ X > 1.0
+ ------------------------------------------------------- */
+ twox = ex + ex;
+ blpha = 0.;
+ ratio = 0.;
+ if (ex <= 4.) {
+ /* ----------------------------------------------------------
+ Calculation of K(ALPHA+1,X)/K(ALPHA,X), 1.0 <= X <= 4.0
+ ----------------------------------------------------------*/
+ d2 = gnm_trunc(estm[0] / ex + estm[1]);
+ m = (long) d2;
+ d1 = d2 + d2;
+ d2 -= .5;
+ d2 *= d2;
+ for (i = 2; i <= m; ++i) {
+ d1 -= 2.;
+ d2 -= d1;
+ ratio = (d3 + d2) / (twox + d1 - ratio);
+ }
+ /* -----------------------------------------------------------
+ Calculation of I(|ALPHA|,X) and I(|ALPHA|+1,X) by backward
+ recurrence and K(ALPHA,X) from the wronskian
+ -----------------------------------------------------------*/
+ d2 = gnm_trunc(estm[2] * ex + estm[3]);
+ m = (long) d2;
+ c = gnumabs(nu);
+ d3 = c + c;
+ d1 = d3 - 1.;
+ f1 = GNUM_MIN;
+ f0 = (2. * (c + d2) / ex + .5 * ex / (c + d2 + 1.)) * GNUM_MIN;
+ for (i = 3; i <= m; ++i) {
+ d2 -= 1.;
+ f2 = (d3 + d2 + d2) * f0;
+ blpha = (1. + d1 / d2) * (f2 + blpha);
+ f2 = f2 / ex + f1;
+ f1 = f0;
+ f0 = f2;
+ }
+ f1 = (d3 + 2.) * f0 / ex + f1;
+ d1 = 0.;
+ t1 = 1.;
+ for (i = 1; i <= 7; ++i) {
+ d1 = c * d1 + p[i - 1];
+ t1 = c * t1 + q[i - 1];
+ }
+ p0 = expgnum(c * (a + c * (p[7] - c * d1 / t1) - loggnum(ex))) / ex;
+ f2 = (c + .5 - ratio) * f1 / ex;
+ bk1 = p0 + (d3 * f0 - f2 + f0 + blpha) / (f2 + f1 + f0) * p0;
+ if (*ize == 1) {
+ bk1 *= expgnum(-ex);
+ }
+ wminf = estf[2] * ex + estf[3];
+ } else {
+ /* ---------------------------------------------------------
+ Calculation of K(ALPHA,X) and K(ALPHA+1,X)/K(ALPHA,X), by
+ backward recurrence, for X > 4.0
+ ----------------------------------------------------------*/
+ dm = gnm_trunc(estm[4] / ex + estm[5]);
+ m = (long) dm;
+ d2 = dm - .5;
+ d2 *= d2;
+ d1 = dm + dm;
+ for (i = 2; i <= m; ++i) {
+ dm -= 1.;
+ d1 -= 2.;
+ d2 -= d1;
+ ratio = (d3 + d2) / (twox + d1 - ratio);
+ blpha = (ratio + ratio * blpha) / dm;
+ }
+ bk1 = 1. / ((M_SQRT_2dPI + M_SQRT_2dPI * blpha) * sqrtgnum(ex));
+ if (*ize == 1)
+ bk1 *= expgnum(-ex);
+ wminf = estf[4] * (ex - gnumabs(ex - estf[6])) + estf[5];
+ }
+ /* ---------------------------------------------------------
+ Calculation of K(ALPHA+1,X)
+ from K(ALPHA,X) and K(ALPHA+1,X)/K(ALPHA,X)
+ --------------------------------------------------------- */
+ bk2 = bk1 + bk1 * (nu + .5 - ratio) / ex;
+ }
+ /*--------------------------------------------------------------------
+ Calculation of 'NCALC', K(ALPHA+I,X), I = 0, 1, ... , NCALC-1,
+ & K(ALPHA+I,X)/K(ALPHA+I-1,X), I = NCALC, NCALC+1, ... , NB-1
+ -------------------------------------------------------------------*/
+ *ncalc = *nb;
+ bk[0] = bk1;
+ if (iend == 0)
+ return;
+
+ j = 1 - k;
+ if (j >= 0)
+ bk[j] = bk2;
+
+ if (iend == 1)
+ return;
+
+ m = imin2((long) (wminf - nu),iend);
+ for (i = 2; i <= m; ++i) {
+ t1 = bk1;
+ bk1 = bk2;
+ twonu += 2.;
+ if (ex < 1.) {
+ if (bk1 >= GNUM_MAX / twonu * ex)
+ break;
+ } else {
+ if (bk1 / ex >= GNUM_MAX / twonu)
+ break;
+ }
+ bk2 = twonu / ex * bk1 + t1;
+ ii = i;
+ ++j;
+ if (j >= 0) {
+ bk[j] = bk2;
+ }
+ }
+
+ m = ii;
+ if (m == iend) {
+ return;
+ }
+ ratio = bk2 / bk1;
+ mplus1 = m + 1;
+ *ncalc = -1;
+ for (i = mplus1; i <= iend; ++i) {
+ twonu += 2.;
+ ratio = twonu / ex + 1./ratio;
+ ++j;
+ if (j >= 1) {
+ bk[j] = ratio;
+ } else {
+ if (bk2 >= GNUM_MAX / ratio)
+ return;
+
+ bk2 *= ratio;
+ }
+ }
+ *ncalc = imax2(1, mplus1 - k);
+ if (*ncalc == 1)
+ bk[0] = bk2;
+ if (*nb == 1)
+ return;
+
+L420:
+ for (i = *ncalc; i < *nb; ++i) { /* i == *ncalc */
+#ifndef IEEE_754
+ if (bk[i-1] >= GNUM_MAX / bk[i])
+ return;
+#endif
+ bk[i] *= bk[i-1];
+ (*ncalc)++;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------ */
+/* --- END MAGIC R SOURCE MARKER --- */
+
+
+/* --- BEGIN IANDJMSMITH SOURCE MARKER --- */
+
+/* Continued fraction for calculation of
+ * 1/i + x/(i+d) + x*x/(i+2*d) + x*x*x/(i+3*d) + ...
+ */
+static gnm_float
+logcf (gnm_float x, gnm_float i, gnm_float d)
+{
+ gnm_float c1 = 2 * d;
+ gnm_float c2 = i + d;
+ gnm_float c4 = c2 + d;
+ gnm_float a1 = c2;
+ gnm_float b1 = i * (c2 - i * x);
+ gnm_float b2 = d * d * x;
+ gnm_float a2 = c4 * c2 - b2;
+ const gnm_float cfVSmall = 1.0e-14;
+
+#if 0
+ assert (i > 0);
+ assert (d >= 0);
+#endif
+
+ b2 = c4 * b1 - i * b2;
+
+ while (gnumabs (a2 * b1 - a1 * b2) > gnumabs (cfVSmall * b1 * b2)) {
+ gnm_float c3 = c2*c2*x;
+ c2 += d;
+ c4 += d;
+ a1 = c4 * a2 - c3 * a1;
+ b1 = c4 * b2 - c3 * b1;
+
+ c3 = c1 * c1 * x;
+ c1 += d;
+ c4 += d;
+ a2 = c4 * a1 - c3 * a2;
+ b2 = c4 * b1 - c3 * b2;
+
+ if (gnumabs (b2) > scalefactor) {
+ a1 *= 1 / scalefactor;
+ b1 *= 1 / scalefactor;
+ a2 *= 1 / scalefactor;
+ b2 *= 1 / scalefactor;
+ } else if (gnumabs (b2) < 1 / scalefactor) {
+ a1 *= scalefactor;
+ b1 *= scalefactor;
+ a2 *= scalefactor;
+ b2 *= scalefactor;
+ }
+ }
+
+ return a2 / b2;
+}
+
+
+/* Accurate calculation of log(1+x)-x, particularly for small x. */
+gnm_float
+log1pmx (gnm_float x)
+{
+ static const gnm_float minLog1Value = -0.79149064;
+ static const gnm_float two = 2;
+
+ if (gnumabs (x) < 1.0e-2) {
+ gnm_float term = x / (2 + x);
+ gnm_float y = term * term;
+ return term * ((((two / 9 * y + two / 7) * y + two / 5) * y + two / 3) * y - x);
+ } else if (x < minLog1Value || x > 1) {
+ return log1pgnum (x) - x;
+ } else {
+ gnm_float term = x / (2 + x);
+ gnm_float y = term * term;
+ return term * (2 * y * logcf (y, 3, 2) - x);
+ }
+}
+
+/* Accurate loggnum (1 - expgnum (p)) for p <= 0. */
+gnm_float
+swap_log_tail (gnm_float lp)
+{
+ if (lp > -1 / loggnum (2))
+ return loggnum (-expm1gnum (lp)); /* Good formula for lp near zero. */
+ else
+ return log1pgnum (-expgnum (lp)); /* Good formula for small lp. */
+}
+
+
+/* Calculation of logfbit(x)-logfbit(1+x). y2 must be < 1. */
+static gnm_float
+logfbitdif (gnm_float x)
+{
+ gnm_float y = 1 / (2 * x + 3);
+ gnm_float y2 = y * y;
+ return y2 * logcf (y2, 3, 2);
+}
+
+/*
+ * lfbc{1-7} from this Mathematica program:
+ *
+ * Table[Numerator[BernoulliB[2n]/(2n(2n - 1))], {n, 1, 22}]
+ * Table[Denominator[BernoulliB[2n]/(2n(2n - 1))], {n, 1, 22}]
+ */
+static const gnm_float lfbc1 = GNM_const (1.0) / 12;
+static const gnm_float lfbc2 = GNM_const (1.0) / 30;
+static const gnm_float lfbc3 = GNM_const (1.0) / 105;
+static const gnm_float lfbc4 = GNM_const (1.0) / 140;
+static const gnm_float lfbc5 = GNM_const (1.0) / 99;
+static const gnm_float lfbc6 = GNM_const (691.0) / 30030;
+static const gnm_float lfbc7 = GNM_const (1.0) / 13;
+/* lfbc{8,9} to make logfbit(6) and logfbit(7) exact. */
+static const gnm_float lfbc8 = GNM_const (3.5068606896459316479e-01);
+static const gnm_float lfbc9 = GNM_const (1.6769998201671114808);
+
+/* This is also stirlerr(x+1). */
+gnm_float
+logfbit (gnm_float x)
+{
+ /*
+ * Error part of Stirling's formula where
+ * log(x!) = log(sqrt(twopi))+(x+0.5)*log(x+1)-(x+1)+logfbit(x).
+ *
+ * Are we ever concerned about the relative error involved in this
+ * function? I don't think so.
+ */
+ if (x >= 1e10) return 1 / (12 * (x + 1));
+ else if (x >= 6) {
+ gnm_float x1 = x + 1;
+ gnm_float x2 = 1 / (x1 * x1);
+ gnm_float x3 =
+ x2 * (lfbc2 - x2 *
+ (lfbc3 - x2 *
+ (lfbc4 - x2 *
+ (lfbc5 - x2 *
+ (lfbc6 - x2 *
+ (lfbc7 - x2 *
+ (lfbc8 - x2 *
+ lfbc9)))))));
+ return lfbc1 * (1 - x3) / x1;
+ }
+ else if (x == 5) return GNM_const (0.13876128823070747998745727023762908562e-1);
+ else if (x == 4) return GNM_const (0.16644691189821192163194865373593391145e-1);
+ else if (x == 3) return GNM_const (0.20790672103765093111522771767848656333e-1);
+ else if (x == 2) return GNM_const (0.27677925684998339148789292746244666596e-1);
+ else if (x == 1) return GNM_const (0.41340695955409294093822081407117508025e-1);
+ else if (x == 0) return GNM_const (0.81061466795327258219670263594382360138e-1);
+ else if (x > -1) {
+ gnm_float x1 = x;
+ gnm_float x2 = 0;
+ while (x1 < 6) {
+ x2 += logfbitdif (x1);
+ x1++;
+ }
+ return x2 + logfbit (x1);
+ }
+ else return gnm_pinf;
+}
+
+/* Calculation of logfbit1(x)-logfbit1(1+x). */
+static gnm_float
+logfbit1dif (gnm_float x)
+{
+ return (logfbitdif (x) - 1 / (4 * (x + 1) * (x + 2))) / (x + 1.5);
+}
+
+/* Derivative logfbit. */
+static gnm_float
+logfbit1 (gnm_float x)
+{
+ if (x >= 1e10) return -lfbc1 * powgnum (x + 1, -2);
+ else if (x >= 6) {
+ gnm_float x1 = x + 1;
+ gnm_float x2 = 1 / (x1 * x1);
+ gnm_float x3 =
+ x2 * (3 * lfbc2 - x2*
+ (5 * lfbc3 - x2 *
+ (7 * lfbc4 - x2 *
+ (9 * lfbc5 - x2 *
+ (11 * lfbc6 - x2 *
+ (13 * lfbc7 - x2 *
+ (15 * lfbc8 - x2 *
+ 17 * lfbc9)))))));
+ return -lfbc1 * (1 - x3) * x2;
+ }
+ else if (x > -1) {
+ gnm_float x1 = x;
+ gnm_float x2 = 0;
+ while (x1 < 6) {
+ x2 += logfbit1dif (x1);
+ x1++;
+ }
+ return x2 + logfbit1 (x1);
+ }
+ else return gnm_ninf;
+}
+
+
+/* Calculation of logfbit3(x)-logfbit3(1+x). */
+static gnm_float
+logfbit3dif (gnm_float x)
+{
+ return -(2 * x + 3) * powgnum ((x + 1) * (x + 2), -3);
+}
+
+
+/* Third derivative logfbit. */
+static gnm_float
+logfbit3 (gnm_float x)
+{
+ if (x >= 1e10) return -0.5 * powgnum (x + 1, -4);
+ else if (x >= 6) {
+ gnm_float x1 = x + 1;
+ gnm_float x2 = 1 / (x1 * x1);
+ gnm_float x3 =
+ x2 * (60 * lfbc2 - x2 *
+ (210 * lfbc3 - x2 *
+ (504 * lfbc4 - x2 *
+ (990 * lfbc5 - x2 *
+ (1716 * lfbc6 - x2 *
+ (2730 * lfbc7 - x2 *
+ (4080 * lfbc8 - x2 *
+ 5814 * lfbc9)))))));
+ return -lfbc1 * (6 - x3) * x2 * x2;
+ }
+ else if (x > -1) {
+ gnm_float x1 = x;
+ gnm_float x2 = 0;
+ while (x1 < 6) {
+ x2 += logfbit3dif (x1);
+ x1++;
+ }
+ return x2 + logfbit3 (x1);
+ }
+ else return gnm_ninf;
+}
+
+/* Calculation of logfbit5(x)-logfbit5(1+x). */
+static gnm_float
+logfbit5dif (gnm_float x)
+{
+ return -6 * (2 * x + 3) * ((5 * x + 15) * x + 12) *
+ powgnum ((x + 1) * (x + 2), -5);
+}
+
+/* Fifth derivative logfbit. */
+static gnm_float
+logfbit5 (gnm_float x)
+{
+ if (x >= 1e10) return -10 * powgnum (x + 1, -6);
+ else if (x >= 6) {
+ gnm_float x1 = x + 1;
+ gnm_float x2 = 1 / (x1 * x1);
+ gnm_float x3 =
+ x2 * (2520 * lfbc2 - x2 *
+ (15120 * lfbc3 - x2 *
+ (55440 * lfbc4 - x2 *
+ (154440 * lfbc5 - x2 *
+ (360360 * lfbc6 - x2 *
+ (742560 * lfbc7 - x2 *
+ (1395360 * lfbc8 - x2 *
+ 2441880 * lfbc9)))))));
+ return -lfbc1 * (120 - x3) * x2 * x2 * x2;
+ } else if (x > -1) {
+ gnm_float x1 = x;
+ gnm_float x2 = 0;
+ while (x1 < 6) {
+ x2 += logfbit5dif (x1);
+ x1++;
+ }
+ return x2 + logfbit5 (x1);
+ }
+ else return gnm_ninf;
+}
+
+/* Calculation of logfbit7(x)-logfbit7(1+x). */
+static gnm_float
+logfbit7dif (gnm_float x)
+{
+ return -120 *
+ (2 * x + 3) *
+ ((((14 * x + 84) * x + 196) * x + 210) * x + 87) *
+ powgnum ((x + 1) * (x + 2), -7);
+}
+
+/* Seventh derivative logfbit. */
+static gnm_float
+logfbit7 (gnm_float x)
+{
+ if (x >= 1e10) return -420 * powgnum (x + 1, -8);
+ else if (x >= 6) {
+ gnm_float x1 = x + 1;
+ gnm_float x2 = 1 / (x1 * x1);
+ gnm_float x3 =
+ x2 * (181440 * lfbc2 - x2 *
+ (1663200 * lfbc3 - x2 *
+ (8648640 * lfbc4 - x2 *
+ (32432400 * lfbc5 - x2 *
+ (98017920 * lfbc6 - x2 *
+ (253955520 * lfbc7 - x2 *
+ (586051200 * lfbc8 - x2 *
+ 1235591280 * lfbc9)))))));
+ return -lfbc1 * (5040 - x3) * x2 * x2 * x2 * x2;
+ } else if (x > -1) {
+ gnm_float x1 = x;
+ gnm_float x2 = 0;
+ while (x1 < 6) {
+ x2 += logfbit7dif (x1);
+ x1++;
+ }
+ return x2 + logfbit7 (x1);
+ }
+ else return gnm_ninf;
+}
+
+
+static gnm_float
+lfbaccdif (gnm_float a, gnm_float b)
+{
+ if (a > 0.03 * (a + b))
+ return logfbit (a + b) - logfbit (b);
+ else {
+ gnm_float a2 = a * a;
+ gnm_float ab2 = a / 2 + b;
+ return a * (logfbit1 (ab2) + a2 / 24 *
+ (logfbit3 (ab2) + a2 / 80 *
+ (logfbit5 (ab2) + a2 / 168 *
+ logfbit7 (ab2))));
+ }
+}
+
+/* Calculates log(gamma(a+1) accurately for for small a (0 < a & a < 0.5). */
+gnm_float
+lgamma1p (gnm_float a)
+{
+ static const gnm_float eulers_const = GNM_const (0.57721566490153286060651209008240243104215933593992);
+
+ /* coeffs[i] holds (zeta(i+2)-1)/(i+2) */
+ static const gnm_float coeffs[40] = {
+ GNM_const (0.3224670334241132182362075833230126e-0),
+ GNM_const (0.6735230105319809513324605383715000e-1),
+ GNM_const (0.2058080842778454787900092413529198e-1),
+ GNM_const (0.7385551028673985266273097291406834e-2),
+ GNM_const (0.2890510330741523285752988298486755e-2),
+ GNM_const (0.1192753911703260977113935692828109e-2),
+ GNM_const (0.5096695247430424223356548135815582e-3),
+ GNM_const (0.2231547584535793797614188036013401e-3),
+ GNM_const (0.9945751278180853371459589003190170e-4),
+ GNM_const (0.4492623673813314170020750240635786e-4),
+ GNM_const (0.2050721277567069155316650397830591e-4),
+ GNM_const (0.9439488275268395903987425104415055e-5),
+ GNM_const (0.4374866789907487804181793223952411e-5),
+ GNM_const (0.2039215753801366236781900709670839e-5),
+ GNM_const (0.9551412130407419832857179772951265e-6),
+ GNM_const (0.4492469198764566043294290331193655e-6),
+ GNM_const (0.2120718480555466586923135901077628e-6),
+ GNM_const (0.1004322482396809960872083050053344e-6),
+ GNM_const (0.4769810169363980565760193417246730e-7),
+ GNM_const (0.2271109460894316491031998116062124e-7),
+ GNM_const (0.1083865921489695409107491757968159e-7),
+ GNM_const (0.5183475041970046655121248647057669e-8),
+ GNM_const (0.2483674543802478317185008663991718e-8),
+ GNM_const (0.1192140140586091207442548202774640e-8),
+ GNM_const (0.5731367241678862013330194857961011e-9),
+ GNM_const (0.2759522885124233145178149692816341e-9),
+ GNM_const (0.1330476437424448948149715720858008e-9),
+ GNM_const (0.6422964563838100022082448087644648e-10),
+ GNM_const (0.3104424774732227276239215783404066e-10),
+ GNM_const (0.1502138408075414217093301048780668e-10),
+ GNM_const (0.7275974480239079662504549924814047e-11),
+ GNM_const (0.3527742476575915083615072228655483e-11),
+ GNM_const (0.1711991790559617908601084114443031e-11),
+ GNM_const (0.8315385841420284819798357793954418e-12),
+ GNM_const (0.4042200525289440065536008957032895e-12),
+ GNM_const (0.1966475631096616490411045679010286e-12),
+ GNM_const (0.9573630387838555763782200936508615e-13),
+ GNM_const (0.4664076026428374224576492565974577e-13),
+ GNM_const (0.2273736960065972320633279596737272e-13),
+ GNM_const (0.1109139947083452201658320007192334e-13)
+ };
+ const int N = sizeof (coeffs) / sizeof (coeffs[0]);
+
+ const gnm_float c = GNM_const (0.2273736845824652515226821577978691e-12); /* zeta(N+2)-1 */
+ gnm_float lgam;
+ int i;
+
+ if (gnumabs (a) >= 0.5)
+ return lgammagnum (a + 1);
+
+ /* Abramowitz & Stegun 6.1.33 */
+ /* http://functions.wolfram.com/06.11.06.0008.01 */
+ lgam = c * logcf (-a / 2, N + 2, 1);
+ for (i = N - 1; i >= 0; i--)
+ lgam = coeffs[i] - a * lgam;
+
+ return (a * lgam - eulers_const) * a - log1pmx (a);
+}
+
+static gnm_float
+compbfunc (gnm_float x, gnm_float a, gnm_float b)
+{
+ const gnm_float sumAcc = 5E-16;
+ gnm_float term = x;
+ gnm_float d = 2;
+ gnm_float sum = term / (a + 1);
+ while (gnumabs (term) > gnumabs (sum * sumAcc)) {
+ term *= (d - b) * x / d;
+ sum += term / (a + d);
+ d++;
+ }
+ return a * (b - 1) * sum;
+}
+
+static gnm_float
+pbeta_smalla (gnm_float x, gnm_float a, gnm_float b, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float r;
+
+#if 0
+ assert (a >= 0 && b >= 0);
+ assert (a < 1);
+ assert (b < 1 || (1 + b) * x <= 1);
+#endif
+
+ if (x > 0.5) {
+ gnm_float olda = a;
+ a = b;
+ b = olda;
+ x = 1 - x;
+ lower_tail = !lower_tail;
+ }
+
+ r = (a + b + 0.5) * log1pmx (a / (1 + b)) +
+ a * (a - 0.5) / (1 + b) +
+ lfbaccdif (a, b);
+ r += a * loggnum ((1 + b) * x) - lgamma1p (a);
+ if (lower_tail) {
+ if (log_p)
+ return r + log1pgnum (-compbfunc (x, a, b)) + loggnum (b / (a + b));
+ else
+ return expgnum (r) * (1 - compbfunc (x, a, b)) * (b / (a + b));
+ } else {
+ /* x=0.500000001 a=0.5 b=0.000001 ends up here [swapped]
+ * with r=-7.94418987455065e-08 and cbf=-3.16694087508444e-07.
+ *
+ * x=0.0000001 a=0.999999 b=0.02 end up here with
+ * r=-16.098276918385 and cbf=-4.89999787339858e-08.
+ */
+ if (log_p) {
+ return swap_log_tail (r + log1pgnum (-compbfunc (x, a, b)) + loggnum (b / (a + b)));
+ } else {
+ r = -expm1gnum (r);
+ r += compbfunc (x, a, b) * (1 - r);
+ r += (a / (a + b)) * (1 - r);
+ return r;
+ }
+ }
+}
+
+/* Cumulative Students t-distribution, with odd parameterisation for
+ * use with binApprox.
+ * p is x*x/(k+x*x)
+ * q is 1-p
+ * logqk2 is LN(q)*k/2
+ * approxtdistDens returns with approx density function, for use in
+ * binApprox
+ */
+static gnm_float
+tdistexp (gnm_float p, gnm_float q, gnm_float logqk2, gnm_float k,
+ gnm_float *approxtdistDens)
+{
+ const gnm_float sumAcc = 5E-16;
+ const gnm_float cfVSmall = 1.0e-14;
+ const gnm_float lstpi = loggnum (2 * M_PIgnum) / 2;
+
+ if (floorgnum (k / 2) * 2 == k)
+ *approxtdistDens = expgnum (logqk2 + logfbit (k - 1) - 2 * logfbit (k * 0.5 - 1) - lstpi);
+ else
+ *approxtdistDens = expgnum (logqk2 + k * log1pmx (1 / k) + 2 * logfbit ((k - 1) * 0.5) - logfbit (k - 1) - lstpi);
+
+ if (k * p < 4 * q) {
+ gnm_float sum = 1;
+ gnm_float aki = k + 1;
+ gnm_float ai = 3;
+ gnm_float term = aki * p / ai;
+
+ while (term > sumAcc * sum) {
+ sum += term;
+ ai += 2;
+ aki += 2;
+ term *= aki * p / ai;
+ }
+ sum += term;
+
+ return 0.5 - *approxtdistDens * sum * sqrtgnum (k * p);
+ } else {
+ gnm_float q1 = 2 * (1 + q);
+ gnm_float q8 = 8 * q;
+ gnm_float a1 = 0;
+ gnm_float b1 = 1;
+ gnm_float c1 = -6 * q;
+ gnm_float a2 = 1;
+ gnm_float b2 = (k - 1) * p + 3;
+ gnm_float cadd = -14 * q;
+ gnm_float c2 = b2 + q1;
+
+ while (gnumabs (a2 * b1 - a1 * b2) > gnumabs (cfVSmall * b1 * b2)) {
+ a1 = c2 * a2 + c1 * a1;
+ b1 = c2 * b2 + c1 * b1;
+ c1 += cadd;
+ cadd -= q8;
+ c2 += q1;
+ a2 = c2 * a1 + c1 * a2;
+ b2 = c2 * b1 + c1 * b2;
+ c1 += cadd;
+ cadd -= q8;
+ c2 += q1;
+
+ if (gnumabs (b2) > scalefactor) {
+ a1 *= 1 / scalefactor;
+ b1 *= 1 / scalefactor;
+ a2 *= 1 / scalefactor;
+ b2 *= 1 / scalefactor;
+ } else if (gnumabs (b2) < 1 / scalefactor) {
+ a1 *= scalefactor;
+ b1 *= scalefactor;
+ a2 *= scalefactor;
+ b2 *= scalefactor;
+ }
+ }
+
+ return *approxtdistDens * (1 - q * a2 / b2) / sqrtgnum (k * p);
+ }
+}
+
+
+/* Asymptotic expansion to calculate the probability that binomial variate
+ * has value <= a.
+ * (diffFromMean = (a+b)*p-a).
+ */
+static gnm_float
+binApprox (gnm_float a, gnm_float b, gnm_float diffFromMean,
+ gboolean lower_tail, gboolean log_p)
+{
+ gnm_float pq1, res, comt, comf, t;
+ gnm_float ib05, ib15, ib25, ib35, ib3;
+ gnm_float elfb, coef15, coef25, coef35;
+ gnm_float approxtdistDens;
+
+ gnm_float n = a + b;
+ gnm_float n1 = n + 1;
+ gnm_float lvv = b - n * diffFromMean;
+ gnm_float lval = (a * log1pmx (lvv / (a * n1)) +
+ b * log1pmx (-lvv / (b * n1))) / n;
+ gnm_float tp = -expm1gnum (lval);
+ gnm_float mfac = n1 * tp;
+ gnm_float ib2 = 1 + mfac;
+ gnm_float t1 = (n + 2) * tp;
+
+ mfac = 2 * mfac;
+
+ ib3 = ib2 + mfac*t1;
+ ib05 = tdistexp (tp, 1 - tp, n1 * lval, 2 * n1, &approxtdistDens);
+ ib15 = sqrtgnum (mfac);
+ mfac = t1 * (GNM_const (2.0) / 3);
+ ib25 = 1 + mfac;
+ ib35 = ib25 + mfac * (n + 3) * tp * (GNM_const (2.0) / 5);
+
+ pq1 = (n * n) / (a * b);
+
+ res = (ib2 * (1 + 2 * pq1) / 135 - 2 * ib3 * ((2 * pq1 - 43) * pq1 - 22) / (2835 * (n + 3))) / (n + 2);
+ res = (GNM_const (1.0) / 3 - res) * 2 * sqrtgnum (pq1 / n1) * (a - b) / n;
+
+ n1 = (n + 1.5) * (n + 2.5);
+ coef15 = (-17 + 2 * pq1) / (24 * (n + 1.5));
+ coef25 = (-503 + 4 * pq1 * (19 + pq1)) / (1152 * n1);
+ coef35 = (-315733 + pq1 * (53310 + pq1 * (8196 - 1112 * pq1))) /
+ (414720 * n1 * (n + 3.5));
+ elfb = ((coef35 + coef25) + coef15) + 1;
+
+ comt = ib15 * ((coef35 * ib35 + coef25 * ib25) + coef15);
+ comf = approxtdistDens / elfb;
+
+ if (lvv > 0)
+ t = ib05 - (res - comt) * comf;
+ else
+ t = ib05 + (res + comt) * comf;
+
+ return (!lower_tail != (lvv > 0)) ? R_D_Clog (t) : R_D_val (t);
+}
+
+/* Probability that binomial variate with sample size i+j and
+ * event prob p (=1-q) has value i (diffFromMean = (i+j)*p-i)
+ */
+static gnm_float
+binomialTerm (gnm_float i, gnm_float j, gnm_float p, gnm_float q,
+ gnm_float diffFromMean, gboolean log_p)
+{
+ const gnm_float minLog1Value = -0.79149064;
+ gnm_float c1,c2,c3;
+ gnm_float c4,c5,c6,ps,logbinomialTerm,dfm;
+ gnm_float t;
+
+ if (i == 0 && j <= 0)
+ return R_D__1;
+
+ if (i <= -1 || j < 0)
+ return R_D__0;
+
+ c1 = (i + 1) + j;
+ if (p < q) {
+ c2 = i;
+ c3 = j;
+ ps = p;
+ dfm = diffFromMean;
+ } else {
+ c3 = i;
+ c2 = j;
+ ps = q;
+ dfm = -diffFromMean;
+ }
+
+ c5 = (dfm - (1 - ps)) / (c2 + 1);
+ c6 = -(dfm + ps) / (c3 + 1);
+
+ if (c5 < minLog1Value) {
+ if (c2 == 0) {
+ logbinomialTerm = c3 * log1pgnum (-ps);
+ return log_p ? logbinomialTerm : expgnum (logbinomialTerm);
+ } else if (ps == 0 && c2 > 0) {
+ return R_D__0;
+ } else {
+ t = loggnum ((ps * c1) / (c2 + 1)) - c5;
+ }
+ } else {
+ t = log1pmx (c5);
+ }
+
+ c4 = logfbit (i + j) - logfbit (i) - logfbit (j);
+ logbinomialTerm = c4 + c2 * t - c5 + (c3 * log1pmx (c6) - c6);
+
+ return log_p
+ ? logbinomialTerm + 0.5 * loggnum (c1 / ((c2 + 1) * (c3 + 1) * 2 * M_PIgnum))
+ : expgnum (logbinomialTerm) * sqrtgnum (c1 / ((c2 + 1) * (c3 + 1) * 2 * M_PIgnum));
+}
+
+
+/*
+ * Probability that binomial variate with sample size ii+jj
+ * and event prob pp (=1-qq) has value <=i.
+ * (diffFromMean = (ii+jj)*pp-ii).
+ */
+static gnm_float
+binomialcf (gnm_float ii, gnm_float jj, gnm_float pp, gnm_float qq,
+ gnm_float diffFromMean, gboolean lower_tail, gboolean log_p)
+{
+ const gnm_float sumAlways = 0;
+ const gnm_float sumFactor = 6;
+ const gnm_float cfSmall = 1.0e-12;
+
+ gnm_float prob,p,q,a1,a2,b1,b2,c1,c2,c3,c4,n1,q1,dfm;
+ gnm_float i,j,ni,nj,numb,ip1;
+ gboolean swapped;
+
+ ip1 = ii + 1;
+ if (ii > -1 && (jj <= 0 || pp == 0)) {
+ return R_DT_1;
+ } else if (ii > -1 && ii < 0) {
+ ii = -ii;
+ ip1 = ii;
+ prob = binomialTerm (ii, jj, pp, qq, (ii + jj) * pp - ii, log_p) *
+ ii / ((ii + jj) * pp);
+ ii--;
+ diffFromMean = (ii + jj) * pp - ii;
+ } else
+ prob = binomialTerm (ii, jj, pp, qq, diffFromMean, log_p);
+
+ n1 = (ii + 3) + jj;
+ if (ii < 0)
+ swapped = FALSE;
+ else if (pp > qq)
+ swapped = (n1 * qq >= jj + 1);
+ else
+ swapped = (n1 * pp <= ii + 2);
+
+ if (prob == R_D__0) {
+ if (swapped == !lower_tail)
+ return R_D__0;
+ else
+ return R_D__1;
+ }
+
+ if (swapped) {
+ j = ip1;
+ ip1 = jj;
+ i = jj - 1;
+ p = qq;
+ q = pp;
+ dfm = 1 - diffFromMean;
+ } else {
+ i = ii;
+ j = jj;
+ p = pp;
+ q = qq;
+ dfm = diffFromMean;
+ }
+
+ if (i > sumAlways) {
+ numb = floorgnum (sumFactor * sqrtgnum (p + 0.5) * expgnum (loggnum (n1 * p * q) / 3));
+ numb = floorgnum (numb - dfm);
+ if (numb > i) numb = floorgnum (i);
+ } else
+ numb = floorgnum (i);
+ if (numb < 0) numb = 0;
+
+ a1 = 0;
+ b1 = 1;
+ q1 = q + 1;
+ a2 = (i - numb) * q;
+ b2 = dfm + numb + 1;
+ c1 = 0;
+
+ c2 = a2;
+ c4 = b2;
+
+ while (gnumabs (a2 * b1 - a1 * b2) > gnumabs (cfSmall * b1 * b2)) {
+ c1++;
+ c2 -= q;
+ c3 = c1 * c2;
+ c4 += q1;
+ a1 = c4 * a2 + c3 * a1;
+ b1 = c4 * b2 + c3 * b1;
+ c1++;
+ c2 -= q;
+ c3 = c1 * c2;
+ c4 += q1;
+ a2 = c4 * a1 + c3 * a2;
+ b2 = c4 * b1 + c3 * b2;
+
+ if (gnumabs (b2) > scalefactor) {
+ a1 *= 1 / scalefactor;
+ b1 *= 1 / scalefactor;
+ a2 *= 1 / scalefactor;
+ b2 *= 1 / scalefactor;
+ } else if (gnumabs (b2) < 1 / scalefactor) {
+ a1 *= scalefactor;
+ b1 *= scalefactor;
+ a2 *= scalefactor;
+ b2 *= scalefactor;
+ }
+ }
+
+ a1 = a2 / b2;
+
+ ni = (i - numb + 1) * q;
+ nj = (j + numb) * p;
+ while (numb > 0) {
+ a1 = (1 + a1) * (ni / nj);
+ ni = ni + q;
+ nj = nj - p;
+ numb--;
+ }
+
+ prob = log_p ? prob + log1pgnum (a1) : prob * (1 + a1);
+
+ if (swapped) {
+ if (log_p)
+ prob += loggnum (ip1 * q / nj);
+ else
+ prob *= ip1 * q / nj;
+ }
+
+ if (swapped == !lower_tail)
+ return prob;
+ else
+ return log_p ? swap_log_tail (prob) : 1 - prob;
+}
+
+static gnm_float
+binomial (gnm_float ii, gnm_float jj, gnm_float pp, gnm_float qq,
+ gnm_float diffFromMean, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float mij = fmin2 (ii, jj);
+
+ if (mij > 500 && gnumabs (diffFromMean) < 0.01 * mij)
+ return binApprox (jj - 1, ii, diffFromMean, lower_tail, log_p);
+
+ return binomialcf (ii, jj, pp, qq, diffFromMean, lower_tail, log_p);
+}
+
+
+gnm_float
+pbeta (gnm_float x, gnm_float a, gnm_float b, gboolean lower_tail, gboolean log_p)
+{
+ if (isnangnum (x) || isnangnum (a) || isnangnum (b))
+ return x + a + b;
+
+ if (x <= 0) return R_DT_0;
+ if (x >= 1) return R_DT_1;
+
+ if (a < 1 && (b < 1 || (1 + b) * x <= 1))
+ return pbeta_smalla (x, a, b, lower_tail, log_p);
+
+ if (b < 1 && (1 + a) * (1 - x) <= 1) /* a/(1+a) <= x ??? */
+ return pbeta_smalla (1 - x, b, a, !lower_tail, log_p);
+
+ if (a < 1)
+ return binomial (-a, b, x, 1 - x, 0, !lower_tail, log_p);
+
+ if (b < 1)
+ return binomial (-b, a, 1 - x, x, 0, lower_tail, log_p);
+
+ return binomial (a - 1, b, x, 1 - x, (a + b - 1) * x - a + 1,
+ !lower_tail, log_p);
+}
+
+/* --- END IANDJMSMITH SOURCE MARKER --- */
+/* ------------------------------------------------------------------------ */
+
+/*
+ * New phyper implementation. Copyright 2004 Morten Welinder.
+ * Distributed under the GNU General Public License.
+ *
+ * Thanks to Ian Smith for ideas.
+ */
+/*
+ * Calculate
+ *
+ * phyper (i, NR, NB, n, TRUE, FALSE)
+ * [log] ----------------------------------
+ * dhyper (i, NR, NB, n, FALSE)
+ *
+ * without actually calling phyper. This assumes that
+ *
+ * i * (NR + NB) <= n * NR
+ *
+ */
+static gnm_float
+pdhyper (gnm_float i, gnm_float NR, gnm_float NB, gnm_float n, gboolean log_p)
+{
+ gnm_float sum = 0;
+ gnm_float term = 1;
+
+ while (i > 0 && term >= GNUM_EPSILON * sum) {
+ term *= i * (NB - n + i) / (n + 1 - i) / (NR + 1 - i);
+ sum += term;
+ i--;
+ }
+
+ return log_p ? log1pgnum (sum) : 1 + sum;
+}
+
+
+gnm_float
+phyper (gnm_float i, gnm_float NR, gnm_float NB, gnm_float n, int lower_tail, int log_p)
+{
+ gnm_float d, pd;
+
+#ifdef IEEE_754
+ if (isnangnum (i) || isnangnum (NR) || isnangnum (NB) || isnangnum (n))
+ return i + NR + NB + n;
+#endif
+
+ i = floorgnum (i + 1e-7);
+ NR = floorgnum (NR + 0.5);
+ NB = floorgnum (NB + 0.5);
+ n = floorgnum (n + 0.5);
+
+ if (NR < 0 || NB < 0 || !finitegnum (NR + NB) || n < 0 || n > NR + NB)
+ ML_ERR_return_NAN;
+
+ if (i * (NR + NB) > n * NR) {
+ /* Swap tails. */
+ gnm_float oldNB = NB;
+ NB = NR;
+ NR = oldNB;
+ i = n - i - 1;
+ lower_tail = !lower_tail;
+ }
+
+ if (i < 0)
+ return R_DT_0;
+
+ d = dhyper (i, NR, NB, n, log_p);
+ pd = pdhyper (i, NR, NB, n, log_p);
+
+ return log_p ? R_DT_log (d + pd) : R_D_Lval (d * pd);
+}
+
+
+gnm_float
+pcauchy (gnm_float x, gnm_float location, gnm_float scale,
+ gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum(x) || isnangnum(location) || isnangnum(scale))
+ return x + location + scale;
+#endif
+ if (scale <= 0) ML_ERR_return_NAN;
+
+ x = (x - location) / scale;
+ if (isnangnum(x)) ML_ERR_return_NAN;
+#ifdef IEEE_754
+ if (!finitegnum(x)) {
+ if(x < 0) return R_DT_0;
+ else return R_DT_1;
+ }
+#endif
+
+ if (!lower_tail)
+ x = -x;
+
+ if (gnumabs (x) > 1) {
+ gnm_float temp = atangnum (1 / x) / M_PIgnum;
+ return (x > 0) ? R_D_Clog (temp) : R_D_val (-temp);
+ } else
+ return R_D_val (0.5 + atangnum (x) / M_PIgnum);
+}
+
+gnm_float
+pf (gnm_float x, gnm_float n1, gnm_float n2, gboolean lower_tail, gboolean log_p)
+{
+#ifdef IEEE_754
+ if (isnangnum (x) || isnangnum (n1) || isnangnum (n2))
+ return x + n2 + n1;
+#endif
+ if (n1 <= 0 || n2 <= 0) ML_ERR_return_NAN;
+
+ if (x <= 0)
+ return R_DT_0;
+
+ /* Avoid squeezing pbeta's first parameter against 1. */
+ if (n1 * x > n2)
+ return pbeta (n2 / (n2 + n1 * x), n2 / 2, n1 / 2,
+ !lower_tail, log_p);
+ else
+ return pbeta (n1 * x / (n2 + n1 * x), n1 / 2, n2 / 2,
+ lower_tail, log_p);
+}
+
+/* ------------------------------------------------------------------------ */
+
+typedef gnm_float (*PFunc) (gnm_float x, const gnm_float shape[],
+ gboolean lower_tail, gboolean log_p);
+typedef gnm_float (*DPFunc) (gnm_float x, const gnm_float shape[],
+ gboolean log_p);
+
+static gnm_float
+pfuncinverter (gnm_float p, const gnm_float shape[],
+ gboolean lower_tail, gboolean log_p,
+ gnm_float xlow, gnm_float xhigh, gnm_float x0,
+ PFunc pfunc, DPFunc dpfunc_dx)
+{
+ gboolean have_xlow = finitegnum (xlow);
+ gboolean have_xhigh = finitegnum (xhigh);
+ gnm_float exlow, exhigh;
+ gnm_float x = 0, e = 0;
+ int i;
+
+ if (p == R_DT_0) return xlow;
+ if (p == R_DT_1) return xhigh;
+
+ exlow = R_DT_0 - p;
+ exhigh = R_DT_1 - p;
+ if (!lower_tail) {
+ exlow = -exlow;
+ exhigh = -exhigh;
+ }
+
+#ifdef DEBUG_pfuncinverter
+ printf ("p=%.15g\n", p);
+#endif
+
+ for (i = 0; i < 100; i++) {
+ if (i == 0) {
+ /* Use supplied guess. */
+ x = x0;
+ if (x0 <= xlow || x0 >= xhigh) {
+ if (have_xlow || have_xhigh)
+ x0 = ((have_xhigh ? xhigh : xlow + 1) +
+ (have_xlow ? xlow : xhigh - 1)) / 2;
+ else
+ x0 = 0;
+ }
+ } else if (i == 1) {
+ /*
+ * Under the assumption that the initial guess was
+ * good, pick a nearby point that is hopefully on
+ * the other side. If we already have both sides,
+ * just bisect.
+ */
+ if (have_xlow && have_xhigh)
+ x = (xlow + xhigh) / 2;
+ else if (have_xlow)
+ x = xlow * 1.1;
+ else
+ x = xhigh / 1.1;
+ } else if (have_xlow && have_xhigh) {
+ switch (i % 8) {
+ case 0:
+ case 4:
+ x = xhigh - (xhigh - xlow) *
+ (exhigh / (exhigh - exlow));
+ break;
+ case 2:
+ x = (xhigh + 1000 * xlow) / 1001;
+ break;
+ case 6:
+ x = (1000 * xhigh + xlow) / 1001;
+ break;
+ default:
+ x = (xhigh + xlow) / 2;
+ }
+ } else if (have_xlow) {
+ /* Agressively seek right in search of xhigh. */
+ x = (xlow < 1) ? 1 : (2 * i) * xlow;
+ } else {
+ /* Agressively seek left in search of xlow. */
+ x = (xhigh > -1) ? -1 : (2 * i) * xhigh;
+ }
+
+ newton_retry:
+ if ((have_xlow && x <= xlow) || (have_xhigh && x >= xhigh))
+ continue;
+
+ e = pfunc (x, shape, lower_tail, log_p) - p;
+ if (!lower_tail) e = -e;
+
+#ifdef DEBUG_pfuncinverter
+ printf (" x=%.15g e=%.15g l=%.15g h=%.15g\n",
+ x, e, xlow, xhigh);
+#endif
+
+ if (e == 0)
+ goto done;
+ else if (e > 0) {
+ xhigh = x;
+ exhigh = e;
+ have_xhigh = TRUE;
+ } else {
+ xlow = x;
+ exlow = e;
+ have_xlow = TRUE;
+ }
+
+ if (have_xlow && have_xhigh) {
+ gnm_float prec = (xhigh - xlow) /
+ (gnumabs (xlow) + gnumabs (xhigh));
+ if (prec < GNUM_EPSILON * 4) {
+ x = (xhigh + xlow) / 2;
+ e = pfunc (x, shape, lower_tail, log_p) - p;
+ if (!lower_tail) e = -e;
+ goto done;
+ }
+
+ if (i % 3 < 2 && (i == 0 || prec < 0.05)) {
+ gnm_float d = dpfunc_dx (x, shape, log_p);
+ if (d) {
+ /*
+ * Deliberately overshoot a bit to help
+ * with getting good points on both
+ * sides of the root.
+ */
+ x = x - e / d * 1.000001;
+ if (x > xlow && x < xhigh) {
+#ifdef DEBUG_pfuncinverter
+ printf ("Newton ok\n");
+#endif
+ i++;
+ goto newton_retry;
+ }
+ }
+ }
+ }
+ }
+
+ ML_ERROR(ME_PRECISION);
+ done:
+ /* Make sure to keep a lucky near-hit. */
+
+ if (have_xhigh && gnumabs (e) > exhigh)
+ e = exhigh, x = xhigh;
+ if (gnumabs (e) > -exlow)
+ e = exlow, x = xlow;
+
+#ifdef DEBUG_pfuncinverter
+ printf ("--> %.15g\n\n", x);
+#endif
+ return x;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static gnm_float
+dgamma1 (gnm_float x, const gnm_float *palpha, gboolean log_p)
+{
+ if (log_p)
+ return 0; /* i.e., bail. */
+ else
+ return dgamma (x, *palpha, 1, log_p);
+}
+
+static gnm_float
+pgamma1 (gnm_float x, const gnm_float *palpha,
+ gboolean lower_tail, gboolean log_p)
+{
+ return pgamma (x, *palpha, 1, lower_tail, log_p);
+}
+
+
+gnm_float
+qgamma (gnm_float p, gnm_float alpha, gnm_float scale,
+ gboolean lower_tail, gboolean log_p)
+{
+ gnm_float res1, x0, v;
+
+#ifdef IEEE_754
+ if (isnangnum(p) || isnangnum(alpha) || isnangnum(scale))
+ return p + alpha + scale;
+#endif
+ R_Q_P01_check(p);
+ if (alpha <= 0) ML_ERR_return_NAN;
+
+ /* Make an initial guess, x0, assuming scale==1. */
+ v = 2 * alpha;
+ if (v < -1.24 * R_DT_log (p))
+ x0 = powgnum (R_DT_qIv (p) * alpha * expgnum (lgammagnum (alpha) + alpha * M_LN2gnum),
+ 1 / alpha) / 2;
+ else {
+ gnm_float x1 = qnorm (p, 0, 1, lower_tail, log_p);
+ gnm_float p1 = 0.222222 / v;
+ x0 = v * powgnum (x1 * sqrtgnum (p1) + 1 - p1, 3) / 2;
+ }
+
+ res1 = pfuncinverter (p, &alpha, lower_tail, log_p, 0, gnm_pinf, x0,
+ pgamma1, dgamma1);
+
+ return res1 * scale;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static gnm_float
+dbeta1 (gnm_float x, const gnm_float shape[], gboolean log_p)
+{
+ if (log_p)
+ return 0; /* i.e., bail. */
+ else
+ return dbeta (x, shape[0], shape[1], log_p);
+}
+
+static gnm_float
+pbeta1 (gnm_float x, const gnm_float shape[],
+ gboolean lower_tail, gboolean log_p)
+{
+ return pbeta (x, shape[0], shape[1], lower_tail, log_p);
+}
+
+
+gnm_float
+qbeta (gnm_float p, gnm_float pin, gnm_float qin, gboolean lower_tail, gboolean log_p)
+{
+ gnm_float x0, shape[2];
+
+#ifdef IEEE_754
+ if (isnangnum (pin) || isnangnum (qin) || isnangnum (p))
+ return pin + qin + p;
+#endif
+ R_Q_P01_check (p);
+
+ if (pin < 0. || qin < 0.) ML_ERR_return_NAN;
+
+ /*
+ * For small pin, p seems to have exponent 1, for large pin, it
+ * seems more like 0.
+ *
+ * For small pin, pin itself seems to have exponent 2, for large pin,
+ * it is more like 1.
+ */
+ x0 = powgnum (R_DT_qIv (p), 1 / (pin + 1)) *
+ powgnum (pin, (pin + 2) / (pin + 1)) /
+ qin;
+ x0 /= (1 + x0);
+
+ shape[0] = pin;
+ shape[1] = qin;
+ return pfuncinverter (p, shape, lower_tail, log_p, 0, 1, x0,
+ pbeta1, dbeta1);
+}
+
+/* ------------------------------------------------------------------------ */
+/* http://www.math.keio.ac.jp/matumoto/CODES/MT2002/mt19937ar.c */
+/* Imported by hand -- MW. */
+
+/*
+ A C-program for MT19937, with initialization improved 2002/1/26.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.keio.ac.jp/matumoto/emt.html
+ email: matumoto at math.keio.ac.jp
+*/
+
+#if 0
+#include <stdio.h>
+#endif
+
+/* Period parameters */
+#define N 624
+#define M 397
+#define MATRIX_A 0x9908b0dfUL /* constant vector a */
+#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
+#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
+
+static unsigned long mt[N]; /* the array for the state vector */
+static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
+
+/* initializes mt[N] with a seed */
+static void init_genrand(unsigned long s)
+{
+ mt[0]= s & 0xffffffffUL;
+ for (mti=1; mti<N; mti++) {
+ mt[mti] =
+ (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti);
+ /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
+ /* In the previous versions, MSBs of the seed affect */
+ /* only MSBs of the array mt[]. */
+ /* 2002/01/09 modified by Makoto Matsumoto */
+ mt[mti] &= 0xffffffffUL;
+ /* for >32 bit machines */
+ }
+}
+
+/* initialize by an array with array-length */
+/* init_key is the array for initializing keys */
+/* key_length is its length */
+static void mt_init_by_array(unsigned long init_key[], int key_length)
+{
+ int i, j, k;
+ init_genrand(19650218UL);
+ i=1; j=0;
+ k = (N>key_length ? N : key_length);
+ for (; k; k--) {
+ mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
+ + init_key[j] + j; /* non linear */
+ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ i++; j++;
+ if (i>=N) { mt[0] = mt[N-1]; i=1; }
+ if (j>=key_length) j=0;
+ }
+ for (k=N-1; k; k--) {
+ mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
+ - i; /* non linear */
+ mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
+ i++;
+ if (i>=N) { mt[0] = mt[N-1]; i=1; }
+ }
+
+ mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
+}
+
+/* generates a random number on [0,0xffffffff]-interval */
+static unsigned long genrand_int32(void)
+{
+ unsigned long y;
+ static unsigned long mag01[2]={0x0UL, MATRIX_A};
+ /* mag01[x] = x * MATRIX_A for x=0,1 */
+
+ if (mti >= N) { /* generate N words at one time */
+ int kk;
+
+ if (mti == N+1) /* if init_genrand() has not been called, */
+ init_genrand(5489UL); /* a default initial seed is used */
+
+ for (kk=0;kk<N-M;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ for (;kk<N-1;kk++) {
+ y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
+ mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
+ }
+ y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
+ mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
+
+ mti = 0;
+ }
+
+ y = mt[mti++];
+
+ /* Tempering */
+ y ^= (y >> 11);
+ y ^= (y << 7) & 0x9d2c5680UL;
+ y ^= (y << 15) & 0xefc60000UL;
+ y ^= (y >> 18);
+
+ return y;
+}
+
+#if 0
+/* generates a random number on [0,0x7fffffff]-interval */
+long genrand_int31(void)
+{
+ return (long)(genrand_int32()>>1);
+}
+
+/* generates a random number on [0,1]-real-interval */
+double genrand_real1(void)
+{
+ return genrand_int32()*(1.0/4294967295.0);
+ /* divided by 2^32-1 */
+}
+
+/* generates a random number on [0,1)-real-interval */
+double genrand_real2(void)
+{
+ return genrand_int32()*(1.0/4294967296.0);
+ /* divided by 2^32 */
+}
+
+/* generates a random number on (0,1)-real-interval */
+double genrand_real3(void)
+{
+ return (((double)genrand_int32()) + 0.5)*(1.0/4294967296.0);
+ /* divided by 2^32 */
+}
+#endif
+
+/* generates a random number on [0,1) with 53-bit resolution*/
+static double genrand_res53(void)
+{
+ unsigned long a=genrand_int32()>>5, b=genrand_int32()>>6;
+ return(a*67108864.0+b)*(1.0/9007199254740992.0);
+}
+/* These real versions are due to Isaku Wada, 2002/01/09 added */
+
+#if 0
+int main(void)
+{
+ int i;
+ unsigned long init[4]={0x123, 0x234, 0x345, 0x456}, length=4;
+ init_by_array(init, length);
+ printf("1000 outputs of genrand_int32()\n");
+ for (i=0; i<1000; i++) {
+ printf("%10lu ", genrand_int32());
+ if (i%5==4) printf("\n");
+ }
+ printf("\n1000 outputs of genrand_real2()\n");
+ for (i=0; i<1000; i++) {
+ printf("%10.8f ", genrand_real2());
+ if (i%5==4) printf("\n");
+ }
+ return 0;
+}
+#endif
+
+
+#undef N
+#undef M
+#undef MATRIX_A
+#undef UPPER_MASK
+#undef LOWER_MASK
+
+/* ------------------------------------------------------------------------ */
+
+/* FIXME: we need something that catches partials and EAGAIN. */
+#define fullread read
+
+#define RANDOM_DEVICE "/dev/urandom"
+
+/*
+ * Conservative random number generator. The result is (supposedly) uniform
+ * and between 0 and 1. (0 possible, 1 not.) The result should have about
+ * 64 bits randomness.
+ */
+gnm_float
+random_01 (void)
+{
+ static int device_fd = -2;
+ static int seeded = -2;
+
+ while (seeded) {
+ if (seeded == -2) {
+ const char *seed = g_getenv ("GNUMERIC_PRNG_SEED");
+ if (seed) {
+ int len = strlen (seed);
+ int i;
+ unsigned long *longs = g_new (unsigned long, len + 1);
+
+ /* We drop only one character into each long. */
+ for (i = 0; i < len; i++)
+ longs[i] = (unsigned char)seed[i];
+ mt_init_by_array (longs, len);
+ g_free (longs);
+ seeded = 1;
+
+ g_warning ("Using pseudo-random numbers.");
+ } else {
+ seeded = 0;
+ break;
+ }
+ }
+
+ /*
+ * Only 52-bit precision. But hey, if you are using pseudo
+ * random numbers that ought to be good enough to you.
+ */
+ return genrand_res53 ();
+ }
+
+ if (device_fd == -2) {
+ device_fd = open (RANDOM_DEVICE, O_RDONLY);
+ /*
+ * We could check that we really have a device, but it hard
+ * to come up with a non-paranoid reason to.
+ */
+ }
+
+ if (device_fd >= 0) {
+ static ssize_t bytes_left = 0;
+ static unsigned char data[32 * sizeof (gnm_float)];
+ gnm_float res = 0;
+ size_t i;
+
+ if (bytes_left < (ssize_t)sizeof (gnm_float)) {
+ ssize_t got = fullread (device_fd, &data, sizeof (data));
+ if (got < (ssize_t)sizeof (gnm_float))
+ goto failure;
+ bytes_left += got;
+ }
+
+ bytes_left -= sizeof (gnm_float);
+ for (i = 0; i < sizeof (gnm_float); i++)
+ res = (res + data[bytes_left + i]) / 256;
+ return res;
+
+ failure:
+ /* It failed when it shouldn't. Disable. */
+ g_warning ("Reading from %s failed; reverting to pseudo-random.",
+ RANDOM_DEVICE);
+ close (device_fd);
+ device_fd = -1;
+ }
+
+#ifdef HAVE_RANDOM
+ {
+ int r1, r2;
+
+ r1 = random () & 2147483647;
+ r2 = random () & 2147483647;
+
+ return (r1 + (r2 / 2147483648.0)) / 2147483648.0;
+ }
+#elif defined (HAVE_DRAND48)
+ return drand48 ();
+#else
+ {
+ /*
+ * We try to work around lack of randomness in rand's
+ * lower bits.
+ */
+ const int prime = 65537;
+ int r1, r2, r3, r4;
+
+ g_assert (RAND_MAX > ((1 << 12) - 1));
+
+ r1 = (rand () ^ (rand () << 12)) % prime;
+ r2 = (rand () ^ (rand () << 12)) % prime;
+ r3 = (rand () ^ (rand () << 12)) % prime;
+ r4 = (rand () ^ (rand () << 12)) % prime;
+
+ return (r1 + (r2 + (r3 + r4 / (gnm_float)prime) / prime) / prime) / prime;
+ }
+#endif
+}
+
+/*
+ * Generate a N(0,1) distributed number.
+ */
+gnm_float
+random_normal (void)
+{
+ static gboolean has_saved = FALSE;
+ static gnm_float saved;
+
+ if (has_saved) {
+ has_saved = FALSE;
+ return saved;
+ } else {
+ gnm_float u, v, r2, rsq;
+ do {
+ u = 2 * random_01 () - 1;
+ v = 2 * random_01 () - 1;
+ r2 = u * u + v * v;
+ } while (r2 > 1 || r2 == 0);
+
+ rsq = sqrtgnum (-2 * loggnum (r2) / r2);
+
+ has_saved = TRUE;
+ saved = v * rsq;
+
+ return u * rsq;
+ }
+}
+
+gnm_float
+random_lognormal (gnm_float zeta, gnm_float sigma)
+{
+ return expgnum (sigma * random_normal () + zeta);
+}
+
+static gnm_float
+random_gaussian (gnm_float sigma)
+{
+ return sigma * random_normal ();
+}
+
+/*
+ * Generate a poisson distributed number.
+ */
+gnm_float
+random_poisson (gnm_float lambda)
+{
+ /*
+ * This may not be optimal code, but it sure is easy to
+ * understand compared to R's code.
+ */
+ return qpois (random_01 (), lambda, TRUE, FALSE);
+}
+
+/*
+ * Generate a binomial distributed number.
+ */
+gnm_float
+random_binomial (gnm_float p, int trials)
+{
+ return qbinom (random_01 (), trials, p, TRUE, FALSE);
+}
+
+/*
+ * Generate a negative binomial distributed number.
+ */
+gnm_float
+random_negbinom (gnm_float p, int f)
+{
+ return qnbinom (random_01 (), f, p, TRUE, FALSE);
+}
+
+/*
+ * Generate an exponential distributed number.
+ */
+gnm_float
+random_exponential (gnm_float b)
+{
+ return -b * loggnum (random_01 ());
+}
+
+/*
+ * Generate a bernoulli distributed number.
+ */
+gnm_float
+random_bernoulli (gnm_float p)
+{
+ gnm_float r = random_01 ();
+
+ return (r <= p) ? 1.0 : 0.0;
+}
+
+/*
+ * Generate a cauchy distributed number. From the GNU Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_cauchy (gnm_float a)
+{
+ gnm_float u;
+
+ do {
+ u = random_01 ();
+ } while (u == 0.5);
+
+ return a * tangnum (M_PIgnum * u);
+}
+
+/*
+ * Generate a Weibull distributed number. From the GNU Scientific
+ * library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_weibull (gnm_float a, gnm_float b)
+{
+ gnm_float x, z;
+
+ do {
+ x = random_01 ();
+ } while (x == 0.0);
+
+ z = powgnum (-loggnum (x), 1 / b);
+
+ return a * z;
+}
+
+/*
+ * Generate a Laplace (two-sided exponential probability) distributed number.
+ * From the GNU Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_laplace (gnm_float a)
+{
+ gnm_float u;
+
+ do {
+ u = 2 * random_01 () - 1.0;
+ } while (u == 0.0);
+
+ if (u < 0)
+ return a * loggnum (-u);
+ else
+ return -a * loggnum (u);
+}
+
+gnm_float
+random_laplace_pdf (gnm_float x, gnm_float a)
+{
+ return (1 / (2 * a)) * expgnum (-gnumabs (x) / a);
+}
+
+/*
+ * Generate a Rayleigh distributed number. From the GNU Scientific library
+ * 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_rayleigh (gnm_float sigma)
+{
+ gnm_float u;
+
+ do {
+ u = random_01 ();
+ } while (u == 0.0);
+
+ return sigma * sqrtgnum (-2.0 * loggnum (u));
+}
+
+/*
+ * Generate a Rayleigh tail distributed number. From the GNU Scientific library
+ * 1.1.1. The Rayleigh tail distribution has the form
+ * p(x) dx = (x / sigma^2) exp((a^2 - x^2)/(2 sigma^2)) dx
+ *
+ * for x = a ... +infty
+ *
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_rayleigh_tail (gnm_float a, gnm_float sigma)
+{
+ gnm_float u;
+
+ do {
+ u = random_01 ();
+ } while (u == 0.0);
+
+ return sqrtgnum (a * a - 2.0 * sigma * sigma * loggnum (u));
+}
+
+/* The Gamma distribution of order a>0 is defined by:
+ *
+ * p(x) dx = {1 / \Gamma(a) b^a } x^{a-1} e^{-x/b} dx
+ *
+ * for x>0. If X and Y are independent gamma-distributed random
+ * variables of order a1 and a2 with the same scale parameter b, then
+ * X+Y has gamma distribution of order a1+a2.
+ *
+ * The algorithms below are from Knuth, vol 2, 2nd ed, p. 129.
+ */
+
+static gnm_float
+gamma_frac (gnm_float a)
+{
+ /* This is exercise 16 from Knuth; see page 135, and the solution is
+ * on page 551. */
+
+ gnm_float x, q;
+ gnm_float p = M_Egnum / (a + M_Egnum);
+ do {
+ gnm_float v;
+ gnm_float u = random_01 ();
+ do {
+ v = random_01 ();
+ } while (v == 0.0);
+
+ if (u < p) {
+ x = powgnum (v, 1 / a);
+ q = expgnum (-x);
+ } else {
+ x = 1 - loggnum (v);
+ q = powgnum (x, a - 1);
+ }
+ } while (random_01 () >= q);
+
+ return x;
+}
+
+static gnm_float
+gamma_large (gnm_float a)
+{
+ /*
+ * Works only if a > 1, and is most efficient if a is large
+ *
+ * This algorithm, reported in Knuth, is attributed to Ahrens. A
+ * faster one, we are told, can be found in: J. H. Ahrens and
+ * U. Dieter, Computing 12 (1974) 223-246.
+ */
+
+ gnm_float sqa, x, y, v;
+ sqa = sqrtgnum (2 * a - 1);
+ do {
+ do {
+ y = tangnum (M_PIgnum * random_01 ());
+ x = sqa * y + a - 1;
+ } while (x <= 0);
+ v = random_01 ();
+ } while (v > (1 + y * y) * expgnum ((a - 1) * loggnum (x / (a - 1)) -
+ sqa * y));
+
+ return x;
+}
+
+static gnm_float
+ran_gamma_int (unsigned int a)
+{
+ if (a < 12) {
+ gnm_float prod;
+
+ do {
+ unsigned int i;
+ prod = 1;
+
+ for (i = 0; i < a; i++)
+ prod *= random_01 ();
+
+ /*
+ * This handles the 0-probability event of getting
+ * an actual zero as well as the possibility of
+ * underflow.
+ */
+ } while (prod == 0);
+
+ return -loggnum (prod);
+ } else
+ return gamma_large (a);
+}
+
+/*
+ * Generate a Gamma distributed number. From the GNU Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_gamma (gnm_float a, gnm_float b)
+{
+ /* assume a > 0 */
+ /* FIXME: why not simply a gnm_float? */
+ unsigned int na = floorgnum (a);
+
+ if (a == na)
+ return b * ran_gamma_int (na);
+ else if (na == 0)
+ return b * gamma_frac (a);
+ else
+ return b * (ran_gamma_int (na) + gamma_frac (a - na));
+}
+
+/*
+ * Generate a Pareto distributed number. From the GNU Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_pareto (gnm_float a, gnm_float b)
+{
+ gnm_float x;
+
+ do {
+ x = random_01 ();
+ } while (x == 0.0);
+
+ return b * powgnum (x, -1 / a);
+}
+
+/*
+ * Generate a F-distributed number. From the GNU Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_fdist (gnm_float nu1, gnm_float nu2)
+{
+ gnm_float Y1 = random_gamma (nu1 / 2, 2.0);
+ gnm_float Y2 = random_gamma (nu2 / 2, 2.0);
+
+ return (Y1 * nu2) / (Y2 * nu1);
+}
+
+/*
+ * Generate a Beta-distributed number. From the GNU Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_beta (gnm_float a, gnm_float b)
+{
+ gnm_float x1 = random_gamma (a, 1.0);
+ gnm_float x2 = random_gamma (b, 1.0);
+
+ return x1 / (x1 + x2);
+}
+
+/*
+ * Generate a Chi-Square-distributed number. From the GNU Scientific library
+ * 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_chisq (gnm_float nu)
+{
+ return 2 * random_gamma (nu / 2, 1.0);
+}
+
+/*
+ * Generate a logistic-distributed number. From the GNU Scientific library
+ * 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_logistic (gnm_float a)
+{
+ gnm_float x;
+
+ do {
+ x = random_01 ();
+ } while (x == 0 || x == 1);
+
+ return a * loggnum (x / (1 - x));
+}
+
+/*
+ * Generate a geometric-distributed number. From the GNU Scientific library
+ * 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_geometric (gnm_float p)
+{
+ gnm_float u;
+
+ if (p == 1)
+ return 1;
+ do {
+ u = random_01 ();
+ } while (u == 0);
+
+ return floorgnum (loggnum (u) / log1pgnum (-p) + 1);
+}
+
+/*
+ * Generate a hypergeometric-distributed number. From the GNU Scientific
+ * library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_hypergeometric (unsigned int n1, unsigned int n2, unsigned int t)
+{
+ unsigned int n = n1 + n2;
+
+ unsigned int i = 0;
+ unsigned int a = n1;
+ unsigned int b = n1 + n2;
+ unsigned int k = 0;
+
+ /* FIXME: performance for large t? */
+
+ if (t > n)
+ t = n;
+
+ if (t < n / 2) {
+ for (i = 0 ; i < t ; i++) {
+ gnm_float u = random_01 ();
+
+ if (b * u < a) {
+ k++;
+ if (k == n1)
+ return k ;
+ a-- ;
+ }
+ b--;
+ }
+ return k;
+ } else {
+ for (i = 0 ; i < n - t ; i++) {
+ gnm_float u = random_01 ();
+
+ if (b * u < a) {
+ k++;
+ if (k == n1)
+ return n1 - k;
+ a--;
+ }
+ b-- ;
+ }
+ return n1 - k;
+ }
+}
+
+
+/*
+ * Generate a logarithmic-distributed number. From the GNU Scientific library
+ * 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_logarithmic (gnm_float p)
+{
+ gnm_float c, v;
+
+ c = log1pgnum (-p);
+ do {
+ v = random_01 ();
+ } while (v == 0);
+
+ if (v >= p)
+ return 1;
+ else {
+ gnm_float u, q;
+
+ do {
+ u = random_01 ();
+ } while (u == 0);
+ q = expm1gnum (c * u);
+
+ if (v <= q * q)
+ return floorgnum (1 + loggnum (v) / loggnum (q));
+ else if (v <= q)
+ return 2;
+ else
+ return 1;
+ }
+}
+
+/*
+ * Generate a T-distributed number. From the GNU Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_tdist (gnm_float nu)
+{
+ if (nu <= 2) {
+ gnm_float Y1 = random_normal ();
+ gnm_float Y2 = random_chisq (nu);
+
+ gnm_float t = Y1 / sqrtgnum (Y2 / nu);
+
+ return t;
+ } else {
+ gnm_float Y1, Y2, Z, t;
+ do {
+ Y1 = random_normal ();
+ Y2 = random_exponential (1 / (nu / 2 - 1));
+
+ Z = Y1 * Y1 / (nu - 2);
+ } while (1 - Z < 0 || expgnum (-Y2 - Z) > (1 - Z));
+
+ /* Note that there is a typo in Knuth's formula, the line below
+ * is taken from the original paper of Marsaglia, Mathematics
+ * of Computation, 34 (1980), p 234-256. */
+
+ t = Y1 / sqrtgnum ((1 - 2 / nu) * (1 - Z));
+ return t;
+ }
+}
+
+/*
+ * Generate a Type I Gumbel-distributed random number. From the GNU
+ * Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_gumbel1 (gnm_float a, gnm_float b)
+{
+ gnm_float x;
+
+ do {
+ x = random_01 ();
+ } while (x == 0.0);
+
+ return (loggnum (b) - loggnum (-loggnum (x))) / a;
+}
+
+/*
+ * Generate a Type II Gumbel-distributed random number. From the GNU
+ * Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ */
+gnm_float
+random_gumbel2 (gnm_float a, gnm_float b)
+{
+ gnm_float x;
+
+ do {
+ x = random_01 ();
+ } while (x == 0.0);
+
+ return powgnum (-b / loggnum (x), 1 / a);
+}
+
+/*
+ * Generate a stable Levy-distributed random number. From the GNU
+ * Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough.
+ *
+ * The stable Levy probability distributions have the form
+ *
+ * p(x) dx = (1/(2 pi)) \int dt exp(- it x - |c t|^alpha)
+ *
+ * with 0 < alpha <= 2.
+ *
+ * For alpha = 1, we get the Cauchy distribution
+ * For alpha = 2, we get the Gaussian distribution with sigma = sqrt(2) c.
+ *
+ * Fromn Chapter 5 of Bratley, Fox and Schrage "A Guide to
+ * Simulation". The original reference given there is,
+ *
+ * J.M. Chambers, C.L. Mallows and B. W. Stuck. "A method for
+ * simulating stable random variates". Journal of the American
+ * Statistical Association, JASA 71 340-344 (1976).
+ */
+gnm_float
+random_levy (gnm_float c, gnm_float alpha)
+{
+ gnm_float u, v, t, s;
+
+ do {
+ u = random_01 ();
+ } while (u == 0.0);
+
+ u = M_PIgnum * (u - 0.5);
+
+ if (alpha == 1) { /* cauchy case */
+ t = tangnum (u);
+ return c * t;
+ }
+
+ do {
+ v = random_exponential (1.0);
+ } while (v == 0);
+
+ if (alpha == 2) { /* gaussian case */
+ t = 2 * singnum (u) * sqrtgnum (v);
+ return c * t;
+ }
+
+ /* general case */
+
+ t = singnum (alpha * u) / powgnum (cosgnum (u), 1 / alpha);
+ s = powgnum (cosgnum ((1 - alpha) * u) / v, (1 - alpha) / alpha);
+
+ return c * t * s;
+}
+
+/*
+ * The following routine for the skew-symmetric case was provided by
+ * Keith Briggs.
+ *
+ * The stable Levy probability distributions have the form
+ *
+ * 2*pi* p(x) dx
+ *
+ * = int dt exp(mu*i*t-|sigma*t|^alpha*(1-i*beta*sign(t)*tan(pi*alpha/2))) for
+ * alpha != 1
+ * = int dt exp(mu*i*t-|sigma*t|^alpha*(1+i*beta*sign(t)*2/pi*log(|t|))) for
+ alpha == 1
+ *
+ * with 0<alpha<=2, -1<=beta<=1, sigma>0.
+ *
+ * For beta=0, sigma=c, mu=0, we get gsl_ran_levy above.
+ *
+ * For alpha = 1, beta=0, we get the Lorentz distribution
+ * For alpha = 2, beta=0, we get the Gaussian distribution
+ *
+ * See A. Weron and R. Weron: Computer simulation of Lévy alpha-stable
+ * variables and processes, preprint Technical University of Wroclaw.
+ * http://www.im.pwr.wroc.pl/~hugo/Publications.html
+ */
+gnm_float
+random_levy_skew (gnm_float c, gnm_float alpha, gnm_float beta)
+{
+ gnm_float V, W, X;
+
+ if (beta == 0) /* symmetric case */
+ return random_levy (c, alpha);
+
+ do {
+ V = random_01 ();
+ } while (V == 0.0);
+
+ V = M_PIgnum * (V - 0.5);
+
+ do {
+ W = random_exponential (1.0);
+ } while (W == 0);
+
+ if (alpha == 1) {
+ X = ((M_PI_2gnum + beta * V) * tangnum (V) -
+ beta * loggnum (M_PI_2gnum * W * cosgnum (V) /
+ (M_PI_2gnum + beta * V))) / M_PI_2gnum;
+ return c * (X + beta * loggnum (c) / M_PI_2gnum);
+ } else {
+ gnm_float t = beta * tangnum (M_PI_2gnum * alpha);
+ gnm_float B = atangnum (t) / alpha;
+ gnm_float S = pow1p (t * t, 1 / (2 * alpha));
+
+ X = S * singnum (alpha * (V + B)) / powgnum (cosgnum (V),
+ 1 / alpha)
+ * powgnum (cosgnum (V - alpha * (V + B)) / W,
+ (1 - alpha) / alpha);
+ return c * X;
+ }
+}
+
+gnm_float
+random_exppow_pdf (gnm_float x, gnm_float a, gnm_float b)
+{
+ gnm_float lngamma = lgamma1p (1 / b);
+
+ return (1 / (2 * a)) * expgnum (-powgnum (gnumabs (x / a), b) - lngamma);
+}
+
+/*
+ * The exponential power probability distribution is
+ *
+ * p(x) dx = (1/(2 a Gamma(1+1/b))) * exp(-|x/a|^b) dx
+ *
+ * for -infty < x < infty. For b = 1 it reduces to the Laplace
+ * distribution.
+ *
+ * The exponential power distribution is related to the gamma
+ * distribution by E = a * pow(G(1/b),1/b), where E is an exponential
+ * power variate and G is a gamma variate.
+ *
+ * We use this relation for b < 1. For b >=1 we use rejection methods
+ * based on the laplace and gaussian distributions which should be
+ * faster.
+ *
+ * See P. R. Tadikamalla, "Random Sampling from the Exponential Power
+ * Distribution", Journal of the American Statistical Association,
+ * September 1980, Volume 75, Number 371, pages 683-686.
+ *
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough
+ */
+
+gnm_float
+random_exppow (gnm_float a, gnm_float b)
+{
+ if (b < 1) {
+ gnm_float u = random_01 ();
+ gnm_float v = random_gamma (1 / b, 1.0);
+ gnm_float z = a * powgnum (v, 1 / b) ;
+
+ if (u > 0.5)
+ return z;
+ else
+ return -z;
+ } else if (b == 1)
+ return random_laplace (a); /* Laplace distribution */
+ else if (b < 2) {
+ /* Use laplace distribution for rejection method */
+ gnm_float x, y, h, ratio, u;
+
+ /* Scale factor chosen by upper bound on ratio at b = 2 */
+ gnm_float s = 1.4489;
+ do {
+ x = random_laplace (a);
+ y = random_laplace_pdf (x, a);
+ h = random_exppow_pdf (x, a, b);
+ ratio = h / (s * y);
+ u = random_01 ();
+ } while (u > ratio);
+
+ return x ;
+ } else if (b == 2) /* Gaussian distribution */
+ return random_gaussian (a / sqrtgnum (2.0));
+ else {
+ /* Use gaussian for rejection method */
+ gnm_float x, y, h, ratio, u;
+ const gnm_float sigma = a / sqrtgnum (2.0);
+
+ /* Scale factor chosen by upper bound on ratio at b = infinity.
+ * This could be improved by using a rational function
+ * approximation to the bounding curve. */
+
+ gnm_float s = 2.4091 ; /* this is sqrt(pi) e / 2 */
+
+ do {
+ x = random_gaussian (sigma) ;
+ y = dnorm (x, 0.0, gnumabs (sigma), FALSE) ;
+ h = random_exppow_pdf (x, a, b) ;
+ ratio = h / (s * y) ;
+ u = random_01 ();
+ } while (u > ratio);
+
+ return x;
+ }
+}
+
+/*
+ * Generate a Gaussian tail-distributed random number. From the GNU
+ * Scientific library 1.1.1.
+ * Copyright (C) 1996, 1997, 1998, 1999, 2000 James Theiler, Brian Gough
+ */
+gnm_float
+random_gaussian_tail (gnm_float a, gnm_float sigma)
+{
+ /*
+ * Returns a gaussian random variable larger than a
+ * This implementation does one-sided upper-tailed deviates.
+ */
+
+ gnm_float s = a / sigma;
+
+ if (s < 1) {
+ /* For small s, use a direct rejection method. The limit s < 1
+ * can be adjusted to optimise the overall efficiency */
+
+ gnm_float x;
+
+ do {
+ x = random_gaussian (1.0);
+ } while (x < s);
+ return x * sigma;
+ } else {
+ /* Use the "supertail" deviates from the last two steps
+ * of Marsaglia's rectangle-wedge-tail method, as described
+ * in Knuth, v2, 3rd ed, pp 123-128. (See also exercise 11,
+ * p139, and the solution, p586.)
+ */
+
+ gnm_float u, v, x;
+
+ do {
+ u = random_01 ();
+ do {
+ v = random_01 ();
+ } while (v == 0.0);
+ x = sqrtgnum (s * s - 2 * loggnum (v));
+ } while (x * u > s);
+ return x * sigma;
+ }
+}
+
+/*
+ * Generate a Landau-distributed random number. From the GNU Scientific
+ * library 1.1.1.
+ *
+ * Copyright (C) 2001 David Morrison
+ *
+ * Adapted from the CERN library routines DENLAN, RANLAN, and DISLAN
+ * as described in http://consult.cern.ch/shortwrups/g110/top.html.
+ * Original author: K.S. K\"olbig.
+ *
+ * The distribution is given by the complex path integral,
+ *
+ * p(x) = (1/(2 pi i)) \int_{c-i\inf}^{c+i\inf} ds exp(s log(s) + x s)
+ *
+ * which can be converted into a real integral over [0,+\inf]
+ *
+ * p(x) = (1/pi) \int_0^\inf dt \exp(-t log(t) - x t) sin(pi t)
+ */
+
+gnm_float
+random_landau (void)
+{
+ static gnm_float F[982] = {
+ 00.000000, 00.000000, 00.000000, 00.000000, 00.000000,
+ -2.244733, -2.204365, -2.168163, -2.135219, -2.104898,
+ -2.076740, -2.050397, -2.025605, -2.002150, -1.979866,
+ -1.958612, -1.938275, -1.918760, -1.899984, -1.881879,
+ -1.864385, -1.847451, -1.831030, -1.815083, -1.799574,
+ -1.784473, -1.769751, -1.755383, -1.741346, -1.727620,
+ -1.714187, -1.701029, -1.688130, -1.675477, -1.663057,
+ -1.650858, -1.638868, -1.627078, -1.615477, -1.604058,
+ -1.592811, -1.581729, -1.570806, -1.560034, -1.549407,
+ -1.538919, -1.528565, -1.518339, -1.508237, -1.498254,
+ -1.488386, -1.478628, -1.468976, -1.459428, -1.449979,
+ -1.440626, -1.431365, -1.422195, -1.413111, -1.404112,
+ -1.395194, -1.386356, -1.377594, -1.368906, -1.360291,
+ -1.351746, -1.343269, -1.334859, -1.326512, -1.318229,
+ -1.310006, -1.301843, -1.293737, -1.285688, -1.277693,
+ -1.269752, -1.261863, -1.254024, -1.246235, -1.238494,
+ -1.230800, -1.223153, -1.215550, -1.207990, -1.200474,
+ -1.192999, -1.185566, -1.178172, -1.170817, -1.163500,
+ -1.156220, -1.148977, -1.141770, -1.134598, -1.127459,
+ -1.120354, -1.113282, -1.106242, -1.099233, -1.092255,
+ -1.085306, -1.078388, -1.071498, -1.064636, -1.057802,
+ -1.050996, -1.044215, -1.037461, -1.030733, -1.024029,
+ -1.017350, -1.010695, -1.004064, -0.997456, -0.990871,
+ -0.984308, -0.977767, -0.971247, -0.964749, -0.958271,
+ -0.951813, -0.945375, -0.938957, -0.932558, -0.926178,
+ -0.919816, -0.913472, -0.907146, -0.900838, -0.894547,
+ -0.888272, -0.882014, -0.875773, -0.869547, -0.863337,
+ -0.857142, -0.850963, -0.844798, -0.838648, -0.832512,
+ -0.826390, -0.820282, -0.814187, -0.808106, -0.802038,
+ -0.795982, -0.789940, -0.783909, -0.777891, -0.771884,
+ -0.765889, -0.759906, -0.753934, -0.747973, -0.742023,
+ -0.736084, -0.730155, -0.724237, -0.718328, -0.712429,
+ -0.706541, -0.700661, -0.694791, -0.688931, -0.683079,
+ -0.677236, -0.671402, -0.665576, -0.659759, -0.653950,
+ -0.648149, -0.642356, -0.636570, -0.630793, -0.625022,
+ -0.619259, -0.613503, -0.607754, -0.602012, -0.596276,
+ -0.590548, -0.584825, -0.579109, -0.573399, -0.567695,
+ -0.561997, -0.556305, -0.550618, -0.544937, -0.539262,
+ -0.533592, -0.527926, -0.522266, -0.516611, -0.510961,
+ -0.505315, -0.499674, -0.494037, -0.488405, -0.482777,
+ -0.477153, -0.471533, -0.465917, -0.460305, -0.454697,
+ -0.449092, -0.443491, -0.437893, -0.432299, -0.426707,
+ -0.421119, -0.415534, -0.409951, -0.404372, -0.398795,
+ -0.393221, -0.387649, -0.382080, -0.376513, -0.370949,
+ -0.365387, -0.359826, -0.354268, -0.348712, -0.343157,
+ -0.337604, -0.332053, -0.326503, -0.320955, -0.315408,
+ -0.309863, -0.304318, -0.298775, -0.293233, -0.287692,
+ -0.282152, -0.276613, -0.271074, -0.265536, -0.259999,
+ -0.254462, -0.248926, -0.243389, -0.237854, -0.232318,
+ -0.226783, -0.221247, -0.215712, -0.210176, -0.204641,
+ -0.199105, -0.193568, -0.188032, -0.182495, -0.176957,
+ -0.171419, -0.165880, -0.160341, -0.154800, -0.149259,
+ -0.143717, -0.138173, -0.132629, -0.127083, -0.121537,
+ -0.115989, -0.110439, -0.104889, -0.099336, -0.093782,
+ -0.088227, -0.082670, -0.077111, -0.071550, -0.065987,
+ -0.060423, -0.054856, -0.049288, -0.043717, -0.038144,
+ -0.032569, -0.026991, -0.021411, -0.015828, -0.010243,
+ -0.004656, 00.000934, 00.006527, 00.012123, 00.017722,
+ 00.023323, 00.028928, 00.034535, 00.040146, 00.045759,
+ 00.051376, 00.056997, 00.062620, 00.068247, 00.073877,
+ 00.079511, 00.085149, 00.090790, 00.096435, 00.102083,
+ 00.107736, 00.113392, 00.119052, 00.124716, 00.130385,
+ 00.136057, 00.141734, 00.147414, 00.153100, 00.158789,
+ 00.164483, 00.170181, 00.175884, 00.181592, 00.187304,
+ 00.193021, 00.198743, 00.204469, 00.210201, 00.215937,
+ 00.221678, 00.227425, 00.233177, 00.238933, 00.244696,
+ 00.250463, 00.256236, 00.262014, 00.267798, 00.273587,
+ 00.279382, 00.285183, 00.290989, 00.296801, 00.302619,
+ 00.308443, 00.314273, 00.320109, 00.325951, 00.331799,
+ 00.337654, 00.343515, 00.349382, 00.355255, 00.361135,
+ 00.367022, 00.372915, 00.378815, 00.384721, 00.390634,
+ 00.396554, 00.402481, 00.408415, 00.414356, 00.420304,
+ 00.426260, 00.432222, 00.438192, 00.444169, 00.450153,
+ 00.456145, 00.462144, 00.468151, 00.474166, 00.480188,
+ 00.486218, 00.492256, 00.498302, 00.504356, 00.510418,
+ 00.516488, 00.522566, 00.528653, 00.534747, 00.540850,
+ 00.546962, 00.553082, 00.559210, 00.565347, 00.571493,
+ 00.577648, 00.583811, 00.589983, 00.596164, 00.602355,
+ 00.608554, 00.614762, 00.620980, 00.627207, 00.633444,
+ 00.639689, 00.645945, 00.652210, 00.658484, 00.664768,
+ 00.671062, 00.677366, 00.683680, 00.690004, 00.696338,
+ 00.702682, 00.709036, 00.715400, 00.721775, 00.728160,
+ 00.734556, 00.740963, 00.747379, 00.753807, 00.760246,
+ 00.766695, 00.773155, 00.779627, 00.786109, 00.792603,
+ 00.799107, 00.805624, 00.812151, 00.818690, 00.825241,
+ 00.831803, 00.838377, 00.844962, 00.851560, 00.858170,
+ 00.864791, 00.871425, 00.878071, 00.884729, 00.891399,
+ 00.898082, 00.904778, 00.911486, 00.918206, 00.924940,
+ 00.931686, 00.938446, 00.945218, 00.952003, 00.958802,
+ 00.965614, 00.972439, 00.979278, 00.986130, 00.992996,
+ 00.999875, 01.006769, 01.013676, 01.020597, 01.027533,
+ 01.034482, 01.041446, 01.048424, 01.055417, 01.062424,
+ 01.069446, 01.076482, 01.083534, 01.090600, 01.097681,
+ 01.104778, 01.111889, 01.119016, 01.126159, 01.133316,
+ 01.140490, 01.147679, 01.154884, 01.162105, 01.169342,
+ 01.176595, 01.183864, 01.191149, 01.198451, 01.205770,
+ 01.213105, 01.220457, 01.227826, 01.235211, 01.242614,
+ 01.250034, 01.257471, 01.264926, 01.272398, 01.279888,
+ 01.287395, 01.294921, 01.302464, 01.310026, 01.317605,
+ 01.325203, 01.332819, 01.340454, 01.348108, 01.355780,
+ 01.363472, 01.371182, 01.378912, 01.386660, 01.394429,
+ 01.402216, 01.410024, 01.417851, 01.425698, 01.433565,
+ 01.441453, 01.449360, 01.457288, 01.465237, 01.473206,
+ 01.481196, 01.489208, 01.497240, 01.505293, 01.513368,
+ 01.521465, 01.529583, 01.537723, 01.545885, 01.554068,
+ 01.562275, 01.570503, 01.578754, 01.587028, 01.595325,
+ 01.603644, 01.611987, 01.620353, 01.628743, 01.637156,
+ 01.645593, 01.654053, 01.662538, 01.671047, 01.679581,
+ 01.688139, 01.696721, 01.705329, 01.713961, 01.722619,
+ 01.731303, 01.740011, 01.748746, 01.757506, 01.766293,
+ 01.775106, 01.783945, 01.792810, 01.801703, 01.810623,
+ 01.819569, 01.828543, 01.837545, 01.846574, 01.855631,
+ 01.864717, 01.873830, 01.882972, 01.892143, 01.901343,
+ 01.910572, 01.919830, 01.929117, 01.938434, 01.947781,
+ 01.957158, 01.966566, 01.976004, 01.985473, 01.994972,
+ 02.004503, 02.014065, 02.023659, 02.033285, 02.042943,
+ 02.052633, 02.062355, 02.072110, 02.081899, 02.091720,
+ 02.101575, 02.111464, 02.121386, 02.131343, 02.141334,
+ 02.151360, 02.161421, 02.171517, 02.181648, 02.191815,
+ 02.202018, 02.212257, 02.222533, 02.232845, 02.243195,
+ 02.253582, 02.264006, 02.274468, 02.284968, 02.295507,
+ 02.306084, 02.316701, 02.327356, 02.338051, 02.348786,
+ 02.359562, 02.370377, 02.381234, 02.392131, 02.403070,
+ 02.414051, 02.425073, 02.436138, 02.447246, 02.458397,
+ 02.469591, 02.480828, 02.492110, 02.503436, 02.514807,
+ 02.526222, 02.537684, 02.549190, 02.560743, 02.572343,
+ 02.583989, 02.595682, 02.607423, 02.619212, 02.631050,
+ 02.642936, 02.654871, 02.666855, 02.678890, 02.690975,
+ 02.703110, 02.715297, 02.727535, 02.739825, 02.752168,
+ 02.764563, 02.777012, 02.789514, 02.802070, 02.814681,
+ 02.827347, 02.840069, 02.852846, 02.865680, 02.878570,
+ 02.891518, 02.904524, 02.917588, 02.930712, 02.943894,
+ 02.957136, 02.970439, 02.983802, 02.997227, 03.010714,
+ 03.024263, 03.037875, 03.051551, 03.065290, 03.079095,
+ 03.092965, 03.106900, 03.120902, 03.134971, 03.149107,
+ 03.163312, 03.177585, 03.191928, 03.206340, 03.220824,
+ 03.235378, 03.250005, 03.264704, 03.279477, 03.294323,
+ 03.309244, 03.324240, 03.339312, 03.354461, 03.369687,
+ 03.384992, 03.400375, 03.415838, 03.431381, 03.447005,
+ 03.462711, 03.478500, 03.494372, 03.510328, 03.526370,
+ 03.542497, 03.558711, 03.575012, 03.591402, 03.607881,
+ 03.624450, 03.641111, 03.657863, 03.674708, 03.691646,
+ 03.708680, 03.725809, 03.743034, 03.760357, 03.777779,
+ 03.795300, 03.812921, 03.830645, 03.848470, 03.866400,
+ 03.884434, 03.902574, 03.920821, 03.939176, 03.957640,
+ 03.976215, 03.994901, 04.013699, 04.032612, 04.051639,
+ 04.070783, 04.090045, 04.109425, 04.128925, 04.148547,
+ 04.168292, 04.188160, 04.208154, 04.228275, 04.248524,
+ 04.268903, 04.289413, 04.310056, 04.330832, 04.351745,
+ 04.372794, 04.393982, 04.415310, 04.436781, 04.458395,
+ 04.480154, 04.502060, 04.524114, 04.546319, 04.568676,
+ 04.591187, 04.613854, 04.636678, 04.659662, 04.682807,
+ 04.706116, 04.729590, 04.753231, 04.777041, 04.801024,
+ 04.825179, 04.849511, 04.874020, 04.898710, 04.923582,
+ 04.948639, 04.973883, 04.999316, 05.024942, 05.050761,
+ 05.076778, 05.102993, 05.129411, 05.156034, 05.182864,
+ 05.209903, 05.237156, 05.264625, 05.292312, 05.320220,
+ 05.348354, 05.376714, 05.405306, 05.434131, 05.463193,
+ 05.492496, 05.522042, 05.551836, 05.581880, 05.612178,
+ 05.642734, 05.673552, 05.704634, 05.735986, 05.767610,
+ 05.799512, 05.831694, 05.864161, 05.896918, 05.929968,
+ 05.963316, 05.996967, 06.030925, 06.065194, 06.099780,
+ 06.134687, 06.169921, 06.205486, 06.241387, 06.277630,
+ 06.314220, 06.351163, 06.388465, 06.426130, 06.464166,
+ 06.502578, 06.541371, 06.580553, 06.620130, 06.660109,
+ 06.700495, 06.741297, 06.782520, 06.824173, 06.866262,
+ 06.908795, 06.951780, 06.995225, 07.039137, 07.083525,
+ 07.128398, 07.173764, 07.219632, 07.266011, 07.312910,
+ 07.360339, 07.408308, 07.456827, 07.505905, 07.555554,
+ 07.605785, 07.656608, 07.708035, 07.760077, 07.812747,
+ 07.866057, 07.920019, 07.974647, 08.029953, 08.085952,
+ 08.142657, 08.200083, 08.258245, 08.317158, 08.376837,
+ 08.437300, 08.498562, 08.560641, 08.623554, 08.687319,
+ 08.751955, 08.817481, 08.883916, 08.951282, 09.019600,
+ 09.088889, 09.159174, 09.230477, 09.302822, 09.376233,
+ 09.450735, 09.526355, 09.603118, 09.681054, 09.760191,
+ 09.840558, 09.922186, 10.005107, 10.089353, 10.174959,
+ 10.261958, 10.350389, 10.440287, 10.531693, 10.624646,
+ 10.719188, 10.815362, 10.913214, 11.012789, 11.114137,
+ 11.217307, 11.322352, 11.429325, 11.538283, 11.649285,
+ 11.762390, 11.877664, 11.995170, 12.114979, 12.237161,
+ 12.361791, 12.488946, 12.618708, 12.751161, 12.886394,
+ 13.024498, 13.165570, 13.309711, 13.457026, 13.607625,
+ 13.761625, 13.919145, 14.080314, 14.245263, 14.414134,
+ 14.587072, 14.764233, 14.945778, 15.131877, 15.322712,
+ 15.518470, 15.719353, 15.925570, 16.137345, 16.354912,
+ 16.578520, 16.808433, 17.044929, 17.288305, 17.538873,
+ 17.796967, 18.062943, 18.337176, 18.620068, 18.912049,
+ 19.213574, 19.525133, 19.847249, 20.180480, 20.525429,
+ 20.882738, 21.253102, 21.637266, 22.036036, 22.450278,
+ 22.880933, 23.329017, 23.795634, 24.281981, 24.789364,
+ 25.319207, 25.873062, 26.452634, 27.059789, 27.696581,
+ 28.365274, 29.068370, 29.808638, 30.589157, 31.413354,
+ 32.285060, 33.208568, 34.188705, 35.230920, 36.341388,
+ 37.527131, 38.796172, 40.157721, 41.622399, 43.202525,
+ 44.912465, 46.769077, 48.792279, 51.005773, 53.437996,
+ 56.123356, 59.103894
+ };
+ gnm_float X, U, V, RANLAN;
+ int I, i;
+
+ do {
+ X = random_01 ();
+ } while (X == 0.0);
+ U = 1000.0 * X;
+ i = U;
+ U = U - i;
+
+ /* Account for difference between C and Fortran convention for lower
+ * bound. */
+ I = i - 1;
+
+ if (I >= 70 && I <= 800)
+ RANLAN = F[I] + U * (F[I + 1] - F[I]);
+ else if (I >= 7 && I <= 980)
+ RANLAN = F[I] + U * (F[I + 1] - F[I] -
+ 0.25 * (1 - U) * (F[I + 2] - F[I + 1] -
+ F[I] + F[I - 1]));
+ else if (I < 7) {
+ V = loggnum (X);
+ U = 1 / V;
+ RANLAN = ((0.99858950 + (3.45213058E1 + 1.70854528E1 * U) * U) /
+ (1 + (3.41760202E1 + 4.01244582 * U) * U)) *
+ ( -loggnum ( -0.91893853 - V) - 1);
+ } else {
+ U = 1 - X;
+ V = U * U;
+ if (X <= 0.999)
+ RANLAN = (1.00060006 + 2.63991156E2 *
+ U + 4.37320068E3 * V) /
+ ((1 + 2.57368075E2 * U + 3.41448018E3 * V) * U);
+ else
+ RANLAN = (1.00001538 + 6.07514119E3 * U +
+ 7.34266409E5 * V) /
+ ((1 + 6.06511919E3 * U + 6.94021044E5 * V) * U);
+ }
+
+ return RANLAN;
+}
+
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Generate 2^n being careful not to overflow
+ */
+gnm_float
+gpow2 (int n)
+{
+#ifdef NEED_FAKE_LDEXPGNUM
+ g_assert (FLT_RADIX == 2);
+
+ /* gpow2 is used in our implementation of ldexpgnum. */
+ if (n >= DBL_MIN_EXP && n <= DBL_MAX_EXP)
+ return (gnm_float) ldexp (1.0, n);
+ else if (n >= GNUM_MIN_EXP && n <= GNUM_MAX_EXP) {
+ gnm_float res = 1.0;
+ gnm_float p = (n >= 0) ? GNM_const (2) : GNM_const (0.5);
+
+ n = abs (n);
+ while (n > 0) {
+ if (n & 1) res *= p;
+ p *= p;
+ n >>= 1;
+ }
+ return res;
+ } else
+ return (n > 0) ? gnm_pinf : ML_UNDERFLOW;
+#else
+ return ldexpgnum (1.0, n);
+#endif
+}
+
+
+/*
+ * Generate 10^n being careful not to overflow
+ */
+gnm_float
+gpow10 (int n)
+{
+ gnm_float res = 1.0;
+ gnm_float p;
+ const int maxn = GNUM_MAX_EXP;
+
+ static const gnm_float fast[] = {
+ GNM_const (1e-20),
+ GNM_const (1e-19),
+ GNM_const (1e-18),
+ GNM_const (1e-17),
+ GNM_const (1e-16),
+ GNM_const (1e-15),
+ GNM_const (1e-14),
+ GNM_const (1e-13),
+ GNM_const (1e-12),
+ GNM_const (1e-11),
+ GNM_const (1e-10),
+ GNM_const (1e-9),
+ GNM_const (1e-8),
+ GNM_const (1e-7),
+ GNM_const (1e-6),
+ GNM_const (1e-5),
+ GNM_const (1e-4),
+ GNM_const (1e-3),
+ GNM_const (1e-2),
+ GNM_const (1e-1),
+ GNM_const (1e0),
+ GNM_const (1e1),
+ GNM_const (1e2),
+ GNM_const (1e3),
+ GNM_const (1e4),
+ GNM_const (1e5),
+ GNM_const (1e6),
+ GNM_const (1e7),
+ GNM_const (1e8),
+ GNM_const (1e9),
+ GNM_const (1e10),
+ GNM_const (1e11),
+ GNM_const (1e12),
+ GNM_const (1e13),
+ GNM_const (1e14),
+ GNM_const (1e15),
+ GNM_const (1e16),
+ GNM_const (1e17),
+ GNM_const (1e18),
+ GNM_const (1e19),
+ GNM_const (1e20)
+ };
+
+ if (n >= -20 && n <= 20)
+ return (fast + 20)[n];
+
+ if (n >= 0) {
+ p = 10.0;
+ n = (n > maxn) ? maxn : n;
+ } else {
+ p = GNM_const (0.1);
+ /* Note carefully that we avoid overflow. */
+ n = (n < -maxn) ? maxn : -n;
+ }
+ while (n > 0) {
+ if (n & 1) res *= p;
+ p *= p;
+ n >>= 1;
+ }
+ return res;
+}
+
+
+/*
+ * Euclid's Algorithm. Assumes non-negative numbers.
+ */
+int
+gcd (int a, int b)
+{
+ while (b != 0) {
+ int r = a % b;
+ a = b;
+ b = r;
+ }
+ return a;
+}
+
+
+gnm_float
+combin (int n, int k)
+{
+ if (n >= 15) {
+ return floorgnum (0.5 + expgnum (lgammagnum (n + 1) - lgammagnum (k + 1) - lgammagnum (n - k + 1)));
+ } else {
+ return fact (n) / fact (k) / fact (n - k);
+ }
+}
+
+gnm_float
+permut (int n, int k)
+{
+ if (n >= 15) {
+ return floorgnum (0.5 + expgnum (lgammagnum (n + 1) - lgammagnum (n - k + 1)));
+ } else {
+ return fact (n) / fact (n - k);
+ }
+}
+
+gnm_float
+fact (int n)
+{
+ static gnm_float table[100];
+ static gboolean init = FALSE;
+
+ if (n < 0)
+ return gnm_nan;
+
+ if (n < (int)G_N_ELEMENTS (table)) {
+ if (!init) {
+ int i;
+ table[0] = 1;
+ for (i = 1; i < (int)G_N_ELEMENTS (table); i++)
+ table[i] = table[i - 1] * i;
+ init = TRUE;
+ }
+ return table[n];
+ } else
+ return floorgnum (0.5 + expgnum (lgammagnum (n + 1)));
+}
+
+gnm_float
+beta (gnm_float a, gnm_float b)
+{
+ int sign;
+ gnm_float absres = expgnum (lbeta3 (a, b, &sign));
+
+ return sign == -1 ? -absres : absres;
+}
+
+gnm_float
+lbeta3 (gnm_float a, gnm_float b, int *sign)
+{
+ int sign_a, sign_b, sign_ab;
+ gnm_float ab = a + b;
+ gnm_float res_a, res_b, res_ab;
+
+ *sign = 1;
+ if (a > 0 && b > 0)
+ return lbeta (a, b);
+
+#ifdef IEEE_754
+ if (isnangnum(ab))
+ return ab;
+#endif
+
+ if ((a <= 0 && a == floorgnum (a)) ||
+ (b <= 0 && b == floorgnum (b)) ||
+ (ab <= 0 && ab == floorgnum (ab)))
+ return gnm_nan;
+
+ res_a = lgamma_rgnum (a, &sign_a);
+ res_b = lgamma_rgnum (b, &sign_b);
+ res_ab = lgamma_rgnum (ab, &sign_ab);
+
+ *sign = sign_a * sign_b * sign_ab;
+ return res_a + res_b - res_ab;
+}
+
+
+/* Calculate (1+x)^r accurately. */
+gnm_float
+pow1p (gnm_float x, gnm_float y)
+{
+ if (gnumabs (x) > 0.5)
+ return powgnum (1 + x, y);
+ else
+ return expgnum (y * log1pgnum (x));
+}
+
+/* Calculate ((1+x)^r)-1 accurately. */
+gnm_float
+pow1pm1 (gnm_float x, gnm_float y)
+{
+ if (x <= -1)
+ return powgnum (1 + x, y) - 1;
+ else
+ return expm1gnum (y * log1pgnum (x));
+}
+
+
+/*
+ ---------------------------------------------------------------------
+ Matrix functions
+ ---------------------------------------------------------------------
+ */
+
+/* Calculates the product of two matrixes.
+ */
+void
+mmult (gnm_float *A, gnm_float *B, int cols_a, int rows_a, int cols_b,
+ gnm_float *product)
+{
+ gnm_float tmp;
+ int c, r, i;
+
+ for (c = 0; c < cols_b; ++c) {
+ for (r = 0; r < rows_a; ++r) {
+ tmp = 0;
+ for (i = 0; i < cols_a; ++i)
+ tmp += A[r + i * rows_a] * B[i + c * cols_a];
+ product[r + c * rows_a] = tmp;
+ }
+ }
+}
+
+void
+continued_fraction (gnm_float val, int max_denom, int *res_num, int *res_denom)
+{
+ int n1, n2, d1, d2;
+ gnm_float x, y;
+
+ if (val < 0) {
+ continued_fraction (gnumabs (val), max_denom, res_num, res_denom);
+ *res_num = -*res_num;
+ return;
+ }
+
+ n1 = 0; d1 = 1;
+ n2 = 1; d2 = 0;
+
+ x = val;
+ y = 1;
+
+ do {
+ int a = (int) (x / y);
+ gnm_float newy = x - a * y;
+ int n3, d3;
+
+ if ((n2 && a > (INT_MAX - n1) / n2) ||
+ (d2 && a > (INT_MAX - d1) / d2) ||
+ a * d2 + d1 > max_denom) {
+ *res_num = n2;
+ *res_denom = d2;
+ return;
+ }
+
+ n3 = a * n2 + n1;
+ d3 = a * d2 + d1;
+
+ x = y;
+ y = newy;
+
+ n1 = n2; n2 = n3;
+ d1 = d2; d2 = d3;
+ } while (y > 1e-10);
+
+ *res_num = n2;
+ *res_denom = d2;
+}
+
+
+void
+stern_brocot (float val, int max_denom, int *res_num, int *res_denom)
+{
+ int an = 0, ad = 1;
+ int bn = 1, bd = 1;
+ int n, d;
+ float sp, delta;
+
+ while ((d = ad + bd) <= max_denom) {
+ sp = 1e-5 * d;/* Quick and dirty, do adaptive later */
+ n = an + bn;
+ delta = val * d - n;
+ if (delta > sp) {
+ an = n;
+ ad = d;
+ } else if (delta < -sp) {
+ bn = n;
+ bd = d;
+ } else {
+ *res_num = n;
+ *res_denom = d;
+ return;
+ }
+ }
+ if (bd > max_denom || gnumabs (val * ad - an) < gnumabs (val * bd - bn)) {
+ *res_num = an;
+ *res_denom = ad;
+ } else {
+ *res_num = bn;
+ *res_denom = bd;
+ }
+}
--- /dev/null
+++ lib/goffice/split/command-context-stderr.h
@@ -0,0 +1,18 @@
+#ifndef GNUMERIC_COMMAND_CONTEXT_STDERR_H
+#define GNUMERIC_COMMAND_CONTEXT_STDERR_H
+
+#include "gnumeric.h"
+#include <glib-object.h>
+
+#define CMD_CONTEXT_STDERR_TYPE (cmd_context_stderr_get_type ())
+#define COMMAND_CONTEXT_STDERR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CMD_CONTEXT_STDERR_TYPE, CmdContextStderr))
+#define IS_COMMAND_CONTEXT_STDERR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CMD_CONTEXT_STDERR_TYPE))
+
+typedef struct _CmdContextStderr CmdContextStderr;
+
+GType cmd_context_stderr_get_type (void);
+GnmCmdContext *cmd_context_stderr_new (void);
+void cmd_context_stderr_set_status (CmdContextStderr *, int status);
+int cmd_context_stderr_get_status (CmdContextStderr *);
+
+#endif /* GNUMERIC_COMMAND_CONTEXT_STDERR_H */
--- /dev/null
+++ lib/goffice/split/plugin-loader-module.h
@@ -0,0 +1,17 @@
+#ifndef GNUMERIC_PLUGIN_LOADER_MODULE_H
+#define GNUMERIC_PLUGIN_LOADER_MODULE_H
+
+#include <glib-object.h>
+
+#define TYPE_GNM_PLUGIN_LOADER_MODULE (gnm_plugin_loader_module_get_type ())
+#define GNM_PLUGIN_LOADER_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_GNM_PLUGIN_LOADER_MODULE, GnmPluginLoaderModule))
+#define GNM_PLUGIN_LOADER_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_GNM_PLUGIN_LOADER_MODULE, GnmPluginLoaderModuleClass))
+#define IS_GNM_PLUGIN_LOADER_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_GNM_PLUGIN_LOADER_MODULE))
+#define IS_GNM_PLUGIN_LOADER_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_GNM_PLUGIN_LOADER_MODULE))
+
+typedef struct _GnmPluginLoaderModule GnmPluginLoaderModule;
+typedef struct _GnmPluginLoaderModuleClass GnmPluginLoaderModuleClass;
+
+GType gnm_plugin_loader_module_get_type (void);
+
+#endif /* GNUMERIC_PLUGIN_LOADER_MODULE_H */
--- /dev/null
+++ lib/goffice/split/io-context.h
@@ -0,0 +1,53 @@
+#ifndef GNUMERIC_IO_CONTEXT_H
+#define GNUMERIC_IO_CONTEXT_H
+
+#include "gnumeric.h"
+#include <glib-object.h>
+#include <stdarg.h>
+
+/* typedef struct _IOContext IOContext; */
+typedef struct _IOContextClass IOContextClass;
+
+#define TYPE_IO_CONTEXT (io_context_get_type ())
+#define IO_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_IO_CONTEXT, IOContext))
+#define IS_IO_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_IO_CONTEXT))
+
+GType io_context_get_type (void);
+IOContext *gnumeric_io_context_new (GnmCmdContext *cc);
+
+void gnumeric_io_error_unknown (IOContext *ioc);
+
+void gnumeric_io_error_info_set (IOContext *ioc, ErrorInfo *error);
+void gnumeric_io_error_string (IOContext *ioc, const gchar *str);
+void gnumeric_io_error_push (IOContext *ioc, ErrorInfo *error);
+void gnumeric_io_error_clear (IOContext *ioc);
+void gnumeric_io_error_display (IOContext *ioc);
+
+gboolean gnumeric_io_error_occurred (IOContext *ioc);
+gboolean gnumeric_io_warning_occurred (IOContext *ioc);
+
+void io_progress_message (IOContext *io_context, const gchar *msg);
+void io_progress_update (IOContext *io_context, gdouble f);
+void io_progress_range_push (IOContext *io_context, gdouble min, gdouble max);
+void io_progress_range_pop (IOContext *io_context);
+
+void count_io_progress_set (IOContext *io_context, gint total, gint step);
+void count_io_progress_update (IOContext *io_context, gint inc);
+
+void value_io_progress_set (IOContext *io_context, gint total, gint step);
+void value_io_progress_update (IOContext *io_context, gint value);
+
+void workbook_io_progress_set (IOContext *io_context, Workbook const *wb, gint step);
+void workbook_io_progress_update (IOContext *io_context, gint inc);
+
+void io_progress_unset (IOContext *io_context);
+
+void gnm_io_context_set_num_files (IOContext *ioc, guint count);
+void gnm_io_context_processing_file (IOContext *ioc, char const *name);
+void gnm_io_warning (IOContext *ioc, char const *fmt, ...) G_GNUC_PRINTF (2, 3);
+void gnm_io_warning_varargs (IOContext *ioc, char const *fmt, va_list args);
+void gnm_io_warning_unknown_font (IOContext *ioc, char const *font_name);
+void gnm_io_warning_unknown_function (IOContext *ioc, char const *funct_name);
+void gnm_io_warning_unsupported_feature (IOContext *ioc, char const *feature);
+
+#endif /* GNUMERIC_IO_CONTEXT_H */
--- /dev/null
+++ lib/goffice/split/str.h
@@ -0,0 +1,19 @@
+#ifndef GNUMERIC_STRING_H
+#define GNUMERIC_STRING_H
+
+#include "gnumeric.h"
+
+struct _GnmString {
+ int ref_count;
+ char *str;
+};
+
+void gnm_string_init (void);
+void gnm_string_shutdown (void);
+
+GnmString *gnm_string_get (char const *s);
+GnmString *gnm_string_get_nocopy (char *s);
+GnmString *gnm_string_ref (GnmString *);
+void gnm_string_unref (GnmString *);
+
+#endif /* GNUMERIC_STRING_H */
--- /dev/null
+++ lib/goffice/split/file.h
@@ -0,0 +1,162 @@
+#ifndef GNUMERIC_FILE_H
+#define GNUMERIC_FILE_H
+
+#include "gnumeric.h"
+#include <glib-object.h>
+#include <gsf/gsf.h>
+
+
+/*
+ * File format levels. They are ordered. When we save a file, we
+ * remember the name, but not if we already have a name at a higher level.
+ * When created, workbooks are assigned a name at level FILE_FL_NEW.
+ */
+typedef enum {
+ FILE_FL_NONE, /* No name assigned, won't happen */
+ FILE_FL_WRITE_ONLY, /* PostScript etc, won't be remembered */
+ FILE_FL_NEW, /* Wb just created */
+ FILE_FL_MANUAL, /* Save gets punted to save as */
+ FILE_FL_MANUAL_REMEMBER, /* Ditto, but remember in history */
+ FILE_FL_AUTO, /* Save will save to this filename */
+ FILE_FL_LAST
+} FileFormatLevel;
+
+/*
+ * File probe level tells file opener (its probe method to be exact), how
+ * hard it should try to recognize the type of the file. File openers may
+ * ignore this or support only some probe levels, but if specifies
+ * "reccomened" behaviour.
+ * Before opening any file we detect its type by calling probe for
+ * every registered file opener (in order of priority) and passing
+ * FILE_PROBE_FILE_NAME as probe level. If none of them recogizes the file,
+ * we increase probe level and try again...
+ */
+typedef enum {
+ FILE_PROBE_FILE_NAME, /* Test only file name, don't read file contents */
+ FILE_PROBE_CONTENT, /* Read the whole file if it's necessary */
+ FILE_PROBE_LAST
+} FileProbeLevel;
+
+/*
+ * FileSaveScope specifies what information file saver can save in a file.
+ * Many savers can save the whole workbook (with all sheets), but others
+ * save only current sheet, usually because of file format limitations.
+ */
+typedef enum {
+ FILE_SAVE_WORKBOOK,
+ FILE_SAVE_SHEET,
+ FILE_SAVE_RANGE,
+ FILE_SAVE_LAST
+} FileSaveScope;
+
+/*
+ * GnmFileOpener
+ */
+
+typedef struct _GnmFileOpenerClass GnmFileOpenerClass;
+
+#define TYPE_GNM_FILE_OPENER (gnm_file_opener_get_type ())
+#define GNM_FILE_OPENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_GNM_FILE_OPENER, GnmFileOpener))
+#define IS_GNM_FILE_OPENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_GNM_FILE_OPENER))
+
+typedef gboolean (*GnmFileOpenerProbeFunc) (GnmFileOpener const *fo,
+ GsfInput *input,
+ FileProbeLevel pl);
+typedef void (*GnmFileOpenerOpenFunc) (GnmFileOpener const *fo,
+ IOContext *io_context,
+ WorkbookView *wbv,
+ GsfInput *input);
+typedef void (*GnmFileOpenerOpenFuncWithEnc) (GnmFileOpener const *fo,
+ gchar const *enc,
+ IOContext *io_context,
+ WorkbookView *wbv,
+ GsfInput *input);
+
+GType gnm_file_opener_get_type (void);
+
+GnmFileOpener *gnm_file_opener_new (char const *id,
+ char const *description,
+ GSList *suffixes,
+ GSList *mimes,
+ GnmFileOpenerProbeFunc probe_func,
+ GnmFileOpenerOpenFunc open_func);
+GnmFileOpener *gnm_file_opener_new_with_enc (char const *id,
+ char const *description,
+ GSList *suffixes,
+ GSList *mimes,
+ GnmFileOpenerProbeFunc probe_func,
+ GnmFileOpenerOpenFuncWithEnc open_func);
+
+
+gboolean gnm_file_opener_probe (GnmFileOpener const *fo, GsfInput *input,
+ FileProbeLevel pl);
+void gnm_file_opener_open (GnmFileOpener const *fo, gchar const *opt_enc,
+ IOContext *io_context,
+ WorkbookView *wbv, GsfInput *input);
+
+char const *gnm_file_opener_get_id (GnmFileOpener const *fo);
+char const *gnm_file_opener_get_description (GnmFileOpener const *fo);
+gboolean gnm_file_opener_is_encoding_dependent (GnmFileOpener const *fo);
+gboolean gnm_file_opener_can_probe (GnmFileOpener const *fo,
+ FileProbeLevel pl);
+GSList const *gnm_file_opener_get_suffixes (GnmFileOpener const *fo);
+GSList const *gnm_file_opener_get_mimes (GnmFileOpener const *fo);
+
+/*
+ * GnmFileSaver
+ */
+
+typedef struct _GnmFileSaverClass GnmFileSaverClass;
+
+#define TYPE_GNM_FILE_SAVER (gnm_file_saver_get_type ())
+#define GNM_FILE_SAVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_GNM_FILE_SAVER, GnmFileSaver))
+#define IS_GNM_FILE_SAVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_GNM_FILE_SAVER))
+
+typedef void (*GnmFileSaverSaveFunc) (GnmFileSaver const *fs,
+ IOContext *io_context,
+ WorkbookView const *wbv,
+ GsfOutput *output);
+GType gnm_file_saver_get_type (void);
+
+GnmFileSaver *gnm_file_saver_new (char const *id,
+ char const *extension,
+ char const *description,
+ FileFormatLevel level,
+ GnmFileSaverSaveFunc save_func);
+
+void gnm_file_saver_set_save_scope (GnmFileSaver *fs, FileSaveScope scope);
+FileSaveScope gnm_file_saver_get_save_scope (GnmFileSaver const *fs);
+
+void gnm_file_saver_save (GnmFileSaver const *fs, IOContext *io_context,
+ WorkbookView const *wbv, GsfOutput *output);
+void gnm_file_saver_set_overwrite_files (GnmFileSaver *fs,
+ gboolean overwrite);
+gboolean gnm_vrfy_uri_ext (gchar const *std_ext,
+ gchar const *uri,
+ gchar **new_uri);
+char const *gnm_file_saver_get_id (GnmFileSaver const *fs);
+char const *gnm_file_saver_get_extension (GnmFileSaver const *fs);
+char const *gnm_file_saver_get_mime_type (GnmFileSaver const *fs);
+char const *gnm_file_saver_get_description (GnmFileSaver const *fs);
+FileFormatLevel gnm_file_saver_get_format_level (GnmFileSaver const *fs);
+
+/*
+ *
+ */
+
+GList *get_file_openers (void);
+void gnm_file_opener_unregister (GnmFileOpener *fo);
+void gnm_file_opener_register (GnmFileOpener *fo, gint priority);
+GnmFileOpener *gnm_file_opener_for_id (char const *id);
+
+GList *get_file_savers (void);
+void gnm_file_saver_unregister (GnmFileSaver *fs);
+void gnm_file_saver_register (GnmFileSaver *fs);
+void gnm_file_saver_register_as_default (GnmFileSaver *fs, gint priority);
+
+GnmFileSaver *gnm_file_saver_get_default (void);
+GnmFileSaver *gnm_file_saver_for_mime_type (char const *mime_type);
+GnmFileSaver *gnm_file_saver_for_file_name (char const *file_name);
+GnmFileSaver *gnm_file_saver_for_id (char const *id);
+
+#endif /* GNUMERIC_FILE_H */
--- /dev/null
+++ lib/goffice/split/plugin-util.c
@@ -0,0 +1,62 @@
+/*
+ * plugin-util.c: Utility functions for gnumeric plugins
+ *
+ * Authors:
+ * Almer. S. Tigelaar. <almer1 at dds.nl>
+ * Zbigniew Chyla <cyba at gnome.pl>
+ *
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "plugin-util.h"
+
+#include "command-context.h"
+#include "io-context.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+/**
+ * gnumeric_fopen_error_info:
+ * @file_name: the file to open
+ * @mode: the file mode
+ * @ret_error: ErrorInfo to fill when error occurs
+ *
+ * a wrapper around fopen ().
+ * It will fill ErrorInfo struct for you.
+ * for more info on the parameters see 'man 3 fopen'
+ *
+ * Return value: a pointer to a FILE struct if successful or NULL if not
+ **/
+FILE *
+gnumeric_fopen_error_info (const char *file_name, const char *mode, ErrorInfo **ret_error)
+{
+ FILE *f;
+
+ g_return_val_if_fail (file_name != NULL, NULL);
+ g_return_val_if_fail (mode != NULL, NULL);
+ g_return_val_if_fail (ret_error != NULL, NULL);
+
+ *ret_error = NULL;
+ f = fopen (file_name, mode);
+ if (f == NULL) {
+ if (strchr (mode, 'w') != NULL && strchr (mode, 'r') == NULL) {
+ *ret_error = error_info_new_printf (
+ _("Error while opening file \"%s\" for writing."),
+ file_name);
+ } else {
+ *ret_error = error_info_new_printf (
+ _("Error while opening file \"%s\" for reading."),
+ file_name);
+ }
+ error_info_add_details (*ret_error, error_info_new_from_errno ());
+ }
+
+ return f;
+}
--- /dev/null
+++ lib/goffice/split/plugin-loader.c
@@ -0,0 +1,273 @@
+/*
+ * plugin-loader.c: Base class for plugin loaders.
+ *
+ * Author: Zbigniew Chyla (cyba at gnome.pl)
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "plugin-loader.h"
+
+#include "plugin.h"
+#include "plugin-service.h"
+
+#include <gsf/gsf-impl-utils.h>
+
+#define PL_GET_CLASS(loader) GNM_PLUGIN_LOADER_CLASS (G_OBJECT_GET_CLASS (loader))
+
+static GObjectClass *parent_class = NULL;
+
+static void
+gnm_plugin_loader_init (GnmPluginLoader *loader)
+{
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+
+ loader->plugin = NULL;
+ loader->is_base_loaded = FALSE;
+ loader->n_loaded_services = 0;
+}
+
+static void
+gnm_plugin_loader_finalize (GObject *obj)
+{
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (obj));
+
+ parent_class->finalize (obj);
+}
+
+static void
+gnm_plugin_loader_unload_service_general_real (GnmPluginLoader *loader,
+ GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ PluginServiceGeneralCallbacks *cbs;
+
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_GENERAL (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ cbs = plugin_service_get_cbs (service);
+ cbs->plugin_func_init = NULL;
+ cbs->plugin_func_cleanup = NULL;
+}
+
+static void
+gnm_plugin_loader_unload_service_plugin_loader_real (GnmPluginLoader *loader,
+ GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ PluginServicePluginLoaderCallbacks *cbs;
+
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_PLUGIN_LOADER (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ cbs = plugin_service_get_cbs (service);
+ cbs->plugin_func_get_loader_type = NULL;
+}
+
+static void
+gnm_plugin_loader_unload_service_ui_real (GnmPluginLoader *loader,
+ GnmPluginService *service,
+ ErrorInfo **ret_error)
+{
+ PluginServiceUICallbacks *cbs;
+
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE_UI (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ cbs = plugin_service_get_cbs (service);
+ cbs->plugin_func_exec_action = NULL;
+}
+
+static void
+gnm_plugin_loader_class_init (GObjectClass *gobject_class)
+{
+ GnmPluginLoaderClass *plugin_loader_class = GNM_PLUGIN_LOADER_CLASS (gobject_class);
+
+ parent_class = g_type_class_peek_parent (gobject_class);
+
+ gobject_class->finalize = gnm_plugin_loader_finalize;
+
+ plugin_loader_class->set_attributes = NULL;
+ plugin_loader_class->load_base = NULL;
+ plugin_loader_class->unload_base = NULL;
+ plugin_loader_class->load_service_general = NULL;
+ plugin_loader_class->unload_service_general = gnm_plugin_loader_unload_service_general_real;
+ // plugin_loader_class->load_service_file_opener = NULL;
+ // plugin_loader_class->unload_service_file_opener = gnm_plugin_loader_unload_service_file_opener_real;
+ // plugin_loader_class->load_service_file_saver = NULL;
+ // plugin_loader_class->unload_service_file_saver = gnm_plugin_loader_unload_service_file_saver_real;
+ // plugin_loader_class->load_service_function_group = NULL;
+ // plugin_loader_class->unload_service_function_group = gnm_plugin_loader_unload_service_function_group_real;
+ plugin_loader_class->load_service_plugin_loader = NULL;
+ plugin_loader_class->unload_service_plugin_loader = gnm_plugin_loader_unload_service_plugin_loader_real;
+ plugin_loader_class->load_service_ui = NULL;
+ plugin_loader_class->unload_service_ui = gnm_plugin_loader_unload_service_ui_real;
+}
+
+GSF_CLASS (GnmPluginLoader, gnm_plugin_loader,
+ gnm_plugin_loader_class_init, gnm_plugin_loader_init,
+ G_TYPE_OBJECT)
+
+void
+gnm_plugin_loader_set_attributes (GnmPluginLoader *loader,
+ GHashTable *attrs,
+ ErrorInfo **ret_error)
+{
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (PL_GET_CLASS (loader)->set_attributes ) {
+ PL_GET_CLASS (loader)->set_attributes (loader, attrs, ret_error);
+ } else {
+ *ret_error = error_info_new_printf (_("Loader has no set_attributes method.\n"));
+ }
+}
+
+void
+gnm_plugin_loader_set_plugin (GnmPluginLoader *loader, GnmPlugin *plugin)
+{
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+ g_return_if_fail (IS_GNM_PLUGIN (plugin));
+
+ loader->plugin = plugin;
+}
+
+void
+gnm_plugin_loader_load_base (GnmPluginLoader *loader, ErrorInfo **ret_error)
+{
+ GnmPluginLoaderClass *gnm_plugin_loader_class;
+ ErrorInfo *error = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+ g_return_if_fail (!loader->is_base_loaded);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ gnm_plugin_loader_class = PL_GET_CLASS (loader);
+ if (gnm_plugin_loader_class->load_base != NULL) {
+ gnm_plugin_loader_class->load_base (loader, &error);
+ } else {
+ *ret_error = error_info_new_printf (_("Loader has no load_base method.\n"));
+ }
+ if (error == NULL) {
+ loader->is_base_loaded = TRUE;
+ plugin_message (3, "Loaded plugin \"%s\".\n", gnm_plugin_get_id (loader->plugin));
+ } else {
+ *ret_error = error;
+ }
+}
+
+void
+gnm_plugin_loader_unload_base (GnmPluginLoader *loader, ErrorInfo **ret_error)
+{
+ GnmPluginLoaderClass *gnm_plugin_loader_class;
+ ErrorInfo *error = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+ g_return_if_fail (loader->is_base_loaded);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ gnm_plugin_loader_class = PL_GET_CLASS (loader);
+ if (gnm_plugin_loader_class->unload_base != NULL) {
+ gnm_plugin_loader_class->unload_base (loader, &error);
+ if (error == NULL) {
+ loader->is_base_loaded = FALSE;
+ plugin_message (3, "Unloaded plugin \"%s\".\n", gnm_plugin_get_id (loader->plugin));
+ } else {
+ *ret_error = error;
+ }
+ }
+}
+
+void
+gnm_plugin_loader_load_service (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error)
+{
+ GnmPluginLoaderClass *gnm_plugin_loader_class;
+ void (*load_service_method) (GnmPluginLoader *, GnmPluginService *, ErrorInfo **) = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE (service));
+ g_return_if_fail (loader->is_base_loaded);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ gnm_plugin_loader_class = PL_GET_CLASS (loader);
+ if (IS_GNM_PLUGIN_SERVICE_GENERAL (service)) {
+ load_service_method = gnm_plugin_loader_class->load_service_general;
+ /*} else if (IS_GNM_PLUGIN_SERVICE_FILE_OPENER (service)) {
+ load_service_method = gnm_plugin_loader_class->load_service_file_opener;
+ } else if (IS_GNM_PLUGIN_SERVICE_FILE_SAVER (service)) {
+ load_service_method = gnm_plugin_loader_class->load_service_file_saver;
+ } else if (IS_GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service)) {
+ load_service_method = gnm_plugin_loader_class->load_service_function_group;*/
+ } else if (IS_GNM_PLUGIN_SERVICE_PLUGIN_LOADER (service)) {
+ load_service_method = gnm_plugin_loader_class->load_service_plugin_loader;
+ } else if (IS_GNM_PLUGIN_SERVICE_UI (service)) {
+ load_service_method = gnm_plugin_loader_class->load_service_ui;
+ } else if (IS_GNM_PLUGIN_SERVICE_SIMPLE (service)) {
+ load_service_method = NULL;
+ } else {
+ *ret_error = error_info_new_printf (_("Service '%s' not supported by loader."),
+ G_OBJECT_TYPE_NAME (service));
+ }
+ if (load_service_method != NULL)
+ load_service_method (loader, service, ret_error);
+
+ if (*ret_error == NULL)
+ loader->n_loaded_services++;
+}
+
+void
+gnm_plugin_loader_unload_service (GnmPluginLoader *loader, GnmPluginService *service, ErrorInfo **ret_error)
+{
+ GnmPluginLoaderClass *gnm_plugin_loader_class;
+ void (*unload_service_method) (GnmPluginLoader *, GnmPluginService *, ErrorInfo **) = NULL;
+ ErrorInfo *error = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN_LOADER (loader));
+ g_return_if_fail (IS_GNM_PLUGIN_SERVICE (service));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ gnm_plugin_loader_class = PL_GET_CLASS (loader);
+
+ if (IS_GNM_PLUGIN_SERVICE_GENERAL (service)) {
+ unload_service_method = gnm_plugin_loader_class->unload_service_general;
+ /*} else if (IS_GNM_PLUGIN_SERVICE_FILE_OPENER (service)) {
+ unload_service_method = gnm_plugin_loader_class->unload_service_file_opener;
+ } else if (IS_GNM_PLUGIN_SERVICE_FILE_SAVER (service)) {
+ unload_service_method = gnm_plugin_loader_class->unload_service_file_saver;
+ } else if (IS_GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service)) {
+ unload_service_method = gnm_plugin_loader_class->unload_service_function_group;*/
+ } else if (IS_GNM_PLUGIN_SERVICE_PLUGIN_LOADER (service)) {
+ unload_service_method = gnm_plugin_loader_class->unload_service_plugin_loader;
+ } else if (IS_GNM_PLUGIN_SERVICE_UI (service)) {
+ unload_service_method = gnm_plugin_loader_class->unload_service_ui;
+ } else if (IS_GNM_PLUGIN_SERVICE_SIMPLE (service)) {
+ unload_service_method = NULL;
+ } else
+ *ret_error = error_info_new_printf (_("Service '%s' not supported by loader."),
+ G_OBJECT_TYPE_NAME (service));
+
+ if (unload_service_method != NULL)
+ unload_service_method (loader, service, &error);
+ if (error == NULL) {
+ g_return_if_fail (loader->n_loaded_services > 0);
+ loader->n_loaded_services--;
+ if (loader->n_loaded_services == 0) {
+ gnm_plugin_loader_unload_base (loader, &error);
+ error_info_free (error);
+ }
+ } else {
+ *ret_error = error;
+ }
+}
+
+gboolean
+gnm_plugin_loader_is_base_loaded (GnmPluginLoader *loader)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN_LOADER (loader), FALSE);
+
+ return loader->is_base_loaded;
+}
--- /dev/null
+++ lib/goffice/split/datetime.h
@@ -0,0 +1,98 @@
+#ifndef GNUMERIC_DATETIME_H
+#define GNUMERIC_DATETIME_H
+
+#include "gnumeric.h"
+#include "numbers.h"
+#include <time.h>
+
+struct _GnmDateConventions {
+ gboolean use_1904; /* Use MacOffice 1904 based date convention,
+ * Rather than the Win32 style 1900 */
+};
+
+/*
+ * Naming conventions:
+ *
+ * "g": a GDate *.
+ * "timet": Unix' time_t.
+ * "serial": Excel serial day number.
+ * "serial_raw": serial plus time as fractional day.
+ */
+
+/* Week numbering methods */
+/* 1: Week starts on Sunday. Days before first Sunday are in week 0. */
+/* 2: Week starts on Monday. Days before first Monday are in week 0. */
+/* 150: ISO 8601 week number. */
+#define WEEKNUM_METHOD_SUNDAY 1
+#define WEEKNUM_METHOD_MONDAY 2
+#define WEEKNUM_METHOD_ISO 150
+
+/* These do not round and produces fractional values, i.e., includes time. */
+gnm_float datetime_value_to_serial_raw (GnmValue const *v, GnmDateConventions const *conv);
+gnm_float datetime_timet_to_serial_raw (time_t t, GnmDateConventions const *conv);
+
+/* These are date-only, no time. */
+int datetime_value_to_serial (GnmValue const *v, GnmDateConventions const *conv);
+int datetime_timet_to_serial (time_t t, GnmDateConventions const *conv);
+gboolean datetime_value_to_g (GDate *res, GnmValue const *v, GnmDateConventions const *conv);
+int datetime_g_to_serial (GDate const *date, GnmDateConventions const *conv);
+void datetime_serial_to_g (GDate *res, int serial, GnmDateConventions const *conv);
+time_t datetime_serial_to_timet (int serial, GnmDateConventions const *conv);
+int datetime_serial_raw_to_serial (gnm_float raw);
+
+/* These are time-only assuming a 24h day. It probably loses completely on */
+/* days with summer time ("daylight savings") changes. */
+int datetime_value_to_seconds (GnmValue const *v);
+int datetime_timet_to_seconds (time_t t);
+int datetime_serial_raw_to_seconds (gnm_float raw);
+
+int datetime_g_days_between (GDate const *date1, GDate const *date2);
+
+/* Number of full months between date1 and date2. */
+/* largest value s.t. g_date_add_months (date1, result) <= date2 */
+/* except that if the day is decreased in g_date_add_monts, treat
+ that as > the date it is decreased to. */
+/* ( datetime_g_months_between ( March 31, April 30 ) == 0
+ even though g_date_add_months ( Mar 31, 1 ) <= Apr 30.... */
+int datetime_g_months_between (GDate const *date1, GDate const *date2);
+/* Number of full years between date1 and date2. */
+/* (g_date_add_years (date1, result) <= date2; largest such value. */
+/* treat add_years (29-feb, x) > 28-feb ) */
+int datetime_g_years_between (GDate const *date1, GDate const *date2);
+/* week number according to the given method. */
+int datetime_weeknum (GDate const *date, int method);
+
+typedef enum { /* see doc/fn-financial-basis.txt for details */
+ BASIS_MSRB_30_360 = 0,
+ BASIS_ACT_ACT = 1,
+ BASIS_ACT_360 = 2,
+ BASIS_ACT_365 = 3,
+ BASIS_30E_360 = 4,
+ BASIS_30Ep_360 = 5,
+ BASIS_MSRB_30_360_SYM = 6 /* Gnumeric extension. */
+} basis_t;
+
+gint32 days_between_basis (GDate const *from, GDate const *to, basis_t basis);
+gnm_float yearfrac (GDate const *from, GDate const *to, basis_t basis);
+int annual_year_basis (GnmValue const *value_date, basis_t basis,
+ GnmDateConventions const *date_conv);
+
+typedef struct {
+ int freq;
+ basis_t basis;
+ gboolean eom;
+ GnmDateConventions const *date_conv;
+} GnmCouponConvention;
+
+void coup_cd (GDate *res, GDate const *settle, GDate const *mat,
+ int freq, gboolean eom, gboolean next);
+gnm_float coupdays (GDate const *settle, GDate const *mat,
+ GnmCouponConvention const *conv);
+gnm_float coupdaybs (GDate const *settle, GDate const *mat,
+ GnmCouponConvention const *conv);
+gnm_float coupdaysnc (GDate const *settle, GDate const *mat,
+ GnmCouponConvention const *conv);
+
+int gnm_date_convention_base (GnmDateConventions const *conv);
+
+#endif /* GNUMERIC_DATETIME_H */
--- /dev/null
+++ lib/goffice/split/func.h
@@ -0,0 +1,251 @@
+#ifndef GNUMERIC_FUNC_H
+#define GNUMERIC_FUNC_H
+
+#include "gnumeric.h"
+#include "dependent.h"
+
+/* Setup of the symbol table */
+void functions_init (void);
+void functions_shutdown (void);
+
+/* Used to build manual */
+void function_dump_defs (char const *filename, gboolean def_or_state);
+
+/******************************************************************************/
+/* Function group support */
+
+typedef struct {
+ GnmString *internal_name, *display_name;
+ gboolean has_translation;
+ GSList *functions;
+} GnmFuncGroup;
+
+GnmFuncGroup *gnm_func_group_get_nth (gint n);
+GnmFuncGroup *gnm_func_group_fetch (char const *name);
+GnmFuncGroup *gnm_func_group_fetch_with_translation (char const *name,
+ char const *translation);
+
+/******************************************************************************/
+
+/*
+ * Function registration routines
+ *
+ * Functions come in two fashions: Those that only deal with
+ * very specific data types and a constant number of arguments,
+ * and those who don't.
+ *
+ * The former kind of functions receives a precomputed array of
+ * GnmValue pointers.
+ *
+ * The latter sort of functions receives the plain ExprNodes and
+ * it is up to that routine to do the value computations and range
+ * processing.
+ */
+
+/**
+ * Argument tokens passed in 'args'
+ *
+ * With intersection and iteration support
+ * f : float (no errors, string conversion attempted)
+ * b : boolean (identical to f, Do we need this ?)
+ * s : string (no errors)
+ * S : 'scalar': any non-error value
+ * E : scalar including errors
+ * Without intersection or iteration support
+ * r : cell range content is _NOT_ guaranteed to have been evaluated yet
+ * A : area either range or array (as above)
+ * a : array
+ * ? : anything
+ *
+ * For optional arguments do:
+ * "ff|ss" where the strings are optional
+ **/
+
+typedef enum {
+ GNM_FUNC_TYPE_ARGS, /* Arguments get marshalled by type */
+ GNM_FUNC_TYPE_NODES, /* Takes unevaulated expers directly */
+
+ /* implementation has not been loaded yet, but we know where it is */
+ GNM_FUNC_TYPE_STUB
+} GnmFuncType;
+
+typedef enum {
+ GNM_FUNC_SIMPLE = 0,
+ GNM_FUNC_VOLATILE = 1 << 0, /* eg now(), today() */
+ GNM_FUNC_RETURNS_NON_SCALAR = 1 << 1, /* eg transpose(), mmult() */
+
+ /* For functions that are not exactly compatible with various import
+ * formats. We need to recalc their results to avoid changing values
+ * unexpectedly when we recalc later. This probably needs to be done
+ * on a per import format basis. It may not belong here.
+ */
+ GNM_FUNC_RECALC_ONLOAD = 1 << 2,
+
+ /* an unknown function that will hopefully be defined later */
+ GNM_FUNC_IS_PLACEHOLDER = 1 << 3,
+ GNM_FUNC_FREE_NAME = 1 << 4,
+ GNM_FUNC_IS_WORKBOOK_LOCAL = 1 << 5,
+
+ GNM_FUNC_AUTO_UNKNOWN = 0 << 8,
+ GNM_FUNC_AUTO_MONETARY = 1 << 8, /* Like PV */
+ GNM_FUNC_AUTO_DATE = 2 << 8, /* Like DATE */
+ GNM_FUNC_AUTO_TIME = 3 << 8, /* Like TIME */
+ GNM_FUNC_AUTO_PERCENT = 4 << 8, /* Like IRR */
+ GNM_FUNC_AUTO_FIRST = 5 << 8, /* Like SUM */
+ GNM_FUNC_AUTO_SECOND = 6 << 8, /* Like IF */
+ GNM_FUNC_AUTO_UNITLESS = 7 << 8, /* Like COUNT */
+ GNM_FUNC_AUTO_MASK = 7 << 8 /* The bits we use for AUTO. */
+} GnmFuncFlags;
+
+/* I do not like this it is going to be different for different apps
+ * probably want to split it into bit file with our notion of its state, and 2
+ * bits of state per import format.
+ */
+typedef enum {
+ GNM_FUNC_IMPL_STATUS_EXISTS = 0,
+ GNM_FUNC_IMPL_STATUS_UNIMPLEMENTED,
+ GNM_FUNC_IMPL_STATUS_SUBSET,
+ GNM_FUNC_IMPL_STATUS_COMPLETE,
+ GNM_FUNC_IMPL_STATUS_SUPERSET,
+ GNM_FUNC_IMPL_STATUS_SUBSET_WITH_EXTENSIONS,
+ GNM_FUNC_IMPL_STATUS_UNDER_DEVELOPMENT,
+ GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC
+} GnmFuncImplStatus;
+
+typedef enum {
+ GNM_FUNC_TEST_STATUS_UNKNOWN = 0,
+ GNM_FUNC_TEST_STATUS_NO_TESTSUITE,
+ GNM_FUNC_TEST_STATUS_BASIC,
+ GNM_FUNC_TEST_STATUS_EXHAUSTIVE,
+ GNM_FUNC_TEST_STATUS_UNDER_DEVELOPMENT
+} GnmFuncTestStatus;
+typedef struct _GnmFuncDescriptor GnmFuncDescriptor;
+
+typedef GnmValue *(*GnmFuncArgs) (FunctionEvalInfo *ei, GnmValue **args);
+typedef GnmValue *(*GnmFuncNodes) (FunctionEvalInfo *ei, GnmExprList *l);
+typedef DependentFlags (*GnmFuncLink) (FunctionEvalInfo *ei);
+typedef void (*GnmFuncUnlink) (FunctionEvalInfo *ei);
+
+typedef void (*GnmFuncRefNotify) (GnmFunc *f, int refcount);
+typedef gboolean (*GnmFuncLoadDesc) (GnmFunc const *f, GnmFuncDescriptor *fd);
+
+struct _GnmFuncDescriptor {
+ char const *name;
+ char const *arg_spec;
+ char const *arg_names;
+ char const **help; /* this is easier for compilers */
+ GnmFuncArgs fn_args;
+ GnmFuncNodes fn_nodes;
+ GnmFuncLink linker;
+ GnmFuncUnlink unlinker;
+ GnmFuncRefNotify ref_notify;
+ GnmFuncFlags flags;
+ GnmFuncImplStatus impl_status;
+ GnmFuncTestStatus test_status;
+};
+
+struct _GnmFunc {
+ char const *name;
+ char const *arg_names;
+ char const *help;
+ GnmFuncType fn_type;
+ union {
+ GnmFuncNodes nodes;
+ struct {
+ char const *arg_spec;
+ GnmFuncArgs func;
+ int min_args, max_args;
+ char *arg_types;
+ } args;
+ GnmFuncLoadDesc load_desc;
+ } fn;
+ GnmFuncGroup *fn_group; /* most recent it was assigned to */
+ GnmFuncLink linker;
+ GnmFuncUnlink unlinker;
+ GnmFuncRefNotify ref_notify;
+ GnmFuncImplStatus impl_status;
+ GnmFuncTestStatus test_status;
+ GnmFuncFlags flags;
+
+ gint ref_count;
+ gpointer user_data;
+};
+
+struct _FunctionEvalInfo {
+ GnmEvalPos const *pos;
+ GnmExprFunction const *func_call;
+};
+
+void gnm_func_free (GnmFunc *func);
+void gnm_func_ref (GnmFunc *func);
+void gnm_func_unref (GnmFunc *func);
+void gnm_func_load_stub (GnmFunc *fn_def);
+char const *gnm_func_get_name (GnmFunc const *fn_def);
+gpointer gnm_func_get_user_data (GnmFunc const *func);
+void gnm_func_set_user_data (GnmFunc *func, gpointer user_data);
+GnmFunc *gnm_func_lookup (char const *name, Workbook const *scope);
+GnmFunc *gnm_func_add (GnmFuncGroup *group,
+ GnmFuncDescriptor const *descriptor);
+GnmFunc *gnm_func_add_stub (GnmFuncGroup *group,
+ char const *name,
+ GnmFuncLoadDesc load_desc,
+ GnmFuncRefNotify opt_ref_notify);
+GnmFunc *gnm_func_add_placeholder (Workbook *optional_context,
+ char const *name,
+ char const *type,
+ gboolean copy_name);
+GnmExpr const *gnm_func_placeholder_factory (const char *name,
+ GnmExprList *args,
+ GnmExprConventions *convs);
+
+
+/* TODO */
+void function_def_count_args (GnmFunc const *fn_def,
+ gint *min, int *max);
+char function_def_get_arg_type (GnmFunc const *fn_def,
+ gint arg_idx);
+char const *function_def_get_arg_type_string (GnmFunc const *fn_def,
+ gint arg_idx);
+char *function_def_get_arg_name (GnmFunc const *fn_def,
+ gint arg_idx);
+
+/*************************************************************************/
+
+GnmValue *function_call_with_list (FunctionEvalInfo *ei, GnmExprList *args,
+ GnmExprEvalFlags flags);
+GnmValue *function_call_with_values (GnmEvalPos const *ep, char const *name,
+ gint argc, GnmValue *values []);
+GnmValue *function_def_call_with_values (GnmEvalPos const *ep, GnmFunc const *fn,
+ gint argc, GnmValue *values []);
+
+/* Utilies to interate through ranges and argument lists */
+typedef GnmValue * (*FunctionIterateCB) (GnmEvalPos const *ep,
+ GnmValue *value, gpointer user_data);
+GnmValue *function_iterate_argument_values (GnmEvalPos const *ep,
+ FunctionIterateCB cb,
+ gpointer user_data,
+ GnmExprList *expr_node_list,
+ gboolean strict,
+ CellIterFlags iter_flags);
+GnmValue *function_iterate_do_value (GnmEvalPos const *ep,
+ FunctionIterateCB cb,
+ gpointer user_data,
+ GnmValue *value,
+ gboolean strict,
+ CellIterFlags iter_flags);
+
+/******************************************************************************/
+
+/* Detailed function help */
+typedef struct {
+ GPtrArray *sections;
+ gboolean help_is_localized;
+ char *help_copy;
+ GnmFunc const *fndef;
+} TokenizedHelp;
+
+TokenizedHelp *tokenized_help_new (GnmFunc const *fn_def);
+char const *tokenized_help_find (TokenizedHelp *tok, char const *token);
+void tokenized_help_destroy (TokenizedHelp *tok);
+
+#endif /* GNUMERIC_FUNC_H */
--- /dev/null
+++ lib/goffice/split/error-info.c
@@ -0,0 +1,203 @@
+/*
+ * error-info.c: ErrorInfo structure.
+ *
+ * Author:
+ * Zbigniew Chyla (cyba at gnome.pl)
+ */
+
+#include <config.h>
+#include "gnumeric.h"
+#include "error-info.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+struct _ErrorInfo {
+ gchar *msg;
+ GnmSeverity severity;
+ GSList *details; /* list of ErrorInfo */
+};
+
+ErrorInfo *
+error_info_new_str (char const *msg)
+{
+ ErrorInfo *error = g_new (ErrorInfo, 1);
+ error->msg = g_strdup (msg);
+ error->severity = GNM_ERROR;
+ error->details = NULL;
+ return error;
+}
+
+ErrorInfo *
+error_info_new_vprintf (GnmSeverity severity, char const *msg_format,
+ va_list args)
+{
+ ErrorInfo *error;
+
+ g_return_val_if_fail (severity >= GNM_WARNING, NULL);
+ g_return_val_if_fail (severity <= GNM_ERROR, NULL);
+
+ error = g_new (ErrorInfo, 1);
+ error->msg = g_strdup_vprintf (msg_format, args);
+ error->severity = severity;
+ error->details = NULL;
+ return error;
+}
+
+
+ErrorInfo *
+error_info_new_printf (char const *msg_format, ...)
+{
+ ErrorInfo *error;
+ va_list args;
+
+ va_start (args, msg_format);
+ error = error_info_new_vprintf (GNM_ERROR, msg_format, args);
+ va_end (args);
+
+ return error;
+}
+
+ErrorInfo *
+error_info_new_str_with_details (char const *msg, ErrorInfo *details)
+{
+ ErrorInfo *error = error_info_new_str (msg);
+ error_info_add_details (error, details);
+ return error;
+}
+
+ErrorInfo *
+error_info_new_str_with_details_list (char const *msg, GSList *details)
+{
+ ErrorInfo *error = error_info_new_str (msg);
+ error_info_add_details_list (error, details);
+ return error;
+}
+
+ErrorInfo *
+error_info_new_from_error_list (GSList *errors)
+{
+ ErrorInfo *error;
+
+ switch (g_slist_length (errors)) {
+ case 0:
+ error = NULL;
+ break;
+ case 1:
+ error = (ErrorInfo *) errors->data;
+ g_slist_free (errors);
+ break;
+ default:
+ error = error_info_new_str_with_details_list (NULL, errors);
+ break;
+ }
+
+ return error;
+}
+
+ErrorInfo *
+error_info_new_from_errno (void)
+{
+ return error_info_new_str (g_strerror (errno));
+}
+
+void
+error_info_add_details (ErrorInfo *error, ErrorInfo *details)
+{
+ g_return_if_fail (error != NULL);
+
+ if (details == NULL)
+ ;
+ else if (details->msg == NULL) {
+ error->details = g_slist_concat (error->details, details->details);
+ g_free (details);
+ } else
+ error->details = g_slist_append (error->details, details);
+}
+
+void
+error_info_add_details_list (ErrorInfo *error, GSList *details)
+{
+ GSList *new_details_list, *l, *ll;
+
+ g_return_if_fail (error != NULL);
+
+ new_details_list = NULL;
+ for (l = details; l != NULL; l = l->next) {
+ ErrorInfo *details_error = l->data;
+ if (details_error->msg == NULL) {
+ for (ll = details_error->details; ll != NULL; ll = ll->next)
+ new_details_list = g_slist_prepend (new_details_list, l->data);
+ g_free (details_error);
+ } else
+ new_details_list = g_slist_prepend (new_details_list, details_error);
+ }
+ g_slist_free (details);
+ new_details_list = g_slist_reverse (new_details_list);
+ error->details = g_slist_concat (error->details, new_details_list);
+}
+
+void
+error_info_free (ErrorInfo *error)
+{
+ GSList *l;
+
+ if (error == NULL)
+ return;
+
+ g_free (error->msg);
+ for (l = error->details; l != NULL; l = l->next)
+ error_info_free ((ErrorInfo *) l->data);
+
+ g_slist_free (error->details);
+ g_free(error);
+}
+
+static void
+error_info_print_with_offset (ErrorInfo *error, gint offset)
+{
+ GSList *l;
+
+ if (error->msg != NULL) {
+ char c = 'E';
+
+ if (error->severity == GNM_WARNING)
+ c = 'W';
+ fprintf (stderr, "%*s%c %s\n", offset, "", c, error->msg);
+ offset += 2;
+ }
+ for (l = error->details; l != NULL; l = l->next)
+ error_info_print_with_offset ((ErrorInfo *) l->data, offset);
+}
+
+void
+error_info_print (ErrorInfo *error)
+{
+ g_return_if_fail (error != NULL);
+
+ error_info_print_with_offset (error, 0);
+}
+
+char const *
+error_info_peek_message (ErrorInfo *error)
+{
+ g_return_val_if_fail (error != NULL, NULL);
+
+ return error->msg;
+}
+
+GSList *
+error_info_peek_details (ErrorInfo *error)
+{
+ g_return_val_if_fail (error != NULL, NULL);
+
+ return error->details;
+}
+
+GnmSeverity
+error_info_peek_severity (ErrorInfo *error)
+{
+ g_return_val_if_fail (error != NULL, GNM_ERROR);
+
+ return error->severity;
+}
--- /dev/null
+++ lib/goffice/split/gui-file.h
@@ -0,0 +1,25 @@
+#ifndef GNUMERIC_GUI_FILE_H
+#define GNUMERIC_GUI_FILE_H
+
+#include "gui-gnumeric.h"
+
+typedef struct {
+ char *name;
+ char *desc;
+ char *ext;
+ gboolean has_pixbuf_saver;
+} GnmImageFormat;
+
+gboolean gui_file_save_as (WorkbookControlGUI *wbcg, WorkbookView *);
+gboolean gui_file_save (WorkbookControlGUI *wbcg, WorkbookView *);
+void gui_file_open (WorkbookControlGUI *wbcg,
+ char const *default_format);
+void gui_wb_view_show (WorkbookControlGUI *wbcg, WorkbookView *wbv);
+gboolean gui_file_read (WorkbookControlGUI *wbcg, char const *file_name,
+ GnmFileOpener const *optional_format,
+ gchar const *optional_encoding);
+char * gui_image_file_select (WorkbookControlGUI *wbcg, const char *initial);
+char * gui_get_image_save_info (WorkbookControlGUI *wbcg, GSList *formats,
+ GnmImageFormat **ret_format);
+
+#endif /* GNUMERIC_GUI_FILE_H */
--- /dev/null
+++ lib/goffice/split/gutils.c
@@ -0,0 +1,814 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * utils.c: Various utility routines that do not depend on the GUI of Gnumeric
+ *
+ * Authors:
+ * Miguel de Icaza (miguel at gnu.org)
+ * Jukka-Pekka Iivonen (iivonen at iki.fi)
+ * Zbigniew Chyla (cyba at gnome.pl)
+ */
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "gutils.h"
+
+#include "paths.h"
+
+//#include "sheet.h"
+#include "ranges.h"
+#include "mathfunc.h"
+//#include "libgnumeric.h"
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <gsf/gsf-impl-utils.h>
+#ifdef HAVE_FLOATINGPOINT_H
+#include <floatingpoint.h>
+#endif
+
+static void
+cb_hash_collect_keys (gpointer key, gpointer value, GSList **accum)
+{
+ *accum = g_slist_prepend (*accum, key);
+}
+
+/**
+ * gnm_hash_keys :
+ * @hash : #GHashTable
+ *
+ * Collects an unordered list of the keys in @hash.
+ *
+ * Returns a list which the caller needs to free.
+ * The content has not additional references added
+ **/
+GSList *
+gnm_hash_keys (GHashTable *hash)
+{
+ GSList *accum = NULL;
+ g_hash_table_foreach (hash,
+ (GHFunc )cb_hash_collect_keys, &accum);
+ return accum;
+}
+
+static void
+cb_hash_collect_values (gpointer key, gpointer value, GSList **accum)
+{
+ *accum = g_slist_prepend (*accum, value);
+}
+
+/**
+ * gnm_hash_values :
+ * @hash : #GHashTable
+ *
+ * Collects an unordered list of the values in @hash.
+ *
+ * Returns a list which the caller needs to free.
+ * The content has not additional references added
+ **/
+GSList *
+gnm_hash_values (GHashTable *hash)
+{
+ GSList *accum = NULL;
+ g_hash_table_foreach (hash,
+ (GHFunc )cb_hash_collect_values, &accum);
+ return accum;
+}
+
+/***************************************************************************/
+void
+gnm_ptr_array_insert (GPtrArray *array, gpointer value, int index)
+{
+ if (index < (int)array->len) {
+ int i = array->len - 1;
+ gpointer last = g_ptr_array_index (array, i);
+ g_ptr_array_add (array, last);
+
+ while (i-- > index) {
+ gpointer tmp = g_ptr_array_index (array, i);
+ g_ptr_array_index (array, i + 1) = tmp;
+ }
+ g_ptr_array_index (array, index) = value;
+ } else
+ g_ptr_array_add (array, value);
+}
+
+/**
+ * gnm_slist_create:
+ * @item1: First item.
+ *
+ * Creates a GList from NULL-terminated list of arguments.
+ *
+ * Return value: created list.
+ **/
+GSList *
+gnm_slist_create (gpointer item1, ...)
+{
+ va_list args;
+ GSList *list = NULL;
+ gpointer item;
+
+ va_start (args, item1);
+ for (item = item1; item != NULL; item = va_arg (args, gpointer)) {
+ list = g_slist_prepend (list, item);
+ }
+ va_end (args);
+
+ return g_slist_reverse (list);
+}
+
+/**
+ * gnm_slist_map:
+ * @list : list of some items
+ * @map_func : mapping function
+ *
+ **/
+GSList *
+gnm_slist_map (GSList const *list, GnmMapFunc map_func)
+{
+ GSList *list_copy = NULL;
+
+ GNM_SLIST_FOREACH (list, void, value,
+ GNM_SLIST_PREPEND (list_copy, map_func (value))
+ );
+
+ return g_slist_reverse (list_copy);
+}
+
+/**
+ * gnm_slist_free_custom:
+ * @list: list of some items
+ * @free_func: function freeing list item
+ *
+ * Clears a list, calling @free_func for each list item.
+ **/
+void
+gnm_slist_free_custom (GSList *list, GFreeFunc free_func)
+{
+ GSList *l;
+
+ for (l = list; l != NULL; l = l->next) {
+ free_func (l->data);
+ }
+ g_slist_free (list);
+}
+
+gint
+gnm_list_index_custom (GList *list, gpointer data, GCompareFunc cmp_func)
+{
+ GList *l;
+ gint i;
+
+ for (l = list, i = 0; l != NULL; l = l->next, i++) {
+ if (cmp_func (l->data, data) == 0) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * gnm_list_free_custom:
+ * @list: list of some items
+ * @free_func: function freeing list item
+ *
+ * Clears a list, calling @free_func for each list item.
+ *
+ */
+void
+gnm_list_free_custom (GList *list, GFreeFunc free_func)
+{
+ GList *l;
+
+ for (l = list; l != NULL; l = l->next) {
+ free_func (l->data);
+ }
+ g_list_free (list);
+}
+
+/**
+ * gnm_strsplit_to_slist:
+ * @string: String to split
+ * @delimiter: Token delimiter
+ *
+ * Splits up string into tokens at delim and returns a string list.
+ *
+ * Returns: string list which you should free after use using function
+ * e_free_string_list().
+ **/
+GSList *
+gnm_strsplit_to_slist (gchar const *string, gchar const *delimiter)
+{
+ gchar **token_v;
+ GSList *string_list = NULL;
+ gint i;
+
+ token_v = g_strsplit (string, delimiter, 0);
+ if (token_v != NULL) {
+ for (i = 0; token_v[i] != NULL; i++) {
+ string_list = g_slist_prepend (string_list, token_v[i]);
+ }
+ string_list = g_slist_reverse (string_list);
+ g_free (token_v);
+ }
+
+ return string_list;
+}
+
+gint
+gnm_utf8_collate_casefold (const char *a, const char *b)
+{
+ char *a2 = g_utf8_casefold (a, -1);
+ char *b2 = g_utf8_casefold (b, -1);
+ int res = g_utf8_collate (a2, b2);
+ g_free (a2);
+ g_free (b2);
+ return res;
+}
+
+gint
+gnm_ascii_strcase_equal (gconstpointer v1, gconstpointer v2)
+{
+ return g_ascii_strcasecmp ((char const *) v1, (char const *)v2) == 0;
+}
+
+/* a char* hash function from ASU */
+guint
+gnm_ascii_strcase_hash (gconstpointer v)
+{
+ unsigned const char *s = (unsigned const char *)v;
+ unsigned const char *p;
+ guint h = 0, g;
+
+ for(p = s; *p != '\0'; p += 1) {
+ h = ( h << 4 ) + g_ascii_tolower (*p);
+ if ( ( g = h & 0xf0000000 ) ) {
+ h = h ^ (g >> 24);
+ h = h ^ g;
+ }
+ }
+
+ return h /* % M */;
+}
+
+#define GNUMERIC_VERSION "FIXME"
+// FIXME -- why doesn't GNC_LIBDIR work?
+static const char * gnumeric_data_dir = DATA_DIR;
+static const char * gnumeric_lib_dir = DATA_DIR;
+
+char *
+gnm_sys_data_dir (char const *subdir)
+{
+ if (subdir == NULL)
+ return (char *)gnumeric_data_dir;
+ return g_build_filename (gnumeric_data_dir, subdir, NULL);
+}
+
+char *
+gnm_sys_lib_dir (char const *subdir)
+{
+ return g_build_filename (gnumeric_lib_dir, subdir, NULL);
+}
+
+#define GLADE_SUFFIX "glade"
+#define PLUGIN_SUFFIX "plugins"
+
+char *
+gnm_sys_glade_dir (void)
+{
+ return gnm_sys_data_dir (GLADE_SUFFIX);
+}
+
+char *
+gnm_sys_plugin_dir (void)
+{
+ return gnm_sys_lib_dir (PLUGIN_SUFFIX);
+}
+
+char *
+gnm_usr_dir (char const *subdir)
+{
+ char const *home_dir = g_get_home_dir ();
+
+ if (!home_dir)
+ return NULL;
+
+ return g_build_filename (home_dir, ".gnumeric",
+ GNUMERIC_VERSION, subdir,
+ NULL);
+}
+
+char *
+gnm_usr_plugin_dir (void)
+{
+ return gnm_usr_dir (PLUGIN_SUFFIX);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Escapes all backslashes and quotes in a string. It is based on glib's
+ * g_strescape.
+ *
+ * Also adds quotes around the result.
+ */
+void
+gnm_strescape (GString *target, char const *string)
+{
+ g_string_append_c (target, '"');
+ /* This loop should be UTF-8 safe. */
+ for (; *string; string++) {
+ switch (*string) {
+ case '"':
+ case '\\':
+ g_string_append_c (target, '\\');
+ default:
+ g_string_append_c (target, *string);
+ }
+ }
+ g_string_append_c (target, '"');
+}
+
+/*
+ * The reverse operation of gnm_strescape. Returns a pointer to the
+ * first char after the string on success or NULL on failure.
+ *
+ * First character of the string should be an ASCII character used
+ * for quoting.
+ */
+const char *
+gnm_strunescape (GString *target, const char *string)
+{
+ char quote = *string++;
+ size_t oldlen = target->len;
+
+ /* This should be UTF-8 safe as long as quote is ASCII. */
+ while (*string != quote) {
+ if (*string == 0)
+ goto error;
+ else if (*string == '\\') {
+ string++;
+ if (*string == 0)
+ goto error;
+ }
+
+ g_string_append_c (target, *string);
+ string++;
+ }
+
+ return ++string;
+
+ error:
+ g_string_truncate (target, oldlen);
+ return NULL;
+}
+
+void
+gnm_string_append_gstring (GString *target, const GString *source)
+{
+ g_string_append_len (target, source->str, source->len);
+}
+
+/**
+ * gnm_utf8_strcapital:
+ * @p: pointer to UTF-8 string
+ * @len: length in bytes, or -1.
+ *
+ * Similar to g_utf8_strup and g_utf8_strup, except that this function
+ * creates a string "Very Much Like: This, One".
+ *
+ * Return value: newly allocated string.
+ **/
+char *
+gnm_utf8_strcapital (const char *p, ssize_t len)
+{
+ const char *pend = (len < 0 ? NULL : p + len);
+ GString *res = g_string_sized_new (len < 0 ? 1 : len + 1);
+ gboolean up = TRUE;
+
+ /*
+ * This does a simple character-by-character mapping and probably
+ * is not linguistically correct.
+ */
+
+ for (; (len < 0 || p < pend) && *p; p = g_utf8_next_char (p)) {
+ gunichar c = g_utf8_get_char (p);
+
+ if (g_unichar_isalpha (c)) {
+ if (up ? g_unichar_isupper (c) : g_unichar_islower (c))
+ /* Correct case -- keep the char. */
+ g_string_append_unichar (res, c);
+ else {
+ char *tmp = up
+ ? g_utf8_strup (p, 1)
+ : g_utf8_strdown (p, 1);
+ g_string_append (res, tmp);
+ g_free (tmp);
+ }
+ up = FALSE;
+ } else {
+ g_string_append_unichar (res, c);
+ up = TRUE;
+ }
+ }
+
+ return g_string_free (res, FALSE);
+}
+
+/* ------------------------------------------------------------------------- */
+
+#undef DEBUG_CHUNK_ALLOCATOR
+
+typedef struct _gnm_mem_chunk_freeblock gnm_mem_chunk_freeblock;
+typedef struct _gnm_mem_chunk_block gnm_mem_chunk_block;
+
+struct _gnm_mem_chunk_freeblock {
+ gnm_mem_chunk_freeblock *next;
+};
+
+struct _gnm_mem_chunk_block {
+ gpointer data;
+ int freecount, nonalloccount;
+ gnm_mem_chunk_freeblock *freelist;
+#ifdef DEBUG_CHUNK_ALLOCATOR
+ int id;
+#endif
+};
+
+struct _GnmMemChunk {
+ char *name;
+ size_t atom_size, user_atom_size, chunk_size, alignment;
+ int atoms_per_block;
+
+ /* List of all blocks. */
+ GSList *blocklist;
+
+ /* List of blocks that are not full. */
+ GList *freeblocks;
+
+#ifdef DEBUG_CHUNK_ALLOCATOR
+ int blockid;
+#endif
+};
+
+
+GnmMemChunk *
+gnm_mem_chunk_new (char const *name, size_t user_atom_size, size_t chunk_size)
+{
+ int atoms_per_block;
+ GnmMemChunk *res;
+ size_t user_alignment, alignment, atom_size;
+ size_t maxalign = 1 + ((sizeof (void *) - 1) |
+ (sizeof (long) - 1) |
+ (sizeof (double) - 1) |
+ (sizeof (gnm_float) - 1));
+
+ /*
+ * The alignment that the caller can expect is 2^(lowest_bit_in_size).
+ * The fancy bit math computes this. Think it over.
+ *
+ * We don't go lower than pointer-size, so this always comes out as
+ * 4 or 8. (Or 16, if gnm_float is long double.) Sometimes, when
+ * user_atom_size is a multiple of 8, this alignment is bigger than
+ * really needed, but we don't know if the structure has elements
+ * with 8-byte alignment. In those cases we waste memory.
+ */
+ user_alignment = ((user_atom_size ^ (user_atom_size - 1)) + 1) / 2;
+ alignment = MIN (MAX (user_alignment, sizeof (gnm_mem_chunk_block *)), maxalign);
+ atom_size = alignment + MAX (user_atom_size, sizeof (gnm_mem_chunk_freeblock));
+ atoms_per_block = MAX (1, chunk_size / atom_size);
+ chunk_size = atoms_per_block * atom_size;
+
+#ifdef DEBUG_CHUNK_ALLOCATOR
+ g_print ("Created %s with alignment=%d, atom_size=%d (%d), chunk_size=%d.\n",
+ name, alignment, atom_size, user_atom_size,
+ chunk_size);
+#endif
+
+ res = g_new (GnmMemChunk, 1);
+ res->alignment = alignment;
+ res->name = g_strdup (name);
+ res->user_atom_size = user_atom_size;
+ res->atom_size = atom_size;
+ res->chunk_size = chunk_size;
+ res->atoms_per_block = atoms_per_block;
+ res->blocklist = NULL;
+ res->freeblocks = NULL;
+#ifdef DEBUG_CHUNK_ALLOCATOR
+ res->blockid = 0;
+#endif
+
+ return res;
+}
+
+void
+gnm_mem_chunk_destroy (GnmMemChunk *chunk, gboolean expect_leaks)
+{
+ GSList *l;
+
+ g_return_if_fail (chunk != NULL);
+
+#ifdef DEBUG_CHUNK_ALLOCATOR
+ g_print ("Destroying %s.\n", chunk->name);
+#endif
+ /*
+ * Since this routine frees all memory allocated for the pool,
+ * it is sometimes convenient not to free at all. For such
+ * cases, don't report leaks.
+ */
+ if (!expect_leaks) {
+ GSList *l;
+ int leaked = 0;
+
+ for (l = chunk->blocklist; l; l = l->next) {
+ gnm_mem_chunk_block *block = l->data;
+ leaked += chunk->atoms_per_block - (block->freecount + block->nonalloccount);
+ }
+ if (leaked) {
+ g_warning ("Leaked %d nodes from %s.",
+ leaked, chunk->name);
+ }
+ }
+
+ for (l = chunk->blocklist; l; l = l->next) {
+ gnm_mem_chunk_block *block = l->data;
+ g_free (block->data);
+ g_free (block);
+ }
+ g_slist_free (chunk->blocklist);
+ g_list_free (chunk->freeblocks);
+ g_free (chunk->name);
+ g_free (chunk);
+}
+
+gpointer
+gnm_mem_chunk_alloc (GnmMemChunk *chunk)
+{
+ gnm_mem_chunk_block *block;
+ char *res;
+
+ /* First try the freelist. */
+ if (chunk->freeblocks) {
+ gnm_mem_chunk_freeblock *res;
+
+ block = chunk->freeblocks->data;
+ res = block->freelist;
+ if (res) {
+ block->freelist = res->next;
+
+ block->freecount--;
+ if (block->freecount == 0 && block->nonalloccount == 0) {
+ /* Block turned full -- remove it from freeblocks. */
+ chunk->freeblocks = g_list_delete_link (chunk->freeblocks,
+ chunk->freeblocks);
+ }
+ return res;
+ }
+ /*
+ * If we get here, the block has free space that was never
+ * allocated.
+ */
+ } else {
+ block = g_new (gnm_mem_chunk_block, 1);
+#ifdef DEBUG_CHUNK_ALLOCATOR
+ block->id = chunk->blockid++;
+ g_print ("Allocating new chunk %d for %s.\n", block->id, chunk->name);
+#endif
+ block->nonalloccount = chunk->atoms_per_block;
+ block->freecount = 0;
+ block->data = g_malloc (chunk->chunk_size);
+ block->freelist = NULL;
+
+ chunk->blocklist = g_slist_prepend (chunk->blocklist, block);
+ chunk->freeblocks = g_list_prepend (chunk->freeblocks, block);
+ }
+
+ res = (char *)block->data +
+ (chunk->atoms_per_block - block->nonalloccount--) * chunk->atom_size;
+ *((gnm_mem_chunk_block **)res) = block;
+
+ if (block->nonalloccount == 0 && block->freecount == 0) {
+ /* Block turned full -- remove it from freeblocks. */
+ chunk->freeblocks = g_list_delete_link (chunk->freeblocks, chunk->freeblocks);
+ }
+
+ return res + chunk->alignment;
+}
+
+gpointer
+gnm_mem_chunk_alloc0 (GnmMemChunk *chunk)
+{
+ gpointer res = gnm_mem_chunk_alloc (chunk);
+ memset (res, 0, chunk->user_atom_size);
+ return res;
+}
+
+void
+gnm_mem_chunk_free (GnmMemChunk *chunk, gpointer mem)
+{
+ gnm_mem_chunk_freeblock *fb = mem;
+ gnm_mem_chunk_block *block =
+ *((gnm_mem_chunk_block **)((char *)mem - chunk->alignment));
+
+#if 0
+ /*
+ * This is useful when the exact location of a leak needs to be
+ * pin-pointed.
+ */
+ memset (mem, 0, chunk->user_atom_size);
+#endif
+
+ fb->next = block->freelist;
+ block->freelist = fb;
+ block->freecount++;
+
+ if (block->freecount == 1 && block->nonalloccount == 0) {
+ /* Block turned non-full. */
+ chunk->freeblocks = g_list_prepend (chunk->freeblocks, block);
+ } else if (block->freecount == chunk->atoms_per_block) {
+ /* Block turned all-free. */
+
+#ifdef DEBUG_CHUNK_ALLOCATOR
+ g_print ("Releasing chunk %d for %s.\n", block->id, chunk->name);
+#endif
+ /*
+ * FIXME -- this could be faster if we rolled our own lists.
+ * Hopefully, however, (a) the number of blocks is small,
+ * and (b) the freed block might be near the beginning ("top")
+ * of the stacks.
+ */
+ chunk->blocklist = g_slist_remove (chunk->blocklist, block);
+ chunk->freeblocks = g_list_remove (chunk->freeblocks, block);
+
+ g_free (block->data);
+ g_free (block);
+ }
+}
+
+/*
+ * Loop over all non-freed memory in the chunk. It's safe to allocate or free
+ * from the chunk in the callback.
+ */
+void
+gnm_mem_chunk_foreach_leak (GnmMemChunk *chunk, GFunc cb, gpointer user)
+{
+ GSList *l, *leaks = NULL;
+
+ for (l = chunk->blocklist; l; l = l->next) {
+ gnm_mem_chunk_block *block = l->data;
+ if (chunk->atoms_per_block - (block->freecount + block->nonalloccount) > 0) {
+ char *freed = g_new0 (char, chunk->atoms_per_block);
+ gnm_mem_chunk_freeblock *fb = block->freelist;
+ int i;
+
+ while (fb) {
+ char *atom = (char *)fb - chunk->alignment;
+ int no = (atom - (char *)block->data) / chunk->atom_size;
+ freed[no] = 1;
+ fb = fb->next;
+ }
+
+ for (i = chunk->atoms_per_block - block->nonalloccount - 1; i >= 0; i--) {
+ if (!freed[i]) {
+ char *atom = (char *)block->data + i * chunk->atom_size;
+ leaks = g_slist_prepend (leaks, atom + chunk->alignment);
+ }
+ }
+ g_free (freed);
+ }
+ }
+
+ g_slist_foreach (leaks, cb, user);
+ g_slist_free (leaks);
+}
+
+int
+gnm_str_compare (void const *x, void const *y)
+{
+ if (x == NULL || y == NULL) {
+ if (x == y)
+ return 0;
+ else
+ return x ? -1 : 1;
+ }
+
+ return strcmp (x, y);
+}
+
+
+const char *
+gnm_guess_encoding (const char *raw, size_t len, const char *user_guess,
+ char **utf8_str)
+{
+ int try;
+
+ g_return_val_if_fail (raw != NULL, NULL);
+
+ for (try = 1; 1; try++) {
+ const char *guess;
+ GError *error = NULL;
+ char *utf8_data;
+
+ switch (try) {
+ case 1: guess = user_guess; break;
+ case 2: g_get_charset (&guess); break;
+ case 3: guess = "ASCII"; break;
+ case 4: guess = "ISO-8859-1"; break;
+ case 5: guess = "UTF-8"; break;
+ default: return NULL;
+ }
+
+ if (!guess)
+ continue;
+
+ utf8_data = g_convert (raw, len, "UTF-8", guess,
+ NULL, NULL, &error);
+ if (!error) {
+ if (utf8_str)
+ *utf8_str = utf8_data;
+ else
+ g_free (utf8_data);
+ return guess;
+ }
+
+ g_error_free (error);
+ }
+}
+
+/**
+ * gnm_get_real_name :
+ *
+ * Return a utf8 encoded string with the current user name.
+ * Caller should _NOT_ free the result.
+ **/
+char const *
+gnm_get_real_name (void)
+{
+ /* We will leak this. */
+ static char *gnm_real_name = NULL;
+
+ if (gnm_real_name == NULL) {
+ char const *name = getenv ("NAME");
+ if (name == NULL)
+ name = g_get_real_name ();
+ if (name == NULL)
+ name = g_get_user_name ();
+ if (name != NULL)
+ (void) gnm_guess_encoding (name, strlen (name),
+ NULL, &gnm_real_name);
+ else
+ gnm_real_name = (char *)"unknown";
+ }
+ return gnm_real_name;
+}
+
+/**
+ * gnm_destroy_password :
+ *
+ * Overwrite a string holding a password. This is a separate routine to
+ * ensure that the compiler does not try to outsmart us.
+ *
+ * Note: this does not free the memory.
+ **/
+void
+gnm_destroy_password (char *passwd)
+{
+ memset (passwd, 0, strlen (passwd));
+}
+
+/* ------------------------------------------------------------------------- */
+
+static GList *timers_stack = NULL;
+
+void
+gnm_time_counter_push (void)
+{
+ GTimer *timer;
+
+ timer = g_timer_new ();
+ timers_stack = g_list_prepend (timers_stack, timer);
+}
+
+gdouble
+gnm_time_counter_pop (void)
+{
+ GTimer *timer;
+ gdouble ret_val;
+
+ g_assert (timers_stack != NULL);
+
+ timer = (GTimer *) timers_stack->data;
+ timers_stack = g_list_remove (timers_stack, timers_stack->data);
+ ret_val = g_timer_elapsed (timer, NULL);
+ g_timer_destroy (timer);
+
+ return ret_val;
+}
+
--- /dev/null
+++ lib/goffice/split/plugin-service.h
@@ -0,0 +1,93 @@
+#ifndef GNUMERIC_PLUGIN_SERVICE_H
+#define GNUMERIC_PLUGIN_SERVICE_H
+
+#include <glib.h>
+#include <gmodule.h>
+#include <libxml/tree.h>
+#include "gnumeric.h"
+#include "application.h"
+#include "file.h"
+#include "func.h"
+#include "error-info.h"
+#include "plugin.h"
+
+#define GNM_PLUGIN_SERVICE_TYPE (plugin_service_get_type ())
+#define GNM_PLUGIN_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_PLUGIN_SERVICE_TYPE, GnmPluginService))
+#define IS_GNM_PLUGIN_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_PLUGIN_SERVICE_TYPE))
+
+GType plugin_service_get_type (void);
+
+#define GNM_PLUGIN_SERVICE_GENERAL_TYPE (plugin_service_general_get_type ())
+#define GNM_PLUGIN_SERVICE_GENERAL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_PLUGIN_SERVICE_GENERAL_TYPE, PluginServiceGeneral))
+#define IS_GNM_PLUGIN_SERVICE_GENERAL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_PLUGIN_SERVICE_GENERAL_TYPE))
+
+GType plugin_service_general_get_type (void);
+typedef struct _PluginServiceGeneral PluginServiceGeneral;
+typedef struct {
+ void (*plugin_func_init) (GnmPluginService *service, ErrorInfo **ret_error);
+ void (*plugin_func_cleanup) (GnmPluginService *service, ErrorInfo **ret_error);
+} PluginServiceGeneralCallbacks;
+
+
+#define GNM_PLUGIN_SERVICE_PLUGIN_LOADER_TYPE (plugin_service_plugin_loader_get_type ())
+#define GNM_PLUGIN_SERVICE_PLUGIN_LOADER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_PLUGIN_SERVICE_PLUGIN_LOADER_TYPE, PluginServicePluginLoader))
+#define IS_GNM_PLUGIN_SERVICE_PLUGIN_LOADER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_PLUGIN_SERVICE_PLUGIN_LOADER_TYPE))
+
+GType plugin_service_plugin_loader_get_type (void);
+typedef struct _PluginServicePluginLoader PluginServicePluginLoader;
+typedef struct {
+ GType (*plugin_func_get_loader_type) (
+ GnmPluginService *service, ErrorInfo **ret_error);
+} PluginServicePluginLoaderCallbacks;
+
+GType plugin_service_plugin_loader_generate_type (GnmPluginService *service,
+ ErrorInfo **ret_error);
+
+#define GNM_PLUGIN_SERVICE_UI_TYPE (plugin_service_ui_get_type ())
+#define GNM_PLUGIN_SERVICE_UI(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_PLUGIN_SERVICE_UI_TYPE, PluginServiceUI))
+#define IS_GNM_PLUGIN_SERVICE_UI(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_PLUGIN_SERVICE_UI_TYPE))
+
+GType plugin_service_ui_get_type (void);
+typedef struct _PluginServiceUI PluginServiceUI;
+typedef struct {
+ void (*plugin_func_exec_action) (
+ GnmPluginService *service, GnmAction const *action,
+ WorkbookControl *wbc, ErrorInfo **ret_error);
+} PluginServiceUICallbacks;
+
+/****************************************************************************/
+
+#define GNM_PLUGIN_SERVICE_GOBJECT_LOADER_TYPE (plugin_service_gobject_loader_get_type ())
+#define GNM_PLUGIN_SERVICE_GOBJECT_LOADER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_PLUGIN_SERVICE_GOBJECT_LOADER_TYPE, PluginServiceGObjectLoader))
+#define IS_GNM_PLUGIN_SERVICE_GOBJECT_LOADER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_PLUGIN_SERVICE_GOBJECT_LOADER_TYPE))
+
+GType plugin_service_gobject_loader_get_type (void);
+typedef struct _PluginServiceGObjectLoader PluginServiceGObjectLoader;
+
+/****************************************************************************/
+#define GNM_PLUGIN_SERVICE_SIMPLE_TYPE (plugin_service_simple_get_type ())
+#define GNM_PLUGIN_SERVICE_SIMPLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_PLUGIN_SERVICE_SIMPLE_TYPE, PluginServiceSimple))
+#define IS_GNM_PLUGIN_SERVICE_SIMPLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_PLUGIN_SERVICE_SIMPLE_TYPE))
+
+GType plugin_service_simple_get_type (void);
+typedef struct _PluginServiceSimple PluginServiceSimple;
+
+/****************************************************************************/
+
+GnmPluginService *plugin_service_new (GnmPlugin *plugin, xmlNode *tree, ErrorInfo **ret_error);
+char const *plugin_service_get_id (GnmPluginService *service);
+char const *plugin_service_get_description (GnmPluginService *service);
+GnmPlugin *plugin_service_get_plugin (GnmPluginService *service);
+gpointer plugin_service_get_cbs (GnmPluginService *service);
+void plugin_service_activate (GnmPluginService *service, ErrorInfo **ret_error);
+void plugin_service_deactivate (GnmPluginService *service, ErrorInfo **ret_error);
+void plugin_service_load (GnmPluginService *service, ErrorInfo **ret_error);
+void plugin_service_unload (GnmPluginService *service, ErrorInfo **ret_error);
+
+typedef GType (*GnmPluginServiceCreate) (void);
+void plugin_services_init (void);
+void plugin_services_shutdown (void);
+void plugin_service_define (char const *type_str,
+ GnmPluginServiceCreate ctor);
+
+#endif /* GNUMERIC_PLUGIN_SERVICE_H */
--- /dev/null
+++ lib/goffice/split/dependent.h
@@ -0,0 +1,137 @@
+#ifndef GNUMERIC_EVAL_H
+#define GNUMERIC_EVAL_H
+
+#include "gnumeric.h"
+#include <stdio.h>
+
+struct _GnmDependent
+{
+ guint flags;
+ Sheet *sheet;
+ GnmExpr const *expression;
+
+ /* Double-linked list. */
+ struct _GnmDependent *next_dep, *prev_dep;
+};
+
+typedef struct {
+ void (*eval) (GnmDependent *dep);
+ void (*set_expr) (GnmDependent *dep, GnmExpr const *new_expr);
+ void (*debug_name) (GnmDependent const *dep, FILE *out);
+} DependentClass;
+
+typedef enum {
+ DEPENDENT_NO_FLAG = 0,
+
+ /* Types */
+ DEPENDENT_CELL = 0x00000001, /* builtin type */
+ DEPENDENT_DYNAMIC_DEP = 0x00000002, /* builtin type */
+ DEPENDENT_NAME = 0x00000003, /* builtin pseudo type */
+ DEPENDENT_TYPE_MASK = 0x00000fff,
+
+ /* Linked into the workbook wide expression list */
+ DEPENDENT_IS_LINKED = 0x00001000,
+ DEPENDENT_NEEDS_RECALC = 0x00002000,
+ DEPENDENT_BEING_CALCULATED = 0x00004000,
+ /* GnmDependent is in the midst of a cyclic calculation */
+ DEPENDENT_BEING_ITERATED = 0x00008000,
+
+ DEPENDENT_GOES_INTERSHEET = 0x00010000,
+ DEPENDENT_GOES_INTERBOOK = 0x00020000,
+ DEPENDENT_USES_NAME = 0x00040000,
+ DEPENDENT_HAS_3D = 0x00080000,
+ DEPENDENT_ALWAYS_UNLINK = 0x00100000,
+ DEPENDENT_HAS_DYNAMIC_DEPS = 0x00200000,
+ DEPENDENT_LINK_FLAGS = 0x003ff000,
+
+ /* An internal utility flag */
+ DEPENDENT_FLAGGED = 0x01000000,
+ DEPENDENT_CAN_RELOCATE = 0x02000000
+} DependentFlags;
+
+#define dependent_type(dep) ((dep)->flags & DEPENDENT_TYPE_MASK)
+#define dependent_is_cell(dep) (dependent_type (dep) == DEPENDENT_CELL)
+#define dependent_needs_recalc(dep) ((dep)->flags & DEPENDENT_NEEDS_RECALC)
+#define dependent_is_linked(dep) ((dep)->flags & DEPENDENT_IS_LINKED)
+
+struct _GnmDepContainer {
+ GnmDependent *head, *tail;
+
+ /* Large ranges hashed on 'range' to accelerate duplicate culling. This
+ * is tranversed by g_hash_table_foreach mostly.
+ */
+ GHashTable **range_hash;
+ GnmMemChunk *range_pool;
+
+ /* Single ranges, this maps an GnmEvalPos * to a GSList of its
+ * dependencies.
+ */
+ GHashTable *single_hash;
+ GnmMemChunk *single_pool;
+
+ /* All of the ExprNames that refer to this container */
+ GHashTable *referencing_names;
+
+ /* Dynamic Deps */
+ GHashTable *dynamic_deps;
+};
+
+typedef void (*DepFunc) (GnmDependent *dep, gpointer user);
+
+guint32 dependent_type_register (DependentClass const *klass);
+void dependent_types_init (void);
+void dependent_types_shutdown (void);
+
+void dependent_set_expr (GnmDependent *dep, GnmExpr const *new_expr);
+void dependent_set_sheet (GnmDependent *dep, Sheet *sheet);
+void dependent_link (GnmDependent *dep, GnmCellPos const *pos);
+void dependent_unlink (GnmDependent *dep, GnmCellPos const *pos);
+gboolean dependent_eval (GnmDependent *dep);
+void dependent_queue_recalc (GnmDependent *dep);
+void dependent_add_dynamic_dep (GnmDependent *dep, GnmValueRange const *v);
+
+GSList *dependents_relocate (GnmExprRelocateInfo const *info);
+void dependents_unrelocate (GSList *info);
+void dependents_unrelocate_free (GSList *info);
+void dependents_link (GSList *deps, GnmExprRewriteInfo const *rwinfo);
+
+void cell_queue_recalc (GnmCell const *cell);
+void cell_foreach_dep (GnmCell const *cell, DepFunc func, gpointer user);
+gboolean cell_eval_content (GnmCell *cell);
+
+void sheet_region_queue_recalc (Sheet const *sheet, GnmRange const *range);
+void sheet_deps_destroy (Sheet *sheet);
+void workbook_deps_destroy (Workbook *wb);
+void workbook_queue_all_recalc (Workbook *wb);
+
+GnmDepContainer *gnm_dep_container_new (void);
+void gnm_dep_container_dump (GnmDepContainer const *deps);
+
+#define DEPENDENT_CONTAINER_FOREACH_DEPENDENT(dc, dep, code) \
+ do { \
+ GnmDependent *dep = (dc)->head; \
+ while (dep) { \
+ GnmDependent *_next = dep->next_dep; \
+ code; \
+ dep = _next; \
+ } \
+ } while (0)
+
+#define DEPENDENT_MAKE_TYPE(t, set_expr_handler) \
+guint \
+t ## _get_dep_type (void) \
+{ \
+ static guint32 type = 0; \
+ if (type == 0) { \
+ static DependentClass klass; \
+ klass.eval = &t ## _eval; \
+ klass.set_expr = set_expr_handler; \
+ klass.debug_name = &t ## _debug_name; \
+ type = dependent_type_register (&klass); \
+ } \
+ return type; \
+}
+
+void dependent_debug_name (GnmDependent const *dep, FILE *out);
+
+#endif /* GNUMERIC_EVAL_H */
--- /dev/null
+++ lib/goffice/split/command-context.h
@@ -0,0 +1,40 @@
+#ifndef GNM_CMD_CONTEXT_H
+#define GNM_CMD_CONTEXT_H
+
+#include "gnumeric.h"
+#include <glib-object.h>
+
+#define GNM_CMD_CONTEXT_TYPE (gnm_cmd_context_get_type ())
+#define GNM_CMD_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_CMD_CONTEXT_TYPE, GnmCmdContext))
+#define IS_GNM_CMD_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNM_CMD_CONTEXT_TYPE))
+
+GType gnm_cmd_context_get_type (void);
+
+void gnm_cmd_context_error (GnmCmdContext *cc, GError *err);
+char *gnm_cmd_context_get_password (GnmCmdContext *cc, char const *fname);
+void gnm_cmd_context_set_sensitive (GnmCmdContext *cc, gboolean flag);
+
+/* utility routines for common errors */
+void gnm_cmd_context_error_system (GnmCmdContext *cc, char const *msg);
+void gnm_cmd_context_error_import (GnmCmdContext *cc, char const *msg);
+void gnm_cmd_context_error_export (GnmCmdContext *cc, char const *msg);
+void gnm_cmd_context_error_invalid (GnmCmdContext *cc,
+ char const *msg, char const *val);
+void gnm_cmd_context_error_info (GnmCmdContext *cc, ErrorInfo *error);
+
+/* An initial set of std errors */
+GQuark gnm_error_system (void);
+GQuark gnm_error_import (void);
+GQuark gnm_error_export (void);
+GQuark gnm_error_invalid (void);
+
+/***************************************************************************/
+/* some gnumeric specific utility routines */
+void gnm_cmd_context_error_calc (GnmCmdContext *cc, char const *msg);
+void gnm_cmd_context_error_splits_array (GnmCmdContext *cc, char const *cmd,
+ GnmRange const *array);
+
+GQuark gnm_error_array (void);
+GQuark gnm_error_calc (void);
+
+#endif /* GNM_CMD_CONTEXT_H */
--- /dev/null
+++ lib/goffice/split/plugin.c
@@ -0,0 +1,1855 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Support for dynamically-loaded Gnumeric plugin components.
+ *
+ * Authors:
+ * Old plugin engine:
+ * Tom Dyas (tdyas at romulus.rutgers.edu)
+ * Dom Lachowicz (dominicl at seas.upenn.edu)
+ * New plugin engine:
+ * Zbigniew Chyla (cyba at gnome.pl)
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "plugin.h"
+
+#include "gui-util.h"
+#include "gutils.h"
+#include "command-context.h"
+#include "file.h"
+//#include "workbook.h"
+//#include "workbook-view.h"
+#include "error-info.h"
+#include "plugin-loader.h"
+#include "plugin-loader-module.h"
+#include "plugin-service.h"
+#include "xml-io.h"
+#include "gnumeric-gconf.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <locale.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <gmodule.h>
+#include <application.h>
+
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xmlmemory.h>
+#include <gsf/gsf-impl-utils.h>
+
+#include <glib-object.h>
+
+
+#define PLUGIN_INFO_FILE_NAME "plugin.xml"
+#define PLUGIN_ID_VALID_CHARS "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+
+#define BUILTIN_LOADER_MODULE_ID "Gnumeric_Builtin:module"
+
+
+static GHashTable *plugins_marked_for_deactivation_hash = NULL;
+static GSList *available_plugins = NULL;
+static GHashTable *available_plugins_id_hash = NULL;
+
+static GHashTable *loader_services = NULL;
+
+
+static void plugin_get_loader_if_needed (GnmPlugin *pinfo, ErrorInfo **ret_error);
+static void plugin_info_read (GnmPlugin *pinfo, const gchar *dir_name, ErrorInfo **ret_error);
+static void gnm_plugin_load_base (GnmPlugin *plugin, ErrorInfo **ret_error);
+
+/*
+ * GnmPlugin
+ */
+
+typedef struct {
+ gchar *plugin_id;
+ GnmPlugin *plugin; /* don't use directly */
+ gboolean force_load;
+} PluginDependency;
+
+struct _GnmPlugin {
+ GTypeModule parent_instance;
+
+ gboolean has_full_info;
+ gchar *dir_name;
+ gchar *id;
+
+ gchar *name;
+ gchar *description;
+ gboolean require_explicit_enabling;
+
+ gboolean is_active;
+ gint use_refcount;
+ GSList *dependencies;
+ gchar *loader_id;
+ GHashTable *loader_attrs;
+ GnmPluginLoader *loader;
+ GSList *services;
+
+ char *saved_textdomain;
+};
+
+typedef struct _GnmPluginClass GnmPluginClass;
+struct _GnmPluginClass {
+ GTypeModuleClass parent_class;
+
+ /* signals */
+ void (*state_changed) (GnmPluginClass *gpc);
+ void (*can_deactivate_changed) (GnmPluginClass *gpc);
+};
+
+enum {
+ STATE_CHANGED,
+ CAN_DEACTIVATE_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint gnm_plugin_signals[LAST_SIGNAL];
+static GObjectClass *parent_class = NULL;
+
+static void plugin_dependency_free (gpointer data);
+
+static void
+gnm_plugin_init (GObject *obj)
+{
+ GnmPlugin *plugin = GNM_PLUGIN (obj);
+
+ plugin->id = NULL;
+ plugin->dir_name = NULL;
+ plugin->has_full_info = FALSE;
+ plugin->saved_textdomain = NULL;
+ plugin->require_explicit_enabling = FALSE;
+}
+
+static void
+gnm_plugin_finalize (GObject *obj)
+{
+ GnmPlugin *plugin = GNM_PLUGIN (obj);
+
+ g_free (plugin->id);
+ plugin->id = NULL;
+ g_free (plugin->dir_name);
+ plugin->dir_name = NULL;
+ if (plugin->has_full_info) {
+ plugin->has_full_info = FALSE;
+ g_free (plugin->name);
+ g_free (plugin->description);
+ gnm_slist_free_custom (plugin->dependencies, plugin_dependency_free);
+ g_free (plugin->loader_id);
+ if (plugin->loader_attrs != NULL) {
+ g_hash_table_destroy (plugin->loader_attrs);
+ }
+ if (plugin->loader != NULL) {
+ g_object_unref (plugin->loader);
+ }
+ gnm_slist_free_custom (plugin->services, g_object_unref);
+ }
+ g_free (plugin->saved_textdomain);
+ plugin->saved_textdomain = NULL;
+
+ parent_class->finalize (obj);
+}
+
+static gboolean
+gnm_plugin_type_module_load (GTypeModule *module)
+{
+ GnmPlugin *plugin = GNM_PLUGIN (module);
+ ErrorInfo *ignored_error;
+
+ g_return_val_if_fail (plugin->is_active, FALSE);
+
+ gnm_plugin_load_base (plugin, &ignored_error);
+ if (ignored_error != NULL) {
+ error_info_print (ignored_error);
+ error_info_free (ignored_error);
+ return FALSE;
+ }
+ gnm_plugin_use_ref (plugin);
+ return TRUE;
+}
+
+static void
+gnm_plugin_type_module_unload (GTypeModule *module)
+{
+ GnmPlugin *plugin = GNM_PLUGIN (module);
+
+ g_return_if_fail (plugin->is_active);
+
+ gnm_plugin_use_unref (plugin);
+}
+
+static void
+gnm_plugin_class_init (GObjectClass *gobject_class)
+{
+ GTypeModuleClass *type_module_class = G_TYPE_MODULE_CLASS (gobject_class);
+
+ parent_class = g_type_class_peek_parent (gobject_class);
+
+ gobject_class->finalize = gnm_plugin_finalize;
+
+ type_module_class->load = gnm_plugin_type_module_load;
+ type_module_class->unload = gnm_plugin_type_module_unload;
+
+ gnm_plugin_signals[STATE_CHANGED] = g_signal_new (
+ "state_changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GnmPluginClass, state_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ gnm_plugin_signals[CAN_DEACTIVATE_CHANGED] = g_signal_new (
+ "can_deactivate_changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GnmPluginClass, can_deactivate_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+GSF_CLASS (GnmPlugin, gnm_plugin, gnm_plugin_class_init, gnm_plugin_init,
+ G_TYPE_TYPE_MODULE)
+
+static GnmPlugin *
+plugin_info_new_from_xml (const gchar *dir_name, ErrorInfo **ret_error)
+{
+ GnmPlugin *plugin;
+ ErrorInfo *error;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ plugin = g_object_new (GNM_PLUGIN_TYPE, NULL);
+ plugin_info_read (plugin, dir_name, &error);
+ if (error == NULL) {
+ plugin->has_full_info = TRUE;
+ } else {
+ *ret_error = error;
+ g_object_unref (plugin);
+ plugin = NULL;
+ }
+
+ return plugin;
+}
+
+static GnmPlugin *
+plugin_info_new_with_id_and_dir_name_only (const gchar *id, const gchar *dir_name)
+{
+ GnmPlugin *plugin;
+
+ plugin = g_object_new (GNM_PLUGIN_TYPE, NULL);
+ g_type_module_set_name (G_TYPE_MODULE (plugin), id);
+ plugin->id = g_strdup (id);
+ plugin->dir_name = g_strdup (dir_name);
+ plugin->has_full_info = FALSE;
+
+ return plugin;
+}
+
+
+/*
+ * PluginFileState - information about plugin.xml files used in previous
+ * and current Gnumeric session.
+ */
+
+typedef struct {
+ gchar *dir_name;
+ gchar *file_state;
+ gchar *plugin_id;
+ enum {PLUGIN_OLD_UNUSED, PLUGIN_OLD_USED, PLUGIN_NEW} age;
+} PluginFileState;
+
+static gboolean plugin_file_state_hash_changed;
+static GHashTable *plugin_file_state_dir_hash;
+
+static gchar *
+get_file_state_as_string (const gchar *file_name)
+{
+ struct stat st;
+
+ if (stat (file_name, &st) == -1) {
+ return NULL;
+ }
+
+ return g_strdup_printf (
+ "%ld:%ld:%ld:%ld",
+ (long int) st.st_dev, (long int) st.st_ino,
+ (long int) st.st_size, (long int) st.st_mtime);
+}
+
+static gchar *
+plugin_file_state_as_string (PluginFileState *state)
+{
+ return g_strdup_printf ("%s|%s|%s", state->plugin_id, state->file_state,
+ state->dir_name);
+}
+
+static PluginFileState *
+plugin_file_state_from_string (const gchar *str)
+{
+ PluginFileState *state;
+ gchar **strv;
+
+ strv = g_strsplit (str, "|", 3);
+ if (strv[0] == NULL || strv[1] == NULL || strv[2] == NULL) {
+ g_strfreev (strv);
+ return NULL;
+ }
+ state = g_new (PluginFileState, 1);
+ state->plugin_id = strv[0];
+ state->file_state = strv[1];
+ state->dir_name = strv[2];
+ state->age = PLUGIN_OLD_UNUSED;
+ g_free (strv);
+
+ return state;
+}
+
+static void
+plugin_file_state_free (gpointer data)
+{
+ PluginFileState *state = data;
+
+ g_free (state->dir_name);
+ g_free (state->file_state);
+ g_free (state->plugin_id);
+ g_free (state);
+}
+
+/* --- */
+
+static gboolean
+plugin_info_read_full_info_if_needed_error_info (GnmPlugin *pinfo, ErrorInfo **ret_error)
+{
+ ErrorInfo *read_error;
+ gchar *old_id, *old_dir_name;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (pinfo->has_full_info) {
+ return TRUE;
+ }
+
+ old_id = pinfo->id;
+ old_dir_name = pinfo->dir_name;
+ plugin_info_read (pinfo, old_dir_name, &read_error);
+ if (read_error == NULL && strcmp (pinfo->id, old_id) == 0) {
+ /* id and dir_name pointers are guaranteed to be valid during plugin's lifetime */
+ g_free (pinfo->id);
+ g_free (pinfo->dir_name);
+ pinfo->id = old_id;
+ pinfo->dir_name = old_dir_name;
+ pinfo->has_full_info = TRUE;
+ } else {
+ plugin_message (1, "Can't read plugin.xml file for %s.\n", old_id);
+ if (read_error == NULL) {
+ read_error = error_info_new_printf (
+ _("File contains plugin info with invalid id (%s), expected %s."),
+ pinfo->id, old_id);
+ }
+ *ret_error = error_info_new_str_with_details (
+ _("Couldn't read plugin info from file."),
+ read_error);
+ g_free (old_id);
+ g_free (old_dir_name);
+ }
+
+ return *ret_error == NULL;
+}
+
+static gboolean
+plugin_info_read_full_info_if_needed (GnmPlugin *pinfo)
+{
+ ErrorInfo *error;
+
+ if (plugin_info_read_full_info_if_needed_error_info (pinfo, &error)) {
+ return TRUE;
+ } else {
+ g_warning ("plugin_info_read_full_info_if_needed: couldn't read plugin info from file.");
+ error_info_print (error);
+ error_info_free (error);
+ return FALSE;
+ }
+}
+
+/*
+ * Accessor functions
+ */
+
+/**
+ * gnm_plugin_get_textdomain:
+ * @plugin : The plugin
+ *
+ * Returns plugin's textdomain for use with textdomain(3) and d*gettext(3)
+ * functions.
+ */
+const gchar *
+gnm_plugin_get_textdomain (GnmPlugin *plugin)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN (plugin), NULL);
+
+ if (plugin->saved_textdomain == NULL) {
+ plugin->saved_textdomain = g_strconcat ("gnumeric__", plugin->id, NULL);
+ }
+
+ return plugin->saved_textdomain;
+}
+
+/**
+ * gnm_plugin_is_active:
+ * @pinfo : The plugin
+ *
+ * Returns : TRUE if @plugin is active and FALSE otherwise.
+ */
+gboolean
+gnm_plugin_is_active (GnmPlugin *plugin)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN (plugin), FALSE);
+
+ if (!plugin->has_full_info) {
+ return FALSE;
+ }
+ return plugin->is_active;
+}
+
+/**
+ * gnm_plugin_get_dir_name:
+ * @plugin : The plugin
+ *
+ * Returns the name of the directory in which @plugin is located.
+ * Returned string is != NULL and stays valid during @plugin's lifetime.
+ */
+const gchar *
+gnm_plugin_get_dir_name (GnmPlugin *pinfo)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN (pinfo), NULL);
+
+ return pinfo->dir_name;
+}
+
+/**
+ * gnm_plugin_get_id:
+ * @plugin : The plugin
+ *
+ * Returns the ID of @plugin (unique string used for idenfification of
+ * plugin).
+ * Returned string is != NULL and stays valid during @plugin's lifetime.
+ */
+const gchar *
+gnm_plugin_get_id (GnmPlugin *pinfo)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN (pinfo), NULL);
+
+ return pinfo->id;
+}
+
+/**
+ * gnm_plugin_get_name:
+ * @plugin : The plugin
+ *
+ * Returns textual name of @plugin. If the real name is not available
+ * for some reason, automatically generated string will be returned.
+ * Returned string is != NULL and stays valid during @plugin's lifetime.
+ */
+const gchar *
+gnm_plugin_get_name (GnmPlugin *pinfo)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN (pinfo), NULL);
+
+ if (!plugin_info_read_full_info_if_needed (pinfo)) {
+ return _("Unknown name");
+ }
+ return pinfo->name;
+}
+
+/**
+ * gnm_plugin_get_description:
+ * @plugin : The plugin
+ *
+ * Returns textual description of @plugin or NULL if description is not
+ * available.
+ * Returned string stays valid during @plugin's lifetime.
+ */
+const gchar *
+gnm_plugin_get_description (GnmPlugin *pinfo)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN (pinfo), NULL);
+
+ if (!plugin_info_read_full_info_if_needed (pinfo)) {
+ return NULL;
+ }
+ return pinfo->description;
+}
+
+/**
+ * gnm_plugin_is_loaded:
+ * @pinfo : The plugin
+ *
+ * Returns : TRUE if @plugin is loaded and FALSE otherwise.
+ */
+gboolean
+gnm_plugin_is_loaded (GnmPlugin *pinfo)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN (pinfo), FALSE);
+
+ if (!pinfo->has_full_info) {
+ return FALSE;
+ }
+ return pinfo->loader != NULL &&
+ gnm_plugin_loader_is_base_loaded (pinfo->loader);
+}
+
+/* - */
+
+/**
+ * plugins_register_loader:
+ * @loader_id : Loader's id
+ * @service : Plugin service of type "plugin_loader"
+ *
+ * Registers new type of plugin loader identified by @loader_id (identifier
+ * consists of loader's plugin id and service id concatenated using colon).
+ * All requests to create new loader object of this type will be passed to
+ * @service.
+ * This function is intended for use by GnmPluginService objects.
+ */
+void
+plugins_register_loader (const gchar *loader_id, GnmPluginService *service)
+{
+ g_return_if_fail (loader_id != NULL);
+ g_return_if_fail (service != NULL);
+
+ g_hash_table_insert (loader_services, g_strdup (loader_id), service);
+}
+
+/**
+ * plugins_unregister_loader:
+ * @loader_id : Loader's id
+ *
+ * Unregisters a type of plugin loader identified by @loader_id. After
+ * callingthis function Gnumeric will be unable to load plugins supported
+ * by the specified loader.
+ * This function is intended for use by GnmPluginService objects.
+ */
+void
+plugins_unregister_loader (const gchar *loader_id)
+{
+ g_return_if_fail (loader_id != NULL);
+
+ g_hash_table_remove (loader_services, loader_id);
+}
+
+static GType
+get_loader_type_by_id (const gchar *id_str, ErrorInfo **ret_error)
+{
+ GnmPluginService *loader_service;
+ ErrorInfo *error;
+ GType loader_type;
+
+ g_return_val_if_fail (id_str != NULL, G_TYPE_NONE);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (strcmp (id_str, BUILTIN_LOADER_MODULE_ID) == 0) {
+ return TYPE_GNM_PLUGIN_LOADER_MODULE;
+ }
+ loader_service = g_hash_table_lookup (loader_services, id_str);
+ if (loader_service == NULL) {
+ *ret_error = error_info_new_printf (
+ _("Unsupported loader type \"%s\"."),
+ id_str);
+ return G_TYPE_NONE;
+ }
+ loader_type = plugin_service_plugin_loader_generate_type (
+ loader_service, &error);
+ if (error != NULL) {
+ *ret_error = error_info_new_printf (
+ _("Error while preparing loader \"%s\"."),
+ id_str);
+ error_info_add_details (*ret_error, error);
+ return G_TYPE_NONE;
+ }
+
+ return loader_type;
+}
+
+static GnmPlugin *
+plugin_dependency_get_plugin (PluginDependency *dep)
+{
+ g_return_val_if_fail (dep != NULL, NULL);
+
+ if (dep->plugin == NULL)
+ dep->plugin = plugins_get_plugin_by_id (dep->plugin_id);
+ return dep->plugin;
+}
+
+static GSList *
+plugin_info_read_dependency_list (xmlNode *tree)
+{
+ GSList *dependency_list = NULL;
+ xmlNode *node;
+
+ g_return_val_if_fail (tree != NULL, NULL);
+ g_return_val_if_fail (strcmp (tree->name, "dependencies") == 0, NULL);
+
+ for (node = tree->xmlChildrenNode; node != NULL; node = node->next) {
+ if (strcmp (node->name, "dep_plugin") == 0) {
+ gchar *plugin_id;
+
+ plugin_id = xmlGetProp (node, (xmlChar *)"id");
+ if (plugin_id != NULL) {
+ PluginDependency *dep;
+
+ dep = g_new (PluginDependency, 1);
+ dep->plugin_id = plugin_id;
+ dep->plugin = NULL;
+ if (!xml_node_get_bool (node, "force_load", &(dep->force_load)))
+ dep->force_load = FALSE;
+ GNM_SLIST_PREPEND (dependency_list, dep);
+ }
+ }
+ }
+
+ return g_slist_reverse (dependency_list);
+}
+
+static GSList *
+plugin_info_read_service_list (GnmPlugin *plugin, xmlNode *tree, ErrorInfo **ret_error)
+{
+ GSList *service_list = NULL;
+ GSList *error_list = NULL;
+ xmlNode *node;
+ gint i;
+
+ g_return_val_if_fail (tree != NULL, NULL);
+
+ node = e_xml_get_child_by_name (tree, (xmlChar *)"services");
+ if (node == NULL)
+ return NULL;
+ node = node->xmlChildrenNode;
+ for (i = 0; node != NULL; i++, node = node->next) {
+ if (strcmp (node->name, "service") == 0) {
+ GnmPluginService *service;
+ ErrorInfo *service_error;
+
+ service = plugin_service_new (plugin, node, &service_error);
+
+ if (service != NULL) {
+ g_assert (service_error == NULL);
+ GNM_SLIST_PREPEND (service_list, service);
+ } else {
+ ErrorInfo *error;
+
+ error = error_info_new_printf (
+ _("Error while reading service #%d info."),
+ i);
+ error_info_add_details (error, service_error);
+ GNM_SLIST_PREPEND (error_list, error);
+ }
+ }
+ }
+ if (error_list != NULL) {
+ GNM_SLIST_REVERSE (error_list);
+ *ret_error = error_info_new_from_error_list (error_list);
+ gnm_slist_free_custom (service_list, g_object_unref);
+ return NULL;
+ } else {
+ return g_slist_reverse (service_list);
+ }
+}
+
+static GHashTable *
+plugin_info_read_loader_attrs (xmlNode *tree)
+{
+ xmlNode *node;
+ GHashTable *hash;
+
+ g_return_val_if_fail (tree != NULL, NULL);
+ g_return_val_if_fail (strcmp (tree->name, "loader") == 0, NULL);
+
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ for (node = tree->xmlChildrenNode; node != NULL; node = node->next) {
+ if (strcmp (node->name, "attribute") == 0) {
+ gchar *name, *value;
+
+ name = xmlGetProp (node, (xmlChar *)"name");
+ if (name != NULL) {
+ if (g_hash_table_lookup (hash, name) == NULL) {
+ value = xmlGetProp (node, (xmlChar *)"value");
+ g_hash_table_insert (hash, name, value);
+ } else {
+ g_warning ("Duplicated \"%s\" attribute in plugin.xml file.", name);
+ g_free (name);
+ }
+ }
+ }
+ }
+
+ return hash;
+}
+
+static void
+plugin_dependency_free (gpointer data)
+{
+ PluginDependency *dep = data;
+
+ g_return_if_fail (dep != NULL);
+
+ g_free (dep->plugin_id);
+ g_free (dep);
+}
+
+static void
+plugin_info_read (GnmPlugin *plugin, const gchar *dir_name, ErrorInfo **ret_error)
+{
+ gchar *file_name;
+ xmlDocPtr doc;
+ gchar *id, *name, *description;
+ xmlNode *tree, *information_node, *dependencies_node, *loader_node;
+ GSList *dependency_list;
+ gchar *loader_id;
+ GHashTable *loader_attrs;
+ gboolean require_explicit_enabling = FALSE;
+
+ g_return_if_fail (IS_GNM_PLUGIN (plugin));
+ g_return_if_fail (dir_name != NULL);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ file_name = g_build_filename (dir_name, PLUGIN_INFO_FILE_NAME, NULL);
+ doc = xmlParseFile (file_name);
+ if (doc == NULL || doc->xmlRootNode == NULL || strcmp (doc->xmlRootNode->name, "plugin") != 0) {
+ if (access (file_name, R_OK) != 0) {
+ *ret_error = error_info_new_printf (
+ _("Can't read plugin info file (\"%s\")."),
+ file_name);
+ } else {
+ *ret_error = error_info_new_printf (
+ _("File \"%s\" is not valid plugin info file."),
+ file_name);
+ }
+ g_free (file_name);
+ xmlFreeDoc (doc);
+ return;
+ }
+ tree = doc->xmlRootNode;
+ id = xmlGetProp (tree, (xmlChar *)"id");
+ information_node = e_xml_get_child_by_name (tree, (xmlChar *)"information");
+ if (information_node != NULL) {
+ xmlNode *node;
+ xmlChar *val;
+
+ node = e_xml_get_child_by_name_by_lang (information_node, "name");
+ if (node != NULL) {
+ val = xmlNodeGetContent (node);
+ name = g_strdup ((gchar *)val);
+ xmlFree (val);
+ } else
+ name = NULL;
+
+ node = e_xml_get_child_by_name_by_lang (information_node, "description");
+ if (node != NULL) {
+ val = xmlNodeGetContent (node);
+ description = g_strdup ((gchar *)val);
+ xmlFree (val);
+ } else
+ description = NULL;
+ if (e_xml_get_child_by_name (information_node, (xmlChar const *)"require_explicit_enabling"))
+ require_explicit_enabling = TRUE;
+ } else {
+ name = NULL;
+ description = NULL;
+ }
+ dependencies_node = e_xml_get_child_by_name (tree, (xmlChar *)"dependencies");
+ if (dependencies_node != NULL) {
+ dependency_list = plugin_info_read_dependency_list (dependencies_node);
+ } else {
+ dependency_list = NULL;
+ }
+ loader_node = e_xml_get_child_by_name (tree, (xmlChar *)"loader");
+ if (loader_node != NULL) {
+ char *p;
+
+ loader_id = xmlGetProp (loader_node, (xmlChar *)"type");
+ if (loader_id != NULL && (p = strchr (loader_id, ':')) != NULL) {
+ loader_attrs = plugin_info_read_loader_attrs (loader_node);
+ if (strcmp (loader_id, BUILTIN_LOADER_MODULE_ID) != 0) {
+ PluginDependency *dep;
+
+ /* Add loader's plugin to the list of dependencies */
+ dep = g_new (PluginDependency, 1);
+ dep->plugin_id = g_strndup (loader_id, p - loader_id);
+ dep->plugin = NULL;
+ dep->force_load = FALSE;
+ GNM_SLIST_PREPEND (dependency_list, dep);
+ }
+ } else {
+ loader_id = NULL;
+ loader_attrs = NULL;
+ }
+ } else {
+ loader_id = NULL;
+ loader_attrs = NULL;
+ }
+ if (id != NULL && name != NULL && loader_id != NULL &&
+ id[strspn (id, PLUGIN_ID_VALID_CHARS)] == '\0') {
+ ErrorInfo *services_error = NULL;
+
+ g_type_module_set_name (G_TYPE_MODULE (plugin), id);
+ plugin->dir_name = g_strdup (dir_name);
+ plugin->id = id;
+ plugin->name = name;
+ plugin->description = description;
+ plugin->require_explicit_enabling = require_explicit_enabling;
+ plugin->is_active = FALSE;
+ plugin->use_refcount = 0;
+ plugin->dependencies = dependency_list;
+ plugin->loader_id = loader_id;
+ plugin->loader_attrs = loader_attrs;
+ plugin->loader = NULL;
+ plugin->services = plugin_info_read_service_list (plugin, tree, &services_error);
+
+ if (services_error != NULL) {
+ *ret_error = error_info_new_printf (
+ _("Errors while reading services for plugin with id=\"%s\"."),
+ id);
+ error_info_add_details (*ret_error, services_error);
+ } else if (plugin->services == NULL)
+ *ret_error = error_info_new_printf (
+ _("No services defined for plugin with id=\"%s\"."),
+ id);
+ else
+ plugin_message (4, "Read plugin.xml file for %s.\n", plugin->id);
+ } else {
+ if (id != NULL) {
+ GSList *error_list = NULL;
+
+ if (id[strspn (id, PLUGIN_ID_VALID_CHARS)] != '\0') {
+ GNM_SLIST_PREPEND (error_list, error_info_new_printf (
+ _("Plugin id contains invalid characters (%s)."), id));
+ }
+ if (name == NULL) {
+ GNM_SLIST_PREPEND (error_list, error_info_new_str (
+ _("Unknown plugin name.")));
+ }
+ if (loader_id == NULL) {
+ GNM_SLIST_PREPEND (error_list, error_info_new_printf (
+ _("No loader defined or loader id invalid for plugin with id=\"%s\"."), id));
+ }
+ g_assert (error_list != NULL);
+ GNM_SLIST_REVERSE (error_list);
+ *ret_error = error_info_new_from_error_list (error_list);
+ } else
+ *ret_error = error_info_new_str (_("Plugin has no id."));
+
+ gnm_slist_free_custom (dependency_list, plugin_dependency_free);
+ g_free (plugin->loader_id);
+ if (plugin->loader_attrs != NULL)
+ g_hash_table_destroy (plugin->loader_attrs);
+ g_free (id);
+ g_free (name);
+ g_free (description);
+ }
+ g_free (file_name);
+ xmlFreeDoc (doc);
+}
+
+static void
+plugin_get_loader_if_needed (GnmPlugin *pinfo, ErrorInfo **ret_error)
+{
+ GType loader_type;
+ ErrorInfo *error;
+
+ g_return_if_fail (IS_GNM_PLUGIN (pinfo));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (!plugin_info_read_full_info_if_needed_error_info (pinfo, ret_error)) {
+ return;
+ }
+ if (pinfo->loader != NULL) {
+ return;
+ }
+ loader_type = get_loader_type_by_id (pinfo->loader_id, &error);
+ if (error == NULL) {
+ GnmPluginLoader *loader;
+ ErrorInfo *error;
+
+ loader = GNM_PLUGIN_LOADER (g_object_new (loader_type, NULL));
+ gnm_plugin_loader_set_attributes (loader, pinfo->loader_attrs, &error);
+ if (error == NULL) {
+ pinfo->loader = loader;
+ gnm_plugin_loader_set_plugin (loader, pinfo);
+ } else {
+ g_object_unref (loader);
+ loader = NULL;
+ *ret_error = error_info_new_printf (
+ _("Error initializing plugin loader (\"%s\")."),
+ pinfo->loader_id);
+ error_info_add_details (*ret_error, error);
+ }
+ } else {
+ *ret_error = error;
+ }
+}
+
+/**
+ * gnm_plugin_activate:
+ * @plugin : The plugin
+ * @ret_error : Pointer used to report errors
+ *
+ * Activates @plugin together with all its dependencies.
+ * In case of error the plugin won't be activated and detailed error
+ * information will be returned using @ret_error.
+ */
+void
+gnm_plugin_activate (GnmPlugin *pinfo, ErrorInfo **ret_error)
+{
+ GSList *error_list = NULL;
+ GSList *l;
+ gint i;
+ static GSList *activate_stack = NULL;
+
+ g_return_if_fail (IS_GNM_PLUGIN (pinfo));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (g_slist_find (activate_stack, pinfo) != NULL) {
+ *ret_error = error_info_new_str (
+ _("Detected cyclic plugin dependencies."));
+ return;
+ }
+ if (!plugin_info_read_full_info_if_needed_error_info (pinfo, ret_error)) {
+ return;
+ }
+ if (pinfo->is_active) {
+ return;
+ }
+
+ /* Activate plugin dependencies */
+ GNM_SLIST_PREPEND (activate_stack, pinfo);
+ GNM_SLIST_FOREACH (pinfo->dependencies, PluginDependency, dep,
+ GnmPlugin *dep_plugin;
+
+ dep_plugin = plugin_dependency_get_plugin (dep);
+ if (dep_plugin != NULL) {
+ ErrorInfo *dep_error;
+
+ gnm_plugin_activate (dep_plugin, &dep_error);
+ if (dep_error != NULL) {
+ ErrorInfo *new_error;
+
+ new_error = error_info_new_printf (
+ _("Couldn't activate plugin with id=\"%s\"."), dep->plugin_id);
+ error_info_add_details (new_error, dep_error);
+ GNM_SLIST_PREPEND (error_list, new_error);
+ }
+ } else {
+ GNM_SLIST_PREPEND (error_list, error_info_new_printf (
+ _("Couldn't find plugin with id=\"%s\"."), dep->plugin_id));
+ }
+ );
+ g_assert (activate_stack != NULL && activate_stack->data == pinfo);
+ activate_stack = g_slist_delete_link (activate_stack, activate_stack);
+ if (error_list != NULL) {
+ *ret_error = error_info_new_str (
+ _("Error while activating plugin dependencies."));
+ error_info_add_details_list (*ret_error, error_list);
+ return;
+ }
+
+ for (l = pinfo->services, i = 0; l != NULL; l = l->next, i++) {
+ GnmPluginService *service = l->data;
+ ErrorInfo *service_error;
+
+ plugin_service_activate (service, &service_error);
+ if (service_error != NULL) {
+ ErrorInfo *error;
+
+ error = error_info_new_printf (
+ _("Error while activating plugin service #%d."), i);
+ error_info_add_details (error, service_error);
+ GNM_SLIST_PREPEND (error_list, error);
+ }
+ }
+ if (error_list != NULL) {
+ *ret_error = error_info_new_from_error_list (error_list);
+ /* FIXME - deactivate activated services */
+ return;
+ }
+ GNM_SLIST_FOREACH (pinfo->dependencies, PluginDependency, dep,
+ gnm_plugin_use_ref (plugin_dependency_get_plugin (dep));
+ );
+ pinfo->is_active = TRUE;
+ g_signal_emit (G_OBJECT (pinfo), gnm_plugin_signals[STATE_CHANGED], 0);
+}
+
+/**
+ * gnm_plugin_deactivate:
+ * @plugin : The plugin
+ * @ret_error : Pointer used to report errors
+ *
+ * Dectivates @plugin. Its dependencies will NOT be automatically
+ * deactivated.
+ * In case of error the plugin won't be deactivated and detailed error
+ * information will be returned using @ret_error.
+ */
+void
+gnm_plugin_deactivate (GnmPlugin *pinfo, ErrorInfo **ret_error)
+{
+ GSList *error_list = NULL;
+ GSList *l;
+ gint i;
+
+ g_return_if_fail (IS_GNM_PLUGIN (pinfo));
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (!pinfo->has_full_info || !pinfo->is_active) {
+ return;
+ }
+ if (pinfo->use_refcount > 0) {
+ *ret_error = error_info_new_str ("Plugin is still in use.");
+ return;
+ }
+ for (l = pinfo->services, i = 0; l != NULL; l = l->next, i++) {
+ GnmPluginService *service = l->data;
+ ErrorInfo *service_error;
+
+ plugin_service_deactivate (service, &service_error);
+ if (service_error != NULL) {
+ ErrorInfo *error;
+
+ error = error_info_new_printf (
+ _("Error while deactivating plugin service #%d."), i);
+ error_info_add_details (error, service_error);
+ GNM_SLIST_PREPEND (error_list, error);
+ }
+ }
+ if (error_list != NULL) {
+ *ret_error = error_info_new_from_error_list (error_list);
+ /* FIXME - some services are still active (or broken) */
+ } else {
+ pinfo->is_active = FALSE;
+ GNM_SLIST_FOREACH (pinfo->dependencies, PluginDependency, dep,
+ gnm_plugin_use_unref (plugin_dependency_get_plugin (dep));
+ );
+ if (pinfo->loader != NULL) {
+ g_object_unref (pinfo->loader);
+ pinfo->loader = NULL;
+ }
+ }
+ g_signal_emit (G_OBJECT (pinfo), gnm_plugin_signals[STATE_CHANGED], 0);
+}
+
+/**
+ * gnm_plugin_can_deactivate:
+ * @pinfo : The plugin
+ *
+ * Tells if the plugin can be deactivated using gnm_plugin_deactivate.
+ *
+ * Returns : TRUE if @plugin can be deactivated and FALSE otherwise.
+ */
+gboolean
+gnm_plugin_can_deactivate (GnmPlugin *pinfo)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN (pinfo), FALSE);
+
+ if (!pinfo->is_active) {
+ return FALSE;
+ }
+ if (!plugin_info_read_full_info_if_needed (pinfo)) {
+ return FALSE;
+ }
+ return pinfo->use_refcount == 0;
+}
+
+static void
+gnm_plugin_load_base (GnmPlugin *plugin, ErrorInfo **ret_error)
+{
+ ErrorInfo *error;
+ GSList *error_list = NULL;
+ static GSList *load_stack = NULL;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (g_slist_find (load_stack, plugin) != NULL) {
+ *ret_error = error_info_new_str (
+ _("Detected cyclic plugin dependencies."));
+ return;
+ }
+ if (gnm_plugin_is_loaded (plugin)) {
+ return;
+ }
+ if (!plugin_info_read_full_info_if_needed_error_info (plugin, ret_error)) {
+ return;
+ }
+ plugin_get_loader_if_needed (plugin, &error);
+ if (error != NULL) {
+ *ret_error = error_info_new_str_with_details (
+ _("Cannot load plugin loader."),
+ error);
+ return;
+ }
+
+ /* Load plugin dependencies */
+ GNM_SLIST_PREPEND (load_stack, plugin);
+ GNM_SLIST_FOREACH (plugin->dependencies, PluginDependency, dep,
+ GnmPlugin *dep_plugin;
+ ErrorInfo *dep_error;
+
+ if (!dep->force_load) {
+ continue;
+ }
+ dep_plugin = plugin_dependency_get_plugin (dep);
+ if (dep_plugin != NULL) {
+ plugin_get_loader_if_needed (dep_plugin, &dep_error);
+ if (dep_error == NULL) {
+ gnm_plugin_load_base (dep_plugin, &dep_error);
+ } else {
+ dep_error = error_info_new_str_with_details (
+ _("Cannot load plugin loader."),
+ dep_error);
+ }
+ if (dep_error != NULL) {
+ ErrorInfo *new_error;
+
+ new_error = error_info_new_printf (
+ _("Couldn't load plugin with id=\"%s\"."), dep->plugin_id);
+ error_info_add_details (new_error, dep_error);
+ GNM_SLIST_PREPEND (error_list, new_error);
+ }
+ } else {
+ GNM_SLIST_PREPEND (error_list, error_info_new_printf (
+ _("Couldn't find plugin with id=\"%s\"."), dep->plugin_id));
+ }
+ );
+ g_assert (load_stack != NULL && load_stack->data == plugin);
+ load_stack = g_slist_delete_link (load_stack, load_stack);
+ if (error_list != NULL) {
+ *ret_error = error_info_new_str (
+ _("Error while loading plugin dependencies."));
+ error_info_add_details_list (*ret_error, error_list);
+ return;
+ }
+
+ gnm_plugin_loader_load_base (plugin->loader, &error);
+ if (error != NULL) {
+ *ret_error = error;
+ return;
+ }
+ g_signal_emit (G_OBJECT (plugin), gnm_plugin_signals[STATE_CHANGED], 0);
+}
+
+/**
+ * gnm_plugin_load_service:
+ * @pinfo : The plugin
+ * @service : Plugin service
+ * @ret_error : Pointer used to report errors
+ *
+ * Loads base part of the plugin if is not loaded and then loads given
+ * plugin service (prepares necessary part of the plugin for direct use).
+ * This function is intended for use by GnmPluginService objects.
+ */
+void
+gnm_plugin_load_service (GnmPlugin *pinfo, GnmPluginService *service, ErrorInfo **ret_error)
+{
+ g_return_if_fail (IS_GNM_PLUGIN (pinfo));
+ g_return_if_fail (service != NULL);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ gnm_plugin_load_base (pinfo, ret_error);
+ if (*ret_error != NULL) {
+ return;
+ }
+ gnm_plugin_loader_load_service (pinfo->loader, service, ret_error);
+}
+
+/**
+ * gnm_plugin_unload_service:
+ * @pinfo : The plugin
+ * @service : Plugin service
+ * @ret_error : Pointer used to report errors
+ *
+ * ...
+ * This function is intended for use by GnmPluginService objects.
+ */
+void
+gnm_plugin_unload_service (GnmPlugin *pinfo, GnmPluginService *service, ErrorInfo **ret_error)
+{
+ g_return_if_fail (IS_GNM_PLUGIN (pinfo));
+ g_return_if_fail (pinfo->loader != NULL);
+ g_return_if_fail (service != NULL);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ if (!plugin_info_read_full_info_if_needed_error_info (pinfo, ret_error)) {
+ return;
+ }
+ gnm_plugin_loader_unload_service (pinfo->loader, service, ret_error);
+}
+
+/**
+ * gnm_plugin_use_ref:
+ * @plugin : The plugin
+ */
+void
+gnm_plugin_use_ref (GnmPlugin *plugin)
+{
+ g_return_if_fail (IS_GNM_PLUGIN (plugin));
+ g_return_if_fail (plugin->is_active);
+
+ plugin->use_refcount++;
+ if (plugin->use_refcount == 1) {
+ g_signal_emit (G_OBJECT (plugin), gnm_plugin_signals[CAN_DEACTIVATE_CHANGED], 0);
+ }
+}
+
+/**
+ * gnm_plugin_use_unref:
+ * @plugin : The plugin
+ */
+void
+gnm_plugin_use_unref (GnmPlugin *plugin)
+{
+ g_return_if_fail (IS_GNM_PLUGIN (plugin));
+ g_return_if_fail (plugin->is_active);
+ g_return_if_fail (plugin->use_refcount > 0);
+
+ plugin->use_refcount--;
+ if (plugin->use_refcount == 0) {
+ g_signal_emit (G_OBJECT (plugin), gnm_plugin_signals[CAN_DEACTIVATE_CHANGED], 0);
+ }
+}
+
+/**
+ * gnm_plugin_get_dependencies_ids:
+ * @plugin : The plugin
+ *
+ * Returns the list of identifiers of plugins that @plugin depends on.
+ * All these plugins will be automatically activated before activating
+ * the @plugin itself.
+ * The caller must free the returned list together with the strings it
+ * points to (use gnm_slist_free_custom (list, g_free) to do this).
+ */
+GSList *
+gnm_plugin_get_dependencies_ids (GnmPlugin *plugin)
+{
+ GSList *list = NULL;
+
+ GNM_SLIST_FOREACH (plugin->dependencies, PluginDependency, dep,
+ GNM_SLIST_PREPEND (list, g_strdup (dep->plugin_id));
+ );
+
+ return g_slist_reverse (list);
+}
+
+/**
+ * gnm_plugin_get_services:
+ * @plugin : The plugin
+ *
+ */
+GSList *
+gnm_plugin_get_services (GnmPlugin *plugin)
+{
+ g_return_val_if_fail (IS_GNM_PLUGIN (plugin), NULL);
+
+ return plugin->services;
+}
+
+/*
+ * May return NULL without errors (is XML file doesn't exist)
+ */
+static GnmPlugin *
+plugin_info_read_for_dir (const gchar *dir_name, ErrorInfo **ret_error)
+{
+ GnmPlugin *pinfo = NULL;
+ gchar *file_name;
+ gchar *file_state;
+ PluginFileState *state;
+ ErrorInfo *plugin_error;
+
+ g_return_val_if_fail (dir_name != NULL, NULL);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ file_name = g_build_filename (dir_name, PLUGIN_INFO_FILE_NAME, NULL);
+ file_state = get_file_state_as_string (file_name);
+ if (file_state == NULL) {
+ g_free (file_name);
+ return NULL;
+ }
+ state = g_hash_table_lookup (plugin_file_state_dir_hash, dir_name);
+ if (state != NULL && strcmp (state->file_state, file_state) == 0) {
+ pinfo = plugin_info_new_with_id_and_dir_name_only (state->plugin_id, state->dir_name);
+ state->age = PLUGIN_OLD_USED;
+ } else if ((pinfo = plugin_info_new_from_xml (dir_name, &plugin_error)) != NULL) {
+ g_assert (plugin_error == NULL);
+ if (state == NULL) {
+ state = g_new (PluginFileState, 1);
+ state->dir_name = g_strdup (dir_name);
+ state->file_state = g_strdup (file_state);
+ state->plugin_id = g_strdup (gnm_plugin_get_id (pinfo));
+ state->age = PLUGIN_NEW;
+ g_hash_table_insert (plugin_file_state_dir_hash, state->dir_name, state);
+ } else {
+ if (strcmp (state->plugin_id, pinfo->id) == 0) {
+ state->age = PLUGIN_OLD_USED;
+ } else {
+ state->age = PLUGIN_NEW;
+ }
+ g_free (state->file_state);
+ g_free (state->plugin_id);
+ state->file_state = g_strdup (file_state);
+ state->plugin_id = g_strdup (gnm_plugin_get_id (pinfo));
+ }
+ plugin_file_state_hash_changed = TRUE;
+ } else {
+ *ret_error = error_info_new_printf (
+ _("Errors occurred while reading plugin informations from file \"%s\"."),
+ file_name);
+ error_info_add_details (*ret_error, plugin_error);
+ }
+ g_free (file_name);
+ g_free (file_state);
+
+ return pinfo;
+}
+
+/*
+ * May return partial list and some error info.
+ */
+static GSList *
+plugin_info_list_read_for_subdirs_of_dir (const gchar *dir_name, ErrorInfo **ret_error)
+{
+ GSList *plugin_info_list = NULL;
+ GDir *dir;
+ char const *d_name;
+ GSList *error_list = NULL;
+
+ g_return_val_if_fail (dir_name != NULL, NULL);
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ dir = g_dir_open (dir_name, 0, NULL);
+ if (dir == NULL)
+ return NULL;
+
+ while ((d_name = g_dir_read_name (dir)) != NULL) {
+ gchar *full_entry_name;
+ ErrorInfo *error;
+ GnmPlugin *pinfo;
+
+ if (strcmp (d_name, ".") == 0 || strcmp (d_name, "..") == 0)
+ continue;
+ full_entry_name = g_build_filename (dir_name, d_name, NULL);
+ pinfo = plugin_info_read_for_dir (full_entry_name, &error);
+ if (pinfo != NULL) {
+ GNM_SLIST_PREPEND (plugin_info_list, pinfo);
+ }
+ if (error != NULL) {
+ GNM_SLIST_PREPEND (error_list, error);
+ }
+ g_free (full_entry_name);
+ }
+ if (error_list != NULL) {
+ GNM_SLIST_REVERSE (error_list);
+ *ret_error = error_info_new_from_error_list (error_list);
+ }
+ g_dir_close (dir);
+
+ return g_slist_reverse (plugin_info_list);
+}
+
+/*
+ * May return partial list and some error info.
+ */
+static GSList *
+plugin_info_list_read_for_subdirs_of_dir_list (GSList *dir_list, ErrorInfo **ret_error)
+{
+ GSList *plugin_info_list = NULL;
+ GSList *dir_iterator;
+ GSList *error_list = NULL;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ for (dir_iterator = dir_list; dir_iterator != NULL; dir_iterator = dir_iterator->next) {
+ gchar *dir_name;
+ ErrorInfo *error;
+ GSList *dir_plugin_info_list;
+
+ dir_name = (gchar *) dir_iterator->data;
+ dir_plugin_info_list = plugin_info_list_read_for_subdirs_of_dir (dir_name, &error);
+ if (error != NULL) {
+ GNM_SLIST_PREPEND (error_list, error);
+ }
+ if (dir_plugin_info_list != NULL) {
+ GNM_SLIST_CONCAT (plugin_info_list, dir_plugin_info_list);
+ }
+ }
+ if (error_list != NULL) {
+ GNM_SLIST_REVERSE (error_list);
+ *ret_error = error_info_new_from_error_list (error_list);
+ }
+
+ return plugin_info_list;
+}
+
+static GSList *
+gnumeric_extra_plugin_dirs (void)
+{
+ GSList *extra_dirs;
+ gchar const *plugin_path_env;
+
+ extra_dirs = gnm_string_slist_copy (gnm_app_prefs->plugin_extra_dirs);
+ plugin_path_env = g_getenv ("GNUMERIC_PLUGIN_PATH");
+ if (plugin_path_env != NULL) {
+ GNM_SLIST_CONCAT (extra_dirs, gnm_strsplit_to_slist (plugin_path_env, ":"));
+ }
+
+ return extra_dirs;
+}
+
+/*
+ * May return partial list and some error info.
+ */
+static GSList *
+plugin_info_list_read_for_all_dirs (ErrorInfo **ret_error)
+{
+ GSList *dir_list;
+ GSList *plugin_info_list;
+ ErrorInfo *error;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ dir_list = gnm_slist_create (gnm_sys_plugin_dir (),
+ gnm_usr_plugin_dir (),
+ NULL);
+ GNM_SLIST_CONCAT (dir_list, gnumeric_extra_plugin_dirs ());
+ plugin_info_list = plugin_info_list_read_for_subdirs_of_dir_list (dir_list, &error);
+ g_slist_foreach (dir_list, (GFunc)g_free, NULL);
+ g_slist_free (dir_list);
+ *ret_error = error;
+
+ return plugin_info_list;
+}
+
+/**
+ * plugin_db_activate_plugin_list:
+ * @plugins : The list of plugins
+ * @ret_error : Pointer used to report errors
+ *
+ * Activates all plugins in the list. If some of the plugins cannot be
+ * activated, the function reports this via @ret_error (errors don't
+ * affect plugins activated successfully).
+ */
+void
+plugin_db_activate_plugin_list (GSList *plugins, ErrorInfo **ret_error)
+{
+ GSList *error_list = NULL;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ GNM_SLIST_FOREACH (plugins, GnmPlugin, pinfo,
+ ErrorInfo *error;
+
+ gnm_plugin_activate (pinfo, &error);
+ if (error != NULL) {
+ ErrorInfo *new_error;
+
+ new_error = error_info_new_printf (
+ _("Couldn't activate plugin \"%s\" (ID: %s)."),
+ pinfo->name, pinfo->id);
+ error_info_add_details (new_error, error);
+ GNM_SLIST_PREPEND (error_list, new_error);
+ }
+ );
+ if (error_list != NULL) {
+ GNM_SLIST_REVERSE (error_list);
+ *ret_error = error_info_new_from_error_list (error_list);
+ }
+}
+
+/**
+ * plugin_db_deactivate_plugin_list:
+ * @plugins : The list of plugins
+ * @ret_error : Pointer used to report errors
+ *
+ * Deactivates all plugins in the list. If some of the plugins cannot be
+ * deactivated, the function reports this via @ret_error (errors don't
+ * affect plugins deactivated successfully).
+ */
+void
+plugin_db_deactivate_plugin_list (GSList *plugins, ErrorInfo **ret_error)
+{
+ GSList *error_list = NULL;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+ GNM_SLIST_FOREACH (plugins, GnmPlugin, pinfo,
+ ErrorInfo *error;
+
+ gnm_plugin_deactivate (pinfo, &error);
+ if (error != NULL) {
+ ErrorInfo *new_error;
+
+ new_error = error_info_new_printf (
+ _("Couldn't deactivate plugin \"%s\" (ID: %s)."),
+ pinfo->name, pinfo->id);
+ error_info_add_details (new_error, error);
+ GNM_SLIST_PREPEND (error_list, new_error);
+ }
+ );
+ if (error_list != NULL) {
+ GNM_SLIST_REVERSE (error_list);
+ *ret_error = error_info_new_from_error_list (error_list);
+ }
+}
+
+/**
+ * plugins_get_available_plugins:
+ *
+ * Returns the list of available plugins. The returned value must not be
+ * freed and stays valid until calling plugins_rescan or plugins_shutdown.
+ */
+GSList *
+plugins_get_available_plugins (void)
+{
+ return available_plugins;
+}
+
+/**
+ * plugins_get_plugin_by_id:
+ * @plugin_id : String containing plugin ID
+ *
+ * Returns GnmPlugin object for plugin with ID equal to @plugin_id or NULL
+ * if there's no plugin available with given id.
+ * Function returns "borrowed" reference, use g_object_ref if you want to
+ * be sure that plugin won't disappear.
+ */
+GnmPlugin *
+plugins_get_plugin_by_id (const gchar *plugin_id)
+{
+ g_return_val_if_fail (plugin_id != NULL, NULL);
+
+ return g_hash_table_lookup (available_plugins_id_hash, plugin_id);
+}
+
+/**
+ * plugin_db_mark_plugin_for_deactivation:
+ * ...
+ */
+void
+plugin_db_mark_plugin_for_deactivation (GnmPlugin *pinfo, gboolean mark)
+{
+ g_return_if_fail (IS_GNM_PLUGIN (pinfo));
+
+ if (mark) {
+ if (plugins_marked_for_deactivation_hash == NULL) {
+ plugins_marked_for_deactivation_hash = g_hash_table_new (&g_str_hash, &g_str_equal);
+ }
+ g_hash_table_insert (plugins_marked_for_deactivation_hash, pinfo->id, pinfo);
+ } else {
+ if (plugins_marked_for_deactivation_hash != NULL) {
+ g_hash_table_remove (plugins_marked_for_deactivation_hash, pinfo->id);
+ }
+ }
+}
+
+/**
+ * plugin_db_is_plugin_marked_for_deactivation:
+ * ...
+ */
+gboolean
+plugin_db_is_plugin_marked_for_deactivation (GnmPlugin *pinfo)
+{
+ return plugins_marked_for_deactivation_hash != NULL &&
+ g_hash_table_lookup (plugins_marked_for_deactivation_hash, pinfo->id) != NULL;
+}
+
+static void
+ghf_set_state_old_unused (gpointer key, gpointer value, gpointer unused)
+{
+ PluginFileState *state = value;
+
+ state->age = PLUGIN_OLD_UNUSED;
+}
+
+/**
+ * plugins_rescan:
+ * @ret_error : Pointer used to report errors
+ * @ret_new_plugins : Optional pointer to return list of new plugins
+ *
+ *
+ */
+void
+plugins_rescan (ErrorInfo **ret_error, GSList **ret_new_plugins)
+{
+ GSList *error_list = NULL;
+ ErrorInfo *error;
+ GSList *new_available_plugins;
+ GHashTable *new_available_plugins_id_hash;
+ GSList *removed_plugins = NULL, *added_plugins = NULL, *still_active_ids = NULL;
+
+ GNM_INIT_RET_ERROR_INFO (ret_error);
+
+ /* re-read plugins list from disk */
+ g_hash_table_foreach (plugin_file_state_dir_hash, ghf_set_state_old_unused, NULL);
+ new_available_plugins = plugin_info_list_read_for_all_dirs (&error);
+ if (error != NULL) {
+ GNM_SLIST_PREPEND (error_list, error_info_new_str_with_details (
+ _("Errors while reading info about available plugins."), error));
+ }
+
+ /* Find and (try to) deactivate not any longer available plugins */
+ new_available_plugins_id_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ GNM_SLIST_FOREACH (new_available_plugins, GnmPlugin, plugin,
+ g_hash_table_insert (
+ new_available_plugins_id_hash, (char *) gnm_plugin_get_id (plugin), plugin);
+ );
+ GNM_SLIST_FOREACH (available_plugins, GnmPlugin, plugin,
+ GnmPlugin *found_plugin;
+
+ found_plugin = g_hash_table_lookup (
+ new_available_plugins_id_hash, gnm_plugin_get_id (plugin));
+ if (found_plugin == NULL ||
+ strcmp (gnm_plugin_get_dir_name (found_plugin),
+ gnm_plugin_get_dir_name (plugin)) != 0) {
+ GNM_SLIST_PREPEND (removed_plugins, plugin);
+ }
+ );
+ g_hash_table_destroy (new_available_plugins_id_hash);
+ plugin_db_deactivate_plugin_list (removed_plugins, &error);
+ if (error != NULL) {
+ GNM_SLIST_PREPEND (error_list, error_info_new_str_with_details (
+ _("Errors while deactivating plugins that are no longer on disk."), error));
+ }
+ GNM_SLIST_FOREACH (removed_plugins, GnmPlugin, plugin,
+ if (gnm_plugin_is_active (plugin)) {
+ GNM_SLIST_PREPEND (still_active_ids, (char *) gnm_plugin_get_id (plugin));
+ } else {
+ GNM_SLIST_REMOVE (available_plugins, plugin);
+ g_hash_table_remove (available_plugins_id_hash, gnm_plugin_get_id (plugin));
+ g_object_unref (plugin);
+ }
+ );
+ g_slist_free (removed_plugins);
+ if (still_active_ids != NULL) {
+ GString *s;
+
+ s = g_string_new (still_active_ids->data);
+ GNM_SLIST_FOREACH (still_active_ids->next, char, id,
+ g_string_append (s, ", ");
+ g_string_append (s, id);
+ );
+ GNM_SLIST_PREPEND (error_list, error_info_new_printf (
+ _("The following plugins are no longer on disk but are still active:\n"
+ "%s.\nYou should restart Gnumeric now."), s->str));
+ g_string_free (s, TRUE);
+ gnm_slist_free_custom (still_active_ids, g_free);
+ }
+
+ /* Find previously not available plugins */
+ GNM_SLIST_FOREACH (new_available_plugins, GnmPlugin, plugin,
+ GnmPlugin *old_plugin;
+
+ old_plugin = g_hash_table_lookup (
+ available_plugins_id_hash, gnm_plugin_get_id (plugin));
+ if (old_plugin == NULL) {
+ GNM_SLIST_PREPEND (added_plugins, plugin);
+ g_object_ref (plugin);
+ }
+ );
+ gnm_slist_free_custom (new_available_plugins, g_object_unref);
+ if (ret_new_plugins != NULL) {
+ *ret_new_plugins = g_slist_copy (added_plugins);
+ }
+ GNM_SLIST_FOREACH (added_plugins, GnmPlugin, plugin,
+ g_hash_table_insert (
+ available_plugins_id_hash, (char *) gnm_plugin_get_id (plugin), plugin);
+ );
+ GNM_SLIST_CONCAT (available_plugins, added_plugins);
+
+ /* handle errors */
+ if (error_list != NULL) {
+ *ret_error = error_info_new_from_error_list (g_slist_reverse (error_list));
+ }
+}
+
+static void
+ghf_collect_new_plugins (gpointer ignored,
+ PluginFileState *s, GSList **plugin_list)
+{
+ if (s->age == PLUGIN_NEW) {
+ GnmPlugin *plugin = plugins_get_plugin_by_id (s->plugin_id);
+ if (plugin != NULL && !plugin->require_explicit_enabling)
+ GNM_SLIST_PREPEND (*plugin_list, plugin);
+ }
+}
+
+/**
+ * plugins_init:
+ * @context : #GnmCmdContext used to report errors
+ *
+ * Initializes the plugin subsystem. Don't call this function more than
+ * once.
+ */
+void
+plugins_init (GnmCmdContext *context)
+{
+ GSList *error_list = NULL;
+ ErrorInfo *error;
+ GSList *plugin_list;
+
+ gnm_time_counter_push ();
+
+ loader_services = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ /* initialize hash table with information about known plugin.xml files */
+ plugin_file_state_dir_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, plugin_file_state_free);
+ GNM_SLIST_FOREACH (gnm_app_prefs->plugin_file_states, char, state_str,
+ PluginFileState *state;
+
+ state = plugin_file_state_from_string (state_str);
+ if (state != NULL)
+ g_hash_table_insert (plugin_file_state_dir_hash, state->dir_name, state);
+ );
+ plugin_file_state_hash_changed = FALSE;
+
+ /* collect information about the available plugins */
+ available_plugins = plugin_info_list_read_for_all_dirs (&error);
+ available_plugins_id_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ GNM_SLIST_FOREACH (available_plugins, GnmPlugin, plugin,
+ g_hash_table_insert (
+ available_plugins_id_hash,
+ (gpointer) gnm_plugin_get_id (plugin), plugin);
+ );
+ if (error != NULL) {
+ GNM_SLIST_PREPEND (error_list, error_info_new_str_with_details (
+ _("Errors while reading info about available plugins."), error));
+ }
+
+ /* get descriptors for all previously active plugins */
+ plugin_list = NULL;
+ GNM_SLIST_FOREACH (gnm_app_prefs->active_plugins, char, plugin_id,
+ GnmPlugin *plugin = plugins_get_plugin_by_id (plugin_id);
+ if (plugin != NULL)
+ GNM_SLIST_PREPEND (plugin_list, plugin);
+ );
+
+ /* get descriptors for new plugins */
+ if (gnm_app_prefs->activate_new_plugins)
+ g_hash_table_foreach (
+ plugin_file_state_dir_hash,
+ (GHFunc) ghf_collect_new_plugins,
+ &plugin_list);
+
+ plugin_list = g_slist_reverse (plugin_list);
+ plugin_db_activate_plugin_list (plugin_list, &error);
+ g_slist_free (plugin_list);
+ if (error != NULL) {
+ GNM_SLIST_PREPEND (error_list, error_info_new_str_with_details (
+ _("Errors while activating plugins."), error));
+ }
+
+ /* report initialization errors */
+ if (error_list != NULL) {
+ GNM_SLIST_REVERSE (error_list);
+ error = error_info_new_str_with_details_list (
+ _("Errors while initializing plugin system."),
+ error_list);
+
+ gnm_cmd_context_error_info (context, error);
+ error_info_free (error);
+ }
+
+ plugin_message (4, "plugins_init() time: %fs\n", gnm_time_counter_pop ());
+}
+
+static void
+ghf_collect_used_plugin_state_strings (gpointer key, gpointer value, gpointer user_data)
+{
+ PluginFileState *state = value;
+ GSList **strings = user_data;
+
+ if (state->age != PLUGIN_OLD_UNUSED) {
+ GNM_SLIST_PREPEND (*strings, plugin_file_state_as_string (state));
+ }
+}
+
+/**
+ * gnm_plugin_try_unref
+ *
+ * Unref plugin object if it is legal to destroy it. Destruction is
+ * not legal if a type or interface has been registered for it. "Once
+ * a GTypeModule is initialized, it must exist forever" - docs of
+ * g_type_module_unuse().
+ */
+static void
+gnm_plugin_try_unref (gpointer plugin)
+{
+ GTypeModule *module = G_TYPE_MODULE (plugin);
+
+ if (!module->type_infos && !module->interface_infos) {
+ g_object_unref (plugin);
+ }
+}
+
+/**
+ * plugins_shutdown:
+ *
+ * Shuts down the plugin subsystem. Call this function only once before
+ * exiting the application. Some plugins may be left active or in broken
+ * state, so calling plugins_init again will NOT work properly.
+ */
+void
+plugins_shutdown (void)
+{
+ GSList *active_list = NULL, *used_plugin_state_strings = NULL;
+ ErrorInfo *ignored_error;
+
+ /* save active plugins list */
+ GNM_SLIST_FOREACH (available_plugins, GnmPlugin, plugin,
+ if (gnm_plugin_is_active (plugin) &&
+ !plugin_db_is_plugin_marked_for_deactivation (plugin)) {
+ GNM_SLIST_PREPEND (active_list, (gpointer) gnm_plugin_get_id (plugin));
+ }
+ );
+ active_list = g_slist_reverse (active_list);
+ gnm_gconf_set_active_plugins (active_list);
+ g_slist_free (active_list);
+
+ if (plugins_marked_for_deactivation_hash != NULL) {
+ g_hash_table_destroy (plugins_marked_for_deactivation_hash);
+ }
+
+ /* deactivate all plugins */
+ plugin_db_deactivate_plugin_list (available_plugins, &ignored_error);
+ error_info_free (ignored_error);
+
+ /* update information stored in gconf database
+ * about known plugin.xml files and destroy hash table */
+ g_hash_table_foreach (
+ plugin_file_state_dir_hash,
+ ghf_collect_used_plugin_state_strings,
+ &used_plugin_state_strings);
+ if (plugin_file_state_hash_changed ||
+ g_hash_table_size (plugin_file_state_dir_hash) != g_slist_length (used_plugin_state_strings)) {
+ gnm_gconf_set_plugin_file_states (used_plugin_state_strings);
+ plugin_message (5, "Plugin cache changed\n");
+ } else
+ gnm_slist_free_custom (used_plugin_state_strings, g_free);
+
+ g_hash_table_destroy (plugin_file_state_dir_hash);
+ g_hash_table_destroy (loader_services);
+ g_hash_table_destroy (available_plugins_id_hash);
+ gnm_slist_free_custom (available_plugins, gnm_plugin_try_unref);
+
+ go_conf_sync ();
+}
+
+void
+plugin_message (gint level, const gchar *format, ...)
+{
+#ifdef PLUGIN_DEBUG
+ va_list args;
+
+ if (level <= PLUGIN_DEBUG) {
+ va_start (args, format);
+ vprintf (format, args);
+ va_end (args);
+ }
+#endif
+}
--- /dev/null
+++ lib/goffice/split/xml-io.h
@@ -0,0 +1,76 @@
+#ifndef GNUMERIC_XML_IO_H
+#define GNUMERIC_XML_IO_H
+
+#include <gdk/gdktypes.h>
+#include "gnumeric.h"
+#include "xml-io-version.h"
+//#include "file.h"
+#include <gsf/gsf-libxml.h>
+#include <libxml/tree.h>
+#include <libxml/xmlmemory.h>
+#include <goffice/utils/goffice-utils.h>
+
+struct _XmlParseContext {
+ xmlDocPtr doc; /* Xml document */
+ xmlNsPtr ns; /* Main name space */
+
+ Sheet *sheet; /* the associated sheet */
+ Workbook *wb; /* the associated workbook */
+ WorkbookView *wb_view;
+ IOContext *io_context;
+
+ GHashTable *style_table;/* old style styles compatibility */
+ GHashTable *expr_map; /*
+ * Emitted expressions with ref count > 1
+ * When writing this is map from expr pointer -> index
+ */
+ GPtrArray *shared_exprs;/*
+ * When reading this is a map from index -> expr pointer
+ */
+ GnumericXMLVersion version;
+
+ GnmExprConventions *exprconv;
+};
+
+XmlParseContext *xml_parse_ctx_new (xmlDoc *doc,
+ xmlNs *ns,
+ WorkbookView *wb_view);
+void xml_parse_ctx_destroy (XmlParseContext *ctxt);
+
+
+xmlNodePtr xml_write_style (XmlParseContext *ctxt, GnmStyle *style);
+
+xmlChar *xml_cellregion_write (WorkbookControl *context,
+ GnmCellRegion *cr, int *size);
+GnmCellRegion *xml_cellregion_read (WorkbookControl *context, Sheet *sheet,
+ guchar *buffer, int length);
+
+/* Some utility routines for setting attributes or content */
+xmlChar *xml_node_get_cstr (xmlNodePtr node, char const *name);
+void xml_node_set_cstr (xmlNodePtr node, char const *name, char const *val);
+gboolean xml_node_get_bool (xmlNodePtr node, char const *name, gboolean *result);
+void xml_node_set_bool (xmlNodePtr node, char const *name, gboolean val);
+gboolean xml_node_get_int (xmlNodePtr node, char const *name, int *result);
+void xml_node_set_int (xmlNodePtr node, char const *name, int val);
+gboolean xml_node_get_double (xmlNodePtr node, char const *name, double *result);
+void xml_node_set_double (xmlNodePtr node, char const *name, double val, int precision);
+gboolean xml_node_get_gocolor (xmlNodePtr node, char const *name, GOColor *result);
+void xml_node_set_gocolor (xmlNodePtr node, char const *name, GOColor val);
+GnmColor *xml_node_get_color (xmlNodePtr node, char const *name);
+void xml_node_set_color (xmlNodePtr node, char const *name, GnmColor const *color);
+
+xmlNodePtr xml_write_style (XmlParseContext *ctxt, GnmStyle *style);
+GnmStyle *xml_read_style (XmlParseContext *ctxt, xmlNodePtr tree);
+
+void xml_init (void);
+
+xmlNode *e_xml_get_child_by_name (xmlNode const *tree, char const *name);
+xmlNode *e_xml_get_child_by_name_no_lang (xmlNode const *tree, char const *name);
+xmlNode *e_xml_get_child_by_name_by_lang (xmlNode const *tree, char const *name);
+
+/* Gnumeric specific SAX utilities */
+void gnm_xml_out_add_color (GsfXMLOut *o, char const *id, GnmColor const *c);
+void gnm_xml_out_add_gocolor (GsfXMLOut *o, char const *id, GOColor c);
+void gnm_xml_out_add_cellpos (GsfXMLOut *o, char const *id, GnmCellPos const *p);
+
+#endif /* GNUMERIC_XML_IO_H */
--- /dev/null
+++ lib/goffice/split/gnumeric-gconf-priv.h
@@ -0,0 +1,119 @@
+#ifndef GNM_CONF_PRIV_H
+#define GNM_CONF_PRIV_H
+
+/*
+ * Note: This file must stay synchronized with the corresponding schema file!
+ *
+ * This file should only be included into gnumeric-gconf.c and
+ * dialogs/dialog-preferences.c
+ */
+
+
+/*
+ * schemas/gnumeric-dialogs.schemas
+ */
+#define FUNCTION_SELECT_GCONF_RECENT "/apps/gnumeric/functionselector/recentfunctions"
+#define FUNCTION_SELECT_GCONF_NUM_OF_RECENT "/apps/gnumeric/functionselector/num-of-recent"
+
+#define CONF_DEFAULT_FONT_DIR "/apps/gnumeric/core/defaultfont/"
+#define CONF_DEFAULT_FONT_NAME CONF_DEFAULT_FONT_DIR "name"
+#define CONF_DEFAULT_FONT_SIZE CONF_DEFAULT_FONT_DIR "size"
+#define CONF_DEFAULT_FONT_BOLD CONF_DEFAULT_FONT_DIR "bold"
+#define CONF_DEFAULT_FONT_ITALIC CONF_DEFAULT_FONT_DIR "italic"
+
+#define PLUGIN_GCONF_DIRECTORY "/apps/gnumeric/plugins"
+#define PLUGIN_GCONF_ACTIVATE_NEW PLUGIN_GCONF_DIRECTORY "/activate-new"
+#define PLUGIN_GCONF_ACTIVE PLUGIN_GCONF_DIRECTORY "/active"
+#define PLUGIN_GCONF_FILE_STATES PLUGIN_GCONF_DIRECTORY "/file-states"
+#define PLUGIN_GCONF_EXTRA_DIRS PLUGIN_GCONF_DIRECTORY "/extra-dirs"
+
+#define AUTOFORMAT_GCONF_DIRECTORY "/apps/gnumeric/autoformat"
+#define AUTOFORMAT_GCONF_EXTRA_DIRS AUTOFORMAT_GCONF_DIRECTORY "/extra-dirs"
+#define AUTOFORMAT_GCONF_SYS_DIR AUTOFORMAT_GCONF_DIRECTORY "/sys-dir"
+#define AUTOFORMAT_GCONF_USR_DIR AUTOFORMAT_GCONF_DIRECTORY "/usr-dir"
+
+#define PRINTSETUP_GCONF_DIRECTORY "/apps/gnumeric/printsetup"
+#define PRINTSETUP_GCONF_ALL_SHEETS PRINTSETUP_GCONF_DIRECTORY "/all-sheets"
+#define PRINTSETUP_GCONF_PRINTER_CONFIG PRINTSETUP_GCONF_DIRECTORY "/printer-config"
+#define PRINTSETUP_GCONF_HEADER PRINTSETUP_GCONF_DIRECTORY "/header"
+#define PRINTSETUP_GCONF_FOOTER PRINTSETUP_GCONF_DIRECTORY "/footer"
+#define PRINTSETUP_GCONF_HF_FONT_NAME PRINTSETUP_GCONF_DIRECTORY "/hf-font-name"
+#define PRINTSETUP_GCONF_HF_FONT_SIZE PRINTSETUP_GCONF_DIRECTORY "/hf-font-size"
+#define PRINTSETUP_GCONF_HF_FONT_BOLD PRINTSETUP_GCONF_DIRECTORY "/hf-font-bold"
+#define PRINTSETUP_GCONF_HF_FONT_ITALIC PRINTSETUP_GCONF_DIRECTORY "/hf-font-italic"
+#define PRINTSETUP_GCONF_CENTER_HORIZONTALLY PRINTSETUP_GCONF_DIRECTORY "/center-horizontally"
+#define PRINTSETUP_GCONF_CENTER_VERTICALLY PRINTSETUP_GCONF_DIRECTORY "/center-vertically"
+#define PRINTSETUP_GCONF_PRINT_GRID_LINES PRINTSETUP_GCONF_DIRECTORY "/print-grid-lines"
+#define PRINTSETUP_GCONF_EVEN_IF_ONLY_STYLES PRINTSETUP_GCONF_DIRECTORY "/print-even-if-only-styles"
+#define PRINTSETUP_GCONF_PRINT_BLACK_AND_WHITE PRINTSETUP_GCONF_DIRECTORY "/print-black-n-white"
+#define PRINTSETUP_GCONF_PRINT_TITLES PRINTSETUP_GCONF_DIRECTORY "/print-titles"
+#define PRINTSETUP_GCONF_RIGHT_THEN_DOWN PRINTSETUP_GCONF_DIRECTORY "/right-then-down"
+#define PRINTSETUP_GCONF_SCALE_PERCENTAGE PRINTSETUP_GCONF_DIRECTORY "/scale-percentage"
+#define PRINTSETUP_GCONF_SCALE_PERCENTAGE_VALUE PRINTSETUP_GCONF_DIRECTORY "/scale-percentage-value"
+#define PRINTSETUP_GCONF_SCALE_WIDTH PRINTSETUP_GCONF_DIRECTORY "/scale-width"
+#define PRINTSETUP_GCONF_SCALE_HEIGHT PRINTSETUP_GCONF_DIRECTORY "/scale-height"
+#define PRINTSETUP_GCONF_REPEAT_TOP PRINTSETUP_GCONF_DIRECTORY "/repeat-top"
+#define PRINTSETUP_GCONF_REPEAT_LEFT PRINTSETUP_GCONF_DIRECTORY "/repeat-left"
+#define PRINTSETUP_GCONF_MARGIN_TOP PRINTSETUP_GCONF_DIRECTORY "/margin-top"
+#define PRINTSETUP_GCONF_MARGIN_BOTTOM PRINTSETUP_GCONF_DIRECTORY "/margin-bottom"
+#define PRINTSETUP_GCONF_HEADER_FORMAT_LEFT PRINTSETUP_GCONF_DIRECTORY "/hf-left"
+#define PRINTSETUP_GCONF_HEADER_FORMAT_MIDDLE PRINTSETUP_GCONF_DIRECTORY "/hf-middle"
+#define PRINTSETUP_GCONF_HEADER_FORMAT_RIGHT PRINTSETUP_GCONF_DIRECTORY "/hf-right"
+
+#define DIALOGS_GCONF_DIRECTORY "/apps/gnumeric/dialogs"
+#define DIALOGS_GCONF_UNFOCUSED_RS DIALOGS_GCONF_DIRECTORY "/rs/unfocused"
+
+/*
+ * schemas/gnumeric-general.schemas
+ */
+
+#define GNM_CONF_UNDO_DIRECTORY "/apps/gnumeric/undo"
+#define GNM_CONF_UNDO_SIZE GNM_CONF_UNDO_DIRECTORY "/size"
+#define GNM_CONF_UNDO_MAXNUM GNM_CONF_UNDO_DIRECTORY "/maxnum"
+#define GNM_CONF_UNDO_SHOW_SHEET_NAME GNM_CONF_UNDO_DIRECTORY "/show_sheet_name"
+#define GNM_CONF_UNDO_MAX_DESCRIPTOR_WIDTH GNM_CONF_UNDO_DIRECTORY "/max_descriptor_width"
+
+#define GNM_CONF_FONT_DIRECTORY "/apps/gnumeric/core/defaultfont"
+#define GNM_CONF_FONT_NAME GNM_CONF_FONT_DIRECTORY "/name"
+#define GNM_CONF_FONT_SIZE GNM_CONF_FONT_DIRECTORY "/size"
+#define GNM_CONF_FONT_BOLD GNM_CONF_FONT_DIRECTORY "/bold"
+#define GNM_CONF_FONT_ITALIC GNM_CONF_FONT_DIRECTORY "/italic"
+
+#define GNM_CONF_FILE_DIRECTORY "/apps/gnumeric/core/file"
+#define GNM_CONF_FILE_HISTORY_N GNM_CONF_FILE_DIRECTORY "/history/n"
+#define GNM_CONF_FILE_HISTORY_FILES GNM_CONF_FILE_DIRECTORY "/history/files"
+#define GNM_CONF_FILE_OVERWRITE_DEFAULT GNM_CONF_FILE_DIRECTORY "/save/def-overwrite"
+#define GNM_CONF_FILE_SINGLE_SHEET_SAVE GNM_CONF_FILE_DIRECTORY "/save/single_sheet"
+
+#define GNM_CONF_WORKBOOK_NSHEETS "/apps/gnumeric/core/workbook/n-sheet"
+
+#define GNM_CONF_GUI_DIRECTORY "/apps/gnumeric/core/gui"
+#define GNM_CONF_GUI_RES_H GNM_CONF_GUI_DIRECTORY "/screen/horizontaldpi"
+#define GNM_CONF_GUI_RES_V GNM_CONF_GUI_DIRECTORY "/screen/verticaldpi"
+#define GNM_CONF_GUI_ED_AUTOCOMPLETE GNM_CONF_GUI_DIRECTORY "/editing/autocomplete"
+#define GNM_CONF_GUI_ED_TRANSITION_KEYS GNM_CONF_GUI_DIRECTORY "/editing/transitionkeys"
+#define GNM_CONF_GUI_ED_LIVESCROLLING GNM_CONF_GUI_DIRECTORY "/editing/livescrolling"
+#define GNM_CONF_GUI_ED_RECALC_LAG GNM_CONF_GUI_DIRECTORY "/editing/recalclag"
+#define GNM_CONF_GUI_WINDOW_X GNM_CONF_GUI_DIRECTORY "/window/x"
+#define GNM_CONF_GUI_WINDOW_Y GNM_CONF_GUI_DIRECTORY "/window/y"
+#define GNM_CONF_GUI_ZOOM GNM_CONF_GUI_DIRECTORY "/window/zoom"
+
+#define GNM_CONF_XML_COMPRESSION "/apps/gnumeric/core/xml/compression-level"
+
+#define GNM_CONF_SORT_DIRECTORY "/apps/gnumeric/core/sort"
+#define GNM_CONF_SORT_DEFAULT_BY_CASE GNM_CONF_SORT_DIRECTORY "/default/by-case"
+#define GNM_CONF_SORT_DEFAULT_RETAIN_FORM GNM_CONF_SORT_DIRECTORY "/default/retain-formats"
+#define GNM_CONF_SORT_DEFAULT_ASCENDING GNM_CONF_SORT_DIRECTORY "/default/ascending"
+#define GNM_CONF_SORT_DIALOG_MAX_INITIAL GNM_CONF_SORT_DIRECTORY "/dialog/max-initial-clauses"
+
+#define GNM_CONF_CUTANDPASTE_DIRECTORY "/apps/gnumeric/cut-and-paste"
+#define GNM_CONF_CUTANDPASTE_PREFER_CLIPBOARD GNM_CONF_CUTANDPASTE_DIRECTORY "/prefer-clipboard"
+
+/*
+ * schemas/gnumeric-plugins.schemas
+ */
+
+#define PLUGIN_GCONF_LATEX "/apps/gnumeric/plugin/latex"
+#define PLUGIN_GCONF_LATEX_USE_UTF8 PLUGIN_GCONF_LATEX "/use-utf8"
+
+#endif /* GNM_CONF_PRIV_H */
--- /dev/null
+++ lib/goffice/split/validation.h
@@ -0,0 +1,67 @@
+#ifndef GNUMERIC_VALIDATION_H
+#define GNUMERIC_VALIDATION_H
+
+#include "gnumeric.h"
+#include "str.h"
+
+typedef enum {
+ VALIDATION_STATUS_VALID, /* things validate */
+ VALIDATION_STATUS_INVALID_DISCARD, /* things do not validate and should be discarded */
+ VALIDATION_STATUS_INVALID_EDIT /* things do not validate and editing should continue */
+} ValidationStatus;
+typedef enum {
+ VALIDATION_STYLE_NONE,
+ VALIDATION_STYLE_STOP,
+ VALIDATION_STYLE_WARNING,
+ VALIDATION_STYLE_INFO,
+ VALIDATION_STYLE_PARSE_ERROR
+} ValidationStyle;
+typedef enum {
+ VALIDATION_TYPE_ANY,
+ VALIDATION_TYPE_AS_INT,
+ VALIDATION_TYPE_AS_NUMBER,
+ VALIDATION_TYPE_IN_LIST,
+ VALIDATION_TYPE_AS_DATE,
+ VALIDATION_TYPE_AS_TIME,
+ VALIDATION_TYPE_TEXT_LENGTH,
+ VALIDATION_TYPE_CUSTOM
+} ValidationType;
+typedef enum {
+ VALIDATION_OP_NONE = -1,
+ VALIDATION_OP_BETWEEN,
+ VALIDATION_OP_NOT_BETWEEN,
+ VALIDATION_OP_EQUAL,
+ VALIDATION_OP_NOT_EQUAL,
+ VALIDATION_OP_GT,
+ VALIDATION_OP_LT,
+ VALIDATION_OP_GTE,
+ VALIDATION_OP_LTE
+} ValidationOp;
+
+struct _GnmValidation {
+ int ref_count;
+
+ GnmString *title;
+ GnmString *msg;
+ GnmExpr const *expr [2];
+ ValidationStyle style;
+ ValidationType type;
+ ValidationOp op;
+ gboolean allow_blank;
+ gboolean use_dropdown;
+};
+
+GnmValidation *validation_new (ValidationStyle style,
+ ValidationType type,
+ ValidationOp op,
+ char const *title, char const *msg,
+ GnmExpr const *expr0, GnmExpr const *expr1,
+ gboolean allow_blank, gboolean use_dropdown);
+
+void validation_ref (GnmValidation *v);
+void validation_unref (GnmValidation *v);
+ValidationStatus validation_eval (WorkbookControl *wbc, GnmStyle const *mstyle,
+ Sheet *sheet, GnmCellPos const *pos,
+ gboolean *showed_dialog);
+
+#endif /* GNUMERIC_VALIDATION_H */
--- /dev/null
+++ lib/goffice/split/validation.c
@@ -0,0 +1,318 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * validation.c: Implementation of validation.
+ *
+ * Copyright (C) Jody Goldberg <jody at gnome.org>
+ *
+ * based on work by
+ * Almer S. Tigelaar <almer at gnome.org>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gnumeric-config.h>
+#include <glib/gi18n.h>
+#include "gnumeric.h"
+#include "numbers.h"
+#include "mathfunc.h"
+#include "validation.h"
+#include "expr.h"
+#include "mstyle.h"
+#include "workbook.h"
+#include "sheet.h"
+#include "cell.h"
+#include "value.h"
+#include "number-match.h"
+#include "workbook-control.h"
+#include "parse-util.h"
+
+#include <math.h>
+#include <string.h>
+
+/**
+ * validation_new :
+ *
+ * @title : will be copied.
+ * @msg : will be copied.
+ * @expr0 : absorb the reference to the expression.
+ * @expr1 : absorb the reference to the expression.
+ */
+GnmValidation *
+validation_new (ValidationStyle style,
+ ValidationType type,
+ ValidationOp op,
+ char const *title, char const *msg,
+ GnmExpr const *expr0, GnmExpr const *expr1,
+ gboolean allow_blank, gboolean use_dropdown)
+{
+ GnmValidation *v;
+
+ if (type == VALIDATION_TYPE_CUSTOM && op != VALIDATION_OP_NONE) {
+ /* This can happen if an .xls file was saved as a .gnumeric. */
+ g_warning ("VALIDATION_TYPE_CUSTOM needs to go with VALIDATION_OP_NONE. Fixing.");
+ op = VALIDATION_OP_NONE;
+ }
+
+ v = g_new0 (GnmValidation, 1);
+ v->ref_count = 1;
+
+ v->title = title ? gnm_string_get (title) : NULL;
+ v->msg = msg ? gnm_string_get (msg) : NULL;
+ v->expr[0] = expr0;
+ v->expr[1] = expr1;
+ v->style = style;
+ v->type = type;
+ v->op = op;
+ v->allow_blank = (allow_blank != FALSE);
+ v->use_dropdown = (use_dropdown != FALSE);
+
+ return v;
+}
+
+void
+validation_ref (GnmValidation *v)
+{
+ g_return_if_fail (v != NULL);
+ v->ref_count++;
+}
+
+void
+validation_unref (GnmValidation *v)
+{
+ g_return_if_fail (v != NULL);
+
+ v->ref_count--;
+
+ if (v->ref_count < 1) {
+ int i;
+
+ if (v->title != NULL) {
+ gnm_string_unref (v->title);
+ v->title = NULL;
+ }
+ if (v->msg != NULL) {
+ gnm_string_unref (v->msg);
+ v->msg = NULL;
+ }
+ for (i = 0 ; i < 2 ; i++)
+ if (v->expr[i] != NULL) {
+ gnm_expr_unref (v->expr[i]);
+ v->expr[i] = NULL;
+ }
+ g_free (v);
+ }
+}
+
+/**
+ * validation_eval:
+ *
+ * Either pass @expr or @val.
+ * The parameters will be validated against the
+ * validation set in the GnmStyle if applicable.
+ **/
+ValidationStatus
+validation_eval (WorkbookControl *wbc, GnmStyle const *mstyle,
+ Sheet *sheet, GnmCellPos const *pos, gboolean *showed_dialog)
+{
+ GnmValidation *v;
+ GnmCell *cell;
+ char *msg = NULL;
+ gboolean allocated_msg = FALSE;
+ ValidationStatus result;
+
+ v = mstyle_get_validation (mstyle);
+ if (v == NULL)
+ return VALIDATION_STATUS_VALID;
+
+ if (v->style == VALIDATION_TYPE_ANY)
+ return VALIDATION_STATUS_VALID;
+
+ cell = sheet_cell_get (sheet, pos->col, pos->row);
+ if (cell != NULL)
+ dependent_eval (CELL_TO_DEP (cell));
+
+ if (cell_is_empty (cell)) {
+ if (v->allow_blank)
+ return VALIDATION_STATUS_VALID;
+ msg = g_strdup_printf (_("Cell %s is not permitted to be blank"),
+ cell_name (cell));
+ } else {
+ GnmExpr const *val_expr = NULL, *expr = NULL;
+ GnmValue *val = cell->value;
+
+ switch (v->type) {
+ case VALIDATION_TYPE_ANY :
+ return VALIDATION_STATUS_VALID;
+
+ case VALIDATION_TYPE_AS_INT :
+ case VALIDATION_TYPE_AS_NUMBER :
+ case VALIDATION_TYPE_AS_DATE : /* What the hell does this do */
+ case VALIDATION_TYPE_AS_TIME : { /* What the hell does this do */
+ GnmValue *res = NULL;
+ /* we know it is not empty */
+ if (val->type == VALUE_ERROR) {
+ msg = g_strdup_printf (_("'%s' is an error"),
+ value_peek_string (val));
+ break;
+ } else if (val->type == VALUE_STRING) {
+ char const *s = value_peek_string (val);
+ res = format_match_number (s, NULL,
+ workbook_date_conv (sheet->workbook));
+ if (res == NULL) {
+ char const *fmt;
+ /* FIXME what else is needed */
+ if (v->type == VALIDATION_TYPE_AS_DATE) {
+ fmt = N_("'%s' is not a valid date");
+ } else if (v->type == VALIDATION_TYPE_AS_TIME) {
+ fmt = N_("'%s' is not a valid time");
+ } else
+ fmt = N_("'%s' is not a number");
+ msg = g_strdup_printf (_(fmt), s);
+ break;
+ }
+ } else
+ res = value_dup (val);
+
+ if (v->type == VALIDATION_TYPE_AS_INT &&
+ res != NULL && res->type == VALUE_FLOAT) {
+ gnm_float f = value_get_as_float (res);
+ gboolean isint = gnumabs (f - gnumeric_fake_round (f)) < 1e-10;
+ if (!isint) {
+ char const *valstr = value_peek_string (val);
+ msg = g_strdup_printf (_("'%s' is not an integer"), valstr);
+ break;
+ }
+ }
+
+ val_expr = gnm_expr_new_constant (res);
+ break;
+ }
+
+ case VALIDATION_TYPE_IN_LIST :
+#warning TODO
+ return VALIDATION_STATUS_VALID;
+
+ case VALIDATION_TYPE_TEXT_LENGTH :
+ /* XL appears to use a very basic value -> string mapping that
+ * ignores formatting.
+ * eg len (12/13/01) == len (37238) = 5
+ * This seems wrong for
+ */
+ val_expr = gnm_expr_new_constant (
+ value_new_int (strlen (value_peek_string (val))));
+ break;
+
+ case VALIDATION_TYPE_CUSTOM :
+ expr = v->expr[0];
+ if (expr == NULL)
+ return VALIDATION_STATUS_VALID;
+ gnm_expr_ref (expr);
+ break;
+ }
+
+ if (msg == NULL && expr == NULL) {
+ GnmExprOp op;
+
+ g_return_val_if_fail (val_expr != NULL, VALIDATION_STATUS_VALID);
+
+ switch (v->op) {
+ case VALIDATION_OP_EQUAL : op = GNM_EXPR_OP_EQUAL; break;
+ case VALIDATION_OP_NOT_EQUAL : op = GNM_EXPR_OP_NOT_EQUAL; break;
+ case VALIDATION_OP_GT : op = GNM_EXPR_OP_GT; break;
+ case VALIDATION_OP_NOT_BETWEEN :
+ case VALIDATION_OP_LT : op = GNM_EXPR_OP_LT; break;
+ case VALIDATION_OP_BETWEEN :
+ case VALIDATION_OP_GTE : op = GNM_EXPR_OP_GTE; break;
+ case VALIDATION_OP_LTE : op = GNM_EXPR_OP_LTE; break;
+ default :
+ g_warning ("Invalid validation operator %d", v->op);
+ return VALIDATION_STATUS_VALID;
+ }
+
+ if (v->expr [0] == NULL)
+ return VALIDATION_STATUS_VALID;
+
+ gnm_expr_ref (v->expr[0]);
+ expr = gnm_expr_new_binary (val_expr, op, v->expr[0]);
+ }
+
+ if (expr != NULL) {
+ GnmParsePos pp;
+ GnmEvalPos ep;
+ char *expr_str;
+ GnmValue *val;
+ gboolean dummy, valid;
+
+ eval_pos_init_cell (&ep, cell);
+ val = gnm_expr_eval (expr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
+ valid = value_get_as_bool (val, &dummy);
+ value_release (val);
+
+ if (valid && v->op != VALIDATION_OP_BETWEEN) {
+ gnm_expr_unref (expr);
+ return VALIDATION_STATUS_VALID;
+ }
+
+ if ((v->op == VALIDATION_OP_BETWEEN && valid) ||
+ v->op == VALIDATION_OP_NOT_BETWEEN) {
+ g_return_val_if_fail (v->expr[1] != NULL, VALIDATION_STATUS_VALID);
+
+ gnm_expr_ref (val_expr);
+ gnm_expr_ref (v->expr[1]);
+ gnm_expr_unref (expr);
+ expr = gnm_expr_new_binary (val_expr,
+ (v->op == VALIDATION_OP_BETWEEN) ? GNM_EXPR_OP_LTE : GNM_EXPR_OP_GT,
+ v->expr[1]);
+ val = gnm_expr_eval (expr, &ep, GNM_EXPR_EVAL_SCALAR_NON_EMPTY);
+ valid = value_get_as_bool (val, &dummy);
+ value_release (val);
+ if (valid) {
+ gnm_expr_unref (expr);
+ return VALIDATION_STATUS_VALID;
+ }
+ }
+
+ expr_str = gnm_expr_as_string (expr,
+ parse_pos_init_evalpos (&pp, &ep),
+ gnm_expr_conventions_default);
+ msg = g_strdup_printf (_("%s is not true."), expr_str);
+ g_free (expr_str);
+ gnm_expr_unref (expr);
+ }
+ }
+
+ if (v->msg != NULL && v->msg->str[0] != '\0') {
+ if (msg != NULL)
+ g_free (msg);
+ msg = v->msg->str;
+ } else {
+ if (msg != NULL)
+ allocated_msg = TRUE;
+ else
+ msg = _("That value is invalid.\n"
+ "Restrictions have been placed on this cell's contents.");
+ }
+
+ if (showed_dialog != NULL)
+ *showed_dialog = TRUE;
+ result = wb_control_validation_msg (wbc, v->style,
+ (v->title != NULL && v->title->str[0] != '\0')
+ ? v->title->str
+ : _("Gnumeric: Validation"),
+ msg);
+ if (allocated_msg)
+ g_free (msg);
+ return result;
+}
--- /dev/null
+++ lib/goffice/split/workbook-control-gui.h
@@ -0,0 +1,49 @@
+#ifndef GNUMERIC_WORKBOOK_CONTROL_GUI_H
+#define GNUMERIC_WORKBOOK_CONTROL_GUI_H
+
+//#include "workbook-control.h"
+#include "gnumeric.h"
+
+#include "gui-gnumeric.h"
+#include <gtk/gtkwindow.h>
+#include <gtk/gtktoggleaction.h>
+
+#define WORKBOOK_CONTROL_GUI_TYPE (workbook_control_gui_get_type ())
+#define WORKBOOK_CONTROL_GUI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WORKBOOK_CONTROL_GUI_TYPE, WorkbookControlGUI))
+#define IS_WORKBOOK_CONTROL_GUI(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), WORKBOOK_CONTROL_GUI_TYPE))
+
+GType workbook_control_gui_get_type (void);
+WorkbookControl *workbook_control_gui_new (WorkbookView *optional_view,
+ Workbook *optional_wb,
+ GdkScreen *optional_screen);
+
+int wbcg_sheet_to_page_index (WorkbookControlGUI *wbcg, Sheet *sheet,
+ SheetControlGUI **res);
+GtkWindow *wbcg_toplevel (WorkbookControlGUI *wbcg);
+void wbcg_set_transient (WorkbookControlGUI *wbcg,
+ GtkWindow *window);
+SheetControlGUI *wbcg_cur_scg (WorkbookControlGUI *wbcg);
+Sheet *wbcg_cur_sheet (WorkbookControlGUI *wbcg);
+Sheet *wbcg_focus_cur_scg (WorkbookControlGUI *wbcg);
+
+gboolean wbcg_ui_update_begin (WorkbookControlGUI *wbcg);
+void wbcg_ui_update_end (WorkbookControlGUI *wbcg);
+
+gboolean wbcg_rangesel_possible (WorkbookControlGUI const *wbcg);
+gboolean wbcg_is_editing (WorkbookControlGUI const *wbcg);
+void wbcg_autosave_cancel (WorkbookControlGUI *wbcg);
+void wbcg_autosave_set (WorkbookControlGUI *wbcg,
+ int minutes, gboolean prompt);
+void wbcg_set_status_text (WorkbookControlGUI *wbcg,
+ char const *text);
+void wbcg_toggle_visibility (WorkbookControlGUI *wbcg,
+ GtkToggleAction *action);
+void wbcg_copy_toolbar_visibility (WorkbookControlGUI *new_wbcg,
+ WorkbookControlGUI *wbcg);
+
+void wbcg_toggle_end_mode (WorkbookControlGUI *wbcg);
+void wbcg_set_end_mode (WorkbookControlGUI *wbcg, gboolean flag);
+
+PangoFontDescription *wbcg_get_font_desc (WorkbookControlGUI *wbcg);
+
+#endif /* GNUMERIC_WORKBOOK_CONTROL_GUI_H */
--- /dev/null
+++ lib/goffice/split/style-color.c
@@ -0,0 +1,296 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * color.c: Color allocation on the Gnumeric spreadsheet
+ *
+ * Author:
+ * Miguel de Icaza (miguel at kernel.org)
+ *
+ */
+#include <config.h>
+#include "gnumeric.h"
+#include "style-color.h"
+#include "style-border.h"
+#include <gtk/gtk.h>
+#include <stdio.h>
+
+/* Public _unallocated_ colours, i.e., no valid .pixel. */
+GdkColor gs_black = { 0, 0x0000, 0x0000, 0x0000 }; /* "Black" */
+GdkColor gs_white = { 0, 0xffff, 0xffff, 0xffff }; /* "White" */
+GdkColor gs_yellow = { 0, 0xffff, 0xffff, 0xe0e0 }; /* "LightYellow" */
+GdkColor gs_lavender = { 0, 0xe6e6, 0xe6e6, 0xfafa }; /* "lavender" */
+GdkColor gs_dark_gray = { 0, 0x3333, 0x3333, 0x3333 }; /* "gray20" */
+GdkColor gs_light_gray = { 0, 0xc7c7, 0xc7c7, 0xc7c7 }; /* "gray78" */
+
+static GHashTable *style_color_hash;
+static GnmColor *sc_black;
+static GnmColor *sc_white;
+static GnmColor *sc_grid;
+
+GnmColor *
+style_color_new_name (char const *name)
+{
+ GdkColor c;
+
+ gdk_color_parse (name, &c);
+ return style_color_new (c.red, c.green, c.blue);
+}
+
+static GnmColor *
+style_color_new_uninterned (gushort red, gushort green, gushort blue,
+ gboolean is_auto)
+{
+ GnmColor *sc = g_new (GnmColor, 1);
+
+ sc->color.red = red;
+ sc->color.green = green;
+ sc->color.blue = blue;
+ sc->color.pixel = gs_white.pixel;
+ sc->name = NULL;
+ sc->is_auto = is_auto;
+
+ /* Make a contrasting selection color with an alpha of .5 */
+ red += (gs_lavender.red - red)/2;
+ green += (gs_lavender.green - green)/2;
+ blue += (gs_lavender.blue - blue)/2;
+ sc->selected_color.red = red;
+ sc->selected_color.green = green;
+ sc->selected_color.blue = blue;
+ sc->selected_color.pixel = gs_white.pixel;
+
+ sc->ref_count = 1;
+
+ return sc;
+}
+
+GnmColor *
+style_color_new (gushort red, gushort green, gushort blue)
+{
+ GnmColor *sc;
+ GnmColor key;
+
+ key.color.red = red;
+ key.color.green = green;
+ key.color.blue = blue;
+ key.is_auto = FALSE;
+
+ sc = g_hash_table_lookup (style_color_hash, &key);
+ if (!sc) {
+ sc = style_color_new_uninterned (red, green, blue, FALSE);
+ g_hash_table_insert (style_color_hash, sc, sc);
+ } else
+ sc->ref_count++;
+
+ return sc;
+}
+
+GnmColor *
+style_color_new_pango (PangoColor *c)
+{
+ return style_color_new (c->red, c->green, c->blue);
+}
+
+/* scale 8 bit/color -> 16 bit/color by cloning */
+GnmColor *
+style_color_new_i8 (guint8 red, guint8 green, guint8 blue)
+{
+ gushort red16, green16, blue16;
+
+ red16 = ((gushort) red) << 8 | red;
+ green16 = ((gushort) green) << 8 | green;
+ blue16 = ((gushort) blue) << 8 | blue;
+
+ return style_color_new (red16, green16, blue16);
+}
+GnmColor *
+style_color_new_go (GOColor c)
+{
+ return style_color_new_i8 (
+ UINT_RGBA_R (c), UINT_RGBA_G (c), UINT_RGBA_B (c));
+}
+
+GnmColor *
+style_color_black (void)
+{
+ if (!sc_black)
+ sc_black = style_color_new (0, 0, 0);
+ return style_color_ref (sc_black);
+}
+
+GnmColor *
+style_color_white (void)
+{
+ if (!sc_white)
+ sc_white = style_color_new (0xffff, 0xffff, 0xffff);
+ return style_color_ref (sc_white);
+}
+
+GnmColor *
+style_color_grid (void)
+{
+ if (!sc_grid)
+ sc_grid = style_color_new (0xc7c7, 0xc7c7, 0xc7c7);
+ return style_color_ref (sc_grid);
+}
+
+/**
+ * Support for Excel auto-colors.
+ */
+
+/**
+ * Always black, as far as we know.
+ */
+GnmColor *
+style_color_auto_font (void)
+{
+ static GnmColor *color = NULL;
+
+ if (!color)
+ color = style_color_new_uninterned (0, 0, 0, TRUE);
+ return style_color_ref (color);
+}
+
+/**
+ * Always white, as far as we know.
+ */
+GnmColor *
+style_color_auto_back (void)
+{
+ static GnmColor *color = NULL;
+
+ if (!color)
+ color = style_color_new_uninterned (0xffff, 0xffff, 0xffff,
+ TRUE);
+ return style_color_ref (color);
+}
+
+/**
+ * Normally black, but follows grid color if so told.
+ */
+GnmColor *
+style_color_auto_pattern (void)
+{
+ static GnmColor *color = NULL;
+
+ if (!color)
+ color = style_color_new_uninterned (0, 0, 0, TRUE);
+ return style_color_ref (color);
+}
+
+GnmColor *
+style_color_ref (GnmColor *sc)
+{
+ if (sc != NULL)
+ sc->ref_count++;
+
+ return sc;
+}
+
+void
+style_color_unref (GnmColor *sc)
+{
+ if (sc == NULL)
+ return;
+
+ g_return_if_fail (sc->ref_count > 0);
+
+ sc->ref_count--;
+ if (sc->ref_count != 0)
+ return;
+
+ /*
+ * There is no need to deallocate colors, as they come from
+ * the GDK Color Context
+ */
+ g_hash_table_remove (style_color_hash, sc);
+ g_free (sc);
+}
+
+gint
+style_color_equal (const GnmColor *k1, const GnmColor *k2)
+{
+ if (k1->color.red == k2->color.red &&
+ k1->color.green == k2->color.green &&
+ k1->color.blue == k2->color.blue &&
+ k1->is_auto == k2->is_auto)
+ return 1;
+
+ return 0;
+}
+
+static guint
+color_hash (gconstpointer v)
+{
+ const GnmColor *k = (const GnmColor *)v;
+
+ return (k->color.red << 16) ^ (k->color.green << 8) ^ (k->color.blue << 0) ^
+ (k->is_auto);
+}
+
+void
+gnumeric_color_init (void)
+{
+ GdkColor error;
+
+ gdk_color_parse ("cyan", &error);
+ if (gdk_screen_get_default () != NULL) {
+ /*
+ * Make sure we can see bogus attempt at getting the pixel
+ * value. This is, by nature, not multi-head safe.
+ */
+ gdk_rgb_find_color (
+ gdk_screen_get_default_colormap (
+ gdk_screen_get_default ()),
+ &error);
+ } else
+ error.pixel = 0;
+
+ gs_black.pixel = error.pixel;
+ gs_white.pixel = error.pixel;
+ gs_yellow.pixel = error.pixel;
+ gs_lavender.pixel = error.pixel;
+ gs_dark_gray.pixel = error.pixel;
+ gs_light_gray.pixel = error.pixel;
+
+ style_color_hash = g_hash_table_new (color_hash,
+ (GEqualFunc) style_color_equal);
+}
+
+static void
+cb_color_leak (gpointer key, gpointer value, gpointer user_data)
+{
+ GnmColor *color = value;
+
+ fprintf (stderr, "Leaking style-color at %p [%04x:%04x:%04x].\n",
+ color, color->color.red, color->color.green, color->color.blue);
+}
+
+void
+gnumeric_color_shutdown (void)
+{
+ /*
+ * FIXME: this doesn't really belong here, but style-border.c isn't
+ * able to clean itself up yet.
+ */
+ GnmBorder *none = style_border_none ();
+ style_color_unref (none->color);
+ none->color = NULL;
+
+ if (sc_black) {
+ style_color_unref (sc_black);
+ sc_black = NULL;
+ }
+
+ if (sc_white) {
+ style_color_unref (sc_white);
+ sc_white = NULL;
+ }
+
+ if (sc_grid) {
+ style_color_unref (sc_grid);
+ sc_grid = NULL;
+ }
+
+ g_hash_table_foreach (style_color_hash, cb_color_leak, NULL);
+ g_hash_table_destroy (style_color_hash);
+ style_color_hash = NULL;
+}
--- /dev/null
+++ lib/goffice/split/gnumeric-gconf.c
@@ -0,0 +1,1174 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gnumeric-gconf.c:
+ *
+ *
+ * Author:
+ * Andreas J. Guelzow <aguelzow at taliesin.ca>
+ *
+ * (C) Copyright 2002-2004 Andreas J. Guelzow <aguelzow at taliesin.ca>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+#include <gnumeric.h>
+#include "application.h"
+#include "gnumeric-gconf.h"
+#include "gnumeric-gconf-priv.h"
+#include "gutils.h"
+#include "mstyle.h"
+
+static GnmAppPrefs prefs;
+GnmAppPrefs const *gnm_app_prefs = &prefs;
+
+#ifdef WITH_GNOME
+#include <format.h>
+#include <value.h>
+#include <number-match.h>
+#include <gconf/gconf-client.h>
+
+static GConfClient *gconf_client = NULL;
+static GConfClient *
+gnm_app_get_gconf_client (void)
+{
+ if (!gconf_client) {
+ gconf_client = gconf_client_get_default ();
+ gconf_client_add_dir (gconf_client, "/apps/gnumeric",
+ GCONF_CLIENT_PRELOAD_RECURSIVE,
+ NULL);
+ }
+ return gconf_client;
+}
+void
+go_conf_sync (void)
+{
+ gconf_client_suggest_sync (gnm_app_get_gconf_client (), NULL);
+}
+
+void
+go_conf_set_bool (char const *key, gboolean val)
+{
+ gconf_client_set_bool (gnm_app_get_gconf_client (), key, val, NULL);
+}
+void
+go_conf_set_int (char const *key, gint val)
+{
+ gconf_client_set_int (gnm_app_get_gconf_client (), key, val, NULL);
+}
+void
+go_conf_set_double (char const *key, gnm_float val)
+{
+ gconf_client_set_float (gnm_app_get_gconf_client (), key, val, NULL);
+}
+void
+go_conf_set_string (char const *key, char const *str)
+{
+ gconf_client_set_string (gnm_app_get_gconf_client (), key, str, NULL);
+}
+void
+go_conf_set_str_list (char const *key, GSList *list)
+{
+ gconf_client_set_list (gnm_app_get_gconf_client (),
+ key, GCONF_VALUE_STRING, list, NULL);
+}
+
+static GConfValue *
+go_conf_get (char const *key, GConfValueType t)
+{
+ GError *err = NULL;
+ GConfValue *val = gconf_client_get (gnm_app_get_gconf_client (), key, &err);
+
+ if (err != NULL) {
+ g_warning ("Unable to load key '%s' : because %s",
+ key, err->message);
+ g_error_free (err);
+ return NULL;
+ }
+ if (val == NULL) {
+ g_warning ("Unable to load key '%s'", key);
+ return NULL;
+ }
+
+ if (val->type != t) {
+#if 1 /* gconf_value_type_to_string is internal */
+ g_warning ("Expected `%d' got `%d' for key %s",
+ t, val->type, key);
+#else
+ g_warning ("Expected `%s' got `%s' for key %s",
+ gconf_value_type_to_string (t),
+ gconf_value_type_to_string (val->type),
+ key);
+#endif
+ gconf_value_free (val);
+ return NULL;
+ }
+
+ return val;
+}
+gboolean
+go_conf_load_bool (char const *key, gboolean default_val)
+{
+ gboolean res;
+ GConfValue *val = go_conf_get (key, GCONF_VALUE_BOOL);
+
+ if (val != NULL) {
+ res = gconf_value_get_bool (val);
+ gconf_value_free (val);
+ } else {
+ g_warning ("Using default value '%s'", default_val ? "true" : "false");
+ return default_val;
+ }
+ return res;
+}
+
+int
+go_conf_load_int (char const *key, int minima, int maxima, int default_val)
+{
+ int res = -1;
+ GConfValue *val = go_conf_get (key, GCONF_VALUE_INT);
+
+ if (val != NULL) {
+ res = gconf_value_get_int (val);
+ gconf_value_free (val);
+ if (res < minima || maxima < res) {
+ g_warning ("Invalid value '%d' for %s. If should be >= %d and <= %d",
+ res, key, minima, maxima);
+ val = NULL;
+ }
+ }
+ if (val == NULL) {
+ g_warning ("Using default value '%d'", default_val);
+ return default_val;
+ }
+ return res;
+}
+
+double
+go_conf_load_double (char const *key,
+ double minima, double maxima, double default_val)
+{
+ double res = -1;
+ GConfValue *val = go_conf_get (key, GCONF_VALUE_FLOAT);
+
+ if (val != NULL) {
+ res = gconf_value_get_float (val);
+ gconf_value_free (val);
+ if (res < minima || maxima < res) {
+ g_warning ("Invalid value '%g' for %s. If should be >= %g and <= %g",
+ res, key, minima, maxima);
+ val = NULL;
+ }
+ }
+ if (val == NULL) {
+ g_warning ("Using default value '%g'", default_val);
+ return default_val;
+ }
+ return res;
+}
+char *
+go_conf_load_string (char const *key)
+{
+ return gconf_client_get_string (gnm_app_get_gconf_client (), key, NULL);
+}
+GSList *
+go_conf_load_str_list (char const *key)
+{
+ return gconf_client_get_list (gnm_app_get_gconf_client (),
+ key, GCONF_VALUE_STRING, NULL);
+}
+
+static GConfSchema *
+get_schema (char const *key)
+{
+ char *schema_key = g_strconcat ("/schemas", key, NULL);
+ GConfSchema *schema = gconf_client_get_schema (
+ gnm_app_get_gconf_client (), schema_key, NULL);
+ g_free (schema_key);
+ return schema;
+}
+char *
+go_conf_get_short_desc (char const *key)
+{
+ GConfSchema *schema = get_schema (key);
+
+ if (schema != NULL) {
+ char *desc = g_strdup (gconf_schema_get_short_desc (schema));
+ gconf_schema_free (schema);
+ return desc;
+ }
+ return NULL;
+}
+char *
+go_conf_get_long_desc (char const *key)
+{
+ GConfSchema *schema = get_schema (key);
+
+ if (schema != NULL) {
+ char *desc = g_strdup (gconf_schema_get_long_desc (schema));
+ gconf_schema_free (schema);
+ return desc;
+ }
+ return NULL;
+}
+
+GType
+go_conf_get_type (char const *key)
+{
+ GConfSchema *schema = get_schema (key);
+ GType t;
+
+ switch (gconf_schema_get_type (schema)) {
+ case GCONF_VALUE_STRING: t = G_TYPE_STRING; break;
+ case GCONF_VALUE_FLOAT: t = G_TYPE_FLOAT; break;
+ case GCONF_VALUE_INT: t = G_TYPE_INT; break;
+ case GCONF_VALUE_BOOL: t = G_TYPE_BOOLEAN; break;
+ default :
+ t = G_TYPE_NONE;
+ }
+
+ if (schema != NULL)
+ gconf_schema_free (schema);
+ return t;
+}
+
+char *
+go_conf_get_value_as_str (char const *key)
+{
+ char *value_string;
+ GConfClient *gconf = gnm_app_get_gconf_client ();
+
+ switch (go_conf_get_type (key)) {
+ case G_TYPE_STRING:
+ value_string = gconf_client_get_string (gconf, key, NULL);
+
+ break;
+ case G_TYPE_INT:
+ value_string = g_strdup_printf ("%i", gconf_client_get_int (gconf, key,
+ NULL));
+ break;
+ case G_TYPE_FLOAT:
+ value_string = g_strdup_printf ("%f", gconf_client_get_float (gconf, key,
+ NULL));
+ break;
+ case G_TYPE_BOOLEAN:
+ value_string = g_strdup (format_boolean (gconf_client_get_bool (gconf, key, NULL)));
+ break;
+ default:
+ value_string = g_strdup ("ERROR FIXME");
+ }
+
+ return value_string;
+}
+
+int
+go_conf_get_bool (char const *key)
+{
+ GConfClient *gconf = gnm_app_get_gconf_client ();
+ return gconf_client_get_bool (gconf, key, NULL);
+}
+
+int
+go_conf_get_int (char const *key)
+{
+ GConfClient *gconf = gnm_app_get_gconf_client ();
+ return gconf_client_get_int (gconf, key, NULL);
+}
+
+double
+go_conf_get_double (char const *key)
+{
+ GConfClient *gconf = gnm_app_get_gconf_client ();
+ return gconf_client_get_float (gconf, key, NULL);
+}
+
+
+gboolean
+go_conf_set_value_from_str (char const *key, char const *val_str)
+{
+ GConfClient *client = gnm_app_get_gconf_client ();
+
+ switch (go_conf_get_type (key)) {
+ case G_TYPE_STRING:
+ go_conf_set_string (key, val_str);
+ break;
+ case G_TYPE_FLOAT: {
+ GnmDateConventions const *conv = NULL; /* workbook_date_conv (state->wb); */
+ GnmValue *value = format_match_number (val_str, NULL, conv);
+ if (value != NULL) {
+ gnm_float the_float = value_get_as_float (value);
+ gconf_client_set_float (client, key, the_float, NULL);
+ }
+ if (value)
+ value_release (value);
+ break;
+ }
+ case G_TYPE_INT: {
+ GnmDateConventions const *conv = NULL; /* workbook_date_conv (state->wb); */
+ GnmValue *value = format_match_number (val_str, NULL, conv);
+ if (value != NULL) {
+ int the_int = value_get_as_int (value);
+ go_conf_set_int (key, the_int);
+ }
+ if (value)
+ value_release (value);
+ break;
+ }
+ case G_TYPE_BOOLEAN: {
+ GnmDateConventions const *conv = NULL; /* workbook_date_conv (state->wb); */
+ GnmValue *value = format_match_number (val_str, NULL, conv);
+ gboolean err, the_bool;
+ if (value != NULL) {
+ err = FALSE;
+ the_bool = value_get_as_bool (value, &err);
+ gconf_client_set_bool (client, key, the_bool, NULL);
+ }
+ if (value)
+ value_release (value);
+ break;
+ }
+ default:
+ g_warning ("Unsupported gconf type in preference dialog");
+ }
+
+ return TRUE;
+}
+
+void
+go_conf_remove_monitor (guint monitor_id)
+{
+ gconf_client_notify_remove (gnm_app_get_gconf_client (),
+ GPOINTER_TO_INT (monitor_id));
+}
+
+typedef struct {
+ void (*monitor) (char const *key, gpointer data);
+ gpointer data;
+} GOConfClosure;
+static void
+cb_key_changed (GConfClient *client, guint cnxn_id,
+ GConfEntry *entry, GOConfClosure *close)
+{
+ close->monitor (gconf_entry_get_key (entry), close->data);
+}
+guint
+go_conf_add_monitor (char const *key,
+ GOConfMonitorFunc monitor, gpointer data)
+{
+ GOConfClosure *close = g_new0 (GOConfClosure, 1);
+ close->monitor = monitor;
+ close->data = data;
+ return gconf_client_notify_add (gnm_app_get_gconf_client (), key,
+ (GConfClientNotifyFunc) cb_key_changed, close, g_free, NULL);
+}
+#else
+void
+go_conf_set_bool (G_GNUC_UNUSED char const *key, G_GNUC_UNUSED gboolean val)
+{
+}
+void
+go_conf_set_int (G_GNUC_UNUSED char const *key, G_GNUC_UNUSED gint val)
+{
+}
+void
+go_conf_set_double (G_GNUC_UNUSED char const *key, G_GNUC_UNUSED gnm_float val)
+{
+}
+void
+go_conf_set_string (G_GNUC_UNUSED char const *key, G_GNUC_UNUSED char const *str)
+{
+}
+void
+go_conf_set_str_list (G_GNUC_UNUSED char const *key, G_GNUC_UNUSED GSList *list)
+{
+}
+gboolean
+go_conf_get_bool (char const *key)
+{
+ return FALSE;
+}
+
+int
+go_conf_get_int (char const *key)
+{
+ return 0;
+}
+
+double
+go_conf_get_double (char const *key)
+{
+ return 0.;
+}
+
+char *
+go_conf_get_string (char const *key)
+{
+ return g_strdup ("");
+}
+
+GSList *
+go_conf_get_str_list (char const *key)
+{
+ return NULL;
+}
+
+gboolean
+go_conf_load_bool (G_GNUC_UNUSED char const *key,
+ gboolean default_val)
+{
+ return default_val;
+}
+int
+go_conf_load_int (G_GNUC_UNUSED char const *key,
+ G_GNUC_UNUSED int minima, G_GNUC_UNUSED int maxima,
+ int default_val)
+{
+ return default_val;
+}
+
+double
+go_conf_load_double (G_GNUC_UNUSED char const *key,
+ G_GNUC_UNUSED double minima, G_GNUC_UNUSED double maxima,
+ double default_val)
+{
+ return default_val;
+}
+char *
+go_conf_load_string (G_GNUC_UNUSED char const *key)
+{
+ return NULL;
+}
+GSList *
+go_conf_load_str_list (G_GNUC_UNUSED char const *key)
+{
+ return NULL;
+}
+char *
+go_conf_get_short_desc (char const *key)
+{
+ return NULL;
+}
+char *
+go_conf_get_long_desc (char const *key)
+{
+ return NULL;
+}
+
+GType
+go_conf_get_type (char const *key)
+{
+ return G_TYPE_NONE;
+}
+
+char *
+go_conf_get_value_as_str (char const *key)
+{
+ return g_strdup ("");
+}
+
+gboolean
+go_conf_set_value_from_str (char const *key, char const *val_str)
+{
+ return TRUE;
+}
+
+void
+go_conf_sync (void)
+{
+}
+
+void
+go_conf_remove_monitor (guint monitor_id)
+{
+}
+
+guint
+go_conf_add_monitor (char const *key,
+ GOConfMonitorFunc monitor, gpointer data)
+{
+ return 1;
+}
+
+#endif
+
+static void
+gnm_conf_init_printer_decoration_font (void)
+{
+ gchar *name;
+ if (prefs.printer_decoration_font == NULL)
+ prefs.printer_decoration_font = mstyle_new ();
+
+ name = go_conf_load_string (PRINTSETUP_GCONF_HF_FONT_NAME);
+ if (name) {
+ mstyle_set_font_name (prefs.printer_decoration_font, name);
+ g_free (name);
+ } else
+ mstyle_set_font_name (prefs.printer_decoration_font, DEFAULT_FONT);
+ mstyle_set_font_size (prefs.printer_decoration_font,
+ go_conf_load_double (PRINTSETUP_GCONF_HF_FONT_SIZE, 1., 100., DEFAULT_SIZE));
+ mstyle_set_font_bold (prefs.printer_decoration_font,
+ go_conf_load_bool (PRINTSETUP_GCONF_HF_FONT_BOLD, FALSE));
+ mstyle_set_font_italic (prefs.printer_decoration_font,
+ go_conf_load_bool (PRINTSETUP_GCONF_HF_FONT_ITALIC, FALSE));
+}
+
+static void
+gnm_conf_init_essential (void)
+{
+ prefs.default_font.name = go_conf_load_string (CONF_DEFAULT_FONT_NAME);
+ if (prefs.default_font.name == NULL)
+ prefs.default_font.name = g_strdup (DEFAULT_FONT);
+ prefs.default_font.size = go_conf_load_double (
+ CONF_DEFAULT_FONT_SIZE, 1., 100., DEFAULT_SIZE);
+ prefs.default_font.is_bold = go_conf_load_bool (
+ CONF_DEFAULT_FONT_BOLD, FALSE);
+ prefs.default_font.is_italic = go_conf_load_bool (
+ CONF_DEFAULT_FONT_ITALIC, FALSE);
+
+ prefs.file_history_max = go_conf_load_int (
+ GNM_CONF_FILE_HISTORY_N, 0, 20, 4);
+ prefs.file_history_files = go_conf_load_str_list (GNM_CONF_FILE_HISTORY_FILES);
+ prefs.plugin_file_states = go_conf_load_str_list (PLUGIN_GCONF_FILE_STATES);
+ prefs.plugin_extra_dirs = go_conf_load_str_list (PLUGIN_GCONF_EXTRA_DIRS);
+ prefs.active_plugins = go_conf_load_str_list (PLUGIN_GCONF_ACTIVE);
+ prefs.activate_new_plugins = go_conf_load_bool (
+ PLUGIN_GCONF_ACTIVATE_NEW, TRUE);
+ printf( "prefs.activate_new_plugins: %d\n", prefs.activate_new_plugins );
+
+ prefs.horizontal_dpi = go_conf_load_double (
+ GNM_CONF_GUI_RES_H, 10., 1000., 96.);
+ prefs.vertical_dpi = go_conf_load_double (
+ GNM_CONF_GUI_RES_V, 10., 1000., 96.);
+ prefs.initial_sheet_number = go_conf_load_int (
+ GNM_CONF_WORKBOOK_NSHEETS, 1, 64, 3);
+ prefs.horizontal_window_fraction = go_conf_load_double (
+ GNM_CONF_GUI_WINDOW_X, .1, 1., .6);
+ prefs.vertical_window_fraction = go_conf_load_double (
+ GNM_CONF_GUI_WINDOW_Y, .1, 1., .6);
+ prefs.zoom = go_conf_load_double (
+ GNM_CONF_GUI_ZOOM, .1, 5., 1.);
+
+ /* Unfortunately we need the printing stuff in essentials since the */
+ /* first pi is created for the new sheet before the idle loop has a */
+ /* chance to run */
+ prefs.printer_config = go_conf_load_string (PRINTSETUP_GCONF_PRINTER_CONFIG);
+ prefs.print_center_horizontally = go_conf_load_bool
+ (PRINTSETUP_GCONF_CENTER_HORIZONTALLY, FALSE);
+ prefs.print_center_vertically = go_conf_load_bool
+ (PRINTSETUP_GCONF_CENTER_VERTICALLY, FALSE);
+ prefs.print_grid_lines = go_conf_load_bool
+ (PRINTSETUP_GCONF_PRINT_GRID_LINES, FALSE);
+ prefs.print_even_if_only_styles = go_conf_load_bool
+ (PRINTSETUP_GCONF_EVEN_IF_ONLY_STYLES, FALSE);
+ prefs.print_black_and_white = go_conf_load_bool
+ (PRINTSETUP_GCONF_PRINT_BLACK_AND_WHITE, FALSE);
+ prefs.print_titles = go_conf_load_bool
+ (PRINTSETUP_GCONF_PRINT_TITLES, FALSE);
+ prefs.print_order_right_then_down = go_conf_load_bool
+ (PRINTSETUP_GCONF_RIGHT_THEN_DOWN, FALSE);
+ prefs.print_scale_percentage = go_conf_load_bool
+ (PRINTSETUP_GCONF_SCALE_PERCENTAGE, TRUE);
+ prefs.print_scale_percentage_value = go_conf_load_double
+ (PRINTSETUP_GCONF_SCALE_PERCENTAGE_VALUE, 1, 500, 100);
+ prefs.print_scale_width = go_conf_load_int
+ (PRINTSETUP_GCONF_SCALE_WIDTH, 0, 100, 1);
+ prefs.print_scale_height = go_conf_load_int
+ (PRINTSETUP_GCONF_SCALE_HEIGHT, 0, 100, 1);
+ prefs.print_repeat_top = go_conf_load_string (PRINTSETUP_GCONF_REPEAT_TOP);
+ prefs.print_repeat_left = go_conf_load_string (PRINTSETUP_GCONF_REPEAT_LEFT);
+#if 0
+ prefs.print_tb_margins.top.points = go_conf_load_double
+ (PRINTSETUP_GCONF_MARGIN_TOP, 0.0, 10000.0, 120.0);
+ prefs.print_tb_margins.bottom.points = go_conf_load_double
+ (PRINTSETUP_GCONF_MARGIN_BOTTOM, 0.0, 10000.0, 120.0);
+ {
+ /* Note: the desired display unit is stored in the */
+ /* printer config. So we are never using this field */
+ /* inside the margin structure, but only setting it */
+ /* in various input routines. */
+ prefs.print_tb_margins.top.desired_display
+ = gnome_print_unit_get_by_abbreviation ("cm");
+ prefs.print_tb_margins.bottom.desired_display
+ = prefs.print_tb_margins.top.desired_display;
+ }
+#endif // 0
+ prefs.print_all_sheets = go_conf_load_bool (
+ PRINTSETUP_GCONF_ALL_SHEETS, TRUE);
+ prefs.printer_header = go_conf_load_str_list (PRINTSETUP_GCONF_HEADER);
+ prefs.printer_footer = go_conf_load_str_list (PRINTSETUP_GCONF_FOOTER);
+ prefs.printer_header_formats_left = go_conf_load_str_list (PRINTSETUP_GCONF_HEADER_FORMAT_LEFT);
+ prefs.printer_header_formats_middle = go_conf_load_str_list (PRINTSETUP_GCONF_HEADER_FORMAT_MIDDLE);
+ prefs.printer_header_formats_right = go_conf_load_str_list (PRINTSETUP_GCONF_HEADER_FORMAT_RIGHT);
+
+ prefs.auto_complete = go_conf_load_bool (GNM_CONF_GUI_ED_AUTOCOMPLETE, TRUE);
+ prefs.live_scrolling = go_conf_load_bool (GNM_CONF_GUI_ED_LIVESCROLLING, TRUE);
+}
+
+static gboolean
+gnm_conf_init_extras (void)
+{
+ char *tmp;
+
+ prefs.num_of_recent_funcs = go_conf_load_int (
+ FUNCTION_SELECT_GCONF_NUM_OF_RECENT, 0, 40, 10);
+ prefs.recent_funcs = go_conf_load_str_list (FUNCTION_SELECT_GCONF_RECENT);
+
+ prefs.transition_keys = go_conf_load_bool (
+ GNM_CONF_GUI_ED_TRANSITION_KEYS, FALSE);
+ prefs.recalc_lag = go_conf_load_int (
+ GNM_CONF_GUI_ED_RECALC_LAG, -5000, 5000, 200);
+ prefs.show_sheet_name = go_conf_load_bool (
+ GNM_CONF_UNDO_SHOW_SHEET_NAME, TRUE);
+ prefs.max_descriptor_width = go_conf_load_int (
+ GNM_CONF_UNDO_MAX_DESCRIPTOR_WIDTH, 5, 256, 15);
+ prefs.undo_size = go_conf_load_int (
+ GNM_CONF_UNDO_SIZE, 1, 1000000, 100000);
+ prefs.undo_max_number = go_conf_load_int (
+ GNM_CONF_UNDO_MAXNUM, 0, 10000, 100);
+
+ prefs.autoformat.extra_dirs = go_conf_load_str_list (AUTOFORMAT_GCONF_EXTRA_DIRS);
+ tmp = go_conf_load_string (AUTOFORMAT_GCONF_SYS_DIR);
+ if (tmp == NULL)
+ tmp = g_strdup ("autoformat-templates");
+ prefs.autoformat.sys_dir = gnm_sys_data_dir (tmp);
+ g_free (tmp);
+ tmp = go_conf_load_string (AUTOFORMAT_GCONF_USR_DIR);
+ if (tmp == NULL)
+ tmp = g_strdup ("autoformat-templates");
+ prefs.autoformat.usr_dir = gnm_usr_dir (tmp);
+ g_free (tmp);
+
+ prefs.xml_compression_level = go_conf_load_int (
+ GNM_CONF_XML_COMPRESSION, 0, 9, 9);
+ prefs.file_overwrite_default_answer = go_conf_load_bool (
+ GNM_CONF_FILE_OVERWRITE_DEFAULT, FALSE);
+ prefs.file_ask_single_sheet_save = go_conf_load_bool (
+ GNM_CONF_FILE_SINGLE_SHEET_SAVE, TRUE);
+ prefs.sort_default_by_case = go_conf_load_bool (
+ GNM_CONF_SORT_DEFAULT_BY_CASE, FALSE);
+ prefs.sort_default_retain_formats = go_conf_load_bool (
+ GNM_CONF_SORT_DEFAULT_RETAIN_FORM, TRUE);
+ prefs.sort_default_ascending = go_conf_load_bool (
+ GNM_CONF_SORT_DEFAULT_ASCENDING, TRUE);
+ prefs.sort_max_initial_clauses = go_conf_load_int (
+ GNM_CONF_SORT_DIALOG_MAX_INITIAL, 0, 256, 10);
+ prefs.unfocused_range_selection = go_conf_load_bool (
+ DIALOGS_GCONF_UNFOCUSED_RS, TRUE);
+ prefs.prefer_clipboard_selection = go_conf_load_bool (
+ GNM_CONF_CUTANDPASTE_PREFER_CLIPBOARD, TRUE);
+ prefs.latex_use_utf8 = go_conf_load_bool (
+ PLUGIN_GCONF_LATEX_USE_UTF8, TRUE);
+
+ gnm_conf_init_printer_decoration_font ();
+
+ return FALSE;
+}
+
+/**
+ * gnm_conf_init
+ *
+ * @fast : Load non-essential prefs in an idle handler
+ **/
+void
+gnm_conf_init (gboolean fast)
+{
+ gnm_conf_init_essential ();
+ if (fast)
+ g_timeout_add (1000, (GSourceFunc) gnm_conf_init_extras, NULL);
+ else
+ gnm_conf_init_extras ();
+}
+
+void
+gnm_conf_shutdown (void)
+{
+ mstyle_unref (prefs.printer_decoration_font);
+ prefs.printer_decoration_font = NULL;
+#ifdef WITH_GNOME
+ if (gconf_client) {
+ gconf_client_remove_dir (gconf_client, "/apps/gnumeric", NULL);
+ g_object_unref (G_OBJECT (gconf_client));
+ gconf_client = NULL;
+ }
+#endif
+}
+
+void
+gnm_gconf_set_plugin_file_states (GSList *list)
+{
+ g_return_if_fail (prefs.plugin_file_states != list);
+
+ /* the const_casts are ok, the const in the header is just to keep
+ * people for doing stupid things */
+ g_slist_foreach ((GSList *)prefs.plugin_file_states, (GFunc)g_free, NULL);
+ g_slist_free ((GSList *)prefs.plugin_file_states);
+ prefs.plugin_file_states = list;
+
+ go_conf_set_str_list (PLUGIN_GCONF_FILE_STATES, list);
+}
+
+void
+gnm_gconf_set_plugin_extra_dirs (GSList *list)
+{
+ g_return_if_fail (prefs.plugin_extra_dirs != list);
+
+ /* the const_casts are ok, the const in the header is just to keep
+ * people for doing stupid things */
+ g_slist_foreach ((GSList *)prefs.plugin_extra_dirs, (GFunc)g_free, NULL);
+ g_slist_free ((GSList *)prefs.plugin_extra_dirs);
+ prefs.plugin_extra_dirs = list;
+
+ go_conf_set_str_list (PLUGIN_GCONF_EXTRA_DIRS, list);
+}
+
+void
+gnm_gconf_set_active_plugins (GSList *list)
+{
+ go_conf_set_str_list (PLUGIN_GCONF_ACTIVE, list);
+}
+
+void
+gnm_gconf_set_activate_new_plugins (gboolean val)
+{
+ go_conf_set_bool (PLUGIN_GCONF_ACTIVATE_NEW, val);
+}
+
+void
+gnm_gconf_set_recent_funcs (GSList *list)
+{
+ go_conf_set_str_list (FUNCTION_SELECT_GCONF_RECENT, list);
+
+ /* the const_casts are ok, the const in the header is just to keep
+ * people for doing stupid things */
+ g_slist_foreach ((GSList *)prefs.recent_funcs, (GFunc)g_free, NULL);
+ g_slist_free ((GSList *)prefs.recent_funcs);
+
+ prefs.recent_funcs = list;
+}
+
+void
+gnm_gconf_set_num_recent_functions (gint val)
+{
+ if (val < 0)
+ val = 0;
+ prefs.num_of_recent_funcs = val;
+ go_conf_set_int ( FUNCTION_SELECT_GCONF_NUM_OF_RECENT, val);
+}
+
+void
+gnm_gconf_set_file_history_files (GSList *list)
+{
+ g_return_if_fail (prefs.file_history_files != list);
+
+ /* the const_casts are ok, the const in the header is just to keep
+ * people for doing stupid things */
+ g_slist_foreach ((GSList *)prefs.file_history_files, (GFunc)g_free, NULL);
+ g_slist_free ((GSList *)prefs.file_history_files);
+ prefs.file_history_files = list;
+ go_conf_set_str_list (GNM_CONF_FILE_HISTORY_FILES, list);
+}
+
+void
+gnm_gconf_set_file_history_number (gint val)
+{
+ if (val < 0)
+ val = 0;
+ prefs.file_history_max = val;
+ go_conf_set_int (GNM_CONF_FILE_HISTORY_N, val);
+}
+
+
+void
+gnm_gconf_set_undo_size (gint val)
+{
+ if (val < 1)
+ val = 1;
+ prefs.undo_size = val;
+ go_conf_set_int (GNM_CONF_UNDO_SIZE, val);
+}
+
+
+void
+gnm_gconf_set_undo_max_number (gint val)
+{
+ if (val < 1)
+ val = 1;
+ prefs.undo_max_number = val;
+ go_conf_set_int (GNM_CONF_UNDO_MAXNUM, val);
+}
+
+void
+gnm_gconf_set_autoformat_sys_dirs (char const * string)
+{
+ go_conf_set_string (AUTOFORMAT_GCONF_SYS_DIR, string);
+}
+
+void
+gnm_gconf_set_autoformat_usr_dirs (char const * string)
+{
+ go_conf_set_string (AUTOFORMAT_GCONF_USR_DIR, string);
+}
+
+void
+gnm_gconf_set_all_sheets (gboolean val)
+{
+ go_conf_set_bool (PRINTSETUP_GCONF_ALL_SHEETS, val);
+}
+
+void
+gnm_gconf_set_printer_config (gchar *str)
+{
+ go_conf_set_string (PRINTSETUP_GCONF_PRINTER_CONFIG, str);
+ g_free (prefs.printer_config);
+ prefs.printer_config = str;
+}
+
+void
+gnm_gconf_set_printer_header (gchar const *left, gchar const *middle,
+ gchar const *right)
+{
+ GSList *list = NULL;
+ list = g_slist_prepend (list, g_strdup (right));
+ list = g_slist_prepend (list, g_strdup (middle));
+ list = g_slist_prepend (list, g_strdup (left));
+ go_conf_set_str_list (PRINTSETUP_GCONF_HEADER, list);
+ gnm_slist_free_custom ((GSList *)prefs.printer_header, g_free);
+ prefs.printer_header = list;
+}
+
+void
+gnm_gconf_set_printer_footer (gchar const *left, gchar const *middle,
+ gchar const *right)
+{
+ GSList *list = NULL;
+ list = g_slist_prepend (list, g_strdup (right));
+ list = g_slist_prepend (list, g_strdup (middle));
+ list = g_slist_prepend (list, g_strdup (left));
+ go_conf_set_str_list (PRINTSETUP_GCONF_FOOTER, list);
+ gnm_slist_free_custom ((GSList *)prefs.printer_footer, g_free);
+ prefs.printer_footer = list;
+}
+
+void
+gnm_gconf_set_print_center_horizontally (gboolean val)
+{
+ go_conf_set_bool (PRINTSETUP_GCONF_CENTER_HORIZONTALLY, val);
+}
+
+void
+gnm_gconf_set_print_center_vertically (gboolean val)
+{
+ go_conf_set_bool (PRINTSETUP_GCONF_CENTER_VERTICALLY, val);
+}
+
+void
+gnm_gconf_set_print_grid_lines (gboolean val)
+{
+ go_conf_set_bool (PRINTSETUP_GCONF_PRINT_GRID_LINES, val);
+}
+
+void
+gnm_gconf_set_print_even_if_only_styles (gboolean val)
+{
+ go_conf_set_bool (PRINTSETUP_GCONF_EVEN_IF_ONLY_STYLES, val);
+}
+
+void
+gnm_gconf_set_print_black_and_white (gboolean val)
+{
+ go_conf_set_bool (PRINTSETUP_GCONF_PRINT_BLACK_AND_WHITE, val);
+}
+
+void
+gnm_gconf_set_print_titles (gboolean val)
+{
+ go_conf_set_bool (PRINTSETUP_GCONF_PRINT_TITLES, val);
+}
+
+void
+gnm_gconf_set_print_order_right_then_down (gboolean val)
+{
+ go_conf_set_bool (PRINTSETUP_GCONF_RIGHT_THEN_DOWN, val);
+}
+
+void
+gnm_gconf_set_print_scale_percentage (gboolean val)
+{
+ go_conf_set_bool (PRINTSETUP_GCONF_SCALE_PERCENTAGE, val);
+}
+
+void
+gnm_gconf_set_print_scale_percentage_value (gnm_float val)
+{
+ go_conf_set_double (PRINTSETUP_GCONF_SCALE_PERCENTAGE_VALUE, val);
+}
+
+#if 0
+void
+gnm_gconf_set_print_tb_margins (PrintMargins const *pm)
+{
+ /* We are not saving the GnomePrintUnits since they are */
+ /* duplicated in the gnomeprintconfig */
+ go_conf_set_double (PRINTSETUP_GCONF_MARGIN_TOP, pm->top.points);
+ go_conf_set_double (PRINTSETUP_GCONF_MARGIN_BOTTOM, pm->bottom.points);
+}
+#endif // 0
+
+void
+gnm_gconf_set_print_header_formats (GSList *left, GSList *middle,
+ GSList *right)
+{
+ go_conf_set_str_list (PRINTSETUP_GCONF_HEADER_FORMAT_LEFT, left);
+ gnm_slist_free_custom (left, g_free);
+ go_conf_set_str_list (PRINTSETUP_GCONF_HEADER_FORMAT_MIDDLE, middle);
+ gnm_slist_free_custom (middle, g_free);
+ go_conf_set_str_list (PRINTSETUP_GCONF_HEADER_FORMAT_RIGHT, right);
+ gnm_slist_free_custom (right, g_free);
+}
+
+void
+gnm_gconf_set_gui_window_x (gnm_float val)
+{
+ prefs.horizontal_window_fraction = val;
+ go_conf_set_double (GNM_CONF_GUI_WINDOW_X, val);
+}
+
+void
+gnm_gconf_set_gui_window_y (gnm_float val)
+{
+ prefs.vertical_window_fraction = val;
+ go_conf_set_double (GNM_CONF_GUI_WINDOW_Y, val);
+}
+
+void
+gnm_gconf_set_gui_zoom (gnm_float val)
+{
+ prefs.zoom = val;
+ go_conf_set_double (GNM_CONF_GUI_WINDOW_Y, val);
+}
+
+void
+gnm_gconf_set_default_font_size (gnm_float val)
+{
+ prefs.default_font.size = val;
+ go_conf_set_double (GNM_CONF_FONT_SIZE, val);
+}
+
+void
+gnm_gconf_set_default_font_name (char const *str)
+{
+ g_return_if_fail (str != NULL);
+
+ /* the const_casts are ok, the const in the header is just to keep
+ * people for doing stupid things */
+ if (prefs.default_font.name != NULL)
+ g_free ((char *) prefs.default_font.name);
+ prefs.default_font.name = g_strdup (str);
+ go_conf_set_string (GNM_CONF_FONT_NAME, str);
+}
+
+void
+gnm_gconf_set_default_font_bold (gboolean val)
+{
+ prefs.default_font.is_bold = val;
+ go_conf_set_bool (GNM_CONF_FONT_BOLD, val);
+}
+
+void
+gnm_gconf_set_default_font_italic (gboolean val)
+{
+ prefs.default_font.is_italic = val;
+ go_conf_set_bool (GNM_CONF_FONT_ITALIC, val);
+}
+
+void
+gnm_gconf_set_hf_font (GnmStyle const *mstyle)
+{
+ GnmStyle *old_style = (prefs.printer_decoration_font != NULL) ?
+ prefs.printer_decoration_font :
+ mstyle_new_default ();
+
+ prefs.printer_decoration_font = mstyle_copy_merge (old_style, mstyle);
+ mstyle_unref (old_style);
+
+ if (mstyle_is_element_set (mstyle, MSTYLE_FONT_SIZE))
+ go_conf_set_double (PRINTSETUP_GCONF_HF_FONT_SIZE,
+ mstyle_get_font_size (mstyle));
+ if (mstyle_is_element_set (mstyle, MSTYLE_FONT_NAME))
+ go_conf_set_string (PRINTSETUP_GCONF_HF_FONT_NAME,
+ mstyle_get_font_name (mstyle));
+ if (mstyle_is_element_set (mstyle, MSTYLE_FONT_BOLD))
+ go_conf_set_bool (PRINTSETUP_GCONF_HF_FONT_BOLD,
+ mstyle_get_font_bold (mstyle));
+ if (mstyle_is_element_set (mstyle, MSTYLE_FONT_ITALIC))
+ go_conf_set_bool (PRINTSETUP_GCONF_HF_FONT_ITALIC,
+ mstyle_get_font_italic (mstyle));
+}
+
+
+void
+gnm_gconf_set_max_descriptor_width (gint val)
+{
+ if (val < 1)
+ val = 1;
+ prefs.max_descriptor_width = val;
+ go_conf_set_int (GNM_CONF_UNDO_MAX_DESCRIPTOR_WIDTH, val);
+}
+
+void
+gnm_gconf_set_sort_dialog_max_initial (gint val)
+{
+ if (val < 1)
+ val = 1;
+ prefs.sort_max_initial_clauses = val;
+ go_conf_set_int (GNM_CONF_SORT_DIALOG_MAX_INITIAL, val);
+}
+
+void
+gnm_gconf_set_workbook_nsheets (gint val)
+{
+ if (val < 1)
+ val = 1;
+ prefs.initial_sheet_number = val;
+ go_conf_set_int (GNM_CONF_WORKBOOK_NSHEETS, val);
+}
+
+void
+gnm_gconf_set_xml_compression (gint val)
+{
+ if (val < 0)
+ val = 0;
+ prefs.xml_compression_level = val;
+ go_conf_set_int (GNM_CONF_XML_COMPRESSION, val);
+}
+
+void
+gnm_gconf_set_show_sheet_name (gboolean val)
+{
+ prefs.show_sheet_name = val;
+ go_conf_set_bool( GNM_CONF_UNDO_SHOW_SHEET_NAME,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_latex_use_utf8 (gboolean val)
+{
+ prefs.latex_use_utf8 = val;
+ go_conf_set_bool( PLUGIN_GCONF_LATEX_USE_UTF8,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_sort_retain_form (gboolean val)
+{
+ prefs.sort_default_retain_formats = val;
+ go_conf_set_bool( GNM_CONF_SORT_DEFAULT_RETAIN_FORM,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_sort_by_case (gboolean val)
+{
+ prefs.sort_default_by_case = val;
+ go_conf_set_bool( GNM_CONF_SORT_DEFAULT_BY_CASE,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_sort_ascending (gboolean val)
+{
+ prefs.sort_default_ascending = val;
+ go_conf_set_bool( GNM_CONF_SORT_DEFAULT_ASCENDING,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_gui_transition_keys (gboolean val)
+{
+ prefs.transition_keys = val;
+ go_conf_set_bool( GNM_CONF_GUI_ED_TRANSITION_KEYS,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_gui_livescrolling (gboolean val)
+{
+ prefs.live_scrolling = val;
+ go_conf_set_bool( GNM_CONF_GUI_ED_LIVESCROLLING,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_file_overwrite (gboolean val)
+{
+ prefs.file_overwrite_default_answer = val;
+ go_conf_set_bool( GNM_CONF_FILE_OVERWRITE_DEFAULT,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_file_single_sheet_save (gboolean val)
+{
+ prefs.file_ask_single_sheet_save = val;
+ go_conf_set_bool( GNM_CONF_FILE_SINGLE_SHEET_SAVE,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_gui_resolution_h (gnm_float val)
+{
+ if (val < 50)
+ val = 50;
+ if (val > 250)
+ val = 250;
+ prefs.horizontal_dpi = val;
+ go_conf_set_double (GNM_CONF_GUI_RES_H, val);
+}
+
+void
+gnm_gconf_set_gui_resolution_v (gnm_float val)
+{
+ if (val < 50)
+ val = 50;
+ if (val > 250)
+ val = 250;
+ prefs.vertical_dpi = val;
+ go_conf_set_double (GNM_CONF_GUI_RES_V, val);
+}
+
+void
+gnm_gconf_set_unfocused_rs (gboolean val)
+{
+ prefs.unfocused_range_selection = val;
+ go_conf_set_bool( DIALOGS_GCONF_UNFOCUSED_RS,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_autocomplete (gboolean val)
+{
+ prefs.auto_complete = val;
+ go_conf_set_bool( GNM_CONF_GUI_ED_AUTOCOMPLETE,
+ val != FALSE);
+}
+
+void
+gnm_gconf_set_prefer_clipboard (gboolean val)
+{
+ prefs.prefer_clipboard_selection = val;
+ go_conf_set_bool( GNM_CONF_CUTANDPASTE_PREFER_CLIPBOARD,
+ val != FALSE);
+}
+
--- /dev/null
+++ lib/goffice/split/global-gnome-font.h
@@ -0,0 +1,8 @@
+#ifndef GNUMERIC_GLOBAL_GNOME_FONT_H
+#define GNUMERIC_GLOBAL_GNOME_FONT_H
+
+extern GList *gnumeric_font_family_list;
+extern GList *gnumeric_point_size_list;
+extern int const gnumeric_point_sizes [];
+
+#endif
--- /dev/null
+++ lib/goffice/split/module-plugin-defs.h
@@ -0,0 +1,92 @@
+#ifndef GNUMERIC_MODULE_PLUGIN_DEFS_H
+#define GNUMERIC_MODULE_PLUGIN_DEFS_H
+
+//#include <gui-gnumeric.h> /* for wbcg typedef */
+// +jsled -- CNP from gui-gnumeric.h
+// typedef struct _WorkbookControl WorkbookControl;
+// -jsled
+#include <plugin.h>
+//#include <func.h> -- not used?
+#include <application.h>
+// +jsled -- CNP from application.h
+//typedef struct _GnmAction GnmAction;
+// -jsled
+
+/*
+ * Every plugin should put somewhere a line with:
+ * GNUMERIC_MODULE_PLUGIN_INFO_DECL;
+ */
+#define GNUMERIC_MODULE_PLUGIN_INFO_DECL ModulePluginFileStruct plugin_file_struct = GNUMERIC_MODULE_PLUGIN_FILE_STRUCT_INITIALIZER
+
+/* This type is intended for use with "ui" service.
+ * Plugins should define arrays of structs of the form:
+ * ModulePluginUIActions <service-id>_actions[] = { ... };
+ */
+typedef struct {
+ char const *name;
+ void (*handler) (GnmAction const *action, WorkbookControl *wbc);
+} ModulePluginUIActions;
+
+/* function executed to activate "general" service */
+void plugin_init_general (ErrorInfo **ret_error);
+/* function executed to deactivate "general" service */
+void plugin_cleanup_general (ErrorInfo **ret_error);
+
+/* optional function executed immediately after loading a plugin */
+void plugin_init (void);
+/* optional function executed before unloading a plugin */
+void plugin_cleanup (void);
+
+
+#ifdef PLUGIN_ID
+
+static GnmPlugin *gnm_get_current_plugin (void)
+{
+ static GnmPlugin *plugin = NULL;
+ if (plugin == NULL) plugin = plugins_get_plugin_by_id (PLUGIN_ID);
+ return plugin;
+}
+#define PLUGIN (gnm_get_current_plugin ())
+
+/* Use this macro for defining types inside plugins */
+#define PLUGIN_CLASS(name, prefix, class_init, instance_init, parent_type) \
+GType \
+prefix ## _get_type (void) \
+{ \
+ GType type = 0; \
+ if (type == 0) { \
+ static GTypeInfo const object_info = { \
+ sizeof (name ## Class), \
+ (GBaseInitFunc) NULL, \
+ (GBaseFinalizeFunc) NULL, \
+ (GClassInitFunc) class_init, \
+ (GClassFinalizeFunc) NULL, \
+ NULL, /* class_data */ \
+ sizeof (name), \
+ 0, /* n_preallocs */ \
+ (GInstanceInitFunc) instance_init, \
+ NULL \
+ }; \
+ type = g_type_module_register_type ( \
+ G_TYPE_MODULE (gnm_get_current_plugin ()), parent_type, #name, \
+ &object_info, 0); \
+ } \
+ return type; \
+}
+
+#endif
+
+
+/* All fields in this structure are PRIVATE. */
+typedef struct {
+ guint32 magic_number;
+ gchar version[64];
+} ModulePluginFileStruct;
+
+// +jsled -- @@fixme; gnucash version, I guess.
+#define GNUMERIC_VERSION "9.8.7"
+// -jsled
+#define GNUMERIC_MODULE_PLUGIN_MAGIC_NUMBER 0x476e756d
+#define GNUMERIC_MODULE_PLUGIN_FILE_STRUCT_INITIALIZER {GNUMERIC_MODULE_PLUGIN_MAGIC_NUMBER, GNUMERIC_VERSION}
+
+#endif /* GNUMERIC_MODULE_PLUGIN_DEFS_H */
--- /dev/null
+++ lib/goffice/split/gui-util.h
@@ -0,0 +1,139 @@
+#ifndef GNUMERIC_GUI_UTIL_H
+#define GNUMERIC_GUI_UTIL_H
+
+#include "workbook-control-gui.h"
+#include "error-info.h"
+#include "command-context.h"
+#include "gnumeric.h"
+#include "gutils.h"
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkcombo.h>
+#include <gtk/gtkfilesel.h>
+#include <gtk/gtkmessagedialog.h>
+#include <gtk/gtkmenu.h>
+#include <gtk/gtktoolbar.h>
+#include <gtk/gtktextview.h>
+#include <gtk/gtkentry.h>
+#include <glade/glade-xml.h>
+
+#define GNM_ACTION_DEF(name) \
+ void name (GtkAction *a, WorkbookControlGUI *wbcg)
+gboolean gnumeric_dialog_question_yes_no (GtkWindow *toplevel,
+ char const *message,
+ gboolean default_answer);
+gboolean gnumeric_dialog_file_selection (WorkbookControlGUI *wbcg,
+ GtkWidget *w);
+void gnumeric_notice (GtkWindow *parent, GtkMessageType type,
+ char const *str);
+void gnumeric_notice_nonmodal (GtkWindow *parent, GtkWidget **ref,
+ GtkMessageType type, char const *str);
+
+void gnumeric_non_modal_dialog (GtkWindow *toplevel, GtkWindow *dialog);
+gint gnumeric_dialog_run (GtkWindow *parent, GtkDialog *dialog);
+GtkWidget* gnumeric_error_info_dialog_new (ErrorInfo *error);
+void gnumeric_error_info_dialog_show (GtkWindow *parent,
+ ErrorInfo *error);
+void gnumeric_set_transient (GtkWindow *parent, GtkWindow *window);
+void gnumeric_keyed_dialog (WorkbookControlGUI *wbcg,
+ GtkWindow *dialog,
+ char const *key);
+gpointer gnumeric_dialog_raise_if_exists (WorkbookControlGUI *wbcg,
+ char const *key);
+void gnumeric_editable_enters (GtkWindow *window, GtkWidget *w);
+
+/* Utility routine as Gtk does not have any decent routine to do this */
+int gtk_radio_group_get_selected (GSList *radio_group);
+/* Utility routine as libglade does not have any decent routine to do this */
+int gnumeric_glade_group_value (GladeXML *gui, char const *group[]);
+
+/* Use this on menus that are popped up */
+void gnumeric_popup_menu (GtkMenu *menu, GdkEventButton *event);
+
+/*
+ * Pseudo-tool-tip support code.
+ */
+void gnumeric_position_tooltip (GtkWidget *tip, int horizontal);
+GtkWidget *gnumeric_create_tooltip (void);
+
+GladeXML *gnm_glade_xml_new (GnmCmdContext *cc, char const *gladefile,
+ char const *root, char const *domain);
+
+typedef struct {
+ char const *name;
+ char const *pixmap;
+ int display_filter;
+ int sensitive_filter;
+
+ int index;
+} GnumericPopupMenuElement;
+
+typedef gboolean (*GnumericPopupMenuHandler) (GnumericPopupMenuElement const *e,
+ gpointer user_data);
+
+void gnumeric_create_popup_menu (GnumericPopupMenuElement const *elements,
+ GnumericPopupMenuHandler handler,
+ gpointer user_data,
+ int display_filter,
+ int sensitive_filter,
+ GdkEventButton *event);
+
+#define gnumeric_filter_modifiers(a) ((a) &(~(GDK_LOCK_MASK|GDK_MOD2_MASK|GDK_MOD5_MASK)))
+
+GnmColor *go_combo_color_get_style_color (GtkWidget *color_combo);
+
+void gnumeric_help_display (char const *link);
+void gnumeric_init_help_button (GtkWidget *w, char const *link);
+void gnumeric_pbox_init_help (GtkWidget *dialog, char const *link);
+
+char *gnumeric_textview_get_text (GtkTextView *text_view);
+void gnumeric_textview_set_text (GtkTextView *text_view, char const *txt);
+
+void focus_on_entry (GtkEntry *entry);
+
+/* WARNING : These do not handle dates correctly
+ * We should be passing in a DateConvention */
+#define entry_to_float(entry, the_float, update) \
+ entry_to_float_with_format (entry, the_float, update, NULL)
+gboolean entry_to_float_with_format (GtkEntry *entry, gnm_float *the_float, gboolean update,
+ GnmFormat *format);
+gboolean entry_to_float_with_format_default (GtkEntry *entry, gnm_float *the_float, gboolean update,
+ GnmFormat *format, gnm_float num);
+gboolean entry_to_int (GtkEntry *entry, gint *the_int, gboolean update);
+void float_to_entry (GtkEntry *entry, gnm_float the_float);
+void int_to_entry (GtkEntry *entry, gint the_int);
+
+GtkWidget *gnumeric_load_image (char const *name);
+GdkPixbuf *gnumeric_load_pixbuf (char const *name);
+char *gnumeric_icondir (char const *subdir);
+
+GdkPixbuf *gnm_pixbuf_tile (GdkPixbuf const *src, int w, int h);
+
+void gnm_setup_label_atk (GtkWidget *label, GtkWidget *target);
+
+int gnm_measure_string (PangoContext *context, PangoFontDescription const *font_desc, char const *str);
+
+void gnm_link_button_and_entry (GtkWidget *button, GtkWidget *entry);
+
+void gnm_widget_set_cursor_type (GtkWidget *w, GdkCursorType ct);
+void gnm_widget_set_cursor (GtkWidget *w, GdkCursor *ct);
+GdkCursor *gnm_fat_cross_cursor (GdkDisplay *display);
+
+GtkWidget * gnumeric_button_new_with_stock_image (char const *text, char const *stock_id);
+GtkWidget * gnumeric_dialog_add_button (GtkDialog *dialog, char const *text, char const *stock_id,
+ gint response_id);
+GtkWidget * gnumeric_message_dialog_new (GtkWindow * parent,
+ GtkDialogFlags flags,
+ GtkMessageType type,
+ char const *primary_message,
+ char const *secondary_message);
+
+GdkPixbuf* gnm_pixbuf_intelligent_scale (GdkPixbuf *pixbuf,
+ guint width, guint height);
+void gnm_widget_disable_focus (GtkWidget *w);
+
+typedef gboolean gnm_iter_search_t (GtkTreeModel *model, GtkTreeIter* iter);
+#define gnm_tree_model_iter_next gtk_tree_model_iter_next
+gboolean gnm_tree_model_iter_prev (GtkTreeModel *model, GtkTreeIter* iter);
+
+
+#endif /* GNUMERIC_GUI_UTIL_H */
--- /dev/null
+++ lib/goffice/split/Makefile.am
@@ -0,0 +1,64 @@
+SUBDIRS = widgets
+
+noinst_LTLIBRARIES = libgoffice-split.la
+
+AM_CFLAGS = $(GLIB_CFLAGS) $(GSF_CFLAGS) $(PRINT_CFLAGS) $(GNOME_CFLAGS) $(GLADE_CFLAGS)
+
+libgoffice_split_la_SOURCES = \
+ command-context.c \
+ command-context.h \
+ command-context-priv.h \
+ command-context-stderr.h \
+ command-context-stderr.c \
+ error-info.c \
+ error-info.h \
+ value.c \
+ value.h \
+ str.h \
+ str.c \
+ mathfunc.h \
+ mathfunc.c \
+ number-match.h \
+ number-match.c \
+ dates.h \
+ dates.c \
+ format.h \
+ format.c \
+ gutils.h \
+ gutils.c \
+ formats.c \
+ datetime.h \
+ datetime.c \
+ ranges.h \
+ regutf8.h \
+ regutf8.c \
+ style-color.h \
+ style-color.c \
+ style-border.h \
+ style-border.c \
+ mstyle.h \
+ mstyle.c \
+ style.h \
+ style.c \
+ gnumeric-gconf.h \
+ gnumeric-gconf.c \
+ global-gnome-font.h \
+ global-gnome-font.c \
+ plugin.h \
+ plugin.c \
+ plugin-util.h \
+ plugin-util.c \
+ plugin-service.h \
+ plugin-service.c \
+ plugin-service-impl.h \
+ plugin-loader.h \
+ plugin-loader.c \
+ plugin-loader-module.h \
+ plugin-loader-module.c \
+ io-context.h \
+ io-context.c \
+ gui-util.h \
+ gui-util.c \
+ xml-io.h
+
+include $(srcdir)/../goffice.mk
--- /dev/null
+++ lib/goffice/split/error-info.h
@@ -0,0 +1,34 @@
+#ifndef GNUMERIC_ERROR_INFO_H
+#define GNUMERIC_ERROR_INFO_H
+
+#include <glib.h>
+
+typedef enum {
+ GNM_WARNING = 1,
+ GNM_ERROR
+} GnmSeverity;
+
+ErrorInfo *error_info_new_str (char const *msg);
+ErrorInfo *error_info_new_printf (char const *msg_format, ...) G_GNUC_PRINTF (1, 2);
+ErrorInfo *error_info_new_vprintf (GnmSeverity severity,
+ char const *msg_format,
+ va_list args);
+ErrorInfo *error_info_new_str_with_details (char const *msg, ErrorInfo *details);
+ErrorInfo *error_info_new_str_with_details_list (char const *msg, GSList *details);
+ErrorInfo *error_info_new_from_error_list (GSList *errors);
+ErrorInfo *error_info_new_from_errno (void);
+void error_info_add_details (ErrorInfo *error, ErrorInfo *details);
+void error_info_add_details_list (ErrorInfo *error, GSList *details);
+void error_info_free (ErrorInfo *error);
+void error_info_print (ErrorInfo *error);
+char const *error_info_peek_message (ErrorInfo *error);
+GSList *error_info_peek_details (ErrorInfo *error);
+GnmSeverity error_info_peek_severity (ErrorInfo *error);
+
+#define GNM_INIT_RET_ERROR_INFO(ret_error) \
+G_STMT_START { \
+ g_assert (ret_error != NULL); \
+ *ret_error = NULL; \
+} G_STMT_END
+
+#endif /* GNUMERIC_ERROR_INFO_H */
--- /dev/null
+++ lib/goffice/app/go-error-stack.h
@@ -0,0 +1,32 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-error-stack.h : A tree of errors
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_ERROR_STACK_H
+#define GO_ERROR_STACK_H
+
+#include <glib-object.h>
+
+GOErrorStack *gog_error_stack_new (GOErrorStack *child,
+ char const *msg, ...) G_GNUC_PRINTF (2, 3);
+void go_error_stack_add_child (GOErrorStack *estack, GOErrorStack *child);
+void go_error_stack_dump (GOErrorStack *estack);
+void go_error_stack_free (GOErrorStack *estack);
+
+#endif /* GO_ERROR_STACK_H */
--- /dev/null
+++ lib/goffice/app/go-doc.h
@@ -0,0 +1,52 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-doc.h : A GOffice document
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_DOC_H
+#define GO_DOC_H
+
+#include <goffice/app/goffice-app.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_DOC_TYPE (go_doc_get_type ())
+#define GO_DOC(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DOC_TYPE, GODoc))
+#define IS_GO_DOC(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DOC_TYPE))
+
+GType go_doc_get_type (void);
+
+#if 0
+GODoc *go_doc_new_from_input (GsfInput *input,
+ GODocImporter const *fmt,
+ GOIOContext *context,
+ gchar const *encoding);
+GODoc *go_doc_new_from_uri (char const *uri,
+ GnmFileOpener const *fmt,
+ GOIOContext *context,
+ gchar const *encoding);
+gboolean go_doc_save (GODoc *doc, GOIOContext *context);
+gboolean go_doc_save_as (GODoc *doc, GODocExporter *fmt, char const *uri,
+ GOIOContext *cc);
+gboolean go_doc_sendto (GODoc *doc, GOIOContext *cc);
+#endif
+
+G_END_DECLS
+
+#endif /* GO_DOC_H */
--- /dev/null
+++ lib/goffice/app/go-app.h
@@ -0,0 +1,52 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-app.h : A GOffice appument
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_APP_H
+#define GO_APP_H
+
+#include <goffice/app/goffice-app.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_APP_TYPE (go_app_get_type ())
+#define GO_APP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_APP_TYPE, GOApp))
+#define IS_GO_APP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_APP_TYPE))
+
+GType go_app_get_type (void);
+
+/* FIXME : should be in GOContext which App inherits from */
+GOPlugin *go_app_get_plugin (char const *id);
+
+/* TODO : I suspect these should be lookups of some sort
+ * eg go_app_find_in_lib_dir (GOApp const *app, subdir);
+ * or possibly
+ * go_app_foreach_lib_dir (GOApp const *app, gboolean (*handler)(path, userdata));
+ **/
+char *go_app_sys_lib_dir (GOApp const *app, char const *subdir);
+char *go_app_sys_data_dir (GOApp const *app, char const *subdir);
+char *go_app_sys_plugin_dir (GOApp const *app);
+
+/* FIXME : Seems gui specific, move to gui-utils */
+char *go_app_sys_glade_dir (GOApp const *app);
+
+G_END_DECLS
+
+#endif /* GO_APP_H */
--- /dev/null
+++ lib/goffice/app/go-object.c
@@ -0,0 +1,128 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-object.c :
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/app/go-object.h>
+#include <goffice/app/go-service-impl.h>
+#include <goffice/app/go-plugin.h>
+#include <goffice/app/go-error-stack.h>
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+
+struct _GOServiceObject {
+ GOService base;
+
+ char *primary_type;
+ GSList *interfaces;
+};
+typedef GOServiceClass GOServiceObjectClass;
+
+static GHashTable *plugin_types = NULL;
+
+static void
+go_service_object_finalize (GObject *obj)
+{
+}
+
+static char *
+go_service_object_description (G_GNUC_UNUSED GOService *s)
+{
+ return g_strdup (_("Objects"));
+}
+
+static void
+go_service_object_class_init (GObjectClass *gobj_class)
+{
+ GOServiceClass *serv_class = (GOServiceClass *)gobj_class;
+ gobj_class->finalize = go_service_object_finalize;
+ serv_class->description = go_service_object_description;
+ plugin_types = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+GSF_CLASS (GOServiceObject, go_service_object,
+ go_service_object_class_init, NULL,
+ GO_SERVICE_TYPE)
+
+char const *
+go_service_object_primary_type (GOServiceObject const *service)
+{
+ g_return_val_if_fail (IS_GO_SERVICE_OBJECT (service), NULL);
+ return service->primary_type;
+}
+
+GSList const *
+go_service_object_interfaces (GOServiceObject const *service)
+{
+ g_return_val_if_fail (IS_GO_SERVICE_OBJECT (service), NULL);
+ return service->interfaces;
+}
+
+/****************************************************************************/
+
+gpointer
+go_object_new_valist (char const *type, char const *first_prop, va_list args)
+{
+ GType t = g_type_from_name (type);
+
+ if (t == 0) {
+ GOService *service = plugin_types ?
+ g_hash_table_lookup (plugin_types, type) : NULL;
+ GOPlugin *plugin;
+ GOErrorStack *err;
+
+ g_return_val_if_fail (service != NULL, NULL);
+
+ plugin = go_service_get_plugin (service);
+
+ if (!go_plugin_is_enabled (plugin))
+ return NULL;
+
+ if (!go_plugin_is_loaded (plugin))
+ err = go_plugin_load (plugin);
+
+ t = g_type_from_name (type);
+ if (t == 0)
+ err = gog_error_stack_new (err,
+ _("Loading plugin '%s' that contains the object '%s'"),
+ go_plugin_get_id (plugin), type);;
+ if (err != NULL) {
+ go_error_stack_dump (err);
+ go_error_stack_free (err);
+ return NULL;
+ }
+
+ g_return_val_if_fail (type != 0, NULL);
+ }
+ return g_object_new_valist (t, first_prop, args);
+}
+
+gpointer
+go_object_new (char const *type, char const *first_prop, ...)
+{
+ gpointer res;
+ va_list args;
+
+ va_start (args, first_prop);
+ res = go_object_new_valist (type, first_prop, args);
+ va_end (args);
+
+ return res;
+}
--- /dev/null
+++ lib/goffice/app/go-plugin.h
@@ -0,0 +1,59 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-plugin.h : A GOffice plugin
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_PLUGIN_H
+#define GO_PLUGIN_H
+
+#include <goffice/app/goffice-app.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_PLUGIN_TYPE (go_plugin_get_type ())
+#define GO_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_PLUGIN_TYPE, GOPlugin))
+#define IS_GO_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_PLUGIN_TYPE))
+
+/**
+ * NOTE NOTE NOTE
+ * Inherits from GTypeModule
+ **/
+GType go_plugin_get_type (void);
+
+/* Methods */
+GOErrorStack *go_plugin_load (GOPlugin *plugin);
+
+/* Info */
+gboolean go_plugin_is_enabled (GOPlugin *plugin);
+gboolean go_plugin_is_loaded (GOPlugin *plugin);
+char const *go_plugin_get_dir (GOPlugin *plugin);
+char const *go_plugin_get_id (GOPlugin *plugin);
+char const *go_plugin_get_name (GOPlugin *plugin);
+char const *go_plugin_get_description (GOPlugin *plugin);
+char const *go_plugin_get_textdomain (GOPlugin *plugin);
+GSList *go_plugin_get_dependencies (GOPlugin *plugin);
+GSList *go_plugin_get_services (GOPlugin *plugin);
+
+/* Utilities */
+char *go_plugin_build_filename (GOPlugin *plugin,
+ char const *first_element, ...);
+
+G_END_DECLS
+
+#endif /* GO_PLUGIN_H */
--- /dev/null
+++ lib/goffice/app/go-doc-control.c
@@ -0,0 +1,40 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-doc-control.c : A controller for GOffice Document
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/app/goffice-app.h>
+#include <goffice/app/go-doc-control-impl.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+static void
+go_doc_control_class_init (GObjectClass *klass)
+{
+}
+
+static void
+go_doc_control_init (GODocControl *obj)
+{
+}
+
+GSF_CLASS (GODocControl, go_doc_control,
+ go_doc_control_class_init, go_doc_control_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/app/go-cmd-context.c
@@ -0,0 +1,155 @@
+/*
+ * go-cmd-context.c : Error dispatch utilities.
+ *
+ * Author:
+ * Jody Goldberg <jody at gnome.org>
+ *
+ * (C) 1999-2004 Jody Goldberg
+ */
+#include <goffice/goffice-config.h>
+#include "go-cmd-context-impl.h"
+#include <goffice/app/goffice-app.h>
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+
+#define GCC_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GO_CMD_CONTEXT_TYPE, GOCmdContextClass))
+
+static GError *
+format_message (GQuark id, char const *message)
+{
+ char const *msg = message ? message : "";
+ return g_error_new_literal (id, 0, msg);
+}
+
+void
+go_cmd_context_error (GOCmdContext *context, GError *err)
+{
+ g_return_if_fail (IS_GO_CMD_CONTEXT (context));
+ GCC_CLASS (context)->error.error (context, err);
+}
+
+void
+go_cmd_context_error_info (GOCmdContext *context, GOErrorStack *stack)
+{
+ g_return_if_fail (IS_GO_CMD_CONTEXT (context));
+ GCC_CLASS (context)->error.error_info (context, stack);
+}
+
+void
+go_cmd_context_error_system (GOCmdContext *context, char const *message)
+{
+ GError *err = format_message (go_error_system (), message);
+ go_cmd_context_error (context, err);
+ g_error_free (err);
+}
+
+void
+go_cmd_context_error_import (GOCmdContext *context, char const *message)
+{
+ GError *err = format_message (go_error_import (), message);
+ go_cmd_context_error (context, err);
+ g_error_free (err);
+}
+
+void
+go_cmd_context_error_export (GOCmdContext *context, char const *message)
+{
+ GError *err = format_message (go_error_export (), message);
+ go_cmd_context_error (context, err);
+ g_error_free (err);
+}
+
+void
+go_cmd_context_error_invalid (GOCmdContext *context, char const *msg, char const *val)
+{
+ GError *err = g_error_new (go_error_invalid(), 0, "Invalid %s : '%s'", msg, val);
+ go_cmd_context_error (context, err);
+ g_error_free (err);
+}
+
+GQuark
+go_error_system (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("go_error_system");
+ return quark;
+}
+GQuark
+go_error_import (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("go_error_import");
+ return quark;
+}
+GQuark
+go_error_export (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("go_error_export");
+ return quark;
+}
+GQuark
+go_error_invalid (void)
+{
+ static GQuark quark;
+ if (!quark)
+ quark = g_quark_from_static_string ("go_error_invalid");
+ return quark;
+}
+
+void
+cmd_context_progress_set (GOCmdContext *context, gfloat f)
+{
+ g_return_if_fail (IS_GO_CMD_CONTEXT (context));
+
+ GCC_CLASS (context)->progress_set (context, f);
+}
+
+void
+cmd_context_progress_message_set (GOCmdContext *context, gchar const *msg)
+{
+ g_return_if_fail (IS_GO_CMD_CONTEXT (context));
+
+ if (msg == NULL)
+ msg = " ";
+ GCC_CLASS (context)->progress_message_set (context, msg);
+}
+
+char *
+go_cmd_context_get_password (GOCmdContext *cc, char const *filename)
+{
+ g_return_val_if_fail (IS_GO_CMD_CONTEXT (cc), NULL);
+
+ return GCC_CLASS (cc)->get_password (cc, filename);
+}
+
+void
+go_cmd_context_set_sensitive (GOCmdContext *cc, gboolean sensitive)
+{
+ g_return_if_fail (IS_GO_CMD_CONTEXT (cc));
+
+ GCC_CLASS (cc)->set_sensitive (cc, sensitive);
+}
+
+GType
+go_cmd_context_get_type (void)
+{
+ static GType go_cmd_context_type = 0;
+
+ if (!go_cmd_context_type) {
+ static GTypeInfo const go_cmd_context_info = {
+ sizeof (GOCmdContextClass), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ go_cmd_context_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GOCmdContext", &go_cmd_context_info, 0);
+ }
+
+ return go_cmd_context_type;
+}
+
--- /dev/null
+++ lib/goffice/app/go-cmd-context-impl.h
@@ -0,0 +1,29 @@
+#ifndef GO_CMD_CONTEXT_IMPL_H
+#define GO_CMD_CONTEXT_IMPL_H
+
+#include <goffice/app/go-cmd-context.h>
+
+typedef struct {
+ GTypeInterface base;
+
+ char * (*get_password) (GOCmdContext *gcc,
+ char const *filename);
+ void (*set_sensitive) (GOCmdContext *gcc,
+ gboolean sensitive);
+ struct {
+ void (*error) (GOCmdContext *gcc, GError *err);
+ void (*error_info) (GOCmdContext *gcc, GOErrorStack *stack);
+ } error;
+
+ void (*progress_set) (GOCmdContext *gcc, float val);
+ void (*progress_message_set) (GOCmdContext *gcc, gchar const *msg);
+} GOCmdContextClass;
+
+#define GO_CMD_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GO_CMD_CONTEXT_TYPE, GOCmdContextClass))
+#define IS_GO_CMD_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), GO_CMD_CONTEXT_TYPE))
+
+/* protected, these do not really belong here, they are associated with io-context */
+void cmd_context_progress_set (GOCmdContext *gcc, float f);
+void cmd_context_progress_message_set (GOCmdContext *gcc, char const *msg);
+
+#endif /* GO_CMD_CONTEXT_IMPL_H */
--- /dev/null
+++ lib/goffice/app/go-cmd-context.h
@@ -0,0 +1,51 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-cmd-context.h:
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_CMD_CONTEXT_H
+#define GO_CMD_CONTEXT_H
+
+#include <goffice/app/goffice-app.h>
+#include <glib-object.h>
+
+#define GO_CMD_CONTEXT_TYPE (go_cmd_context_get_type ())
+#define GO_CMD_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_CMD_CONTEXT_TYPE, GOCmdContext))
+#define IS_GO_CMD_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_CMD_CONTEXT_TYPE))
+
+GType go_cmd_context_get_type (void);
+
+void go_cmd_context_error (GOCmdContext *cc, GError *err);
+char *go_cmd_context_get_password (GOCmdContext *cc, char const *fname);
+void go_cmd_context_set_sensitive (GOCmdContext *cc, gboolean flag);
+
+/* utility routines for common errors */
+void go_cmd_context_error_system (GOCmdContext *cc, char const *msg);
+void go_cmd_context_error_import (GOCmdContext *cc, char const *msg);
+void go_cmd_context_error_export (GOCmdContext *cc, char const *msg);
+void go_cmd_context_error_invalid (GOCmdContext *cc,
+ char const *msg, char const *val);
+void go_cmd_context_error_info (GOCmdContext *cc, GOErrorStack *stack);
+
+/* An initial set of std errors */
+GQuark go_error_system (void);
+GQuark go_error_import (void);
+GQuark go_error_export (void);
+GQuark go_error_invalid (void);
+
+#endif /* GO_CMD_CONTEXT_H */
--- /dev/null
+++ lib/goffice/app/go-doc-control-impl.h
@@ -0,0 +1,45 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-doc-impl.h : Implementation details of a GOffice Document
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_DOC_CONTROL_IMPL_H
+#define GO_DOC_CONTROL_IMPL_H
+
+#include <goffice/app/go-doc-control.h>
+
+G_BEGIN_DECLS
+
+struct _GODocControl {
+ GObject base;
+
+ struct {
+ } state[GO_DOC_CONTROL_STATE_MAX];
+};
+
+typedef struct {
+ GObjectClass base;
+} GODocControlClass;
+
+#define GO_DOC_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DOC_CONTROL_TYPE, GODocControlClass))
+#define IS_GO_DOC_CONTROL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DOC_CONTROL_TYPE))
+#define GO_DOC_CONTROL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DOC_CONTROL_TYPE, GODocControlClass))
+
+G_END_DECLS
+
+#endif /* GO_DOC_CONTROL_IMPL_H */
--- /dev/null
+++ lib/goffice/app/go-object.h
@@ -0,0 +1,41 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-object.h : A GOPlugin aware wrapper for g_object_new
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_OBJECT_H
+#define GO_OBJECT_H
+
+#include <glib-object.h>
+
+gpointer go_object_new (char const *type, char const *first_prop, ...);
+gpointer go_object_new_valist (char const *type, char const *first_prop,
+ va_list vargs);
+
+/*****************************************************************************/
+
+#define GO_SERVICE_OBJECT_TYPE (go_service_object_get_type ())
+#define GO_SERVICE_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_SERVICE_OBJECT_TYPE, GOServiceObject))
+#define IS_GO_SERVICE_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_SERVICE_OBJECT_TYPE))
+typedef struct _GOServiceObject GOServiceObject;
+GType go_service_object_get_type (void);
+
+char const *go_service_object_primary_type (GOServiceObject const *service);
+GSList const *go_service_object_interfaces (GOServiceObject const *service);
+
+#endif /* GO_OBJECT_H */
--- /dev/null
+++ lib/goffice/app/go-service.h
@@ -0,0 +1,40 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-service.h : A GOffice plugin
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_SERVICE_H
+#define GO_SERVICE_H
+
+#include <goffice/app/goffice-app.h>
+#include <glib-object.h>
+
+#define GO_SERVICE_TYPE (go_service_get_type ())
+#define GO_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_SERVICE_TYPE, GOService))
+#define IS_GO_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_SERVICE_TYPE))
+
+GType go_service_get_type (void);
+GOPlugin *go_service_get_plugin (GOService const *service);
+
+#define GO_SERVICE_SIMPLE_TYPE (go_service_simple_get_type ())
+#define GO_SERVICE_SIMPLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_SERVICE_SIMPLE_TYPE, GOServiceSimple))
+#define IS_GO_SERVICE_SIMPLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_SERVICE_SIMPLE_TYPE))
+typedef struct _GOServiceSimple GOServiceSimple;
+GType go_service_simple_get_type (void);
+
+#endif /* GO_SERVICE_H */
--- /dev/null
+++ lib/goffice/app/go-doc-control.h
@@ -0,0 +1,43 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-doc.h : A GOffice document
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_DOC_CONTROL_H
+#define GO_DOC_CONTROL_H
+
+#include <goffice/app/goffice-app.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_DOC_CONTROL_TYPE (go_doc_control_get_type ())
+#define GO_DOC_CONTROL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DOC_CONTROL_TYPE, GODocControl))
+#define IS_GO_DOC_CONTROL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DOC_CONTROL_TYPE))
+
+typedef enum {
+ GO_DOC_CONTROL_STATE_NORMAL = 0,
+ GO_DOC_CONTROL_STATE_FULLSCREEN,
+ GO_DOC_CONTROL_STATE_MAX
+} GODocControlState;
+
+GType go_doc_control_get_type (void);
+
+G_END_DECLS
+
+#endif /* GO_DOC_CONTROL_H */
--- /dev/null
+++ lib/goffice/app/go-doc-impl.h
@@ -0,0 +1,42 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-doc-impl.h : Implementation details of a GOffice Document
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_DOC_IMPL_H
+#define GO_DOC_IMPL_H
+
+#include <goffice/app/go-doc.h>
+
+G_BEGIN_DECLS
+
+struct _GODoc {
+ GObject base;
+};
+
+typedef struct {
+ GObjectClass base;
+} GODocClass;
+
+#define GO_DOC_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DOC_TYPE, GODocClass))
+#define IS_GO_DOC_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DOC_TYPE))
+#define GO_DOC_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DOC_TYPE, GODocClass))
+
+G_END_DECLS
+
+#endif /* GO_DOC_IMPL_H */
--- /dev/null
+++ lib/goffice/app/go-doc.c
@@ -0,0 +1,40 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-doc.c : A GOffice Document
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/app/goffice-app.h>
+#include <goffice/app/go-doc-impl.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+static void
+go_doc_class_init (GObjectClass *klass)
+{
+}
+
+static void
+go_doc_init (GODoc *obj)
+{
+}
+
+GSF_CLASS (GODoc, go_doc,
+ go_doc_class_init, go_doc_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/app/go-service-impl.h
@@ -0,0 +1,52 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-service-impl.h : Implementation details for the abstract GOService
+ * interface
+ *
+ * Copyright (C) 2001-2004 Zbigniew Chyla (cyba at gnome.pl)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GO_SERVICE_IMPL_H
+#define GO_SERVICE_IMPL_H
+
+#include <goffice/app/go-service.h>
+#include <glib-object.h>
+#include <libxml/tree.h>
+
+G_BEGIN_DECLS
+
+struct _GOService {
+ GObject base;
+ GOPlugin *plugin;
+};
+typedef struct {
+ GObjectClass base;
+ GOErrorStack *(*read_details) (GOService *service, xmlNode *tree);
+ GOErrorStack *(*activate) (GOService *service);
+ GOErrorStack *(*deactivate) (GOService *service);
+ char *(*description) (GOService *service);
+} GOServiceClass;
+
+struct _GOServiceSimple {
+ GOService base;
+};
+typedef GOServiceClass GOServiceSimpleClass;
+
+G_END_DECLS
+
+#endif /* GO_GRAPH_ITEM_IMPL_H */
+
--- /dev/null
+++ lib/goffice/app/goffice-app.h
@@ -0,0 +1,39 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * goffice-app.h:
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOFFICE_APP_H
+#define GOFFICE_APP_H
+
+#include <glib/gmacros.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GOApp GOApp;
+typedef struct _GODoc GODoc;
+typedef struct _GODocControl GODocControl;
+typedef struct _GOPlugin GOPlugin;
+typedef struct _GOPluginService GOPluginService;
+typedef struct _GOPluginLoader GOPluginLoader;
+typedef struct _GOErrorInfo GOErrorInfo;
+
+G_END_DECLS
+
+#endif /* GOFFICE_GRAPH_H */
--- /dev/null
+++ lib/goffice/app/Makefile.am
@@ -0,0 +1,18 @@
+SUBDIRS =
+
+noinst_LTLIBRARIES = libgoffice-app.la
+
+libgoffice_app_la_SOURCES = \
+ goffice-app.h \
+ go-doc.c \
+ go-doc.h \
+ go-doc-impl.h \
+ go-doc-control.c \
+ go-doc-control.h \
+ go-doc-control-impl.h
+
+libgoffice_app_la_LIBADD = ${GLIB_LIBS}
+
+AM_CFLAGS = ${GLIB_CFLAGS} ${GSF_CFLAGS}
+
+include $(srcdir)/../goffice.mk
Index: gnc-gnome-utils.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome-utils/gnc-gnome-utils.c,v
retrieving revision 1.2.4.8
retrieving revision 1.2.4.9
diff -Lsrc/gnome-utils/gnc-gnome-utils.c -Lsrc/gnome-utils/gnc-gnome-utils.c -u -r1.2.4.8 -r1.2.4.9
--- src/gnome-utils/gnc-gnome-utils.c
+++ src/gnome-utils/gnc-gnome-utils.c
@@ -32,6 +32,8 @@
#include "gnc-html-guppi.h"
#endif
+#include "gnc-html-graph-gog.h"
+
#include "argv-list-converters.h"
#include "gnc-gnome-utils.h"
#include "gnc-html.h"
--- /dev/null
+++ src/gnome-utils/gnc-html-graph-gog.h
@@ -0,0 +1,6 @@
+#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 */
--- /dev/null
+++ src/gnome-utils/gnc-html-graph-gog.c
@@ -0,0 +1,437 @@
+#include "config.h"
+
+#include <string.h>
+
+#include <gtkhtml/gtkhtml.h>
+#include <gtkhtml/gtkhtml-embedded.h>
+
+#include "gnc-html-graph-gog.h"
+#include "gnc-html.h"
+#include "gnc-trace.h"
+
+#include "goffice.h"
+#include "graph/gog-graph.h"
+#include "graph/gog-object.h"
+#include "graph/gog-plot.h"
+#include "graph/gog-series.h"
+#include "graph/go-data-simple.h"
+#include "utils/go-color.h"
+#include "graph/gog-renderer-pixbuf.h"
+#include "graph/gog-renderer-svg.h"
+#include "graph/gog-data-set.h"
+#include "graph/gog-styled-object.h"
+#include "graph/gog-style.h"
+
+#include <gsf/gsf.h>
+#include <gsf/gsf-output-memory.h>
+
+static short module = MOD_GUI;
+
+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_scatter(gnc_html * html, GtkHTMLEmbedded * eb, gpointer d);
+
+void
+gnc_html_graph_gog_init(void) {
+
+ PINFO( "init gog graphing" );
+
+ libgoffice_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 );
+}
+
+static double *
+read_doubles(const char * string, int nvalues) {
+ int n;
+ int choffset=0;
+ int accum = 0;
+ double * retval = g_new0(double, nvalues);
+
+ //gnc_push_locale ("C");
+
+ for(n=0; n<nvalues; n++) {
+ sscanf(string+accum, "%le%n", &retval[n], &choffset);
+ accum += choffset;
+ }
+
+ //gnc_pop_locale ();
+
+ return retval;
+}
+
+static char **
+read_strings(const char * string, int nvalues) {
+ int n;
+ int choffset=0;
+ int accum = 0;
+ char ** retval = g_new0(char *, nvalues);
+ char thischar;
+ const char * inptr = string;
+ int escaped = FALSE;
+
+ for (n=0; n < nvalues; n++) {
+ retval[n] = g_new0(char, strlen(string+accum)+1);
+ retval[n][0] = 0;
+ choffset = 0;
+ while ((thischar = *inptr) != 0) {
+ if (thischar == '\\') {
+ escaped = TRUE;
+ inptr++;
+ }
+ else if ((thischar != ' ') || escaped) {
+ retval[n][choffset] = thischar;
+ retval[n][choffset+1] = 0;
+ choffset++;
+ escaped = FALSE;
+ inptr++;
+ }
+ else {
+ /* an unescaped space */
+ escaped = FALSE;
+ inptr++;
+ break;
+ }
+ }
+ accum += choffset;
+ /* printf("retval[%d] = '%s'\n", n, retval[n]); */
+ }
+
+ return retval;
+}
+
+static void
+free_strings(char ** strings, int nstrings) {
+ int count;
+
+ if (!strings) return;
+
+ for (count=0; count < nstrings; count++) {
+ g_free(strings[count]);
+ strings[count] = NULL;
+ }
+ g_free(strings);
+}
+
+static void
+addPixbufGraphWidget( GtkHTMLEmbedded *eb, GogObject *graph )
+{
+ GtkWidget *widget;
+ GogRenderer *pixbufRend;
+ GdkPixbuf *buf;
+ gboolean updateStatus;
+
+ // 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 0
+ // example SVG use. Also, nice for debugging.
+ {
+ GsfOutput *mem;
+ gboolean output;
+
+ mem = gsf_output_memory_new();
+ output = gog_graph_export_to_svg( graph, mem, eb->width, eb->height, 1. );
+ printf( "svg: [%s]\n", (guchar*)gsf_output_memory_get_bytes( GSF_OUTPUT_MEMORY(mem) ) );
+ }
+#endif // 0
+
+ pixbufRend = g_object_new( GOG_RENDERER_PIXBUF_TYPE,
+ "model", graph,
+ NULL );
+ updateStatus = gog_renderer_pixbuf_update( GOG_RENDERER_PIXBUF(pixbufRend), eb->width, eb->height, 1. );
+ buf = gog_renderer_pixbuf_get(GOG_RENDERER_PIXBUF(pixbufRend));
+ 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_usize(GTK_WIDGET(eb), eb->width, eb->height);
+}
+
+
+/*
+ * 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 int
+handle_piechart(gnc_html * html, GtkHTMLEmbedded * eb, gpointer d)
+{
+ GogObject *graph, *chart, *tmp, *legend;
+ GogPlot *plot;
+ GogSeries *series;
+ GOData *labelData, *sliceData;
+ int datasize;
+ double *data = NULL;
+ char **labels = NULL, **colors = NULL;
+
+ // First, 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 );
+ sscanf( datasizeStr, "%d", &datasize );
+ data = read_doubles( dataStr, datasize );
+ labels = read_strings( labelsStr, datasize );
+ colors = read_strings( colorStr, datasize );
+ }
+
+ graph = g_object_new( GOG_GRAPH_TYPE, NULL );
+ GOG_STYLED_OBJECT(graph)->style->outline.width = 5;
+ GOG_STYLED_OBJECT(graph)->style->outline.color = RGBA_BLACK;
+
+ chart = gog_object_add_by_name( graph, "Chart", NULL );
+ plot = gog_plot_new_by_name( "GogPiePlot" );
+ if ( !plot )
+ {
+ // FIXME - log betterer
+ printf( "plugin not loaded" );
+ return FALSE;
+ }
+ g_object_set (G_OBJECT (plot),
+ NULL);
+ gog_object_add_by_name( chart, "Plot", GOG_OBJECT(plot) );
+ series = gog_plot_new_series( plot );
+ labelData = go_data_vector_str_new( labels, datasize );
+ gog_series_set_dim( series, 0, labelData, NULL );
+ go_data_emit_changed (GO_DATA (labelData));
+
+ sliceData = go_data_vector_val_new( data, datasize );
+ gog_series_set_dim( series, 1, sliceData, NULL );
+ go_data_emit_changed (GO_DATA (sliceData));
+
+ // fixme: colors
+ {
+ char *titleParam;
+ GOData *title;
+
+ titleParam = g_hash_table_lookup( eb->params, "title" );
+
+ tmp = gog_object_add_by_name (chart, "Title", NULL);
+ gog_object_set_pos (tmp, GOG_POSITION_N | GOG_POSITION_ALIGN_START);
+ title = go_data_scalar_str_new (titleParam, FALSE);
+ gog_dataset_set_dim (GOG_DATASET (tmp), 0, title, NULL);
+ }
+
+ legend = gog_object_add_by_name( chart, "Legend", NULL );
+
+ addPixbufGraphWidget( eb, graph );
+
+ PINFO( "piechart rendered" );
+ return TRUE;
+}
+
+/**
+ * datarows:int
+ * datacols:int
+ * data:doubles[], datarows*datacols
+ * x_axis_label:string
+ * y_axis_label:string
+ * col_labels:string[]
+ * row_labels:string[]
+ * col_colors:string
+ * rotate_row_labels:boolean
+ * stacked:boolean
+ **/
+static int
+handle_barchart(gnc_html * html, GtkHTMLEmbedded * eb, gpointer d)
+{
+ GogObject *graph, *chart, *tmp, *legend;
+ GogPlot *plot;
+ GogSeries *series;
+ GOData *labelData, *sliceData;
+ int datarows, datacols;
+ double *data = NULL;
+ char **col_labels = NULL, **row_labels = NULL, **col_colors = NULL;
+ //char *x_axis_label, *y_axis_label;
+ //gboolean rotate_row_labels;
+ gboolean stacked = FALSE;
+ char *barType = "normal";
+ int barOverlap = 0 /*percent*/; // seperate bars; no overlap.
+
+ // First, parse data from the text-ized params.
+ {
+ char *datarowsStr, *datacolsStr, *dataStr, *colLabelsStr, *rowLabelsStr, *colColorsStr, *stackedStr;
+ gint stackedInt;
+
+ datarowsStr = g_hash_table_lookup(eb->params, "data_rows");
+ datacolsStr = g_hash_table_lookup(eb->params, "data_cols");
+ dataStr = g_hash_table_lookup(eb->params, "data" );
+ colLabelsStr = g_hash_table_lookup(eb->params, "col_labels");
+ rowLabelsStr = g_hash_table_lookup(eb->params, "row_labels");
+ colColorsStr = g_hash_table_lookup(eb->params, "col_colors");
+ stackedStr = NULL;
+ stackedStr = g_hash_table_lookup(eb->params, "stacked");
+ sscanf( stackedStr, "%d", &stackedInt );
+ stacked = (gboolean)stackedInt;
+
+#if 0 // too strong at the moment.
+ g_return_val_if_fail( datarowsStr != NULL
+ && datacolsStr != NULL
+ && dataStr != NULL
+ && colLabelsStr != NULL
+ && rowLabelsStr != NULL
+ && colColorsStr != NULL, FALSE );
+#endif // 0
+ sscanf( datarowsStr, "%d", &datarows );
+ sscanf( datacolsStr, "%d", &datacols );
+ data = read_doubles( dataStr, datarows*datacols );
+ row_labels = read_strings( rowLabelsStr, datarows );
+ col_labels = read_strings( colLabelsStr, datacols );
+ col_colors = read_strings( colColorsStr, datacols );
+ }
+
+ graph = g_object_new( GOG_GRAPH_TYPE, NULL );
+ chart = gog_object_add_by_name( graph, "Chart", NULL );
+ // series => bars [gnc:cols]
+ // elements => segments [gnc:rows]
+ plot = gog_plot_new_by_name( "GogBarColPlot" );
+ if ( !plot )
+ {
+ // FIXME - log betterer
+ printf( "plugin not loaded" );
+ return FALSE;
+ }
+ if ( stacked )
+ {
+ barType = "stacked";
+ barOverlap = 100 /*percent*/;
+ // when stacked, we want the bars on _top_ of eachother.
+ }
+ g_object_set (G_OBJECT (plot),
+ "vary_style_by_element", TRUE,
+ "type", barType,
+ "overlap_percentage", barOverlap,
+ NULL);
+ gog_object_add_by_name( chart, "Plot", GOG_OBJECT(plot) );
+
+ labelData = go_data_vector_str_new( (char const * const *)col_labels, datacols );
+ {
+ int i;
+ // foreach row:
+ // series = row
+
+ for ( i = 0; i < datacols; i++ )
+ {
+ series = gog_plot_new_series( plot );
+
+ g_object_ref( labelData );
+ gog_series_set_dim( series, 0, labelData, NULL );
+ go_data_emit_changed (GO_DATA (labelData));
+
+ sliceData = go_data_vector_val_new( data + (i*datarows), datarows );
+ gog_series_set_dim( series, 1, sliceData, NULL );
+ go_data_emit_changed (GO_DATA (sliceData));
+ }
+ }
+
+ // fixme: colors
+ {
+ char *titleParam;
+ GOData *title;
+
+ titleParam = g_hash_table_lookup( eb->params, "title" );
+
+ tmp = gog_object_add_by_name (chart, "Title", NULL);
+ gog_object_set_pos (tmp, GOG_POSITION_N | GOG_POSITION_ALIGN_START);
+ title = go_data_scalar_str_new (titleParam, FALSE);
+ gog_dataset_set_dim (GOG_DATASET (tmp), 0, title, NULL);
+ }
+
+ legend = gog_object_add_by_name( chart, "Legend", NULL );
+
+ // we need to do this twice for the barchart... :p
+ gog_object_update( GOG_OBJECT(graph) );
+
+ addPixbufGraphWidget( eb, graph );
+
+ PINFO( "barchart rendered." );
+ return TRUE;
+}
+
+static int
+handle_scatter(gnc_html * html, GtkHTMLEmbedded * eb, gpointer d)
+{
+ GogObject *graph, *chart, *legend;
+ GogPlot *plot;
+ GogSeries *series;
+ GOData *sliceData;
+ char *title, *subtitle, *xAxisLabel, *yAxisLabel;
+ int datasize;
+ double *xData, *yData;
+
+ {
+ char *datasizeStr, *xDataStr, *yDataStr;
+
+ title = g_hash_table_lookup( eb->params, "title" );
+ subtitle = g_hash_table_lookup( eb->params, "subtitle" );
+
+ datasizeStr = g_hash_table_lookup( eb->params, "datasize" );
+ sscanf( datasizeStr, "%d", &datasize );
+
+ 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 );
+
+ xAxisLabel = g_hash_table_lookup( eb->params, "x_axis_label" );
+ yAxisLabel = g_hash_table_lookup( eb->params, "y_axis_label" );
+ }
+
+ graph = g_object_new( GOG_GRAPH_TYPE, NULL );
+ chart = gog_object_add_by_name( graph, "Chart", NULL );
+ plot = gog_plot_new_by_name( "GogXYPlot" );
+ if ( !plot )
+ {
+ // FIXME - log betterer
+ printf( "plugin not loaded" );
+ return FALSE;
+ }
+ //g_object_set (G_OBJECT (plot), NULL);
+ gog_object_add_by_name( chart, "Plot", GOG_OBJECT(plot) );
+
+ series = gog_plot_new_series( plot );
+
+ sliceData = go_data_vector_val_new( xData, datasize );
+ gog_series_set_dim( series, 0, sliceData, NULL );
+ go_data_emit_changed (GO_DATA (sliceData));
+
+ sliceData = go_data_vector_val_new( yData, datasize );
+ gog_series_set_dim( series, 1, sliceData, NULL );
+ go_data_emit_changed (GO_DATA (sliceData));
+
+ // fixme: colors
+ // fixme: title, subtitle
+ // fixme: axis labels
+
+ //legend = gog_object_add_by_name( chart, "Legend", NULL );
+
+ // And twice for the scatter, too... :p
+ gog_object_update( GOG_OBJECT(graph) );
+
+ addPixbufGraphWidget( eb, graph );
+
+ PINFO( "scatter rendered." );
+
+ return TRUE;
+}
Index: gnc-html.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome-utils/gnc-html.c,v
retrieving revision 1.23.4.9
retrieving revision 1.23.4.10
diff -Lsrc/gnome-utils/gnc-html.c -Lsrc/gnome-utils/gnc-html.c -u -r1.23.4.9 -r1.23.4.10
--- src/gnome-utils/gnc-html.c
+++ src/gnome-utils/gnc-html.c
@@ -53,6 +53,7 @@
#include "gnc-html.h"
#include "gnc-http.h"
#include "gnc-html-history.h"
+#include "gnc-html-graph-gog.h"
#include "gnc-ui.h"
#include "gnc-ui-util.h"
#include "messages.h"
@@ -380,8 +381,14 @@
{ URL_TYPE_OTHER, "" },
{ NULL, NULL }};
+ PINFO( "initializing gnc_html..." );
+ printf( "initializing gnc_html...\n" );
+
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();
}
Index: Makefile.am
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome-utils/Makefile.am,v
retrieving revision 1.48.2.25
retrieving revision 1.48.2.26
diff -Lsrc/gnome-utils/Makefile.am -Lsrc/gnome-utils/Makefile.am -u -r1.48.2.25 -r1.48.2.26
--- src/gnome-utils/Makefile.am
+++ src/gnome-utils/Makefile.am
@@ -14,6 +14,9 @@
-I${top_srcdir}/src/backend/file \
-I${top_srcdir}/src/network-utils \
-I${top_srcdir}/src/app-utils \
+ -I${top_srcdir}/lib/goffice \
+ -I${top_srcdir}/lib/goffice/split \
+ -I${top_srcdir}/lib \
-I${top_srcdir}/src \
${GUILE_INCS} \
${LIBGUPPI_CFLAGS} \
@@ -22,7 +25,8 @@
${GNOME_PRINT_CFLAGS} \
${GNOME_CFLAGS} \
${GTKHTML_CFLAGS} \
- ${G_WRAP_COMPILE_ARGS}
+ ${G_WRAP_COMPILE_ARGS} \
+ ${GSF_CFLAGS}
libgncmod_gnome_utils_la_SOURCES = \
QuickFill.c \
@@ -57,6 +61,7 @@
gnc-gui-query.c \
gnc-html-history.c \
gnc-html-guppi.c \
+ gnc-html-graph-gog.c \
gnc-html.c \
gnc-icons.c \
gnc-mdi-utils.c \
@@ -108,6 +113,7 @@
gnc-gui-query.h \
gnc-html-history.h \
gnc-html-guppi.h \
+ gnc-html-graph-gog.h \
gnc-html.h \
gnc-icons.h \
gnc-mdi-utils.h \
@@ -147,6 +153,7 @@
${top_builddir}/src/calculation/libgncmod-calculation.la \
${top_builddir}/src/network-utils/libgncmod-network-utils.la \
${top_builddir}/src/app-utils/libgncmod-app-utils.la \
+ ${top_builddir}/lib/goffice/libgoffice.la \
${GUILE_LIBS} \
${LIBGUPPI_LIBS} \
${GNOME_LIBS} \
@@ -156,7 +163,9 @@
${GLADE_LIBS} \
${GUILE_LIBS} \
${GLIB_LIBS} \
- ${DB_LIBS}
+ ${DB_LIBS} \
+ ${GSF_LIBS} \
+ ${XML_LIBS}
libgw_gnome_utils_la_SOURCES = gw-gnome-utils.c
libgw_gnome_utils_la_LDFLAGS = -module
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/internal.h
@@ -0,0 +1,683 @@
+/* File import from pcre to goffice by import-pcre. Do not edit. */
+
+/* This file has been programatically changed. */
+/* This makes the following file fall under GPL license, see internal.c. */
+
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+
+/* This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10 at cam.ac.uk>
+
+ Copyright (c) 1997-2003 University of Cambridge
+
+-----------------------------------------------------------------------------
+Permission is granted to anyone to use this software for any purpose on any
+computer system, and to redistribute it freely, subject to the following
+restrictions:
+
+1. This software 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.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+4. If PCRE is embedded in any software that is released under the GNU
+ General Purpose Licence (GPL), then the terms of that licence shall
+ supersede any condition above with which it is incompatible.
+-----------------------------------------------------------------------------
+*/
+
+/* This header contains definitions that are shared between the different
+modules, but which are not relevant to the outside. */
+
+/* Get the definitions provided by running "configure" */
+
+#include <config.h>
+
+/* Standard C headers plus the external interface definition. The only time
+setjmp and stdarg are used is when NO_RECURSE is set. */
+
+/* Whatever the question is, ctype.h is not the answer. */
+/* #include <ctype.h> */
+#include <limits.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef PCRE_SPY
+#define PCRE_DEFINITION /* Win32 __declspec(export) trigger for .dll */
+#endif
+
+#include "pcre.h"
+
+/* When compiling for use with the Virtual Pascal compiler, these functions
+need to have their names changed. PCRE must be compiled with the -DVPCOMPAT
+option on the command line. */
+
+#ifdef VPCOMPAT
+#define strncmp(s1,s2,m) _strncmp(s1,s2,m)
+#define memcpy(d,s,n) _memcpy(d,s,n)
+#define memmove(d,s,n) _memmove(d,s,n)
+#define memset(s,c,n) _memset(s,c,n)
+#else /* VPCOMPAT */
+
+/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(),
+define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY
+is set. Otherwise, include an emulating function for those systems that have
+neither (there some non-Unix environments where this is the case). This assumes
+that all calls to memmove are moving strings upwards in store, which is the
+case in PCRE. */
+
+#if ! HAVE_MEMMOVE
+#undef memmove /* some systems may have a macro */
+#if HAVE_BCOPY
+#define memmove(a, b, c) bcopy(b, a, c)
+#else /* HAVE_BCOPY */
+void *
+pcre_memmove(unsigned char *dest, const unsigned char *src, size_t n)
+{
+int i;
+dest += n;
+src += n;
+for (i = 0; i < n; ++i) *(--dest) = *(--src);
+}
+#define memmove(a, b, c) pcre_memmove(a, b, c)
+#endif /* not HAVE_BCOPY */
+#endif /* not HAVE_MEMMOVE */
+#endif /* not VPCOMPAT */
+
+
+/* PCRE keeps offsets in its compiled code as 2-byte quantities by default.
+These are used, for example, to link from the start of a subpattern to its
+alternatives and its end. The use of 2 bytes per offset limits the size of the
+compiled regex to around 64K, which is big enough for almost everybody.
+However, I received a request for an even bigger limit. For this reason, and
+also to make the code easier to maintain, the storing and loading of offsets
+from the byte string is now handled by the macros that are defined here.
+
+The macros are controlled by the value of LINK_SIZE. This defaults to 2 in
+the config.h file, but can be overridden by using -D on the command line. This
+is automated on Unix systems via the "configure" command. */
+
+#if LINK_SIZE == 2
+
+#define PUT(a,n,d) \
+ (a[n] = (d) >> 8), \
+ (a[(n)+1] = (d) & 255)
+
+#define GET(a,n) \
+ (((a)[n] << 8) | (a)[(n)+1])
+
+#define MAX_PATTERN_SIZE (1 << 16)
+
+
+#elif LINK_SIZE == 3
+
+#define PUT(a,n,d) \
+ (a[n] = (d) >> 16), \
+ (a[(n)+1] = (d) >> 8), \
+ (a[(n)+2] = (d) & 255)
+
+#define GET(a,n) \
+ (((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2])
+
+#define MAX_PATTERN_SIZE (1 << 24)
+
+
+#elif LINK_SIZE == 4
+
+#define PUT(a,n,d) \
+ (a[n] = (d) >> 24), \
+ (a[(n)+1] = (d) >> 16), \
+ (a[(n)+2] = (d) >> 8), \
+ (a[(n)+3] = (d) & 255)
+
+#define GET(a,n) \
+ (((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3])
+
+#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */
+
+
+#else
+#error LINK_SIZE must be either 2, 3, or 4
+#endif
+
+
+/* Convenience macro defined in terms of the others */
+
+#define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE
+
+
+/* PCRE uses some other 2-byte quantities that do not change when the size of
+offsets changes. There are used for repeat counts and for other things such as
+capturing parenthesis numbers in back references. */
+
+#define PUT2(a,n,d) \
+ a[n] = (d) >> 8; \
+ a[(n)+1] = (d) & 255
+
+#define GET2(a,n) \
+ (((a)[n] << 8) | (a)[(n)+1])
+
+#define PUT2INC(a,n,d) PUT2(a,n,d), a += 2
+
+
+/* In case there is no definition of offsetof() provided - though any proper
+Standard C system should have one. */
+
+#ifndef offsetof
+#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field))
+#endif
+
+/* These are the public options that can change during matching. */
+
+#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL)
+
+/* Private options flags start at the most significant end of the four bytes,
+but skip the top bit so we can use ints for convenience without getting tangled
+with negative values. The public options defined in pcre.h start at the least
+significant end. Make sure they don't overlap, though now that we have expanded
+to four bytes there is plenty of space. */
+
+#define PCRE_FIRSTSET 0x40000000 /* first_byte is set */
+#define PCRE_REQCHSET 0x20000000 /* req_byte is set */
+#define PCRE_STARTLINE 0x10000000 /* start after \n for multiline */
+#define PCRE_ICHANGED 0x08000000 /* i option changes within regex */
+
+/* Options for the "extra" block produced by pcre_study(). */
+
+#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */
+
+/* Masks for identifying the public options which are permitted at compile
+time, run time or study time, respectively. */
+
+#define PUBLIC_OPTIONS \
+ (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \
+ PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \
+ PCRE_NO_AUTO_CAPTURE|PCRE_NO_UTF8_CHECK)
+
+#define PUBLIC_EXEC_OPTIONS \
+ (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK)
+
+#define PUBLIC_STUDY_OPTIONS 0 /* None defined */
+
+/* Magic number to provide a small check against being handed junk. */
+
+#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */
+
+/* Negative values for the firstchar and reqchar variables */
+
+#define REQ_UNSET (-2)
+#define REQ_NONE (-1)
+
+/* Flags added to firstbyte or reqbyte; a "non-literal" item is either a
+variable-length repeat, or a anything other than literal characters. */
+
+#define REQ_CASELESS 0x0100 /* indicates caselessness */
+#define REQ_VARY 0x0200 /* reqbyte followed non-literal item */
+
+/* Miscellaneous definitions */
+
+typedef int BOOL;
+
+#define FALSE 0
+#define TRUE 1
+
+/* Escape items that are just an encoding of a particular data value. Note that
+ESC_n is defined as yet another macro, which is set in config.h to either \n
+(the default) or \r (which some people want). */
+
+#ifndef ESC_e
+#define ESC_e 27
+#endif
+
+#ifndef ESC_f
+#define ESC_f '\f'
+#endif
+
+#ifndef ESC_n
+#define ESC_n NEWLINE
+#endif
+
+#ifndef ESC_r
+#define ESC_r '\r'
+#endif
+
+/* We can't officially use ESC_t because it is a POSIX reserved identifier
+(presumably because of all the others like size_t). */
+
+#ifndef ESC_tee
+#define ESC_tee '\t'
+#endif
+
+/* These are escaped items that aren't just an encoding of a particular data
+value such as \n. They must have non-zero values, as check_escape() returns
+their negation. Also, they must appear in the same order as in the opcode
+definitions below, up to ESC_z. There's a dummy for OP_ANY because it
+corresponds to "." rather than an escape sequence. The final one must be
+ESC_REF as subsequent values are used for \1, \2, \3, etc. There is are two
+tests in the code for an escape greater than ESC_b and less than ESC_Z to
+detect the types that may be repeated. These are the types that consume a
+character. If any new escapes are put in between that don't consume a
+character, that code will have to change. */
+
+enum { ESC_A = 1, ESC_G, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W,
+ ESC_w, ESC_dum1, ESC_C, ESC_Z, ESC_z, ESC_E, ESC_Q, ESC_REF };
+
+/* Flag bits and data types for the extended class (OP_XCLASS) for classes that
+contain UTF-8 characters with values greater than 255. */
+
+#define XCL_NOT 0x01 /* Flag: this is a negative class */
+#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */
+
+#define XCL_END 0 /* Marks end of individual items */
+#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */
+#define XCL_RANGE 2 /* A range (two multibyte chars) follows */
+
+
+/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets
+that extract substrings. Starting from 1 (i.e. after OP_END), the values up to
+OP_EOD must correspond in order to the list of escapes immediately above.
+Note that whenever this list is updated, the two macro definitions that follow
+must also be updated to match. */
+
+enum {
+ OP_END, /* 0 End of pattern */
+
+ /* Values corresponding to backslashed metacharacters */
+
+ OP_SOD, /* 1 Start of data: \A */
+ OP_SOM, /* 2 Start of match (subject + offset): \G */
+ OP_NOT_WORD_BOUNDARY, /* 3 \B */
+ OP_WORD_BOUNDARY, /* 4 \b */
+ OP_NOT_DIGIT, /* 5 \D */
+ OP_DIGIT, /* 6 \d */
+ OP_NOT_WHITESPACE, /* 7 \S */
+ OP_WHITESPACE, /* 8 \s */
+ OP_NOT_WORDCHAR, /* 9 \W */
+ OP_WORDCHAR, /* 10 \w */
+ OP_ANY, /* 11 Match any character */
+ OP_ANYBYTE, /* 12 Match any byte (\C); different to OP_ANY for UTF-8 */
+ OP_EODN, /* 13 End of data or \n at end of data: \Z. */
+ OP_EOD, /* 14 End of data: \z */
+
+ OP_OPT, /* 15 Set runtime options */
+ OP_CIRC, /* 16 Start of line - varies with multiline switch */
+ OP_DOLL, /* 17 End of line - varies with multiline switch */
+ OP_CHARS, /* 18 Match string of characters */
+ OP_NOT, /* 19 Match anything but the following char */
+
+ OP_STAR, /* 20 The maximizing and minimizing versions of */
+ OP_MINSTAR, /* 21 all these opcodes must come in pairs, with */
+ OP_PLUS, /* 22 the minimizing one second. */
+ OP_MINPLUS, /* 23 This first set applies to single characters */
+ OP_QUERY, /* 24 */
+ OP_MINQUERY, /* 25 */
+ OP_UPTO, /* 26 From 0 to n matches */
+ OP_MINUPTO, /* 27 */
+ OP_EXACT, /* 28 Exactly n matches */
+
+ OP_NOTSTAR, /* 29 The maximizing and minimizing versions of */
+ OP_NOTMINSTAR, /* 30 all these opcodes must come in pairs, with */
+ OP_NOTPLUS, /* 31 the minimizing one second. */
+ OP_NOTMINPLUS, /* 32 This set applies to "not" single characters */
+ OP_NOTQUERY, /* 33 */
+ OP_NOTMINQUERY, /* 34 */
+ OP_NOTUPTO, /* 35 From 0 to n matches */
+ OP_NOTMINUPTO, /* 36 */
+ OP_NOTEXACT, /* 37 Exactly n matches */
+
+ OP_TYPESTAR, /* 38 The maximizing and minimizing versions of */
+ OP_TYPEMINSTAR, /* 39 all these opcodes must come in pairs, with */
+ OP_TYPEPLUS, /* 40 the minimizing one second. These codes must */
+ OP_TYPEMINPLUS, /* 41 be in exactly the same order as those above. */
+ OP_TYPEQUERY, /* 42 This set applies to character types such as \d */
+ OP_TYPEMINQUERY, /* 43 */
+ OP_TYPEUPTO, /* 44 From 0 to n matches */
+ OP_TYPEMINUPTO, /* 45 */
+ OP_TYPEEXACT, /* 46 Exactly n matches */
+
+ OP_CRSTAR, /* 47 The maximizing and minimizing versions of */
+ OP_CRMINSTAR, /* 48 all these opcodes must come in pairs, with */
+ OP_CRPLUS, /* 49 the minimizing one second. These codes must */
+ OP_CRMINPLUS, /* 50 be in exactly the same order as those above. */
+ OP_CRQUERY, /* 51 These are for character classes and back refs */
+ OP_CRMINQUERY, /* 52 */
+ OP_CRRANGE, /* 53 These are different to the three seta above. */
+ OP_CRMINRANGE, /* 54 */
+
+ OP_CLASS, /* 55 Match a character class, chars < 256 only */
+ OP_NCLASS, /* 56 Same, but the bitmap was created from a negative
+ class - the difference is relevant only when a UTF-8
+ character > 255 is encountered. */
+
+ OP_XCLASS, /* 57 Extended class for handling UTF-8 chars within the
+ class. This does both positive and negative. */
+
+ OP_REF, /* 58 Match a back reference */
+ OP_RECURSE, /* 59 Match a numbered subpattern (possibly recursive) */
+ OP_CALLOUT, /* 60 Call out to external function if provided */
+
+ OP_ALT, /* 61 Start of alternation */
+ OP_KET, /* 62 End of group that doesn't have an unbounded repeat */
+ OP_KETRMAX, /* 63 These two must remain together and in this */
+ OP_KETRMIN, /* 64 order. They are for groups the repeat for ever. */
+
+ /* The assertions must come before ONCE and COND */
+
+ OP_ASSERT, /* 65 Positive lookahead */
+ OP_ASSERT_NOT, /* 66 Negative lookahead */
+ OP_ASSERTBACK, /* 67 Positive lookbehind */
+ OP_ASSERTBACK_NOT, /* 68 Negative lookbehind */
+ OP_REVERSE, /* 69 Move pointer back - used in lookbehind assertions */
+
+ /* ONCE and COND must come after the assertions, with ONCE first, as there's
+ a test for >= ONCE for a subpattern that isn't an assertion. */
+
+ OP_ONCE, /* 70 Once matched, don't back up into the subpattern */
+ OP_COND, /* 71 Conditional group */
+ OP_CREF, /* 72 Used to hold an extraction string number (cond ref) */
+
+ OP_BRAZERO, /* 73 These two must remain together and in this */
+ OP_BRAMINZERO, /* 74 order. */
+
+ OP_BRANUMBER, /* 75 Used for extracting brackets whose number is greater
+ than can fit into an opcode. */
+
+ OP_BRA /* 76 This and greater values are used for brackets that
+ extract substrings up to a basic limit. After that,
+ use is made of OP_BRANUMBER. */
+};
+
+/* WARNING: There is an implicit assumption in study.c that all opcodes are
+less than 128 in value. This makes handling UTF-8 character sequences easier.
+*/
+
+
+/* This macro defines textual names for all the opcodes. There are used only
+for debugging, in pcre.c when DEBUG is defined, and also in pcretest.c. The
+macro is referenced only in printint.c. */
+
+#define OP_NAME_LIST \
+ "End", "\\A", "\\G", "\\B", "\\b", "\\D", "\\d", \
+ "\\S", "\\s", "\\W", "\\w", "Any", "Anybyte", "\\Z", "\\z", \
+ "Opt", "^", "$", "chars", "not", \
+ "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \
+ "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \
+ "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \
+ "*", "*?", "+", "+?", "?", "??", "{", "{", \
+ "class", "nclass", "xclass", "Ref", "Recurse", "Callout", \
+ "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", \
+ "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cond ref",\
+ "Brazero", "Braminzero", "Branumber", "Bra"
+
+
+/* This macro defines the length of fixed length operations in the compiled
+regex. The lengths are used when searching for specific things, and also in the
+debugging printing of a compiled regex. We use a macro so that it can be
+incorporated both into pcre.c and pcretest.c without being publicly exposed.
+
+As things have been extended, some of these are no longer fixed lenths, but are
+minima instead. For example, the length of a single-character repeat may vary
+in UTF-8 mode. The code that uses this table must know about such things. */
+
+#define OP_LENGTHS \
+ 1, /* End */ \
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* \A, \G, \B, \B, \D, \d, \S, \s, \W, \w */ \
+ 1, 1, 1, 1, 2, 1, 1, /* Any, Anybyte, \Z, \z, Opt, ^, $ */ \
+ 2, /* Chars - the minimum length */ \
+ 2, /* not */ \
+ /* Positive single-char repeats ** These are */ \
+ 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in */ \
+ 4, 4, 4, /* upto, minupto, exact ** UTF-8 mode */ \
+ /* Negative single-char repeats - only for chars < 256 */ \
+ 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \
+ 4, 4, 4, /* NOT upto, minupto, exact */ \
+ /* Positive type repeats */ \
+ 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \
+ 4, 4, 4, /* Type upto, minupto, exact */ \
+ /* Character class & ref repeats */ \
+ 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \
+ 5, 5, /* CRRANGE, CRMINRANGE */ \
+ 33, /* CLASS */ \
+ 33, /* NCLASS */ \
+ 0, /* XCLASS - variable length */ \
+ 3, /* REF */ \
+ 1+LINK_SIZE, /* RECURSE */ \
+ 2, /* CALLOUT */ \
+ 1+LINK_SIZE, /* Alt */ \
+ 1+LINK_SIZE, /* Ket */ \
+ 1+LINK_SIZE, /* KetRmax */ \
+ 1+LINK_SIZE, /* KetRmin */ \
+ 1+LINK_SIZE, /* Assert */ \
+ 1+LINK_SIZE, /* Assert not */ \
+ 1+LINK_SIZE, /* Assert behind */ \
+ 1+LINK_SIZE, /* Assert behind not */ \
+ 1+LINK_SIZE, /* Reverse */ \
+ 1+LINK_SIZE, /* Once */ \
+ 1+LINK_SIZE, /* COND */ \
+ 3, /* CREF */ \
+ 1, 1, /* BRAZERO, BRAMINZERO */ \
+ 3, /* BRANUMBER */ \
+ 1+LINK_SIZE /* BRA */ \
+
+
+/* The highest extraction number before we have to start using additional
+bytes. (Originally PCRE didn't have support for extraction counts highter than
+this number.) The value is limited by the number of opcodes left after OP_BRA,
+i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional
+opcodes. */
+
+#define EXTRACT_BASIC_MAX 150
+
+/* A magic value for OP_CREF to indicate the "in recursion" condition. */
+
+#define CREF_RECURSE 0xffff
+
+/* The texts of compile-time error messages are defined as macros here so that
+they can be accessed by the POSIX wrapper and converted into error codes. Yes,
+I could have used error codes in the first place, but didn't feel like changing
+just to accommodate the POSIX wrapper. */
+
+#define ERR1 "\\ at end of pattern"
+#define ERR2 "\\c at end of pattern"
+#define ERR3 "unrecognized character follows \\"
+#define ERR4 "numbers out of order in {} quantifier"
+#define ERR5 "number too big in {} quantifier"
+#define ERR6 "missing terminating ] for character class"
+#define ERR7 "invalid escape sequence in character class"
+#define ERR8 "range out of order in character class"
+#define ERR9 "nothing to repeat"
+#define ERR10 "operand of unlimited repeat could match the empty string"
+#define ERR11 "internal error: unexpected repeat"
+#define ERR12 "unrecognized character after (?"
+#define ERR13 "POSIX named classes are supported only within a class"
+#define ERR14 "missing )"
+#define ERR15 "reference to non-existent subpattern"
+#define ERR16 "erroffset passed as NULL"
+#define ERR17 "unknown option bit(s) set"
+#define ERR18 "missing ) after comment"
+#define ERR19 "parentheses nested too deeply"
+#define ERR20 "regular expression too large"
+#define ERR21 "failed to get memory"
+#define ERR22 "unmatched parentheses"
+#define ERR23 "internal error: code overflow"
+#define ERR24 "unrecognized character after (?<"
+#define ERR25 "lookbehind assertion is not fixed length"
+#define ERR26 "malformed number after (?("
+#define ERR27 "conditional group contains more than two branches"
+#define ERR28 "assertion expected after (?("
+#define ERR29 "(?R or (?digits must be followed by )"
+#define ERR30 "unknown POSIX class name"
+#define ERR31 "POSIX collating elements are not supported"
+#define ERR32 "this version of PCRE is not compiled with PCRE_UTF8 support"
+#define ERR33 "spare error"
+#define ERR34 "character value in \\x{...} sequence is too large"
+#define ERR35 "invalid condition (?(0)"
+#define ERR36 "\\C not allowed in lookbehind assertion"
+#define ERR37 "PCRE does not support \\L, \\l, \\N, \\P, \\p, \\U, \\u, or \\X"
+#define ERR38 "number after (?C is > 255"
+#define ERR39 "closing ) for (?C expected"
+#define ERR40 "recursive call could loop indefinitely"
+#define ERR41 "unrecognized character after (?P"
+#define ERR42 "syntax error after (?P"
+#define ERR43 "two named groups have the same name"
+#define ERR44 "invalid UTF-8 string"
+
+/* All character handling must be done as unsigned characters. Otherwise there
+are problems with top-bit-set characters and functions such as g_unichar_isspace().
+However, we leave the interface to the outside world as char *, because that
+should make things easier for callers. We define a short type for unsigned char
+to save lots of typing. I tried "uchar", but it causes problems on Digital
+Unix, where it is defined in sys/types, so use "uschar" instead. */
+
+typedef unsigned char uschar;
+
+/* The real format of the start of the pcre block; the index of names and the
+code vector run on as long as necessary after the end. */
+
+typedef struct real_pcre {
+ unsigned long int magic_number;
+ size_t size; /* Total that was malloced */
+ const unsigned char *tables; /* Pointer to tables */
+ unsigned long int options;
+ unsigned short int top_bracket;
+ unsigned short int top_backref;
+ unsigned short int first_byte;
+ unsigned short int req_byte;
+ unsigned short int name_entry_size; /* Size of any name items; 0 => none */
+ unsigned short int name_count; /* Number of name items */
+} real_pcre;
+
+/* The format of the block used to store data from pcre_study(). */
+
+typedef struct pcre_study_data {
+ size_t size; /* Total that was malloced */
+ uschar options;
+ uschar start_bits[32];
+} pcre_study_data;
+
+/* Structure for passing "static" information around between the functions
+doing the compiling, so that they are thread-safe. */
+
+typedef struct compile_data {
+ const uschar *lcc; /* Points to lower casing table */
+ const uschar *fcc; /* Points to case-flipping table */
+ const uschar *cbits; /* Points to character type table */
+ const uschar *ctypes; /* Points to table of type maps */
+ const uschar *start_code; /* The start of the compiled code */
+ uschar *name_table; /* The name/number table */
+ int names_found; /* Number of entries so far */
+ int name_entry_size; /* Size of each entry */
+ int top_backref; /* Maximum back reference */
+ unsigned int backref_map; /* Bitmap of low back refs */
+ int req_varyopt; /* "After variable item" flag for reqbyte */
+} compile_data;
+
+/* Structure for maintaining a chain of pointers to the currently incomplete
+branches, for testing for left recursion. */
+
+typedef struct branch_chain {
+ struct branch_chain *outer;
+ uschar *current;
+} branch_chain;
+
+/* Structure for items in a linked list that represents an explicit recursive
+call within the pattern. */
+
+typedef struct recursion_info {
+ struct recursion_info *prevrec; /* Previous recursion record (or NULL) */
+ int group_num; /* Number of group that was called */
+ const uschar *after_call; /* "Return value": points after the call in the expr */
+ const uschar *save_start; /* Old value of md->start_match */
+ int *offset_save; /* Pointer to start of saved offsets */
+ int saved_max; /* Number of saved offsets */
+} recursion_info;
+
+/* When compiling in a mode that doesn't use recursive calls to match(),
+a structure is used to remember local variables on the heap. It is defined in
+pcre.c, close to the match() function, so that it is easy to keep it in step
+with any changes of local variable. However, the pointer to the current frame
+must be saved in some "static" place over a longjmp(). We declare the
+structure here so that we can put a pointer in the match_data structure.
+NOTE: This isn't used for a "normal" compilation of pcre. */
+
+struct heapframe;
+
+/* Structure for passing "static" information around between the functions
+doing the matching, so that they are thread-safe. */
+
+typedef struct match_data {
+ unsigned long int match_call_count; /* As it says */
+ unsigned long int match_limit;/* As it says */
+ int *offset_vector; /* Offset vector */
+ int offset_end; /* One past the end */
+ int offset_max; /* The maximum usable for return data */
+ const uschar *lcc; /* Points to lower casing table */
+ const uschar *ctypes; /* Points to table of type maps */
+ BOOL offset_overflow; /* Set if too many extractions */
+ BOOL notbol; /* NOTBOL flag */
+ BOOL noteol; /* NOTEOL flag */
+ BOOL utf8; /* UTF8 flag */
+ BOOL endonly; /* Dollar not before final \n */
+ BOOL notempty; /* Empty string match not wanted */
+ const uschar *start_code; /* For use when recursing */
+ const uschar *start_subject; /* Start of the subject string */
+ const uschar *end_subject; /* End of the subject string */
+ const uschar *start_match; /* Start of this match attempt */
+ const uschar *end_match_ptr; /* Subject position at end match */
+ int end_offset_top; /* Highwater mark at end of match */
+ int capture_last; /* Most recent capture number */
+ int start_offset; /* The start offset value */
+ recursion_info *recursive; /* Linked list of recursion data */
+ void *callout_data; /* To pass back to callouts */
+ struct heapframe *thisframe; /* Used only when compiling for no recursion */
+} match_data;
+
+/* Bit definitions for entries in the pcre_ctypes table. */
+
+#define ctype_space 0x01
+#define ctype_letter 0x02
+#define ctype_digit 0x04
+#define ctype_xdigit 0x08
+#define ctype_word 0x10 /* alphameric or '_' */
+#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */
+
+/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set
+of bits for a class map. Some classes are built by combining these tables. */
+
+#define cbit_space 0 /* [:space:] or \s */
+#define cbit_xdigit 32 /* [:xdigit:] */
+#define cbit_digit 64 /* [:digit:] or \d */
+#define cbit_upper 96 /* [:upper:] */
+#define cbit_lower 128 /* [:lower:] */
+#define cbit_word 160 /* [:word:] or \w */
+#define cbit_graph 192 /* [:graph:] */
+#define cbit_print 224 /* [:print:] */
+#define cbit_punct 256 /* [:punct:] */
+#define cbit_cntrl 288 /* [:cntrl:] */
+#define cbit_length 320 /* Length of the cbits table */
+
+/* Offsets of the various tables from the base tables pointer, and
+total length. */
+
+#define lcc_offset 0
+#define fcc_offset 256
+#define cbits_offset 512
+#define ctypes_offset (cbits_offset + cbit_length)
+#define tables_length (ctypes_offset + 256)
+
+/* End of internal.h */
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/get.c
@@ -0,0 +1,354 @@
+/* File import from pcre to goffice by import-pcre. Do not edit. */
+
+/* This file has been programatically changed. */
+/* This makes the following file fall under GPL license, see below. */
+
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10 at cam.ac.uk>
+
+ Copyright (c) 1997-2003 University of Cambridge
+
+-----------------------------------------------------------------------------
+Permission is granted to anyone to use this software for any purpose on any
+computer system, and to redistribute it freely, subject to the following
+restrictions:
+
+1. This software 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.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+4. If PCRE is embedded in any software that is released under the GNU
+ General Purpose Licence (GPL), then the terms of that licence shall
+ supersede any condition above with which it is incompatible.
+-----------------------------------------------------------------------------
+*/
+
+/* This module contains some convenience functions for extracting substrings
+from the subject string after a regex match has succeeded. The original idea
+for these functions came from Scott Wimer <scottw at cgibuilder.com>. */
+
+
+/* Include the internals header, which itself includes Standard C headers plus
+the external pcre header. */
+
+#include "internal.h"
+
+
+/*************************************************
+* Find number for named string *
+*************************************************/
+
+/* This function is used by the two extraction functions below, as well
+as being generally available.
+
+Arguments:
+ code the compiled regex
+ stringname the name whose number is required
+
+Returns: the number of the named parentheses, or a negative number
+ (PCRE_ERROR_NOSUBSTRING) if not found
+*/
+
+int
+pcre_get_stringnumber(const pcre *code, const char *stringname)
+{
+int rc;
+int entrysize;
+int top, bot;
+uschar *nametable;
+
+if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0)
+ return rc;
+if (top <= 0) return PCRE_ERROR_NOSUBSTRING;
+
+if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0)
+ return rc;
+if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0)
+ return rc;
+
+bot = 0;
+while (top > bot)
+ {
+ int mid = (top + bot) / 2;
+ uschar *entry = nametable + entrysize*mid;
+ int c = strcmp(stringname, (char *)(entry + 2));
+ if (c == 0) return (entry[0] << 8) + entry[1];
+ if (c > 0) bot = mid + 1; else top = mid;
+ }
+
+return PCRE_ERROR_NOSUBSTRING;
+}
+
+
+
+/*************************************************
+* Copy captured string to given buffer *
+*************************************************/
+
+/* This function copies a single captured substring into a given buffer.
+Note that we use memcpy() rather than strncpy() in case there are binary zeros
+in the string.
+
+Arguments:
+ subject the subject string that was matched
+ ovector pointer to the offsets table
+ stringcount the number of substrings that were captured
+ (i.e. the yield of the pcre_exec call, unless
+ that was zero, in which case it should be 1/3
+ of the offset table size)
+ stringnumber the number of the required substring
+ buffer where to put the substring
+ size the size of the buffer
+
+Returns: if successful:
+ the length of the copied string, not including the zero
+ that is put on the end; can be zero
+ if not successful:
+ PCRE_ERROR_NOMEMORY (-6) buffer too small
+ PCRE_ERROR_NOSUBSTRING (-7) no such captured substring
+*/
+
+int
+pcre_copy_substring(const char *subject, int *ovector, int stringcount,
+ int stringnumber, char *buffer, int size)
+{
+int yield;
+if (stringnumber < 0 || stringnumber >= stringcount)
+ return PCRE_ERROR_NOSUBSTRING;
+stringnumber *= 2;
+yield = ovector[stringnumber+1] - ovector[stringnumber];
+if (size < yield + 1) return PCRE_ERROR_NOMEMORY;
+memcpy(buffer, subject + ovector[stringnumber], yield);
+buffer[yield] = 0;
+return yield;
+}
+
+
+
+/*************************************************
+* Copy named captured string to given buffer *
+*************************************************/
+
+/* This function copies a single captured substring into a given buffer,
+identifying it by name.
+
+Arguments:
+ code the compiled regex
+ subject the subject string that was matched
+ ovector pointer to the offsets table
+ stringcount the number of substrings that were captured
+ (i.e. the yield of the pcre_exec call, unless
+ that was zero, in which case it should be 1/3
+ of the offset table size)
+ stringname the name of the required substring
+ buffer where to put the substring
+ size the size of the buffer
+
+Returns: if successful:
+ the length of the copied string, not including the zero
+ that is put on the end; can be zero
+ if not successful:
+ PCRE_ERROR_NOMEMORY (-6) buffer too small
+ PCRE_ERROR_NOSUBSTRING (-7) no such captured substring
+*/
+
+int
+pcre_copy_named_substring(const pcre *code, const char *subject, int *ovector,
+ int stringcount, const char *stringname, char *buffer, int size)
+{
+int n = pcre_get_stringnumber(code, stringname);
+if (n <= 0) return n;
+return pcre_copy_substring(subject, ovector, stringcount, n, buffer, size);
+}
+
+
+
+/*************************************************
+* Copy all captured strings to new store *
+*************************************************/
+
+/* This function gets one chunk of store and builds a list of pointers and all
+of the captured substrings in it. A NULL pointer is put on the end of the list.
+
+Arguments:
+ subject the subject string that was matched
+ ovector pointer to the offsets table
+ stringcount the number of substrings that were captured
+ (i.e. the yield of the pcre_exec call, unless
+ that was zero, in which case it should be 1/3
+ of the offset table size)
+ listptr set to point to the list of pointers
+
+Returns: if successful: 0
+ if not successful:
+ PCRE_ERROR_NOMEMORY (-6) failed to get store
+*/
+
+int
+pcre_get_substring_list(const char *subject, int *ovector, int stringcount,
+ const char ***listptr)
+{
+int i;
+int size = sizeof(char *);
+int double_count = stringcount * 2;
+char **stringlist;
+char *p;
+
+for (i = 0; i < double_count; i += 2)
+ size += sizeof(char *) + ovector[i+1] - ovector[i] + 1;
+
+stringlist = (char **)(pcre_malloc)(size);
+if (stringlist == NULL) return PCRE_ERROR_NOMEMORY;
+
+*listptr = (const char **)stringlist;
+p = (char *)(stringlist + stringcount + 1);
+
+for (i = 0; i < double_count; i += 2)
+ {
+ int len = ovector[i+1] - ovector[i];
+ memcpy(p, subject + ovector[i], len);
+ *stringlist++ = p;
+ p += len;
+ *p++ = 0;
+ }
+
+*stringlist = NULL;
+return 0;
+}
+
+
+
+/*************************************************
+* Free store obtained by get_substring_list *
+*************************************************/
+
+/* This function exists for the benefit of people calling PCRE from non-C
+programs that can call its functions, but not free() or (pcre_free)() directly.
+
+Argument: the result of a previous pcre_get_substring_list()
+Returns: nothing
+*/
+
+void
+pcre_free_substring_list(const char **pointer)
+{
+(pcre_free)((void *)pointer);
+}
+
+
+
+/*************************************************
+* Copy captured string to new store *
+*************************************************/
+
+/* This function copies a single captured substring into a piece of new
+store
+
+Arguments:
+ subject the subject string that was matched
+ ovector pointer to the offsets table
+ stringcount the number of substrings that were captured
+ (i.e. the yield of the pcre_exec call, unless
+ that was zero, in which case it should be 1/3
+ of the offset table size)
+ stringnumber the number of the required substring
+ stringptr where to put a pointer to the substring
+
+Returns: if successful:
+ the length of the string, not including the zero that
+ is put on the end; can be zero
+ if not successful:
+ PCRE_ERROR_NOMEMORY (-6) failed to get store
+ PCRE_ERROR_NOSUBSTRING (-7) substring not present
+*/
+
+int
+pcre_get_substring(const char *subject, int *ovector, int stringcount,
+ int stringnumber, const char **stringptr)
+{
+int yield;
+char *substring;
+if (stringnumber < 0 || stringnumber >= stringcount)
+ return PCRE_ERROR_NOSUBSTRING;
+stringnumber *= 2;
+yield = ovector[stringnumber+1] - ovector[stringnumber];
+substring = (char *)(pcre_malloc)(yield + 1);
+if (substring == NULL) return PCRE_ERROR_NOMEMORY;
+memcpy(substring, subject + ovector[stringnumber], yield);
+substring[yield] = 0;
+*stringptr = substring;
+return yield;
+}
+
+
+
+/*************************************************
+* Copy named captured string to new store *
+*************************************************/
+
+/* This function copies a single captured substring, identified by name, into
+new store.
+
+Arguments:
+ code the compiled regex
+ subject the subject string that was matched
+ ovector pointer to the offsets table
+ stringcount the number of substrings that were captured
+ (i.e. the yield of the pcre_exec call, unless
+ that was zero, in which case it should be 1/3
+ of the offset table size)
+ stringname the name of the required substring
+ stringptr where to put the pointer
+
+Returns: if successful:
+ the length of the copied string, not including the zero
+ that is put on the end; can be zero
+ if not successful:
+ PCRE_ERROR_NOMEMORY (-6) couldn't get memory
+ PCRE_ERROR_NOSUBSTRING (-7) no such captured substring
+*/
+
+int
+pcre_get_named_substring(const pcre *code, const char *subject, int *ovector,
+ int stringcount, const char *stringname, const char **stringptr)
+{
+int n = pcre_get_stringnumber(code, stringname);
+if (n <= 0) return n;
+return pcre_get_substring(subject, ovector, stringcount, n, stringptr);
+}
+
+
+
+
+/*************************************************
+* Free store obtained by get_substring *
+*************************************************/
+
+/* This function exists for the benefit of people calling PCRE from non-C
+programs that can call its functions, but not free() or (pcre_free)() directly.
+
+Argument: the result of a previous pcre_get_substring()
+Returns: nothing
+*/
+
+void
+pcre_free_substring(const char *pointer)
+{
+(pcre_free)((void *)pointer);
+}
+
+/* End of get.c */
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/pcreposix.h
@@ -0,0 +1,93 @@
+/* File import from pcre to goffice by import-pcre. Do not edit. */
+
+/* This file has been programatically changed. */
+/* This makes the following file fall under GPL license, see pcreposix.c. */
+
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* Copyright (c) 1997-2003 University of Cambridge */
+
+#ifndef _PCREPOSIX_H
+#define _PCREPOSIX_H
+
+/* This is the header for the POSIX wrapper interface to the PCRE Perl-
+Compatible Regular Expression library. It defines the things POSIX says should
+be there. I hope. */
+
+/* Have to include stdlib.h in order to ensure that size_t is defined. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options defined by POSIX. */
+
+#define REG_ICASE 0x01
+#define REG_NEWLINE 0x02
+#define REG_NOTBOL 0x04
+#define REG_NOTEOL 0x08
+
+/* These are not used by PCRE, but by defining them we make it easier
+to slot PCRE into existing programs that make POSIX calls. */
+
+#define REG_EXTENDED 0
+#define REG_NOSUB 0
+
+/* Error values. Not all these are relevant or used by the wrapper. */
+
+enum {
+ REG_ASSERT = 1, /* internal error ? */
+ REG_BADBR, /* invalid repeat counts in {} */
+ REG_BADPAT, /* pattern error */
+ REG_BADRPT, /* ? * + invalid */
+ REG_EBRACE, /* unbalanced {} */
+ REG_EBRACK, /* unbalanced [] */
+ REG_ECOLLATE, /* collation error - not relevant */
+ REG_ECTYPE, /* bad class */
+ REG_EESCAPE, /* bad escape sequence */
+ REG_EMPTY, /* empty expression */
+ REG_EPAREN, /* unbalanced () */
+ REG_ERANGE, /* bad range inside [] */
+ REG_ESIZE, /* expression too big */
+ REG_ESPACE, /* failed to get memory */
+ REG_ESUBREG, /* bad back reference */
+ REG_INVARG, /* bad argument */
+ REG_NOMATCH /* match failed */
+};
+
+
+/* The structure representing a compiled regular expression. */
+
+typedef struct {
+ void *re_pcre;
+ size_t re_nsub;
+ size_t re_erroffset;
+} go_regex_t;
+
+/* The structure in which a captured offset is returned. */
+
+typedef int go_regoff_t;
+
+typedef struct {
+ go_regoff_t rm_so;
+ go_regoff_t rm_eo;
+} regmatch_t;
+
+/* The functions */
+
+extern int go_regcomp(go_regex_t *, const char *, int);
+extern int go_regexec(const go_regex_t *, const char *, size_t, regmatch_t *, int);
+extern size_t go_regerror(int, const go_regex_t *, char *, size_t);
+extern void go_regfree(go_regex_t *);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* End of pcreposix.h */
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/pcre.c
@@ -0,0 +1,8319 @@
+/* File import from pcre to goffice by import-pcre. Do not edit. */
+
+/* This file has been programatically changed. */
+/* This makes the following file fall under GPL license, see below. */
+
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10 at cam.ac.uk>
+
+ Copyright (c) 1997-2003 University of Cambridge
+
+-----------------------------------------------------------------------------
+Permission is granted to anyone to use this software for any purpose on any
+computer system, and to redistribute it freely, subject to the following
+restrictions:
+
+1. This software 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.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+4. If PCRE is embedded in any software that is released under the GNU
+ General Purpose Licence (GPL), then the terms of that licence shall
+ supersede any condition above with which it is incompatible.
+-----------------------------------------------------------------------------
+*/
+
+
+/* Define DEBUG to get debugging output on stdout. */
+/* #define DEBUG */
+
+/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef
+inline, and there are *still* stupid compilers about that don't like indented
+pre-processor statements. I suppose it's only been 10 years... */
+
+#ifdef DEBUG
+#define DPRINTF(p) printf p
+#else
+#define DPRINTF(p) /*nothing*/
+#endif
+
+/* Include the internals header, which itself includes <goffice/goffice-config.h>, the Standard
+C headers, and the external pcre header. */
+
+#include "internal.h"
+
+
+/* Allow compilation as C++ source code, should anybody want to do that. */
+
+#ifdef __cplusplus
+#define class pcre_class
+#endif
+
+
+/* Maximum number of items on the nested bracket stacks at compile time. This
+applies to the nesting of all kinds of parentheses. It does not limit
+un-nested, non-capturing parentheses. This number can be made bigger if
+necessary - it is used to dimension one int and one unsigned char vector at
+compile time. */
+
+#define BRASTACK_SIZE 200
+
+
+/* Maximum number of ints of offset to save on the stack for recursive calls.
+If the offset vector is bigger, malloc is used. This should be a multiple of 3,
+because the offset vector is always a multiple of 3 long. */
+
+#define REC_STACK_SAVE_MAX 30
+
+
+/* The number of bytes in a literal character string above which we can't add
+any more is set at 250 in order to allow for UTF-8 characters. (In theory it
+could be 255 when UTF-8 support is excluded, but that means that some of the
+test output would be different, which just complicates things.) */
+
+#define MAXLIT 250
+
+
+/* The maximum remaining length of subject we are prepared to search for a
+req_byte match. */
+
+#define REQ_BYTE_MAX 1000
+
+
+/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that
+the definition is next to the definition of the opcodes in internal.h. */
+
+static const uschar OP_lengths[] = { OP_LENGTHS };
+
+/* Min and max values for the common repeats; for the maxima, 0 => infinity */
+
+static const char rep_min[] = { 0, 0, 1, 1, 0, 0 };
+static const char rep_max[] = { 0, 0, 0, 0, 1, 1 };
+
+/* Table for handling escaped characters in the range '0'-'z'. Positive returns
+are simple data values; negative values are for special things like \d and so
+on. Zero means further processing is needed (for things like \x), or the escape
+is invalid. */
+
+#if !EBCDIC /* This is the "normal" table for ASCII systems */
+static const short int escapes[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */
+ 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */
+ '@', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G, /* @ - G */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */
+ 0, -ESC_Q, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */
+ 0, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */
+ '`', 7, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0, /* ` - g */
+ 0, 0, 0, 0, 0, 0, ESC_n, 0, /* h - o */
+ 0, 0, ESC_r, -ESC_s, ESC_tee, 0, 0, -ESC_w, /* p - w */
+ 0, 0, -ESC_z /* x - z */
+};
+
+#else /* This is the "abnormal" table for EBCDIC systems */
+static const short int escapes[] = {
+/* 48 */ 0, 0, 0, '.', '<', '(', '+', '|',
+/* 50 */ '&', 0, 0, 0, 0, 0, 0, 0,
+/* 58 */ 0, 0, '!', '$', '*', ')', ';', '~',
+/* 60 */ '-', '/', 0, 0, 0, 0, 0, 0,
+/* 68 */ 0, 0, '|', ',', '%', '_', '>', '?',
+/* 70 */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 78 */ 0, '`', ':', '#', '@', '\'', '=', '"',
+/* 80 */ 0, 7, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0,
+/* 88 */ 0, 0, 0, '{', 0, 0, 0, 0,
+/* 90 */ 0, 0, 0, 'l', 0, ESC_n, 0, 0,
+/* 98 */ 0, ESC_r, 0, '}', 0, 0, 0, 0,
+/* A0 */ 0, '~', -ESC_s, ESC_tee, 0, 0, -ESC_w, 0,
+/* A8 */ 0,-ESC_z, 0, 0, 0, '[', 0, 0,
+/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-',
+/* C0 */ '{',-ESC_A, -ESC_B, -ESC_C, -ESC_D,-ESC_E, 0, -ESC_G,
+/* C8 */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* D0 */ '}', 0, 0, 0, 0, 0, 0, 0,
+/* D8 */-ESC_Q, 0, 0, 0, 0, 0, 0, 0,
+/* E0 */ '\\', 0, -ESC_S, 0, 0, 0, -ESC_W, 0,
+/* E8 */ 0,-ESC_Z, 0, 0, 0, 0, 0, 0,
+/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+/* F8 */ 0, 0, 0, 0, 0, 0, 0, 0
+};
+#endif
+
+
+/* Tables of names of POSIX character classes and their lengths. The list is
+terminated by a zero length entry. The first three must be alpha, upper, lower,
+as this is assumed for handling case independence. */
+
+static const char *const posix_names[] = {
+ "alpha", "lower", "upper",
+ "alnum", "ascii", "blank", "cntrl", "digit", "graph",
+ "print", "punct", "space", "word", "xdigit" };
+
+static const uschar posix_name_lengths[] = {
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 };
+
+/* Table of class bit maps for each POSIX class; up to three may be combined
+to form the class. The table for [:blank:] is dynamically modified to remove
+the vertical space characters. */
+
+static const int posix_class_maps[] = {
+ cbit_lower, cbit_upper, -1, /* alpha */
+ cbit_lower, -1, -1, /* lower */
+ cbit_upper, -1, -1, /* upper */
+ cbit_digit, cbit_lower, cbit_upper, /* alnum */
+ cbit_print, cbit_cntrl, -1, /* ascii */
+ cbit_space, -1, -1, /* blank - a GNU extension */
+ cbit_cntrl, -1, -1, /* cntrl */
+ cbit_digit, -1, -1, /* digit */
+ cbit_graph, -1, -1, /* graph */
+ cbit_print, -1, -1, /* print */
+ cbit_punct, -1, -1, /* punct */
+ cbit_space, -1, -1, /* space */
+ cbit_word, -1, -1, /* word - a Perl extension */
+ cbit_xdigit,-1, -1 /* xdigit */
+};
+
+/* Table to identify digits and hex digits. This is used when compiling
+patterns. Note that the tables in chartables are dependent on the locale, and
+may mark arbitrary characters as digits - but the PCRE compiling code expects
+to handle only 0-9, a-z, and A-Z as digits when compiling. That is why we have
+a private table here. It costs 256 bytes, but it is a lot faster than doing
+character value tests (at least in some simple cases I timed), and in some
+applications one wants PCRE to compile efficiently as well as match
+efficiently.
+
+For convenience, we use the same bit definitions as in chartables:
+
+ 0x04 decimal digit
+ 0x08 hexadecimal digit
+
+Then we can use ctype_digit and ctype_xdigit in the code. */
+
+#if !EBCDIC /* This is the "normal" case, for ASCII systems */
+static const unsigned char digitab[] =
+ {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8- 15 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - ' */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ( - / */
+ 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /* 0 - 7 */
+ 0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, /* 8 - ? */
+ 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* @ - G */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* H - O */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* P - W */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* X - _ */
+ 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* ` - g */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* h - o */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* p - w */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* x -127 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */
+
+#else /* This is the "abnormal" case, for EBCDIC systems */
+static const unsigned char digitab[] =
+ {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 0 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8- 15 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 10 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 32- 39 20 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 40- 47 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 48- 55 30 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 56- 63 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - 71 40 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 72- | */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* & - 87 50 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 88- ¬ */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - -103 60 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 104- ? */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 70 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- " */
+ 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* 128- g 80 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* h -143 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144- p 90 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* q -159 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160- x A0 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* y -175 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ^ -183 B0 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
+ 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* { - G C0 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* H -207 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* } - P D0 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Q -223 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* \ - X E0 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Y -239 */
+ 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /* 0 - 7 F0 */
+ 0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00};/* 8 -255 */
+
+static const unsigned char ebcdic_chartab[] = { /* chartable partial dup */
+ 0x80,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 0- 7 */
+ 0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00, /* 8- 15 */
+ 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 16- 23 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */
+ 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 32- 39 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 40- 47 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 48- 55 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 56- 63 */
+ 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - 71 */
+ 0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80, /* 72- | */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* & - 87 */
+ 0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00, /* 88- ¬ */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - -103 */
+ 0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x80, /* 104- ? */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- " */
+ 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* 128- g */
+ 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* h -143 */
+ 0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* 144- p */
+ 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* q -159 */
+ 0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /* 160- x */
+ 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* y -175 */
+ 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ^ -183 */
+ 0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
+ 0x80,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* { - G */
+ 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* H -207 */
+ 0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* } - P */
+ 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* Q -223 */
+ 0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /* \ - X */
+ 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* Y -239 */
+ 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */
+ 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x00};/* 8 -255 */
+#endif
+
+
+/* Definition to allow mutual recursion */
+
+static BOOL
+ compile_regex(int, int, int *, uschar **, const uschar **, const char **,
+ BOOL, int, int *, int *, branch_chain *, compile_data *);
+
+/* Structure for building a chain of data that actually lives on the
+stack, for holding the values of the subject pointer at the start of each
+subpattern, so as to detect when an empty string has been matched by a
+subpattern - to break infinite loops. When NO_RECURSE is set, these blocks
+are on the heap, not on the stack. */
+
+typedef struct eptrblock {
+ struct eptrblock *epb_prev;
+ const uschar *epb_saved_eptr;
+} eptrblock;
+
+/* Flag bits for the match() function */
+
+#define match_condassert 0x01 /* Called to check a condition assertion */
+#define match_isgroup 0x02 /* Set if start of bracketed group */
+
+/* Non-error returns from the match() function. Error returns are externally
+defined PCRE_ERROR_xxx codes, which are all negative. */
+
+#define MATCH_MATCH 1
+#define MATCH_NOMATCH 0
+
+
+
+/*************************************************
+* Global variables *
+*************************************************/
+
+/* PCRE is thread-clean and doesn't use any global variables in the normal
+sense. However, it calls memory allocation and free functions via the four
+indirections below, and it can optionally do callouts. These values can be
+changed by the caller, but are shared between all threads. However, when
+compiling for Virtual Pascal, things are done differently (see pcre.in). */
+
+#ifndef VPCOMPAT
+#ifdef __cplusplus
+extern "C" void *(*pcre_malloc)(size_t) = malloc;
+extern "C" void (*pcre_free)(void *) = free;
+extern "C" void *(*pcre_stack_malloc)(size_t) = malloc;
+extern "C" void (*pcre_stack_free)(void *) = free;
+extern "C" int (*pcre_callout)(pcre_callout_block *) = NULL;
+#else
+void *(*pcre_malloc)(size_t) = malloc;
+void (*pcre_free)(void *) = free;
+void *(*pcre_stack_malloc)(size_t) = malloc;
+void (*pcre_stack_free)(void *) = free;
+int (*pcre_callout)(pcre_callout_block *) = NULL;
+#endif
+#endif
+
+
+/*************************************************
+* Macros and tables for character handling *
+*************************************************/
+
+/* When UTF-8 encoding is being used, a character is no longer just a single
+byte. The macros for character handling generate simple sequences when used in
+byte-mode, and more complicated ones for UTF-8 characters. */
+
+#ifndef SUPPORT_UTF8
+#define GETCHAR(c, eptr) c = *eptr;
+#define GETCHARINC(c, eptr) c = *eptr++;
+#define GETCHARINCTEST(c, eptr) c = *eptr++;
+#define GETCHARLEN(c, eptr, len) c = *eptr;
+#define BACKCHAR(eptr)
+
+#else /* SUPPORT_UTF8 */
+
+/* Get the next UTF-8 character, not advancing the pointer. This is called when
+we know we are in UTF-8 mode. */
+
+#define GETCHAR(c, eptr) \
+ c = *eptr; \
+ if ((c & 0xc0) == 0xc0) \
+ { \
+ int gcii; \
+ int gcaa = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
+ int gcss = 6*gcaa; \
+ c = (c & utf8_table3[gcaa]) << gcss; \
+ for (gcii = 1; gcii <= gcaa; gcii++) \
+ { \
+ gcss -= 6; \
+ c |= (eptr[gcii] & 0x3f) << gcss; \
+ } \
+ }
+
+/* Get the next UTF-8 character, advancing the pointer. This is called when we
+know we are in UTF-8 mode. */
+
+#define GETCHARINC(c, eptr) \
+ c = *eptr++; \
+ if ((c & 0xc0) == 0xc0) \
+ { \
+ int gcaa = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
+ int gcss = 6*gcaa; \
+ c = (c & utf8_table3[gcaa]) << gcss; \
+ while (gcaa-- > 0) \
+ { \
+ gcss -= 6; \
+ c |= (*eptr++ & 0x3f) << gcss; \
+ } \
+ }
+
+/* Get the next character, testing for UTF-8 mode, and advancing the pointer */
+
+#define GETCHARINCTEST(c, eptr) \
+ c = *eptr++; \
+ if (1 /* md->utf8 */ && (c & 0xc0) == 0xc0) \
+ { \
+ int gcaa = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
+ int gcss = 6*gcaa; \
+ c = (c & utf8_table3[gcaa]) << gcss; \
+ while (gcaa-- > 0) \
+ { \
+ gcss -= 6; \
+ c |= (*eptr++ & 0x3f) << gcss; \
+ } \
+ }
+
+/* Get the next UTF-8 character, not advancing the pointer, incrementing length
+if there are extra bytes. This is called when we know we are in UTF-8 mode. */
+
+#define GETCHARLEN(c, eptr, len) \
+ c = *eptr; \
+ if ((c & 0xc0) == 0xc0) \
+ { \
+ int gcii; \
+ int gcaa = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \
+ int gcss = 6*gcaa; \
+ c = (c & utf8_table3[gcaa]) << gcss; \
+ for (gcii = 1; gcii <= gcaa; gcii++) \
+ { \
+ gcss -= 6; \
+ c |= (eptr[gcii] & 0x3f) << gcss; \
+ } \
+ len += gcaa; \
+ }
+
+/* If the pointer is not at the start of a character, move it back until
+it is. Called only in UTF-8 mode. */
+
+#define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr--;
+
+#endif
+
+
+
+/*************************************************
+* Default character tables *
+*************************************************/
+
+/* A default set of character tables is included in the PCRE binary. Its source
+is built by the maketables auxiliary program, which uses the default C ctypes
+functions, and put in the file chartables.c. These tables are used by PCRE
+whenever the caller of pcre_compile() does not provide an alternate set of
+tables. */
+
+static const unsigned char *
+make_pcre_default_tables (void)
+{
+ static const unsigned char *res = NULL;
+ if (res == NULL) {
+ res = pcre_maketables ();
+ }
+ return res;
+}
+
+/* #include "chartables.c" */
+
+
+
+#ifdef SUPPORT_UTF8
+/*************************************************
+* Tables for UTF-8 support *
+*************************************************/
+
+/* These are the breakpoints for different numbers of bytes in a UTF-8
+character. */
+
+static const int utf8_table1[] =
+ { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff};
+
+/* These are the indicator bits and the mask for the data bits to set in the
+first byte of a character, indexed by the number of additional bytes. */
+
+static const int utf8_table2[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc};
+static const int utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+
+/* Table of the number of extra characters, indexed by the first character
+masked with 0x3f. The highest number for a valid UTF-8 character is in fact
+0x3d. */
+
+static const uschar utf8_table4[] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };
+
+
+/*************************************************
+* Convert character value to UTF-8 *
+*************************************************/
+
+/* This function takes an integer value in the range 0 - 0x7fffffff
+and encodes it as a UTF-8 character in 0 to 6 bytes.
+
+Arguments:
+ cvalue the character value
+ buffer pointer to buffer for result - at least 6 bytes long
+
+Returns: number of characters placed in the buffer
+*/
+
+static int
+ord2utf8(int cvalue, uschar *buffer)
+{
+register int i, j;
+for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++)
+ if (cvalue <= utf8_table1[i]) break;
+buffer += i;
+for (j = i; j > 0; j--)
+ {
+ *buffer-- = 0x80 | (cvalue & 0x3f);
+ cvalue >>= 6;
+ }
+*buffer = utf8_table2[i] | cvalue;
+return i + 1;
+}
+#endif
+
+
+
+/*************************************************
+* Print compiled regex *
+*************************************************/
+
+/* The code for doing this is held in a separate file that is also included in
+pcretest.c. It defines a function called print_internals(). */
+
+#ifdef DEBUG
+#include "printint.c"
+#endif
+
+
+
+/*************************************************
+* Return version string *
+*************************************************/
+
+#define STRING(a) # a
+#define XSTRING(s) STRING(s)
+
+const char *
+pcre_version(void)
+{
+return XSTRING(PCRE_MAJOR) "." XSTRING(PCRE_MINOR) " " XSTRING(PCRE_DATE);
+}
+
+
+
+
+/*************************************************
+* (Obsolete) Return info about compiled pattern *
+*************************************************/
+
+/* This is the original "info" function. It picks potentially useful data out
+of the private structure, but its interface was too rigid. It remains for
+backwards compatibility. The public options are passed back in an int - though
+the re->options field has been expanded to a long int, all the public options
+at the low end of it, and so even on 16-bit systems this will still be OK.
+Therefore, I haven't changed the API for pcre_info().
+
+Arguments:
+ external_re points to compiled code
+ optptr where to pass back the options
+ first_byte where to pass back the first character,
+ or -1 if multiline and all branches start ^,
+ or -2 otherwise
+
+Returns: number of capturing subpatterns
+ or negative values on error
+*/
+
+int
+pcre_info(const pcre *external_re, int *optptr, int *first_byte)
+{
+const real_pcre *re = (const real_pcre *)external_re;
+if (re == NULL) return PCRE_ERROR_NULL;
+if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC;
+if (optptr != NULL) *optptr = (int)(re->options & PUBLIC_OPTIONS);
+if (first_byte != NULL)
+ *first_byte = ((re->options & PCRE_FIRSTSET) != 0)? re->first_byte :
+ ((re->options & PCRE_STARTLINE) != 0)? -1 : -2;
+return re->top_bracket;
+}
+
+
+
+/*************************************************
+* Return info about compiled pattern *
+*************************************************/
+
+/* This is a newer "info" function which has an extensible interface so
+that additional items can be added compatibly.
+
+Arguments:
+ external_re points to compiled code
+ extra_data points extra data, or NULL
+ what what information is required
+ where where to put the information
+
+Returns: 0 if data returned, negative on error
+*/
+
+int
+pcre_fullinfo(const pcre *external_re, const pcre_extra *extra_data, int what,
+ void *where)
+{
+const real_pcre *re = (const real_pcre *)external_re;
+const pcre_study_data *study = NULL;
+
+if (re == NULL || where == NULL) return PCRE_ERROR_NULL;
+if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC;
+
+if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_STUDY_DATA) != 0)
+ study = (const pcre_study_data *)extra_data->study_data;
+
+switch (what)
+ {
+ case PCRE_INFO_OPTIONS:
+ *((unsigned long int *)where) = re->options & PUBLIC_OPTIONS;
+ break;
+
+ case PCRE_INFO_SIZE:
+ *((size_t *)where) = re->size;
+ break;
+
+ case PCRE_INFO_STUDYSIZE:
+ *((size_t *)where) = (study == NULL)? 0 : study->size;
+ break;
+
+ case PCRE_INFO_CAPTURECOUNT:
+ *((int *)where) = re->top_bracket;
+ break;
+
+ case PCRE_INFO_BACKREFMAX:
+ *((int *)where) = re->top_backref;
+ break;
+
+ case PCRE_INFO_FIRSTBYTE:
+ *((int *)where) =
+ ((re->options & PCRE_FIRSTSET) != 0)? re->first_byte :
+ ((re->options & PCRE_STARTLINE) != 0)? -1 : -2;
+ break;
+
+ case PCRE_INFO_FIRSTTABLE:
+ *((const uschar **)where) =
+ (study != NULL && (study->options & PCRE_STUDY_MAPPED) != 0)?
+ study->start_bits : NULL;
+ break;
+
+ case PCRE_INFO_LASTLITERAL:
+ *((int *)where) =
+ ((re->options & PCRE_REQCHSET) != 0)? re->req_byte : -1;
+ break;
+
+ case PCRE_INFO_NAMEENTRYSIZE:
+ *((int *)where) = re->name_entry_size;
+ break;
+
+ case PCRE_INFO_NAMECOUNT:
+ *((int *)where) = re->name_count;
+ break;
+
+ case PCRE_INFO_NAMETABLE:
+ *((const uschar **)where) = (const uschar *)re + sizeof(real_pcre);
+ break;
+
+ default: return PCRE_ERROR_BADOPTION;
+ }
+
+return 0;
+}
+
+
+
+/*************************************************
+* Return info about what features are configured *
+*************************************************/
+
+/* This is function which has an extensible interface so that additional items
+can be added compatibly.
+
+Arguments:
+ what what information is required
+ where where to put the information
+
+Returns: 0 if data returned, negative on error
+*/
+
+int
+pcre_config(int what, void *where)
+{
+switch (what)
+ {
+ case PCRE_CONFIG_UTF8:
+#ifdef SUPPORT_UTF8
+ *((int *)where) = 1;
+#else
+ *((int *)where) = 0;
+#endif
+ break;
+
+ case PCRE_CONFIG_NEWLINE:
+ *((int *)where) = NEWLINE;
+ break;
+
+ case PCRE_CONFIG_LINK_SIZE:
+ *((int *)where) = LINK_SIZE;
+ break;
+
+ case PCRE_CONFIG_POSIX_MALLOC_THRESHOLD:
+ *((int *)where) = POSIX_MALLOC_THRESHOLD;
+ break;
+
+ case PCRE_CONFIG_MATCH_LIMIT:
+ *((unsigned int *)where) = MATCH_LIMIT;
+ break;
+
+ case PCRE_CONFIG_STACKRECURSE:
+#ifdef NO_RECURSE
+ *((int *)where) = 0;
+#else
+ *((int *)where) = 1;
+#endif
+ break;
+
+ default: return PCRE_ERROR_BADOPTION;
+ }
+
+return 0;
+}
+
+
+
+#ifdef DEBUG
+/*************************************************
+* Debugging function to print chars *
+*************************************************/
+
+/* Print a sequence of chars in printable format, stopping at the end of the
+subject if the requested.
+
+Arguments:
+ p points to characters
+ length number to print
+ is_subject TRUE if printing from within md->start_subject
+ md pointer to matching data block, if is_subject is TRUE
+
+Returns: nothing
+*/
+
+static void
+pchars(const uschar *p, int length, BOOL is_subject, match_data *md)
+{
+int c;
+if (is_subject && length > md->end_subject - p) length = md->end_subject - p;
+while (length-- > 0)
+ if (g_unichar_isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c);
+}
+#endif
+
+
+
+
+/*************************************************
+* Handle escapes *
+*************************************************/
+
+/* This function is called when a \ has been encountered. It either returns a
+positive value for a simple escape such as \n, or a negative value which
+encodes one of the more complicated things such as \d. When UTF-8 is enabled,
+a positive value greater than 255 may be returned. On entry, ptr is pointing at
+the \. On exit, it is on the final character of the escape sequence.
+
+Arguments:
+ ptrptr points to the pattern position pointer
+ errorptr points to the pointer to the error message
+ bracount number of previous extracting brackets
+ options the options bits
+ isclass TRUE if inside a character class
+
+Returns: zero or positive => a data character
+ negative => a special escape sequence
+ on error, errorptr is set
+*/
+
+static int
+check_escape(const uschar **ptrptr, const char **errorptr, int bracount,
+ int options, BOOL isclass)
+{
+const uschar *ptr = *ptrptr;
+int c, i;
+
+/* If backslash is at the end of the pattern, it's an error. */
+
+c = *(++ptr);
+if (c == 0) *errorptr = ERR1;
+
+/* Non-alphamerics are literals. For digits or letters, do an initial lookup in
+a table. A non-zero result is something that can be returned immediately.
+Otherwise further processing may be required. */
+
+#if !EBCDIC /* ASCII coding */
+else if (c < '0' || c > 'z') {} /* Not alphameric */
+else if ((i = escapes[c - '0']) != 0) c = i;
+
+#else /* EBCDIC coding */
+else if (c < 'a' || (ebcdic_chartab[c] & 0x0E) == 0) {} /* Not alphameric */
+else if ((i = escapes[c - 0x48]) != 0) c = i;
+#endif
+
+/* Escapes that need further processing, or are illegal. */
+
+else
+ {
+ const uschar *oldptr;
+ switch (c)
+ {
+ /* A number of Perl escapes are not handled by PCRE. We give an explicit
+ error. */
+
+ case 'l':
+ case 'L':
+ case 'N':
+ case 'p':
+ case 'P':
+ case 'u':
+ case 'U':
+ case 'X':
+ *errorptr = ERR37;
+ break;
+
+ /* The handling of escape sequences consisting of a string of digits
+ starting with one that is not zero is not straightforward. By experiment,
+ the way Perl works seems to be as follows:
+
+ Outside a character class, the digits are read as a decimal number. If the
+ number is less than 10, or if there are that many previous extracting
+ left brackets, then it is a back reference. Otherwise, up to three octal
+ digits are read to form an escaped byte. Thus \123 is likely to be octal
+ 123 (cf \0123, which is octal 012 followed by the literal 3). If the octal
+ value is greater than 377, the least significant 8 bits are taken. Inside a
+ character class, \ followed by a digit is always an octal number. */
+
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+
+ if (!isclass)
+ {
+ oldptr = ptr;
+ c -= '0';
+ while ((digitab[ptr[1]] & ctype_digit) != 0)
+ c = c * 10 + *(++ptr) - '0';
+ if (c < 10 || c <= bracount)
+ {
+ c = -(ESC_REF + c);
+ break;
+ }
+ ptr = oldptr; /* Put the pointer back and fall through */
+ }
+
+ /* Handle an octal number following \. If the first digit is 8 or 9, Perl
+ generates a binary zero byte and treats the digit as a following literal.
+ Thus we have to pull back the pointer by one. */
+
+ if ((c = *ptr) >= '8')
+ {
+ ptr--;
+ c = 0;
+ break;
+ }
+
+ /* \0 always starts an octal number, but we may drop through to here with a
+ larger first octal digit. */
+
+ case '0':
+ c -= '0';
+ while(i++ < 2 && ptr[1] >= '0' && ptr[1] <= '7')
+ c = c * 8 + *(++ptr) - '0';
+ c &= 255; /* Take least significant 8 bits */
+ break;
+
+ /* \x is complicated when UTF-8 is enabled. \x{ddd} is a character number
+ which can be greater than 0xff, but only if the ddd are hex digits. */
+
+ case 'x':
+#ifdef SUPPORT_UTF8
+ if (ptr[1] == '{' && (options & PCRE_UTF8) != 0)
+ {
+ const uschar *pt = ptr + 2;
+ register int count = 0;
+ c = 0;
+ while ((digitab[*pt] & ctype_xdigit) != 0)
+ {
+ int cc = *pt++;
+ count++;
+#if !EBCDIC /* ASCII coding */
+ if (cc >= 'a') cc -= 32; /* Convert to upper case */
+ c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10));
+#else /* EBCDIC coding */
+ if (cc >= 'a' && cc <= 'z') cc += 64; /* Convert to upper case */
+ c = c * 16 + cc - ((cc >= '0')? '0' : ('A' - 10));
+#endif
+ }
+ if (*pt == '}')
+ {
+ if (c < 0 || count > 8) *errorptr = ERR34;
+ ptr = pt;
+ break;
+ }
+ /* If the sequence of hex digits does not end with '}', then we don't
+ recognize this construct; fall through to the normal \x handling. */
+ }
+#endif
+
+ /* Read just a single hex char */
+
+ c = 0;
+ while (i++ < 2 && (digitab[ptr[1]] & ctype_xdigit) != 0)
+ {
+ int cc; /* Some compilers don't like ++ */
+ cc = *(++ptr); /* in initializers */
+#if !EBCDIC /* ASCII coding */
+ if (cc >= 'a') cc -= 32; /* Convert to upper case */
+ c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10));
+#else /* EBCDIC coding */
+ if (cc <= 'z') cc += 64; /* Convert to upper case */
+ c = c * 16 + cc - ((cc >= '0')? '0' : ('A' - 10));
+#endif
+ }
+ break;
+
+ /* Other special escapes not starting with a digit are straightforward */
+
+ case 'c':
+ c = *(++ptr);
+ if (c == 0)
+ {
+ *errorptr = ERR2;
+ return 0;
+ }
+
+ /* A letter is upper-cased; then the 0x40 bit is flipped. This coding
+ is ASCII-specific, but then the whole concept of \cx is ASCII-specific.
+ (However, an EBCDIC equivalent has now been added.) */
+
+#if !EBCDIC /* ASCII coding */
+ if (c >= 'a' && c <= 'z') c -= 32;
+ c ^= 0x40;
+#else /* EBCDIC coding */
+ if (c >= 'a' && c <= 'z') c += 64;
+ c ^= 0xC0;
+#endif
+ break;
+
+ /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any
+ other alphameric following \ is an error if PCRE_EXTRA was set; otherwise,
+ for Perl compatibility, it is a literal. This code looks a bit odd, but
+ there used to be some cases other than the default, and there may be again
+ in future, so I haven't "optimized" it. */
+
+ default:
+ if ((options & PCRE_EXTRA) != 0) switch(c)
+ {
+ default:
+ *errorptr = ERR3;
+ break;
+ }
+ break;
+ }
+ }
+
+*ptrptr = ptr;
+return c;
+}
+
+
+
+/*************************************************
+* Check for counted repeat *
+*************************************************/
+
+/* This function is called when a '{' is encountered in a place where it might
+start a quantifier. It looks ahead to see if it really is a quantifier or not.
+It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd}
+where the ddds are digits.
+
+Arguments:
+ p pointer to the first char after '{'
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+is_counted_repeat(const uschar *p)
+{
+if ((digitab[*p++] & ctype_digit) == 0) return FALSE;
+while ((digitab[*p] & ctype_digit) != 0) p++;
+if (*p == '}') return TRUE;
+
+if (*p++ != ',') return FALSE;
+if (*p == '}') return TRUE;
+
+if ((digitab[*p++] & ctype_digit) == 0) return FALSE;
+while ((digitab[*p] & ctype_digit) != 0) p++;
+
+return (*p == '}');
+}
+
+
+
+/*************************************************
+* Read repeat counts *
+*************************************************/
+
+/* Read an item of the form {n,m} and return the values. This is called only
+after is_counted_repeat() has confirmed that a repeat-count quantifier exists,
+so the syntax is guaranteed to be correct, but we need to check the values.
+
+Arguments:
+ p pointer to first char after '{'
+ minp pointer to int for min
+ maxp pointer to int for max
+ returned as -1 if no max
+ errorptr points to pointer to error message
+
+Returns: pointer to '}' on success;
+ current ptr on error, with errorptr set
+*/
+
+static const uschar *
+read_repeat_counts(const uschar *p, int *minp, int *maxp, const char **errorptr)
+{
+int min = 0;
+int max = -1;
+
+while ((digitab[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0';
+
+if (*p == '}') max = min; else
+ {
+ if (*(++p) != '}')
+ {
+ max = 0;
+ while((digitab[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0';
+ if (max < min)
+ {
+ *errorptr = ERR4;
+ return p;
+ }
+ }
+ }
+
+/* Do paranoid checks, then fill in the required variables, and pass back the
+pointer to the terminating '}'. */
+
+if (min > 65535 || max > 65535)
+ *errorptr = ERR5;
+else
+ {
+ *minp = min;
+ *maxp = max;
+ }
+return p;
+}
+
+
+
+/*************************************************
+* Find first significant op code *
+*************************************************/
+
+/* This is called by several functions that scan a compiled expression looking
+for a fixed first character, or an anchoring op code etc. It skips over things
+that do not influence this. For some calls, a change of option is important.
+
+Arguments:
+ code pointer to the start of the group
+ options pointer to external options
+ optbit the option bit whose changing is significant, or
+ zero if none are
+
+Returns: pointer to the first significant opcode
+*/
+
+static const uschar*
+first_significant_code(const uschar *code, int *options, int optbit)
+{
+for (;;)
+ {
+ switch ((int)*code)
+ {
+ case OP_OPT:
+ if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit))
+ *options = (int)code[1];
+ code += 2;
+ break;
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ do code += GET(code, 1); while (*code == OP_ALT);
+ /* Fall through */
+
+ case OP_CALLOUT:
+ case OP_CREF:
+ case OP_BRANUMBER:
+ case OP_WORD_BOUNDARY:
+ case OP_NOT_WORD_BOUNDARY:
+ code += OP_lengths[*code];
+ break;
+
+ default:
+ return code;
+ }
+ }
+/* Control never reaches here */
+}
+
+
+
+
+/*************************************************
+* Find the fixed length of a pattern *
+*************************************************/
+
+/* Scan a pattern and compute the fixed length of subject that will match it,
+if the length is fixed. This is needed for dealing with backward assertions.
+In UTF8 mode, the result is in characters rather than bytes.
+
+Arguments:
+ code points to the start of the pattern (the bracket)
+ options the compiling options
+
+Returns: the fixed length, or -1 if there is no fixed length,
+ or -2 if \C was encountered
+*/
+
+static int
+find_fixedlength(uschar *code, int options)
+{
+int length = -1;
+
+register int branchlength = 0;
+register uschar *cc = code + 1 + LINK_SIZE;
+
+/* Scan along the opcodes for this branch. If we get to the end of the
+branch, check the length against that of the other branches. */
+
+for (;;)
+ {
+ int d;
+ register int op = *cc;
+ if (op >= OP_BRA) op = OP_BRA;
+
+ switch (op)
+ {
+ case OP_BRA:
+ case OP_ONCE:
+ case OP_COND:
+ d = find_fixedlength(cc, options);
+ if (d < 0) return d;
+ branchlength += d;
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ cc += 1 + LINK_SIZE;
+ break;
+
+ /* Reached end of a branch; if it's a ket it is the end of a nested
+ call. If it's ALT it is an alternation in a nested call. If it is
+ END it's the end of the outer call. All can be handled by the same code. */
+
+ case OP_ALT:
+ case OP_KET:
+ case OP_KETRMAX:
+ case OP_KETRMIN:
+ case OP_END:
+ if (length < 0) length = branchlength;
+ else if (length != branchlength) return -1;
+ if (*cc != OP_ALT) return length;
+ cc += 1 + LINK_SIZE;
+ branchlength = 0;
+ break;
+
+ /* Skip over assertive subpatterns */
+
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ do cc += GET(cc, 1); while (*cc == OP_ALT);
+ /* Fall through */
+
+ /* Skip over things that don't match chars */
+
+ case OP_REVERSE:
+ case OP_BRANUMBER:
+ case OP_CREF:
+ case OP_OPT:
+ case OP_CALLOUT:
+ case OP_SOD:
+ case OP_SOM:
+ case OP_EOD:
+ case OP_EODN:
+ case OP_CIRC:
+ case OP_DOLL:
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ cc += OP_lengths[*cc];
+ break;
+
+ /* Handle char strings. In UTF-8 mode we must count characters, not bytes.
+ This requires a scan of the string, unfortunately. We assume valid UTF-8
+ strings, so all we do is reduce the length by one for every byte whose bits
+ are 10xxxxxx. */
+
+ case OP_CHARS:
+ branchlength += *(++cc);
+#ifdef SUPPORT_UTF8
+ if ((options & PCRE_UTF8) != 0)
+ for (d = 1; d <= *cc; d++)
+ if ((cc[d] & 0xc0) == 0x80) branchlength--;
+#endif
+ cc += *cc + 1;
+ break;
+
+ /* Handle exact repetitions. The count is already in characters, but we
+ need to skip over a multibyte character in UTF8 mode. */
+
+ case OP_EXACT:
+ branchlength += GET2(cc,1);
+ cc += 4;
+#ifdef SUPPORT_UTF8
+ if ((options & PCRE_UTF8) != 0)
+ {
+ while((*cc & 0x80) == 0x80) cc++;
+ }
+#endif
+ break;
+
+ case OP_TYPEEXACT:
+ branchlength += GET2(cc,1);
+ cc += 4;
+ break;
+
+ /* Handle single-char matchers */
+
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ branchlength++;
+ cc++;
+ break;
+
+ /* The single-byte matcher isn't allowed */
+
+ case OP_ANYBYTE:
+ return -2;
+
+ /* Check a class for variable quantification */
+
+#ifdef SUPPORT_UTF8
+ case OP_XCLASS:
+ cc += GET(cc, 1) - 33;
+ /* Fall through */
+#endif
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ cc += 33;
+
+ switch (*cc)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ return -1;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ if (GET2(cc,1) != GET2(cc,3)) return -1;
+ branchlength += GET2(cc,1);
+ cc += 5;
+ break;
+
+ default:
+ branchlength++;
+ }
+ break;
+
+ /* Anything else is variable length */
+
+ default:
+ return -1;
+ }
+ }
+/* Control never gets here */
+}
+
+
+
+
+/*************************************************
+* Scan compiled regex for numbered bracket *
+*************************************************/
+
+/* This little function scans through a compiled pattern until it finds a
+capturing bracket with the given number.
+
+Arguments:
+ code points to start of expression
+ utf8 TRUE in UTF-8 mode
+ number the required bracket number
+
+Returns: pointer to the opcode for the bracket, or NULL if not found
+*/
+
+static const uschar *
+find_bracket(const uschar *code, BOOL utf8, int number)
+{
+#ifndef SUPPORT_UTF8
+utf8 = utf8; /* Stop pedantic compilers complaining */
+#endif
+
+for (;;)
+ {
+ register int c = *code;
+ if (c == OP_END) return NULL;
+ else if (c == OP_CHARS) code += code[1] + OP_lengths[c];
+ else if (c > OP_BRA)
+ {
+ int n = c - OP_BRA;
+ if (n > EXTRACT_BASIC_MAX) n = GET2(code, 2+LINK_SIZE);
+ if (n == number) return (uschar *)code;
+ code += OP_lengths[OP_BRA];
+ }
+ else
+ {
+ code += OP_lengths[c];
+
+#ifdef SUPPORT_UTF8
+
+ /* In UTF-8 mode, opcodes that are followed by a character may be followed
+ by a multi-byte character. The length in the table is a minimum, so we have
+ to scan along to skip the extra characters. All opcodes are less than 128,
+ so we can use relatively efficient code. */
+
+ if (1 /* utf8 */) switch(c)
+ {
+ case OP_EXACT:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ while ((*code & 0xc0) == 0x80) code++;
+ break;
+
+ /* XCLASS is used for classes that cannot be represented just by a bit
+ map. This includes negated single high-valued characters. The length in
+ the table is zero; the actual length is stored in the compled code. */
+
+ case OP_XCLASS:
+ code += GET(code, 1) + 1;
+ break;
+ }
+#endif
+ }
+ }
+}
+
+
+
+/*************************************************
+* Scan compiled regex for recursion reference *
+*************************************************/
+
+/* This little function scans through a compiled pattern until it finds an
+instance of OP_RECURSE.
+
+Arguments:
+ code points to start of expression
+ utf8 TRUE in UTF-8 mode
+
+Returns: pointer to the opcode for OP_RECURSE, or NULL if not found
+*/
+
+static const uschar *
+find_recurse(const uschar *code, BOOL utf8)
+{
+#ifndef SUPPORT_UTF8
+utf8 = utf8; /* Stop pedantic compilers complaining */
+#endif
+
+for (;;)
+ {
+ register int c = *code;
+ if (c == OP_END) return NULL;
+ else if (c == OP_RECURSE) return code;
+ else if (c == OP_CHARS) code += code[1] + OP_lengths[c];
+ else if (c > OP_BRA)
+ {
+ code += OP_lengths[OP_BRA];
+ }
+ else
+ {
+ code += OP_lengths[c];
+
+#ifdef SUPPORT_UTF8
+
+ /* In UTF-8 mode, opcodes that are followed by a character may be followed
+ by a multi-byte character. The length in the table is a minimum, so we have
+ to scan along to skip the extra characters. All opcodes are less than 128,
+ so we can use relatively efficient code. */
+
+ if (1 /* utf8 */) switch(c)
+ {
+ case OP_EXACT:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ while ((*code & 0xc0) == 0x80) code++;
+ break;
+
+ /* XCLASS is used for classes that cannot be represented just by a bit
+ map. This includes negated single high-valued characters. The length in
+ the table is zero; the actual length is stored in the compled code. */
+
+ case OP_XCLASS:
+ code += GET(code, 1) + 1;
+ break;
+ }
+#endif
+ }
+ }
+}
+
+
+
+/*************************************************
+* Scan compiled branch for non-emptiness *
+*************************************************/
+
+/* This function scans through a branch of a compiled pattern to see whether it
+can match the empty string or not. It is called only from could_be_empty()
+below. Note that first_significant_code() skips over assertions. If we hit an
+unclosed bracket, we return "empty" - this means we've struck an inner bracket
+whose current branch will already have been scanned.
+
+Arguments:
+ code points to start of search
+ endcode points to where to stop
+ utf8 TRUE if in UTF8 mode
+
+Returns: TRUE if what is matched could be empty
+*/
+
+static BOOL
+could_be_empty_branch(const uschar *code, const uschar *endcode, BOOL utf8)
+{
+register int c;
+for (code = first_significant_code(code + 1 + LINK_SIZE, NULL, 0);
+ code < endcode;
+ code = first_significant_code(code + OP_lengths[c], NULL, 0))
+ {
+ const uschar *ccode;
+
+ c = *code;
+
+ if (c >= OP_BRA)
+ {
+ BOOL empty_branch;
+ if (GET(code, 1) == 0) return TRUE; /* Hit unclosed bracket */
+
+ /* Scan a closed bracket */
+
+ empty_branch = FALSE;
+ do
+ {
+ if (!empty_branch && could_be_empty_branch(code, endcode, utf8))
+ empty_branch = TRUE;
+ code += GET(code, 1);
+ }
+ while (*code == OP_ALT);
+ if (!empty_branch) return FALSE; /* All branches are non-empty */
+ code += 1 + LINK_SIZE;
+ c = *code;
+ }
+
+ else switch (c)
+ {
+ /* Check for quantifiers after a class */
+
+#ifdef SUPPORT_UTF8
+ case OP_XCLASS:
+ ccode = code + GET(code, 1);
+ goto CHECK_CLASS_REPEAT;
+#endif
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ ccode = code + 33;
+
+#ifdef SUPPORT_UTF8
+ CHECK_CLASS_REPEAT:
+#endif
+
+ switch (*ccode)
+ {
+ case OP_CRSTAR: /* These could be empty; continue */
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ break;
+
+ default: /* Non-repeat => class must match */
+ case OP_CRPLUS: /* These repeats aren't empty */
+ case OP_CRMINPLUS:
+ return FALSE;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ if (GET2(ccode, 1) > 0) return FALSE; /* Minimum > 0 */
+ break;
+ }
+ break;
+
+ /* Opcodes that must match a character */
+
+ case OP_NOT_DIGIT:
+ case OP_DIGIT:
+ case OP_NOT_WHITESPACE:
+ case OP_WHITESPACE:
+ case OP_NOT_WORDCHAR:
+ case OP_WORDCHAR:
+ case OP_ANY:
+ case OP_ANYBYTE:
+ case OP_CHARS:
+ case OP_NOT:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_EXACT:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTEXACT:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEEXACT:
+ return FALSE;
+
+ /* End of branch */
+
+ case OP_KET:
+ case OP_KETRMAX:
+ case OP_KETRMIN:
+ case OP_ALT:
+ return TRUE;
+
+ /* In UTF-8 mode, STAR, MINSTAR, QUERY, MINQUERY, UPTO, and MINUPTO may be
+ followed by a multibyte character */
+
+#ifdef SUPPORT_UTF8
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ if (1 /* utf8 */) while ((code[2] & 0xc0) == 0x80) code++;
+ break;
+#endif
+ }
+ }
+
+return TRUE;
+}
+
+
+
+/*************************************************
+* Scan compiled regex for non-emptiness *
+*************************************************/
+
+/* This function is called to check for left recursive calls. We want to check
+the current branch of the current pattern to see if it could match the empty
+string. If it could, we must look outwards for branches at other levels,
+stopping when we pass beyond the bracket which is the subject of the recursion.
+
+Arguments:
+ code points to start of the recursion
+ endcode points to where to stop (current RECURSE item)
+ bcptr points to the chain of current (unclosed) branch starts
+ utf8 TRUE if in UTF-8 mode
+
+Returns: TRUE if what is matched could be empty
+*/
+
+static BOOL
+could_be_empty(const uschar *code, const uschar *endcode, branch_chain *bcptr,
+ BOOL utf8)
+{
+while (bcptr != NULL && bcptr->current >= code)
+ {
+ if (!could_be_empty_branch(bcptr->current, endcode, utf8)) return FALSE;
+ bcptr = bcptr->outer;
+ }
+return TRUE;
+}
+
+
+
+/*************************************************
+* Check for POSIX class syntax *
+*************************************************/
+
+/* This function is called when the sequence "[:" or "[." or "[=" is
+encountered in a character class. It checks whether this is followed by an
+optional ^ and then a sequence of letters, terminated by a matching ":]" or
+".]" or "=]".
+
+Argument:
+ ptr pointer to the initial [
+ endptr where to return the end pointer
+ cd pointer to compile data
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+check_posix_syntax(const uschar *ptr, const uschar **endptr, compile_data *cd)
+{
+int terminator; /* Don't combine these lines; the Solaris cc */
+terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */
+if (*(++ptr) == '^') ptr++;
+while ((cd->ctypes[*ptr] & ctype_letter) != 0) ptr++;
+if (*ptr == terminator && ptr[1] == ']')
+ {
+ *endptr = ptr;
+ return TRUE;
+ }
+return FALSE;
+}
+
+
+
+
+/*************************************************
+* Check POSIX class name *
+*************************************************/
+
+/* This function is called to check the name given in a POSIX-style class entry
+such as [:alnum:].
+
+Arguments:
+ ptr points to the first letter
+ len the length of the name
+
+Returns: a value representing the name, or -1 if unknown
+*/
+
+static int
+check_posix_name(const uschar *ptr, int len)
+{
+register int yield = 0;
+while (posix_name_lengths[yield] != 0)
+ {
+ if (len == posix_name_lengths[yield] &&
+ strncmp((const char *)ptr, posix_names[yield], len) == 0) return yield;
+ yield++;
+ }
+return -1;
+}
+
+
+/*************************************************
+* Adjust OP_RECURSE items in repeated group *
+*************************************************/
+
+/* OP_RECURSE items contain an offset from the start of the regex to the group
+that is referenced. This means that groups can be replicated for fixed
+repetition simply by copying (because the recursion is allowed to refer to
+earlier groups that are outside the current group). However, when a group is
+optional (i.e. the minimum quantifier is zero), OP_BRAZERO is inserted before
+it, after it has been compiled. This means that any OP_RECURSE items within it
+that refer to the group itself or any contained groups have to have their
+offsets adjusted. That is the job of this function. Before it is called, the
+partially compiled regex must be temporarily terminated with OP_END.
+
+Arguments:
+ group points to the start of the group
+ adjust the amount by which the group is to be moved
+ utf8 TRUE in UTF-8 mode
+ cd contains pointers to tables etc.
+
+Returns: nothing
+*/
+
+static void
+adjust_recurse(uschar *group, int adjust, BOOL utf8, compile_data *cd)
+{
+uschar *ptr = group;
+while ((ptr = (uschar *)find_recurse(ptr, utf8)) != NULL)
+ {
+ int offset = GET(ptr, 1);
+ if (cd->start_code + offset >= group) PUT(ptr, 1, offset + adjust);
+ ptr += 1 + LINK_SIZE;
+ }
+}
+
+
+
+/*************************************************
+* Compile one branch *
+*************************************************/
+
+/* Scan the pattern, compiling it into the code vector. If the options are
+changed during the branch, the pointer is used to change the external options
+bits.
+
+Arguments:
+ optionsptr pointer to the option bits
+ brackets points to number of extracting brackets used
+ code points to the pointer to the current code point
+ ptrptr points to the current pattern pointer
+ errorptr points to pointer to error message
+ firstbyteptr set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE)
+ reqbyteptr set to the last literal character required, else < 0
+ bcptr points to current branch chain
+ cd contains pointers to tables etc.
+
+Returns: TRUE on success
+ FALSE, with *errorptr set on error
+*/
+
+static BOOL
+compile_branch(int *optionsptr, int *brackets, uschar **codeptr,
+ const uschar **ptrptr, const char **errorptr, int *firstbyteptr,
+ int *reqbyteptr, branch_chain *bcptr, compile_data *cd)
+{
+int repeat_type, op_type;
+int repeat_min = 0, repeat_max = 0; /* To please picky compilers */
+int bravalue = 0;
+int length;
+int greedy_default, greedy_non_default;
+int firstbyte, reqbyte;
+int zeroreqbyte, zerofirstbyte;
+int req_caseopt, reqvary, tempreqvary;
+int condcount = 0;
+int options = *optionsptr;
+register int c;
+register uschar *code = *codeptr;
+uschar *tempcode;
+BOOL inescq = FALSE;
+BOOL groupsetfirstbyte = FALSE;
+const uschar *ptr = *ptrptr;
+const uschar *tempptr;
+uschar *previous = NULL;
+uschar class[32];
+
+#ifdef SUPPORT_UTF8
+BOOL class_utf8;
+BOOL utf8 = (options & PCRE_UTF8) != 0;
+uschar *class_utf8data;
+uschar utf8_char[6];
+#else
+BOOL utf8 = FALSE;
+#endif
+
+/* Set up the default and non-default settings for greediness */
+
+greedy_default = ((options & PCRE_UNGREEDY) != 0);
+greedy_non_default = greedy_default ^ 1;
+
+/* Initialize no first char, no required char. REQ_UNSET means "no char
+matching encountered yet". It gets changed to REQ_NONE if we hit something that
+matches a non-fixed char first char; reqbyte just remains unset if we never
+find one.
+
+When we hit a repeat whose minimum is zero, we may have to adjust these values
+to take the zero repeat into account. This is implemented by setting them to
+zerofirstbyte and zeroreqbyte when such a repeat is encountered. The individual
+item types that can be repeated set these backoff variables appropriately. */
+
+firstbyte = reqbyte = zerofirstbyte = zeroreqbyte = REQ_UNSET;
+
+/* The variable req_caseopt contains either the REQ_CASELESS value or zero,
+according to the current setting of the caseless flag. REQ_CASELESS is a bit
+value > 255. It is added into the firstbyte or reqbyte variables to record the
+case status of the value. */
+
+req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0;
+
+/* Switch on next character until the end of the branch */
+
+for (;; ptr++)
+ {
+ BOOL negate_class;
+ BOOL possessive_quantifier;
+ int class_charcount;
+ int class_lastchar;
+ int newoptions;
+ int recno;
+ int skipbytes;
+ int subreqbyte;
+ int subfirstbyte;
+
+ c = *ptr;
+ if (inescq && c != 0) goto NORMAL_CHAR;
+
+ if ((options & PCRE_EXTENDED) != 0)
+ {
+ if ((cd->ctypes[c] & ctype_space) != 0) continue;
+ if (c == '#')
+ {
+ /* The space before the ; is to avoid a warning on a silly compiler
+ on the Macintosh. */
+ while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+ if (c != 0) continue; /* Else fall through to handle end of string */
+ }
+ }
+
+ switch(c)
+ {
+ /* The branch terminates at end of string, |, or ). */
+
+ case 0:
+ case '|':
+ case ')':
+ *firstbyteptr = firstbyte;
+ *reqbyteptr = reqbyte;
+ *codeptr = code;
+ *ptrptr = ptr;
+ return TRUE;
+
+ /* Handle single-character metacharacters. In multiline mode, ^ disables
+ the setting of any following char as a first character. */
+
+ case '^':
+ if ((options & PCRE_MULTILINE) != 0)
+ {
+ if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
+ }
+ previous = NULL;
+ *code++ = OP_CIRC;
+ break;
+
+ case '$':
+ previous = NULL;
+ *code++ = OP_DOLL;
+ break;
+
+ /* There can never be a first char if '.' is first, whatever happens about
+ repeats. The value of reqbyte doesn't change either. */
+
+ case '.':
+ if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
+ zerofirstbyte = firstbyte;
+ zeroreqbyte = reqbyte;
+ previous = code;
+ *code++ = OP_ANY;
+ break;
+
+ /* Character classes. If the included characters are all < 255 in value, we
+ build a 32-byte bitmap of the permitted characters, except in the special
+ case where there is only one such character. For negated classes, we build
+ the map as usual, then invert it at the end. However, we use a different
+ opcode so that data characters > 255 can be handled correctly.
+
+ If the class contains characters outside the 0-255 range, a different
+ opcode is compiled. It may optionally have a bit map for characters < 256,
+ but those above are are explicitly listed afterwards. A flag byte tells
+ whether the bitmap is present, and whether this is a negated class or not.
+ */
+
+ case '[':
+ previous = code;
+
+ /* PCRE supports POSIX class stuff inside a class. Perl gives an error if
+ they are encountered at the top level, so we'll do that too. */
+
+ if ((ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') &&
+ check_posix_syntax(ptr, &tempptr, cd))
+ {
+ *errorptr = (ptr[1] == ':')? ERR13 : ERR31;
+ goto FAILED;
+ }
+
+ /* If the first character is '^', set the negation flag and skip it. */
+
+ if ((c = *(++ptr)) == '^')
+ {
+ negate_class = TRUE;
+ c = *(++ptr);
+ }
+ else
+ {
+ negate_class = FALSE;
+ }
+
+ /* Keep a count of chars with values < 256 so that we can optimize the case
+ of just a single character (as long as it's < 256). For higher valued UTF-8
+ characters, we don't yet do any optimization. */
+
+ class_charcount = 0;
+ class_lastchar = -1;
+
+#ifdef SUPPORT_UTF8
+ class_utf8 = FALSE; /* No chars >= 256 */
+ class_utf8data = code + LINK_SIZE + 34; /* For UTF-8 items */
+#endif
+
+ /* Initialize the 32-char bit map to all zeros. We have to build the
+ map in a temporary bit of store, in case the class contains only 1
+ character (< 256), because in that case the compiled code doesn't use the
+ bit map. */
+
+ memset(class, 0, 32 * sizeof(uschar));
+
+ /* Process characters until ] is reached. By writing this as a "do" it
+ means that an initial ] is taken as a data character. The first pass
+ through the regex checked the overall syntax, so we don't need to be very
+ strict here. At the start of the loop, c contains the first byte of the
+ character. */
+
+ do
+ {
+#ifdef SUPPORT_UTF8
+ if (utf8 && c > 127)
+ { /* Braces are required because the */
+ GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */
+ }
+#endif
+
+ /* Inside \Q...\E everything is literal except \E */
+
+ if (inescq)
+ {
+ if (c == '\\' && ptr[1] == 'E')
+ {
+ inescq = FALSE;
+ ptr++;
+ continue;
+ }
+ else goto LONE_SINGLE_CHARACTER;
+ }
+
+ /* Handle POSIX class names. Perl allows a negation extension of the
+ form [:^name:]. A square bracket that doesn't match the syntax is
+ treated as a literal. We also recognize the POSIX constructions
+ [.ch.] and [=ch=] ("collating elements") and fault them, as Perl
+ 5.6 and 5.8 do. */
+
+ if (c == '[' &&
+ (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') &&
+ check_posix_syntax(ptr, &tempptr, cd))
+ {
+ BOOL local_negate = FALSE;
+ int posix_class, i;
+ register const uschar *cbits = cd->cbits;
+
+ if (ptr[1] != ':')
+ {
+ *errorptr = ERR31;
+ goto FAILED;
+ }
+
+ ptr += 2;
+ if (*ptr == '^')
+ {
+ local_negate = TRUE;
+ ptr++;
+ }
+
+ posix_class = check_posix_name(ptr, tempptr - ptr);
+ if (posix_class < 0)
+ {
+ *errorptr = ERR30;
+ goto FAILED;
+ }
+
+ /* If matching is caseless, upper and lower are converted to
+ alpha. This relies on the fact that the class table starts with
+ alpha, lower, upper as the first 3 entries. */
+
+ if ((options & PCRE_CASELESS) != 0 && posix_class <= 2)
+ posix_class = 0;
+
+ /* Or into the map we are building up to 3 of the static class
+ tables, or their negations. The [:blank:] class sets up the same
+ chars as the [:space:] class (all white space). We remove the vertical
+ white space chars afterwards. */
+
+ posix_class *= 3;
+ for (i = 0; i < 3; i++)
+ {
+ BOOL blankclass = strncmp((char *)ptr, "blank", 5) == 0;
+ int taboffset = posix_class_maps[posix_class + i];
+ if (taboffset < 0) break;
+ if (local_negate)
+ {
+ for (c = 0; c < 32; c++) class[c] |= ~cbits[c+taboffset];
+ if (blankclass) class[1] |= 0x3c;
+ }
+ else
+ {
+ for (c = 0; c < 32; c++) class[c] |= cbits[c+taboffset];
+ if (blankclass) class[1] &= ~0x3c;
+ }
+ }
+
+ ptr = tempptr + 1;
+ class_charcount = 10; /* Set > 1; assumes more than 1 per class */
+ continue; /* End of POSIX syntax handling */
+ }
+
+ /* Backslash may introduce a single character, or it may introduce one
+ of the specials, which just set a flag. Escaped items are checked for
+ validity in the pre-compiling pass. The sequence \b is a special case.
+ Inside a class (and only there) it is treated as backspace. Elsewhere
+ it marks a word boundary. Other escapes have preset maps ready to
+ or into the one we are building. We assume they have more than one
+ character in them, so set class_charcount bigger than one. */
+
+ if (c == '\\')
+ {
+ c = check_escape(&ptr, errorptr, *brackets, options, TRUE);
+ if (-c == ESC_b) c = '\b'; /* \b is backslash in a class */
+
+ if (-c == ESC_Q) /* Handle start of quoted string */
+ {
+ if (ptr[1] == '\\' && ptr[2] == 'E')
+ {
+ ptr += 2; /* avoid empty string */
+ }
+ else inescq = TRUE;
+ continue;
+ }
+
+ else if (c < 0)
+ {
+ register const uschar *cbits = cd->cbits;
+ class_charcount = 10; /* Greater than 1 is what matters */
+ switch (-c)
+ {
+ case ESC_d:
+ for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_digit];
+ continue;
+
+ case ESC_D:
+ for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_digit];
+ continue;
+
+ case ESC_w:
+ for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_word];
+ continue;
+
+ case ESC_W:
+ for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_word];
+ continue;
+
+ case ESC_s:
+ for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_space];
+ class[1] &= ~0x08; /* Perl 5.004 onwards omits VT from \s */
+ continue;
+
+ case ESC_S:
+ for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_space];
+ class[1] |= 0x08; /* Perl 5.004 onwards omits VT from \s */
+ continue;
+
+ /* Unrecognized escapes are faulted if PCRE is running in its
+ strict mode. By default, for compatibility with Perl, they are
+ treated as literals. */
+
+ default:
+ if ((options & PCRE_EXTRA) != 0)
+ {
+ *errorptr = ERR7;
+ goto FAILED;
+ }
+ c = *ptr; /* The final character */
+ }
+ }
+
+ /* Fall through if we have a single character (c >= 0). This may be
+ > 256 in UTF-8 mode. */
+
+ } /* End of backslash handling */
+
+ /* A single character may be followed by '-' to form a range. However,
+ Perl does not permit ']' to be the end of the range. A '-' character
+ here is treated as a literal. */
+
+ if (ptr[1] == '-' && ptr[2] != ']')
+ {
+ int d;
+ ptr += 2;
+
+#ifdef SUPPORT_UTF8
+ if (1 /* utf8 */)
+ { /* Braces are required because the */
+ GETCHARLEN(d, ptr, ptr); /* macro generates multiple statements */
+ }
+ else
+#endif
+ d = *ptr;
+
+ /* The second part of a range can be a single-character escape, but
+ not any of the other escapes. Perl 5.6 treats a hyphen as a literal
+ in such circumstances. */
+
+ if (d == '\\')
+ {
+ const uschar *oldptr = ptr;
+ d = check_escape(&ptr, errorptr, *brackets, options, TRUE);
+
+ /* \b is backslash; any other special means the '-' was literal */
+
+ if (d < 0)
+ {
+ if (d == -ESC_b) d = '\b'; else
+ {
+ ptr = oldptr - 2;
+ goto LONE_SINGLE_CHARACTER; /* A few lines below */
+ }
+ }
+ }
+
+ /* Check that the two values are in the correct order */
+
+ if (d < c)
+ {
+ *errorptr = ERR8;
+ goto FAILED;
+ }
+
+ /* If d is greater than 255, we can't just use the bit map, so set up
+ for the UTF-8 supporting class type. If we are not caseless, we can
+ just set up a single range. If we are caseless, the characters < 256
+ are handled with a bitmap, in order to get the case-insensitive
+ handling. */
+
+#ifdef SUPPORT_UTF8
+ if (d > 255)
+ {
+ class_utf8 = TRUE;
+ *class_utf8data++ = XCL_RANGE;
+ if ((options & PCRE_CASELESS) == 0)
+ {
+ class_utf8data += ord2utf8(c, class_utf8data);
+ class_utf8data += ord2utf8(d, class_utf8data);
+ continue; /* Go get the next char in the class */
+ }
+ class_utf8data += ord2utf8(256, class_utf8data);
+ class_utf8data += ord2utf8(d, class_utf8data);
+ d = 255;
+ /* Fall through */
+ }
+#endif
+ /* We use the bit map if the range is entirely < 255, or if part of it
+ is < 255 and matching is caseless. */
+
+ for (; c <= d; c++)
+ {
+ class[c/8] |= (1 << (c&7));
+ if ((options & PCRE_CASELESS) != 0)
+ {
+ int uc = cd->fcc[c]; /* flip case */
+ class[uc/8] |= (1 << (uc&7));
+ }
+ class_charcount++; /* in case a one-char range */
+ class_lastchar = c;
+ }
+
+ continue; /* Go get the next char in the class */
+ }
+
+ /* Handle a lone single character - we can get here for a normal
+ non-escape char, or after \ that introduces a single character. */
+
+ LONE_SINGLE_CHARACTER:
+
+ /* Handle a multibyte character */
+
+#ifdef SUPPORT_UTF8
+ if (utf8 && c > 255)
+ {
+ class_utf8 = TRUE;
+ *class_utf8data++ = XCL_SINGLE;
+ class_utf8data += ord2utf8(c, class_utf8data);
+ }
+ else
+#endif
+ /* Handle a single-byte character */
+ {
+ class [c/8] |= (1 << (c&7));
+ if ((options & PCRE_CASELESS) != 0)
+ {
+ c = cd->fcc[c]; /* flip case */
+ class[c/8] |= (1 << (c&7));
+ }
+ class_charcount++;
+ class_lastchar = c;
+ }
+ }
+
+ /* Loop until ']' reached; the check for end of string happens inside the
+ loop. This "while" is the end of the "do" above. */
+
+ while ((c = *(++ptr)) != ']' || inescq);
+
+ /* If class_charcount is 1, we saw precisely one character with a value <
+ 256. In UTF-8 mode, we can optimize if there were no characters >= 256 and
+ the one character is < 128. In non-UTF-8 mode we can always optimize.
+
+ The optimization throws away the bit map. We turn the item into a
+ 1-character OP_CHARS if it's positive, or OP_NOT if it's negative. Note
+ that OP_NOT does not support multibyte characters. In the positive case, it
+ can cause firstbyte to be set. Otherwise, there can be no first char if
+ this item is first, whatever repeat count may follow. In the case of
+ reqbyte, save the previous value for reinstating. */
+
+#ifdef SUPPORT_UTF8
+ if (class_charcount == 1 &&
+ (!utf8 ||
+ (!class_utf8 && class_lastchar < 128)))
+#else
+ if (class_charcount == 1)
+#endif
+ {
+ zeroreqbyte = reqbyte;
+ if (negate_class)
+ {
+ if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
+ zerofirstbyte = firstbyte;
+ *code++ = OP_NOT;
+ }
+ else
+ {
+ if (firstbyte == REQ_UNSET)
+ {
+ zerofirstbyte = REQ_NONE;
+ firstbyte = class_lastchar | req_caseopt;
+ }
+ else
+ {
+ zerofirstbyte = firstbyte;
+ reqbyte = class_lastchar | req_caseopt | cd->req_varyopt;
+ }
+ *code++ = OP_CHARS;
+ *code++ = 1;
+ }
+ *code++ = class_lastchar;
+ break; /* End of class handling */
+ } /* End of 1-byte optimization */
+
+ /* Otherwise, if this is the first thing in the branch, there can be no
+ first char setting, whatever the repeat count. Any reqbyte setting must
+ remain unchanged after any kind of repeat. */
+
+ if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE;
+ zerofirstbyte = firstbyte;
+ zeroreqbyte = reqbyte;
+
+ /* If there are characters with values > 255, we have to compile an
+ extended class, with its own opcode. If there are no characters < 256,
+ we can omit the bitmap. */
+
+#ifdef SUPPORT_UTF8
+ if (class_utf8)
+ {
+ *class_utf8data++ = XCL_END; /* Marks the end of extra data */
+ *code++ = OP_XCLASS;
+ code += LINK_SIZE;
+ *code = negate_class? XCL_NOT : 0;
+
+ /* If the map is required, install it, and move on to the end of
+ the extra data */
+
+ if (class_charcount > 0)
+ {
+ *code++ |= XCL_MAP;
+ memcpy(code, class, 32);
+ code = class_utf8data;
+ }
+
+ /* If the map is not required, slide down the extra data. */
+
+ else
+ {
+ int len = class_utf8data - (code + 33);
+ memmove(code + 1, code + 33, len);
+ code += len + 1;
+ }
+
+ /* Now fill in the complete length of the item */
+
+ PUT(previous, 1, code - previous);
+ break; /* End of class handling */
+ }
+#endif
+
+ /* If there are no characters > 255, negate the 32-byte map if necessary,
+ and copy it into the code vector. If this is the first thing in the branch,
+ there can be no first char setting, whatever the repeat count. Any reqbyte
+ setting must remain unchanged after any kind of repeat. */
+
+ if (negate_class)
+ {
+ *code++ = OP_NCLASS;
+ for (c = 0; c < 32; c++) code[c] = ~class[c];
+ }
+ else
+ {
+ *code++ = OP_CLASS;
+ memcpy(code, class, 32);
+ }
+ code += 32;
+ break;
+
+ /* Various kinds of repeat */
+
+ case '{':
+ if (!is_counted_repeat(ptr+1)) goto NORMAL_CHAR;
+ ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorptr);
+ if (*errorptr != NULL) goto FAILED;
+ goto REPEAT;
+
+ case '*':
+ repeat_min = 0;
+ repeat_max = -1;
+ goto REPEAT;
+
+ case '+':
+ repeat_min = 1;
+ repeat_max = -1;
+ goto REPEAT;
+
+ case '?':
+ repeat_min = 0;
+ repeat_max = 1;
+
+ REPEAT:
+ if (previous == NULL)
+ {
+ *errorptr = ERR9;
+ goto FAILED;
+ }
+
+ if (repeat_min == 0)
+ {
+ firstbyte = zerofirstbyte; /* Adjust for zero repeat */
+ reqbyte = zeroreqbyte; /* Ditto */
+ }
+
+ /* Remember whether this is a variable length repeat */
+
+ reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY;
+
+ op_type = 0; /* Default single-char op codes */
+ possessive_quantifier = FALSE; /* Default not possessive quantifier */
+
+ /* Save start of previous item, in case we have to move it up to make space
+ for an inserted OP_ONCE for the additional '+' extension. */
+
+ tempcode = previous;
+
+ /* If the next character is '+', we have a possessive quantifier. This
+ implies greediness, whatever the setting of the PCRE_UNGREEDY option.
+ If the next character is '?' this is a minimizing repeat, by default,
+ but if PCRE_UNGREEDY is set, it works the other way round. We change the
+ repeat type to the non-default. */
+
+ if (ptr[1] == '+')
+ {
+ repeat_type = 0; /* Force greedy */
+ possessive_quantifier = TRUE;
+ ptr++;
+ }
+ else if (ptr[1] == '?')
+ {
+ repeat_type = greedy_non_default;
+ ptr++;
+ }
+ else repeat_type = greedy_default;
+
+ /* If previous was a recursion, we need to wrap it inside brackets so that
+ it can be replicated if necessary. */
+
+ if (*previous == OP_RECURSE)
+ {
+ memmove(previous + 1 + LINK_SIZE, previous, 1 + LINK_SIZE);
+ code += 1 + LINK_SIZE;
+ *previous = OP_BRA;
+ PUT(previous, 1, code - previous);
+ *code = OP_KET;
+ PUT(code, 1, code - previous);
+ code += 1 + LINK_SIZE;
+ }
+
+ /* If previous was a string of characters, chop off the last one and use it
+ as the subject of the repeat. If there was only one character, we can
+ abolish the previous item altogether. If a one-char item has a minumum of
+ more than one, ensure that it is set in reqbyte - it might not be if a
+ sequence such as x{3} is the first thing in a branch because the x will
+ have gone into firstbyte instead. */
+
+ if (*previous == OP_CHARS)
+ {
+ /* Deal with UTF-8 characters that take up more than one byte. It's
+ easier to write this out separately than try to macrify it. Use c to
+ hold the length of the character in bytes, plus 0x80 to flag that it's a
+ length rather than a small character. */
+
+#ifdef SUPPORT_UTF8
+ if (utf8 && (code[-1] & 0x80) != 0)
+ {
+ uschar *lastchar = code - 1;
+ while((*lastchar & 0xc0) == 0x80) lastchar--;
+ c = code - lastchar; /* Length of UTF-8 character */
+ memcpy(utf8_char, lastchar, c); /* Save the char */
+ if (lastchar == previous + 2) /* There was only one character */
+ {
+ code = previous; /* Abolish the previous item */
+ }
+ else
+ {
+ previous[1] -= c; /* Adjust length of previous */
+ code = lastchar; /* Lost char off the end */
+ tempcode = code; /* Adjust position to be moved for '+' */
+ }
+ c |= 0x80; /* Flag c as a length */
+ }
+ else
+#endif
+
+ /* Handle the case of a single byte - either with no UTF8 support, or
+ with UTF-8 disabled, or for a UTF-8 character < 128. */
+
+ {
+ c = *(--code);
+ if (code == previous + 2) /* There was only one character */
+ {
+ code = previous; /* Abolish the previous item */
+ if (repeat_min > 1) reqbyte = c | req_caseopt | cd->req_varyopt;
+ }
+ else
+ {
+ previous[1]--; /* adjust length */
+ tempcode = code; /* Adjust position to be moved for '+' */
+ }
+ }
+
+ goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */
+ }
+
+ /* If previous was a single negated character ([^a] or similar), we use
+ one of the special opcodes, replacing it. The code is shared with single-
+ character repeats by setting opt_type to add a suitable offset into
+ repeat_type. OP_NOT is currently used only for single-byte chars. */
+
+ else if (*previous == OP_NOT)
+ {
+ op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */
+ c = previous[1];
+ code = previous;
+ goto OUTPUT_SINGLE_REPEAT;
+ }
+
+ /* If previous was a character type match (\d or similar), abolish it and
+ create a suitable repeat item. The code is shared with single-character
+ repeats by setting op_type to add a suitable offset into repeat_type. */
+
+ else if (*previous < OP_EODN)
+ {
+ op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */
+ c = *previous;
+ code = previous;
+
+ OUTPUT_SINGLE_REPEAT:
+
+ /* If the maximum is zero then the minimum must also be zero; Perl allows
+ this case, so we do too - by simply omitting the item altogether. */
+
+ if (repeat_max == 0) goto END_REPEAT;
+
+ /* Combine the op_type with the repeat_type */
+
+ repeat_type += op_type;
+
+ /* A minimum of zero is handled either as the special case * or ?, or as
+ an UPTO, with the maximum given. */
+
+ if (repeat_min == 0)
+ {
+ if (repeat_max == -1) *code++ = OP_STAR + repeat_type;
+ else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type;
+ else
+ {
+ *code++ = OP_UPTO + repeat_type;
+ PUT2INC(code, 0, repeat_max);
+ }
+ }
+
+ /* The case {1,} is handled as the special case + */
+
+ else if (repeat_min == 1 && repeat_max == -1)
+ *code++ = OP_PLUS + repeat_type;
+
+ /* The case {n,n} is just an EXACT, while the general case {n,m} is
+ handled as an EXACT followed by an UPTO. An EXACT of 1 is optimized. */
+
+ else
+ {
+ if (repeat_min != 1)
+ {
+ *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */
+ PUT2INC(code, 0, repeat_min);
+ }
+
+ /* If the mininum is 1 and the previous item was a character string,
+ we either have to put back the item that got cancelled if the string
+ length was 1, or add the character back onto the end of a longer
+ string. For a character type nothing need be done; it will just get
+ put back naturally. Note that the final character is always going to
+ get added below, so we leave code ready for its insertion. */
+
+ else if (*previous == OP_CHARS)
+ {
+ if (code == previous) code += 2; else
+
+ /* In UTF-8 mode, a multibyte char has its length in c, with the 0x80
+ bit set as a flag. The length will always be between 2 and 6. */
+
+#ifdef SUPPORT_UTF8
+ if (utf8 && c >= 128) previous[1] += c & 7; else
+#endif
+ previous[1]++;
+ }
+
+ /* For a single negated character we also have to put back the
+ item that got cancelled. At present this applies only to single byte
+ characters in any mode. */
+
+ else if (*previous == OP_NOT) code++;
+
+ /* If the maximum is unlimited, insert an OP_STAR. Before doing so,
+ we have to insert the character for the previous code. In UTF-8 mode,
+ long characters have their length in c, with the 0x80 bit as a flag. */
+
+ if (repeat_max < 0)
+ {
+#ifdef SUPPORT_UTF8
+ if (utf8 && c >= 128)
+ {
+ memcpy(code, utf8_char, c & 7);
+ code += c & 7;
+ }
+ else
+#endif
+ *code++ = c;
+ *code++ = OP_STAR + repeat_type;
+ }
+
+ /* Else insert an UPTO if the max is greater than the min, again
+ preceded by the character, for the previously inserted code. */
+
+ else if (repeat_max != repeat_min)
+ {
+#ifdef SUPPORT_UTF8
+ if (utf8 && c >= 128)
+ {
+ memcpy(code, utf8_char, c & 7);
+ code += c & 7;
+ }
+ else
+#endif
+ *code++ = c;
+ repeat_max -= repeat_min;
+ *code++ = OP_UPTO + repeat_type;
+ PUT2INC(code, 0, repeat_max);
+ }
+ }
+
+ /* The character or character type itself comes last in all cases. */
+
+#ifdef SUPPORT_UTF8
+ if (utf8 && c >= 128)
+ {
+ memcpy(code, utf8_char, c & 7);
+ code += c & 7;
+ }
+ else
+#endif
+
+ *code++ = c;
+ }
+
+ /* If previous was a character class or a back reference, we put the repeat
+ stuff after it, but just skip the item if the repeat was {0,0}. */
+
+ else if (*previous == OP_CLASS ||
+ *previous == OP_NCLASS ||
+#ifdef SUPPORT_UTF8
+ *previous == OP_XCLASS ||
+#endif
+ *previous == OP_REF)
+ {
+ if (repeat_max == 0)
+ {
+ code = previous;
+ goto END_REPEAT;
+ }
+ if (repeat_min == 0 && repeat_max == -1)
+ *code++ = OP_CRSTAR + repeat_type;
+ else if (repeat_min == 1 && repeat_max == -1)
+ *code++ = OP_CRPLUS + repeat_type;
+ else if (repeat_min == 0 && repeat_max == 1)
+ *code++ = OP_CRQUERY + repeat_type;
+ else
+ {
+ *code++ = OP_CRRANGE + repeat_type;
+ PUT2INC(code, 0, repeat_min);
+ if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */
+ PUT2INC(code, 0, repeat_max);
+ }
+ }
+
+ /* If previous was a bracket group, we may have to replicate it in certain
+ cases. */
+
+ else if (*previous >= OP_BRA || *previous == OP_ONCE ||
+ *previous == OP_COND)
+ {
+ register int i;
+ int ketoffset = 0;
+ int len = code - previous;
+ uschar *bralink = NULL;
+
+ /* If the maximum repeat count is unlimited, find the end of the bracket
+ by scanning through from the start, and compute the offset back to it
+ from the current code pointer. There may be an OP_OPT setting following
+ the final KET, so we can't find the end just by going back from the code
+ pointer. */
+
+ if (repeat_max == -1)
+ {
+ register uschar *ket = previous;
+ do ket += GET(ket, 1); while (*ket != OP_KET);
+ ketoffset = code - ket;
+ }
+
+ /* The case of a zero minimum is special because of the need to stick
+ OP_BRAZERO in front of it, and because the group appears once in the
+ data, whereas in other cases it appears the minimum number of times. For
+ this reason, it is simplest to treat this case separately, as otherwise
+ the code gets far too messy. There are several special subcases when the
+ minimum is zero. */
+
+ if (repeat_min == 0)
+ {
+ /* If the maximum is also zero, we just omit the group from the output
+ altogether. */
+
+ if (repeat_max == 0)
+ {
+ code = previous;
+ goto END_REPEAT;
+ }
+
+ /* If the maximum is 1 or unlimited, we just have to stick in the
+ BRAZERO and do no more at this point. However, we do need to adjust
+ any OP_RECURSE calls inside the group that refer to the group itself or
+ any internal group, because the offset is from the start of the whole
+ regex. Temporarily terminate the pattern while doing this. */
+
+ if (repeat_max <= 1)
+ {
+ *code = OP_END;
+ adjust_recurse(previous, 1, utf8, cd);
+ memmove(previous+1, previous, len);
+ code++;
+ *previous++ = OP_BRAZERO + repeat_type;
+ }
+
+ /* If the maximum is greater than 1 and limited, we have to replicate
+ in a nested fashion, sticking OP_BRAZERO before each set of brackets.
+ The first one has to be handled carefully because it's the original
+ copy, which has to be moved up. The remainder can be handled by code
+ that is common with the non-zero minimum case below. We have to
+ adjust the value or repeat_max, since one less copy is required. Once
+ again, we may have to adjust any OP_RECURSE calls inside the group. */
+
+ else
+ {
+ int offset;
+ *code = OP_END;
+ adjust_recurse(previous, 2 + LINK_SIZE, utf8, cd);
+ memmove(previous + 2 + LINK_SIZE, previous, len);
+ code += 2 + LINK_SIZE;
+ *previous++ = OP_BRAZERO + repeat_type;
+ *previous++ = OP_BRA;
+
+ /* We chain together the bracket offset fields that have to be
+ filled in later when the ends of the brackets are reached. */
+
+ offset = (bralink == NULL)? 0 : previous - bralink;
+ bralink = previous;
+ PUTINC(previous, 0, offset);
+ }
+
+ repeat_max--;
+ }
+
+ /* If the minimum is greater than zero, replicate the group as many
+ times as necessary, and adjust the maximum to the number of subsequent
+ copies that we need. If we set a first char from the group, and didn't
+ set a required char, copy the latter from the former. */
+
+ else
+ {
+ if (repeat_min > 1)
+ {
+ if (groupsetfirstbyte && reqbyte < 0) reqbyte = firstbyte;
+ for (i = 1; i < repeat_min; i++)
+ {
+ memcpy(code, previous, len);
+ code += len;
+ }
+ }
+ if (repeat_max > 0) repeat_max -= repeat_min;
+ }
+
+ /* This code is common to both the zero and non-zero minimum cases. If
+ the maximum is limited, it replicates the group in a nested fashion,
+ remembering the bracket starts on a stack. In the case of a zero minimum,
+ the first one was set up above. In all cases the repeat_max now specifies
+ the number of additional copies needed. */
+
+ if (repeat_max >= 0)
+ {
+ for (i = repeat_max - 1; i >= 0; i--)
+ {
+ *code++ = OP_BRAZERO + repeat_type;
+
+ /* All but the final copy start a new nesting, maintaining the
+ chain of brackets outstanding. */
+
+ if (i != 0)
+ {
+ int offset;
+ *code++ = OP_BRA;
+ offset = (bralink == NULL)? 0 : code - bralink;
+ bralink = code;
+ PUTINC(code, 0, offset);
+ }
+
+ memcpy(code, previous, len);
+ code += len;
+ }
+
+ /* Now chain through the pending brackets, and fill in their length
+ fields (which are holding the chain links pro tem). */
+
+ while (bralink != NULL)
+ {
+ int oldlinkoffset;
+ int offset = code - bralink + 1;
+ uschar *bra = code - offset;
+ oldlinkoffset = GET(bra, 1);
+ bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset;
+ *code++ = OP_KET;
+ PUTINC(code, 0, offset);
+ PUT(bra, 1, offset);
+ }
+ }
+
+ /* If the maximum is unlimited, set a repeater in the final copy. We
+ can't just offset backwards from the current code point, because we
+ don't know if there's been an options resetting after the ket. The
+ correct offset was computed above. */
+
+ else code[-ketoffset] = OP_KETRMAX + repeat_type;
+ }
+
+ /* Else there's some kind of shambles */
+
+ else
+ {
+ *errorptr = ERR11;
+ goto FAILED;
+ }
+
+ /* If the character following a repeat is '+', we wrap the entire repeated
+ item inside OP_ONCE brackets. This is just syntactic sugar, taken from
+ Sun's Java package. The repeated item starts at tempcode, not at previous,
+ which might be the first part of a string whose (former) last char we
+ repeated. However, we don't support '+' after a greediness '?'. */
+
+ if (possessive_quantifier)
+ {
+ int len = code - tempcode;
+ memmove(tempcode + 1+LINK_SIZE, tempcode, len);
+ code += 1 + LINK_SIZE;
+ len += 1 + LINK_SIZE;
+ tempcode[0] = OP_ONCE;
+ *code++ = OP_KET;
+ PUTINC(code, 0, len);
+ PUT(tempcode, 1, len);
+ }
+
+ /* In all case we no longer have a previous item. We also set the
+ "follows varying string" flag for subsequently encountered reqbytes if
+ it isn't already set and we have just passed a varying length item. */
+
+ END_REPEAT:
+ previous = NULL;
+ cd->req_varyopt |= reqvary;
+ break;
+
+
+ /* Start of nested bracket sub-expression, or comment or lookahead or
+ lookbehind or option setting or condition. First deal with special things
+ that can come after a bracket; all are introduced by ?, and the appearance
+ of any of them means that this is not a referencing group. They were
+ checked for validity in the first pass over the string, so we don't have to
+ check for syntax errors here. */
+
+ case '(':
+ newoptions = options;
+ skipbytes = 0;
+
+ if (*(++ptr) == '?')
+ {
+ int set, unset;
+ int *optset;
+
+ switch (*(++ptr))
+ {
+ case '#': /* Comment; skip to ket */
+ ptr++;
+ while (*ptr != ')') ptr++;
+ continue;
+
+ case ':': /* Non-extracting bracket */
+ bravalue = OP_BRA;
+ ptr++;
+ break;
+
+ case '(':
+ bravalue = OP_COND; /* Conditional group */
+
+ /* Condition to test for recursion */
+
+ if (ptr[1] == 'R')
+ {
+ code[1+LINK_SIZE] = OP_CREF;
+ PUT2(code, 2+LINK_SIZE, CREF_RECURSE);
+ skipbytes = 3;
+ ptr += 3;
+ }
+
+ /* Condition to test for a numbered subpattern match. We know that
+ if a digit follows ( then there will just be digits until ) because
+ the syntax was checked in the first pass. */
+
+ else if ((digitab[ptr[1]] && ctype_digit) != 0)
+ {
+ int condref; /* Don't amalgamate; some compilers */
+ condref = *(++ptr) - '0'; /* grumble at autoincrement in declaration */
+ while (*(++ptr) != ')') condref = condref*10 + *ptr - '0';
+ if (condref == 0)
+ {
+ *errorptr = ERR35;
+ goto FAILED;
+ }
+ ptr++;
+ code[1+LINK_SIZE] = OP_CREF;
+ PUT2(code, 2+LINK_SIZE, condref);
+ skipbytes = 3;
+ }
+ /* For conditions that are assertions, we just fall through, having
+ set bravalue above. */
+ break;
+
+ case '=': /* Positive lookahead */
+ bravalue = OP_ASSERT;
+ ptr++;
+ break;
+
+ case '!': /* Negative lookahead */
+ bravalue = OP_ASSERT_NOT;
+ ptr++;
+ break;
+
+ case '<': /* Lookbehinds */
+ switch (*(++ptr))
+ {
+ case '=': /* Positive lookbehind */
+ bravalue = OP_ASSERTBACK;
+ ptr++;
+ break;
+
+ case '!': /* Negative lookbehind */
+ bravalue = OP_ASSERTBACK_NOT;
+ ptr++;
+ break;
+ }
+ break;
+
+ case '>': /* One-time brackets */
+ bravalue = OP_ONCE;
+ ptr++;
+ break;
+
+ case 'C': /* Callout - may be followed by digits */
+ *code++ = OP_CALLOUT;
+ {
+ int n = 0;
+ while ((digitab[*(++ptr)] & ctype_digit) != 0)
+ n = n * 10 + *ptr - '0';
+ if (n > 255)
+ {
+ *errorptr = ERR38;
+ goto FAILED;
+ }
+ *code++ = n;
+ }
+ previous = NULL;
+ continue;
+
+ case 'P': /* Named subpattern handling */
+ if (*(++ptr) == '<') /* Definition */
+ {
+ int i, namelen;
+ uschar *slot = cd->name_table;
+ const uschar *name; /* Don't amalgamate; some compilers */
+ name = ++ptr; /* grumble at autoincrement in declaration */
+
+ while (*ptr++ != '>');
+ namelen = ptr - name - 1;
+
+ for (i = 0; i < cd->names_found; i++)
+ {
+ int crc = memcmp(name, slot+2, namelen);
+ if (crc == 0)
+ {
+ if (slot[2+namelen] == 0)
+ {
+ *errorptr = ERR43;
+ goto FAILED;
+ }
+ crc = -1; /* Current name is substring */
+ }
+ if (crc < 0)
+ {
+ memmove(slot + cd->name_entry_size, slot,
+ (cd->names_found - i) * cd->name_entry_size);
+ break;
+ }
+ slot += cd->name_entry_size;
+ }
+
+ PUT2(slot, 0, *brackets + 1);
+ memcpy(slot + 2, name, namelen);
+ slot[2+namelen] = 0;
+ cd->names_found++;
+ goto NUMBERED_GROUP;
+ }
+
+ if (*ptr == '=' || *ptr == '>') /* Reference or recursion */
+ {
+ int i, namelen;
+ int type = *ptr++;
+ const uschar *name = ptr;
+ uschar *slot = cd->name_table;
+
+ while (*ptr != ')') ptr++;
+ namelen = ptr - name;
+
+ for (i = 0; i < cd->names_found; i++)
+ {
+ if (strncmp((char *)name, (char *)slot+2, namelen) == 0) break;
+ slot += cd->name_entry_size;
+ }
+ if (i >= cd->names_found)
+ {
+ *errorptr = ERR15;
+ goto FAILED;
+ }
+
+ recno = GET2(slot, 0);
+
+ if (type == '>') goto HANDLE_RECURSION; /* A few lines below */
+
+ /* Back reference */
+
+ previous = code;
+ *code++ = OP_REF;
+ PUT2INC(code, 0, recno);
+ cd->backref_map |= (recno < 32)? (1 << recno) : 1;
+ if (recno > cd->top_backref) cd->top_backref = recno;
+ continue;
+ }
+
+ /* Should never happen */
+ break;
+
+ case 'R': /* Pattern recursion */
+ ptr++; /* Same as (?0) */
+ /* Fall through */
+
+ /* Recursion or "subroutine" call */
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ const uschar *called;
+ recno = 0;
+ while((digitab[*ptr] & ctype_digit) != 0)
+ recno = recno * 10 + *ptr++ - '0';
+
+ /* Come here from code above that handles a named recursion */
+
+ HANDLE_RECURSION:
+
+ previous = code;
+
+ /* Find the bracket that is being referenced. Temporarily end the
+ regex in case it doesn't exist. */
+
+ *code = OP_END;
+ called = (recno == 0)?
+ cd->start_code : find_bracket(cd->start_code, utf8, recno);
+
+ if (called == NULL)
+ {
+ *errorptr = ERR15;
+ goto FAILED;
+ }
+
+ /* If the subpattern is still open, this is a recursive call. We
+ check to see if this is a left recursion that could loop for ever,
+ and diagnose that case. */
+
+ if (GET(called, 1) == 0 && could_be_empty(called, code, bcptr, utf8))
+ {
+ *errorptr = ERR40;
+ goto FAILED;
+ }
+
+ /* Insert the recursion/subroutine item */
+
+ *code = OP_RECURSE;
+ PUT(code, 1, called - cd->start_code);
+ code += 1 + LINK_SIZE;
+ }
+ continue;
+
+ /* Character after (? not specially recognized */
+
+ default: /* Option setting */
+ set = unset = 0;
+ optset = &set;
+
+ while (*ptr != ')' && *ptr != ':')
+ {
+ switch (*ptr++)
+ {
+ case '-': optset = &unset; break;
+
+ case 'i': *optset |= PCRE_CASELESS; break;
+ case 'm': *optset |= PCRE_MULTILINE; break;
+ case 's': *optset |= PCRE_DOTALL; break;
+ case 'x': *optset |= PCRE_EXTENDED; break;
+ case 'U': *optset |= PCRE_UNGREEDY; break;
+ case 'X': *optset |= PCRE_EXTRA; break;
+ }
+ }
+
+ /* Set up the changed option bits, but don't change anything yet. */
+
+ newoptions = (options | set) & (~unset);
+
+ /* If the options ended with ')' this is not the start of a nested
+ group with option changes, so the options change at this level. Compile
+ code to change the ims options if this setting actually changes any of
+ them. We also pass the new setting back so that it can be put at the
+ start of any following branches, and when this group ends (if we are in
+ a group), a resetting item can be compiled.
+
+ Note that if this item is right at the start of the pattern, the
+ options will have been abstracted and made global, so there will be no
+ change to compile. */
+
+ if (*ptr == ')')
+ {
+ if ((options & PCRE_IMS) != (newoptions & PCRE_IMS))
+ {
+ *code++ = OP_OPT;
+ *code++ = newoptions & PCRE_IMS;
+ }
+
+ /* Change options at this level, and pass them back for use
+ in subsequent branches. Reset the greedy defaults and the case
+ value for firstbyte and reqbyte. */
+
+ *optionsptr = options = newoptions;
+ greedy_default = ((newoptions & PCRE_UNGREEDY) != 0);
+ greedy_non_default = greedy_default ^ 1;
+ req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0;
+
+ previous = NULL; /* This item can't be repeated */
+ continue; /* It is complete */
+ }
+
+ /* If the options ended with ':' we are heading into a nested group
+ with possible change of options. Such groups are non-capturing and are
+ not assertions of any kind. All we need to do is skip over the ':';
+ the newoptions value is handled below. */
+
+ bravalue = OP_BRA;
+ ptr++;
+ }
+ }
+
+ /* If PCRE_NO_AUTO_CAPTURE is set, all unadorned brackets become
+ non-capturing and behave like (?:...) brackets */
+
+ else if ((options & PCRE_NO_AUTO_CAPTURE) != 0)
+ {
+ bravalue = OP_BRA;
+ }
+
+ /* Else we have a referencing group; adjust the opcode. If the bracket
+ number is greater than EXTRACT_BASIC_MAX, we set the opcode one higher, and
+ arrange for the true number to follow later, in an OP_BRANUMBER item. */
+
+ else
+ {
+ NUMBERED_GROUP:
+ if (++(*brackets) > EXTRACT_BASIC_MAX)
+ {
+ bravalue = OP_BRA + EXTRACT_BASIC_MAX + 1;
+ code[1+LINK_SIZE] = OP_BRANUMBER;
+ PUT2(code, 2+LINK_SIZE, *brackets);
+ skipbytes = 3;
+ }
+ else bravalue = OP_BRA + *brackets;
+ }
+
+ /* Process nested bracketed re. Assertions may not be repeated, but other
+ kinds can be. We copy code into a non-register variable in order to be able
+ to pass its address because some compilers complain otherwise. Pass in a
+ new setting for the ims options if they have changed. */
+
+ previous = (bravalue >= OP_ONCE)? code : NULL;
+ *code = bravalue;
+ tempcode = code;
+ tempreqvary = cd->req_varyopt; /* Save value before bracket */
+
+ if (!compile_regex(
+ newoptions, /* The complete new option state */
+ options & PCRE_IMS, /* The previous ims option state */
+ brackets, /* Extracting bracket count */
+ &tempcode, /* Where to put code (updated) */
+ &ptr, /* Input pointer (updated) */
+ errorptr, /* Where to put an error message */
+ (bravalue == OP_ASSERTBACK ||
+ bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */
+ skipbytes, /* Skip over OP_COND/OP_BRANUMBER */
+ &subfirstbyte, /* For possible first char */
+ &subreqbyte, /* For possible last char */
+ bcptr, /* Current branch chain */
+ cd)) /* Tables block */
+ goto FAILED;
+
+ /* At the end of compiling, code is still pointing to the start of the
+ group, while tempcode has been updated to point past the end of the group
+ and any option resetting that may follow it. The pattern pointer (ptr)
+ is on the bracket. */
+
+ /* If this is a conditional bracket, check that there are no more than
+ two branches in the group. */
+
+ else if (bravalue == OP_COND)
+ {
+ uschar *tc = code;
+ condcount = 0;
+
+ do {
+ condcount++;
+ tc += GET(tc,1);
+ }
+ while (*tc != OP_KET);
+
+ if (condcount > 2)
+ {
+ *errorptr = ERR27;
+ goto FAILED;
+ }
+
+ /* If there is just one branch, we must not make use of its firstbyte or
+ reqbyte, because this is equivalent to an empty second branch. */
+
+ if (condcount == 1) subfirstbyte = subreqbyte = REQ_NONE;
+ }
+
+ /* Handle updating of the required and first characters. Update for normal
+ brackets of all kinds, and conditions with two branches (see code above).
+ If the bracket is followed by a quantifier with zero repeat, we have to
+ back off. Hence the definition of zeroreqbyte and zerofirstbyte outside the
+ main loop so that they can be accessed for the back off. */
+
+ zeroreqbyte = reqbyte;
+ zerofirstbyte = firstbyte;
+ groupsetfirstbyte = FALSE;
+
+ if (bravalue >= OP_BRA || bravalue == OP_ONCE || bravalue == OP_COND)
+ {
+ /* If we have not yet set a firstbyte in this branch, take it from the
+ subpattern, remembering that it was set here so that a repeat of more
+ than one can replicate it as reqbyte if necessary. If the subpattern has
+ no firstbyte, set "none" for the whole branch. In both cases, a zero
+ repeat forces firstbyte to "none". */
+
+ if (firstbyte == REQ_UNSET)
+ {
+ if (subfirstbyte >= 0)
+ {
+ firstbyte = subfirstbyte;
+ groupsetfirstbyte = TRUE;
+ }
+ else firstbyte = REQ_NONE;
+ zerofirstbyte = REQ_NONE;
+ }
+
+ /* If firstbyte was previously set, convert the subpattern's firstbyte
+ into reqbyte if there wasn't one, using the vary flag that was in
+ existence beforehand. */
+
+ else if (subfirstbyte >= 0 && subreqbyte < 0)
+ subreqbyte = subfirstbyte | tempreqvary;
+
+ /* If the subpattern set a required byte (or set a first byte that isn't
+ really the first byte - see above), set it. */
+
+ if (subreqbyte >= 0) reqbyte = subreqbyte;
+ }
+
+ /* For a forward assertion, we take the reqbyte, if set. This can be
+ helpful if the pattern that follows the assertion doesn't set a different
+ char. For example, it's useful for /(?=abcde).+/. We can't set firstbyte
+ for an assertion, however because it leads to incorrect effect for patterns
+ such as /(?=a)a.+/ when the "real" "a" would then become a reqbyte instead
+ of a firstbyte. This is overcome by a scan at the end if there's no
+ firstbyte, looking for an asserted first char. */
+
+ else if (bravalue == OP_ASSERT && subreqbyte >= 0) reqbyte = subreqbyte;
+
+ /* Now update the main code pointer to the end of the group. */
+
+ code = tempcode;
+
+ /* Error if hit end of pattern */
+
+ if (*ptr != ')')
+ {
+ *errorptr = ERR14;
+ goto FAILED;
+ }
+ break;
+
+ /* Check \ for being a real metacharacter; if not, fall through and handle
+ it as a data character at the start of a string. Escape items are checked
+ for validity in the pre-compiling pass. */
+
+ case '\\':
+ tempptr = ptr;
+ c = check_escape(&ptr, errorptr, *brackets, options, FALSE);
+
+ /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values
+ are arranged to be the negation of the corresponding OP_values. For the
+ back references, the values are ESC_REF plus the reference number. Only
+ back references and those types that consume a character may be repeated.
+ We can test for values between ESC_b and ESC_Z for the latter; this may
+ have to change if any new ones are ever created. */
+
+ if (c < 0)
+ {
+ if (-c == ESC_Q) /* Handle start of quoted string */
+ {
+ if (ptr[1] == '\\' && ptr[2] == 'E') ptr += 2; /* avoid empty string */
+ else inescq = TRUE;
+ continue;
+ }
+
+ /* For metasequences that actually match a character, we disable the
+ setting of a first character if it hasn't already been set. */
+
+ if (firstbyte == REQ_UNSET && -c > ESC_b && -c < ESC_Z)
+ firstbyte = REQ_NONE;
+
+ /* Set values to reset to if this is followed by a zero repeat. */
+
+ zerofirstbyte = firstbyte;
+ zeroreqbyte = reqbyte;
+
+ /* Back references are handled specially */
+
+ if (-c >= ESC_REF)
+ {
+ int number = -c - ESC_REF;
+ previous = code;
+ *code++ = OP_REF;
+ PUT2INC(code, 0, number);
+ }
+ else
+ {
+ previous = (-c > ESC_b && -c < ESC_Z)? code : NULL;
+ *code++ = -c;
+ }
+ continue;
+ }
+
+ /* Data character: reset and fall through */
+
+ ptr = tempptr;
+ c = '\\';
+
+ /* Handle a run of data characters until a metacharacter is encountered.
+ The first character is guaranteed not to be whitespace or # when the
+ extended flag is set. */
+
+ NORMAL_CHAR:
+ default:
+ previous = code;
+ *code = OP_CHARS;
+ code += 2;
+ length = 0;
+
+ do
+ {
+ /* If in \Q...\E, check for the end; if not, we always have a literal */
+
+ if (inescq)
+ {
+ if (c == '\\' && ptr[1] == 'E')
+ {
+ inescq = FALSE;
+ ptr++;
+ }
+ else
+ {
+ *code++ = c;
+ length++;
+ }
+ continue;
+ }
+
+ /* Skip white space and comments for /x patterns */
+
+ if ((options & PCRE_EXTENDED) != 0)
+ {
+ if ((cd->ctypes[c] & ctype_space) != 0) continue;
+ if (c == '#')
+ {
+ /* The space before the ; is to avoid a warning on a silly compiler
+ on the Macintosh. */
+ while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+ if (c == 0) break;
+ continue;
+ }
+ }
+
+ /* Backslash may introduce a data char or a metacharacter. Escaped items
+ are checked for validity in the pre-compiling pass. Stop the string
+ before a metaitem. */
+
+ if (c == '\\')
+ {
+ tempptr = ptr;
+ c = check_escape(&ptr, errorptr, *brackets, options, FALSE);
+ if (c < 0) { ptr = tempptr; break; }
+
+ /* If a character is > 127 in UTF-8 mode, we have to turn it into
+ two or more bytes in the UTF-8 encoding. */
+
+#ifdef SUPPORT_UTF8
+ if (utf8 && c > 127)
+ {
+ uschar buffer[8];
+ int len = ord2utf8(c, buffer);
+ for (c = 0; c < len; c++) *code++ = buffer[c];
+ length += len;
+ continue;
+ }
+#endif
+ }
+
+ /* Ordinary character or single-char escape */
+
+ *code++ = c;
+ length++;
+ }
+
+ /* This "while" is the end of the "do" above. */
+
+ while (length < MAXLIT && (cd->ctypes[c = *(++ptr)] & ctype_meta) == 0);
+
+ /* Update the first and last requirements. These are always bytes, even in
+ UTF-8 mode. However, there is a special case to be considered when there
+ are only one or two characters. Because this gets messy in UTF-8 mode, the
+ code is kept separate. When we get here "length" contains the number of
+ bytes. */
+
+#ifdef SUPPORT_UTF8
+ if (utf8 && length > 1)
+ {
+ uschar *t = previous + 3; /* After this code, t */
+ while (t < code && (*t & 0xc0) == 0x80) t++; /* follows the 1st char */
+
+ /* Handle the case when there is only one multibyte character. It must
+ have at least two bytes because of the "length > 1" test above. */
+
+ if (t == code)
+ {
+ /* If no previous first byte, set it from this character, but revert to
+ none on a zero repeat. */
+
+ if (firstbyte == REQ_UNSET)
+ {
+ zerofirstbyte = REQ_NONE;
+ firstbyte = previous[2];
+ }
+
+ /* Otherwise, leave the first byte value alone, and don't change it on
+ a zero repeat */
+
+ else zerofirstbyte = firstbyte;
+
+ /* In both cases, a zero repeat resets the previous required byte */
+
+ zeroreqbyte = reqbyte;
+ }
+
+ /* Handle the case when there is more than one character. These may be
+ single-byte or multibyte characters */
+
+ else
+ {
+ t = code - 1; /* After this code, t is at the */
+ while ((*t & 0xc0) == 0x80) t--; /* start of the last character */
+
+ /* If no previous first byte, set it from the first character, and
+ retain it on a zero repeat (of the last character). The required byte
+ is reset on a zero repeat, either to the byte before the last
+ character, unless this is the first byte of the string. In that case,
+ it reverts to its previous value. */
+
+ if (firstbyte == REQ_UNSET)
+ {
+ zerofirstbyte = firstbyte = previous[2] | req_caseopt;
+ zeroreqbyte = (t - 1 == previous + 2)?
+ reqbyte : t[-1] | req_caseopt | cd->req_varyopt;
+ }
+
+ /* If there was a previous first byte, leave it alone, and don't change
+ it on a zero repeat. The required byte is reset on a zero repeat to the
+ byte before the last character. */
+
+ else
+ {
+ zerofirstbyte = firstbyte;
+ zeroreqbyte = t[-1] | req_caseopt | cd->req_varyopt;
+ }
+ }
+
+ /* In all cases (we know length > 1), the new required byte is the last
+ byte of the string. */
+
+ reqbyte = code[-1] | req_caseopt | cd->req_varyopt;
+ }
+
+ else /* End of UTF-8 coding */
+#endif
+
+ /* This is the code for non-UTF-8 operation, either without UTF-8 support,
+ or when UTF-8 is not enabled. */
+
+ {
+ /* firstbyte was not previously set; take it from this string */
+
+ if (firstbyte == REQ_UNSET)
+ {
+ if (length == 1)
+ {
+ zerofirstbyte = REQ_NONE;
+ firstbyte = previous[2] | req_caseopt;
+ zeroreqbyte = reqbyte;
+ }
+ else
+ {
+ zerofirstbyte = firstbyte = previous[2] | req_caseopt;
+ zeroreqbyte = (length > 2)?
+ (code[-2] | req_caseopt | cd->req_varyopt) : reqbyte;
+ reqbyte = code[-1] | req_caseopt | cd->req_varyopt;
+ }
+ }
+
+ /* firstbyte was previously set */
+
+ else
+ {
+ zerofirstbyte = firstbyte;
+ zeroreqbyte = (length == 1)? reqbyte :
+ code[-2] | req_caseopt | cd->req_varyopt;
+ reqbyte = code[-1] | req_caseopt | cd->req_varyopt;
+ }
+ }
+
+ /* Set the length in the data vector, and advance to the next state. */
+
+ previous[1] = length;
+ if (length < MAXLIT) ptr--;
+ break;
+ }
+ } /* end of big loop */
+
+/* Control never reaches here by falling through, only by a goto for all the
+error states. Pass back the position in the pattern so that it can be displayed
+to the user for diagnosing the error. */
+
+FAILED:
+*ptrptr = ptr;
+return FALSE;
+}
+
+
+
+
+/*************************************************
+* Compile sequence of alternatives *
+*************************************************/
+
+/* On entry, ptr is pointing past the bracket character, but on return
+it points to the closing bracket, or vertical bar, or end of string.
+The code variable is pointing at the byte into which the BRA operator has been
+stored. If the ims options are changed at the start (for a (?ims: group) or
+during any branch, we need to insert an OP_OPT item at the start of every
+following branch to ensure they get set correctly at run time, and also pass
+the new options into every subsequent branch compile.
+
+Argument:
+ options option bits, including any changes for this subpattern
+ oldims previous settings of ims option bits
+ brackets -> int containing the number of extracting brackets used
+ codeptr -> the address of the current code pointer
+ ptrptr -> the address of the current pattern pointer
+ errorptr -> pointer to error message
+ lookbehind TRUE if this is a lookbehind assertion
+ skipbytes skip this many bytes at start (for OP_COND, OP_BRANUMBER)
+ firstbyteptr place to put the first required character, or a negative number
+ reqbyteptr place to put the last required character, or a negative number
+ bcptr pointer to the chain of currently open branches
+ cd points to the data block with tables pointers etc.
+
+Returns: TRUE on success
+*/
+
+static BOOL
+compile_regex(int options, int oldims, int *brackets, uschar **codeptr,
+ const uschar **ptrptr, const char **errorptr, BOOL lookbehind, int skipbytes,
+ int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, compile_data *cd)
+{
+const uschar *ptr = *ptrptr;
+uschar *code = *codeptr;
+uschar *last_branch = code;
+uschar *start_bracket = code;
+uschar *reverse_count = NULL;
+int firstbyte, reqbyte;
+int branchfirstbyte, branchreqbyte;
+branch_chain bc;
+
+bc.outer = bcptr;
+bc.current = code;
+
+firstbyte = reqbyte = REQ_UNSET;
+
+/* Offset is set zero to mark that this bracket is still open */
+
+PUT(code, 1, 0);
+code += 1 + LINK_SIZE + skipbytes;
+
+/* Loop for each alternative branch */
+
+for (;;)
+ {
+ /* Handle a change of ims options at the start of the branch */
+
+ if ((options & PCRE_IMS) != oldims)
+ {
+ *code++ = OP_OPT;
+ *code++ = options & PCRE_IMS;
+ }
+
+ /* Set up dummy OP_REVERSE if lookbehind assertion */
+
+ if (lookbehind)
+ {
+ *code++ = OP_REVERSE;
+ reverse_count = code;
+ PUTINC(code, 0, 0);
+ }
+
+ /* Now compile the branch */
+
+ if (!compile_branch(&options, brackets, &code, &ptr, errorptr,
+ &branchfirstbyte, &branchreqbyte, &bc, cd))
+ {
+ *ptrptr = ptr;
+ return FALSE;
+ }
+
+ /* If this is the first branch, the firstbyte and reqbyte values for the
+ branch become the values for the regex. */
+
+ if (*last_branch != OP_ALT)
+ {
+ firstbyte = branchfirstbyte;
+ reqbyte = branchreqbyte;
+ }
+
+ /* If this is not the first branch, the first char and reqbyte have to
+ match the values from all the previous branches, except that if the previous
+ value for reqbyte didn't have REQ_VARY set, it can still match, and we set
+ REQ_VARY for the regex. */
+
+ else
+ {
+ /* If we previously had a firstbyte, but it doesn't match the new branch,
+ we have to abandon the firstbyte for the regex, but if there was previously
+ no reqbyte, it takes on the value of the old firstbyte. */
+
+ if (firstbyte >= 0 && firstbyte != branchfirstbyte)
+ {
+ if (reqbyte < 0) reqbyte = firstbyte;
+ firstbyte = REQ_NONE;
+ }
+
+ /* If we (now or from before) have no firstbyte, a firstbyte from the
+ branch becomes a reqbyte if there isn't a branch reqbyte. */
+
+ if (firstbyte < 0 && branchfirstbyte >= 0 && branchreqbyte < 0)
+ branchreqbyte = branchfirstbyte;
+
+ /* Now ensure that the reqbytes match */
+
+ if ((reqbyte & ~REQ_VARY) != (branchreqbyte & ~REQ_VARY))
+ reqbyte = REQ_NONE;
+ else reqbyte |= branchreqbyte; /* To "or" REQ_VARY */
+ }
+
+ /* If lookbehind, check that this branch matches a fixed-length string,
+ and put the length into the OP_REVERSE item. Temporarily mark the end of
+ the branch with OP_END. */
+
+ if (lookbehind)
+ {
+ int length;
+ *code = OP_END;
+ length = find_fixedlength(last_branch, options);
+ DPRINTF(("fixed length = %d\n", length));
+ if (length < 0)
+ {
+ *errorptr = (length == -2)? ERR36 : ERR25;
+ *ptrptr = ptr;
+ return FALSE;
+ }
+ PUT(reverse_count, 0, length);
+ }
+
+ /* Reached end of expression, either ')' or end of pattern. Go back through
+ the alternative branches and reverse the chain of offsets, with the field in
+ the BRA item now becoming an offset to the first alternative. If there are
+ no alternatives, it points to the end of the group. The length in the
+ terminating ket is always the length of the whole bracketed item. If any of
+ the ims options were changed inside the group, compile a resetting op-code
+ following, except at the very end of the pattern. Return leaving the pointer
+ at the terminating char. */
+
+ if (*ptr != '|')
+ {
+ int length = code - last_branch;
+ do
+ {
+ int prev_length = GET(last_branch, 1);
+ PUT(last_branch, 1, length);
+ length = prev_length;
+ last_branch -= length;
+ }
+ while (length > 0);
+
+ /* Fill in the ket */
+
+ *code = OP_KET;
+ PUT(code, 1, code - start_bracket);
+ code += 1 + LINK_SIZE;
+
+ /* Resetting option if needed */
+
+ if ((options & PCRE_IMS) != oldims && *ptr == ')')
+ {
+ *code++ = OP_OPT;
+ *code++ = oldims;
+ }
+
+ /* Set values to pass back */
+
+ *codeptr = code;
+ *ptrptr = ptr;
+ *firstbyteptr = firstbyte;
+ *reqbyteptr = reqbyte;
+ return TRUE;
+ }
+
+ /* Another branch follows; insert an "or" node. Its length field points back
+ to the previous branch while the bracket remains open. At the end the chain
+ is reversed. It's done like this so that the start of the bracket has a
+ zero offset until it is closed, making it possible to detect recursion. */
+
+ *code = OP_ALT;
+ PUT(code, 1, code - last_branch);
+ bc.current = last_branch = code;
+ code += 1 + LINK_SIZE;
+ ptr++;
+ }
+/* Control never reaches here */
+}
+
+
+
+
+/*************************************************
+* Check for anchored expression *
+*************************************************/
+
+/* Try to find out if this is an anchored regular expression. Consider each
+alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket
+all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then
+it's anchored. However, if this is a multiline pattern, then only OP_SOD
+counts, since OP_CIRC can match in the middle.
+
+We can also consider a regex to be anchored if OP_SOM starts all its branches.
+This is the code for \G, which means "match at start of match position, taking
+into account the match offset".
+
+A branch is also implicitly anchored if it starts with .* and DOTALL is set,
+because that will try the rest of the pattern at all possible matching points,
+so there is no point trying again.... er ....
+
+.... except when the .* appears inside capturing parentheses, and there is a
+subsequent back reference to those parentheses. We haven't enough information
+to catch that case precisely.
+
+At first, the best we could do was to detect when .* was in capturing brackets
+and the highest back reference was greater than or equal to that level.
+However, by keeping a bitmap of the first 31 back references, we can catch some
+of the more common cases more precisely.
+
+Arguments:
+ code points to start of expression (the bracket)
+ options points to the options setting
+ bracket_map a bitmap of which brackets we are inside while testing; this
+ handles up to substring 31; after that we just have to take
+ the less precise approach
+ backref_map the back reference bitmap
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+is_anchored(register const uschar *code, int *options, unsigned int bracket_map,
+ unsigned int backref_map)
+{
+do {
+ const uschar *scode =
+ first_significant_code(code + 1+LINK_SIZE, options, PCRE_MULTILINE);
+ register int op = *scode;
+
+ /* Capturing brackets */
+
+ if (op > OP_BRA)
+ {
+ int new_map;
+ op -= OP_BRA;
+ if (op > EXTRACT_BASIC_MAX) op = GET2(scode, 2+LINK_SIZE);
+ new_map = bracket_map | ((op < 32)? (1 << op) : 1);
+ if (!is_anchored(scode, options, new_map, backref_map)) return FALSE;
+ }
+
+ /* Other brackets */
+
+ else if (op == OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND)
+ {
+ if (!is_anchored(scode, options, bracket_map, backref_map)) return FALSE;
+ }
+
+ /* .* is not anchored unless DOTALL is set and it isn't in brackets that
+ are or may be referenced. */
+
+ else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR) &&
+ (*options & PCRE_DOTALL) != 0)
+ {
+ if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE;
+ }
+
+ /* Check for explicit anchoring */
+
+ else if (op != OP_SOD && op != OP_SOM &&
+ ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC))
+ return FALSE;
+ code += GET(code, 1);
+ }
+while (*code == OP_ALT); /* Loop for each alternative */
+return TRUE;
+}
+
+
+
+/*************************************************
+* Check for starting with ^ or .* *
+*************************************************/
+
+/* This is called to find out if every branch starts with ^ or .* so that
+"first char" processing can be done to speed things up in multiline
+matching and for non-DOTALL patterns that start with .* (which must start at
+the beginning or after \n). As in the case of is_anchored() (see above), we
+have to take account of back references to capturing brackets that contain .*
+because in that case we can't make the assumption.
+
+Arguments:
+ code points to start of expression (the bracket)
+ bracket_map a bitmap of which brackets we are inside while testing; this
+ handles up to substring 31; after that we just have to take
+ the less precise approach
+ backref_map the back reference bitmap
+
+Returns: TRUE or FALSE
+*/
+
+static BOOL
+is_startline(const uschar *code, unsigned int bracket_map,
+ unsigned int backref_map)
+{
+do {
+ const uschar *scode = first_significant_code(code + 1+LINK_SIZE, NULL, 0);
+ register int op = *scode;
+
+ /* Capturing brackets */
+
+ if (op > OP_BRA)
+ {
+ int new_map;
+ op -= OP_BRA;
+ if (op > EXTRACT_BASIC_MAX) op = GET2(scode, 2+LINK_SIZE);
+ new_map = bracket_map | ((op < 32)? (1 << op) : 1);
+ if (!is_startline(scode, new_map, backref_map)) return FALSE;
+ }
+
+ /* Other brackets */
+
+ else if (op == OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND)
+ { if (!is_startline(scode, bracket_map, backref_map)) return FALSE; }
+
+ /* .* is not anchored unless DOTALL is set and it isn't in brackets that
+ may be referenced. */
+
+ else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR)
+ {
+ if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE;
+ }
+
+ /* Check for explicit circumflex */
+
+ else if (op != OP_CIRC) return FALSE;
+ code += GET(code, 1);
+ }
+while (*code == OP_ALT); /* Loop for each alternative */
+return TRUE;
+}
+
+
+
+/*************************************************
+* Check for asserted fixed first char *
+*************************************************/
+
+/* During compilation, the "first char" settings from forward assertions are
+discarded, because they can cause conflicts with actual literals that follow.
+However, if we end up without a first char setting for an unanchored pattern,
+it is worth scanning the regex to see if there is an initial asserted first
+char. If all branches start with the same asserted char, or with a bracket all
+of whose alternatives start with the same asserted char (recurse ad lib), then
+we return that char, otherwise -1.
+
+Arguments:
+ code points to start of expression (the bracket)
+ options pointer to the options (used to check casing changes)
+ inassert TRUE if in an assertion
+
+Returns: -1 or the fixed first char
+*/
+
+static int
+find_firstassertedchar(const uschar *code, int *options, BOOL inassert)
+{
+register int c = -1;
+do {
+ int d;
+ const uschar *scode =
+ first_significant_code(code + 1+LINK_SIZE, options, PCRE_CASELESS);
+ register int op = *scode;
+
+ if (op >= OP_BRA) op = OP_BRA;
+
+ switch(op)
+ {
+ default:
+ return -1;
+
+ case OP_BRA:
+ case OP_ASSERT:
+ case OP_ONCE:
+ case OP_COND:
+ if ((d = find_firstassertedchar(scode, options, op == OP_ASSERT)) < 0)
+ return -1;
+ if (c < 0) c = d; else if (c != d) return -1;
+ break;
+
+ case OP_EXACT: /* Fall through */
+ scode++;
+
+ case OP_CHARS: /* Fall through */
+ scode++;
+
+ case OP_PLUS:
+ case OP_MINPLUS:
+ if (!inassert) return -1;
+ if (c < 0)
+ {
+ c = scode[1];
+ if ((*options & PCRE_CASELESS) != 0) c |= REQ_CASELESS;
+ }
+ else if (c != scode[1]) return -1;
+ break;
+ }
+
+ code += GET(code, 1);
+ }
+while (*code == OP_ALT);
+return c;
+}
+
+
+
+
+#ifdef SUPPORT_UTF8
+/*************************************************
+* Validate a UTF-8 string *
+*************************************************/
+
+/* This function is called (optionally) at the start of compile or match, to
+validate that a supposed UTF-8 string is actually valid. The early check means
+that subsequent code can assume it is dealing with a valid string. The check
+can be turned off for maximum performance, but then consequences of supplying
+an invalid string are then undefined.
+
+Arguments:
+ string points to the string
+ length length of string, or -1 if the string is zero-terminated
+
+Returns: < 0 if the string is a valid UTF-8 string
+ >= 0 otherwise; the value is the offset of the bad byte
+*/
+
+static int
+valid_utf8(const uschar *string, int length)
+{
+register const uschar *p;
+
+if (length < 0)
+ {
+ for (p = string; *p != 0; p++);
+ length = p - string;
+ }
+
+for (p = string; length-- > 0; p++)
+ {
+ register int ab;
+ register int c = *p;
+ if (c < 128) continue;
+ if ((c & 0xc0) != 0xc0) return p - string;
+ ab = utf8_table4[c & 0x3f]; /* Number of additional bytes */
+ if (length < ab) return p - string;
+ length -= ab;
+
+ /* Check top bits in the second byte */
+ if ((*(++p) & 0xc0) != 0x80) return p - string;
+
+ /* Check for overlong sequences for each different length */
+ switch (ab)
+ {
+ /* Check for xx00 000x */
+ case 1:
+ if ((c & 0x3e) == 0) return p - string;
+ continue; /* We know there aren't any more bytes to check */
+
+ /* Check for 1110 0000, xx0x xxxx */
+ case 2:
+ if (c == 0xe0 && (*p & 0x20) == 0) return p - string;
+ break;
+
+ /* Check for 1111 0000, xx00 xxxx */
+ case 3:
+ if (c == 0xf0 && (*p & 0x30) == 0) return p - string;
+ break;
+
+ /* Check for 1111 1000, xx00 0xxx */
+ case 4:
+ if (c == 0xf8 && (*p & 0x38) == 0) return p - string;
+ break;
+
+ /* Check for leading 0xfe or 0xff, and then for 1111 1100, xx00 00xx */
+ case 5:
+ if (c == 0xfe || c == 0xff ||
+ (c == 0xfc && (*p & 0x3c) == 0)) return p - string;
+ break;
+ }
+
+ /* Check for valid bytes after the 2nd, if any; all must start 10 */
+ while (--ab > 0)
+ {
+ if ((*(++p) & 0xc0) != 0x80) return p - string;
+ }
+ }
+
+return -1;
+}
+#endif
+
+
+
+/*************************************************
+* Compile a Regular Expression *
+*************************************************/
+
+/* This function takes a string and returns a pointer to a block of store
+holding a compiled version of the expression.
+
+Arguments:
+ pattern the regular expression
+ options various option bits
+ errorptr pointer to pointer to error text
+ erroroffset ptr offset in pattern where error was detected
+ tables pointer to character tables or NULL
+
+Returns: pointer to compiled data block, or NULL on error,
+ with errorptr and erroroffset set
+*/
+
+pcre *
+pcre_compile(const char *pattern, int options, const char **errorptr,
+ int *erroroffset, const unsigned char *tables)
+{
+real_pcre *re;
+int length = 1 + LINK_SIZE; /* For initial BRA plus length */
+int runlength;
+int c, firstbyte, reqbyte;
+int bracount = 0;
+int branch_extra = 0;
+int branch_newextra;
+int item_count = -1;
+int name_count = 0;
+int max_name_size = 0;
+#ifdef SUPPORT_UTF8
+int lastcharlength = 0;
+BOOL utf8;
+BOOL class_utf8;
+#endif
+BOOL inescq = FALSE;
+unsigned int brastackptr = 0;
+size_t size;
+uschar *code;
+const uschar *codestart;
+const uschar *ptr;
+compile_data compile_block;
+int brastack[BRASTACK_SIZE];
+uschar bralenstack[BRASTACK_SIZE];
+
+/* We can't pass back an error message if errorptr is NULL; I guess the best we
+can do is just return NULL. */
+
+if (errorptr == NULL) return NULL;
+*errorptr = NULL;
+
+/* However, we can give a message for this error */
+
+if (erroroffset == NULL)
+ {
+ *errorptr = ERR16;
+ return NULL;
+ }
+*erroroffset = 0;
+
+/* Can't support UTF8 unless PCRE has been compiled to include the code. */
+
+#ifdef SUPPORT_UTF8
+utf8 = (options & PCRE_UTF8) != 0;
+if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0 &&
+ (*erroroffset = valid_utf8((uschar *)pattern, -1)) >= 0)
+ {
+ *errorptr = ERR44;
+ return NULL;
+ }
+#else
+if ((options & PCRE_UTF8) != 0)
+ {
+ *errorptr = ERR32;
+ return NULL;
+ }
+#endif
+
+if ((options & ~PUBLIC_OPTIONS) != 0)
+ {
+ *errorptr = ERR17;
+ return NULL;
+ }
+
+/* Set up pointers to the individual character tables */
+
+if (tables == NULL) tables = make_pcre_default_tables ();
+compile_block.lcc = tables + lcc_offset;
+compile_block.fcc = tables + fcc_offset;
+compile_block.cbits = tables + cbits_offset;
+compile_block.ctypes = tables + ctypes_offset;
+
+/* Maximum back reference and backref bitmap. This is updated for numeric
+references during the first pass, but for named references during the actual
+compile pass. The bitmap records up to 31 back references to help in deciding
+whether (.*) can be treated as anchored or not. */
+
+compile_block.top_backref = 0;
+compile_block.backref_map = 0;
+
+/* Reflect pattern for debugging output */
+
+DPRINTF(("------------------------------------------------------------------\n"));
+DPRINTF(("%s\n", pattern));
+
+/* The first thing to do is to make a pass over the pattern to compute the
+amount of store required to hold the compiled code. This does not have to be
+perfect as long as errors are overestimates. At the same time we can detect any
+flag settings right at the start, and extract them. Make an attempt to correct
+for any counted white space if an "extended" flag setting appears late in the
+pattern. We can't be so clever for #-comments. */
+
+ptr = (const uschar *)(pattern - 1);
+while ((c = *(++ptr)) != 0)
+ {
+ int min, max;
+ int class_optcount;
+ int bracket_length;
+ int duplength;
+
+ /* If we are inside a \Q...\E sequence, all chars are literal */
+
+ if (inescq) goto NORMAL_CHAR;
+
+ /* Otherwise, first check for ignored whitespace and comments */
+
+ if ((options & PCRE_EXTENDED) != 0)
+ {
+ if ((compile_block.ctypes[c] & ctype_space) != 0) continue;
+ if (c == '#')
+ {
+ /* The space before the ; is to avoid a warning on a silly compiler
+ on the Macintosh. */
+ while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+ if (c == 0) break;
+ continue;
+ }
+ }
+
+ item_count++; /* Is zero for the first non-comment item */
+
+ switch(c)
+ {
+ /* A backslashed item may be an escaped "normal" character or a
+ character type. For a "normal" character, put the pointers and
+ character back so that tests for whitespace etc. in the input
+ are done correctly. */
+
+ case '\\':
+ {
+ const uschar *save_ptr = ptr;
+ c = check_escape(&ptr, errorptr, bracount, options, FALSE);
+ if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+ if (c >= 0)
+ {
+ ptr = save_ptr;
+ c = '\\';
+ goto NORMAL_CHAR;
+ }
+ }
+
+ /* If \Q, enter "literal" mode */
+
+ if (-c == ESC_Q)
+ {
+ inescq = TRUE;
+ continue;
+ }
+
+ /* Other escapes need one byte, and are of length one for repeats */
+
+ length++;
+#ifdef SUPPORT_UTF8
+ lastcharlength = 1;
+#endif
+
+ /* A back reference needs an additional 2 bytes, plus either one or 5
+ bytes for a repeat. We also need to keep the value of the highest
+ back reference. */
+
+ if (c <= -ESC_REF)
+ {
+ int refnum = -c - ESC_REF;
+ compile_block.backref_map |= (refnum < 32)? (1 << refnum) : 1;
+ if (refnum > compile_block.top_backref)
+ compile_block.top_backref = refnum;
+ length += 2; /* For single back reference */
+ if (ptr[1] == '{' && is_counted_repeat(ptr+2))
+ {
+ ptr = read_repeat_counts(ptr+2, &min, &max, errorptr);
+ if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+ if ((min == 0 && (max == 1 || max == -1)) ||
+ (min == 1 && max == -1))
+ length++;
+ else length += 5;
+ if (ptr[1] == '?') ptr++;
+ }
+ }
+ continue;
+
+ case '^': /* Single-byte metacharacters */
+ case '.':
+ case '$':
+ length++;
+#ifdef SUPPORT_UTF8
+ lastcharlength = 1;
+#endif
+ continue;
+
+ case '*': /* These repeats won't be after brackets; */
+ case '+': /* those are handled separately */
+ case '?':
+ length++;
+ goto POSESSIVE; /* A few lines below */
+
+ /* This covers the cases of braced repeats after a single char, metachar,
+ class, or back reference. */
+
+ case '{':
+ if (!is_counted_repeat(ptr+1)) goto NORMAL_CHAR;
+ ptr = read_repeat_counts(ptr+1, &min, &max, errorptr);
+ if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+
+ /* These special cases just insert one extra opcode */
+
+ if ((min == 0 && (max == 1 || max == -1)) ||
+ (min == 1 && max == -1))
+ length++;
+
+ /* These cases might insert additional copies of a preceding character. */
+
+ else
+ {
+#ifdef SUPPORT_UTF8
+ /* In UTF-8 mode, we should find the length in lastcharlength */
+ if (1 /* utf8 */)
+ {
+ if (min != 1)
+ {
+ length -= lastcharlength; /* Uncount the original char or metachar */
+ if (min > 0) length += 3 + lastcharlength;
+ }
+ length += lastcharlength + ((max > 0)? 3 : 1);
+ }
+ else
+#endif
+
+ /* Not UTF-8 mode: all characters are one byte */
+ {
+ if (min != 1)
+ {
+ length--; /* Uncount the original char or metachar */
+ if (min > 0) length += 4;
+ }
+
+ length += (max > 0)? 4 : 2;
+ }
+ }
+
+ if (ptr[1] == '?') ptr++; /* Needs no extra length */
+
+ POSESSIVE: /* Test for possessive quantifier */
+ if (ptr[1] == '+')
+ {
+ ptr++;
+ length += 2 + 2*LINK_SIZE; /* Allow for atomic brackets */
+ }
+ continue;
+
+ /* An alternation contains an offset to the next branch or ket. If any ims
+ options changed in the previous branch(es), and/or if we are in a
+ lookbehind assertion, extra space will be needed at the start of the
+ branch. This is handled by branch_extra. */
+
+ case '|':
+ length += 1 + LINK_SIZE + branch_extra;
+ continue;
+
+ /* A character class uses 33 characters provided that all the character
+ values are less than 256. Otherwise, it uses a bit map for low valued
+ characters, and individual items for others. Don't worry about character
+ types that aren't allowed in classes - they'll get picked up during the
+ compile. A character class that contains only one single-byte character
+ uses 2 or 3 bytes, depending on whether it is negated or not. Notice this
+ where we can. (In UTF-8 mode we can do this only for chars < 128.) */
+
+ case '[':
+ class_optcount = 0;
+
+#ifdef SUPPORT_UTF8
+ class_utf8 = FALSE;
+#endif
+
+ if (*(++ptr) == '^') ptr++;
+
+ /* Written as a "do" so that an initial ']' is taken as data */
+
+ if (*ptr != 0) do
+ {
+ /* Inside \Q...\E everything is literal except \E */
+
+ if (inescq)
+ {
+ if (*ptr != '\\' || ptr[1] != 'E') goto NON_SPECIAL_CHARACTER;
+ inescq = FALSE;
+ ptr += 1;
+ continue;
+ }
+
+ /* Outside \Q...\E, check for escapes */
+
+ if (*ptr == '\\')
+ {
+#ifdef SUPPORT_UTF8
+ int prevchar = ptr[-1];
+#endif
+ int ch = check_escape(&ptr, errorptr, bracount, options, TRUE);
+ if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+
+ /* \b is backspace inside a class */
+
+ if (-ch == ESC_b) ch = '\b';
+
+ /* \Q enters quoting mode */
+
+ if (-ch == ESC_Q)
+ {
+ inescq = TRUE;
+ continue;
+ }
+
+ /* Handle escapes that turn into characters */
+
+ if (ch >= 0)
+ {
+#ifdef SUPPORT_UTF8
+ if (1 /* utf8 */)
+ {
+ if (ch > 127) class_optcount = 10; /* Ensure > 1 */
+ if (ch > 255)
+ {
+ uschar buffer[6];
+ if (!class_utf8)
+ {
+ class_utf8 = TRUE;
+ length += LINK_SIZE + 1 + 1;
+ }
+ length += 1 + ord2utf8(ch, buffer);
+
+ /* If this wide character is preceded by '-', add an extra 2 to
+ the length in case the previous character was < 128, because in
+ this case the whole range will be put into the list. */
+
+ if (prevchar == '-') length += 2;
+ }
+ }
+#endif
+ class_optcount++; /* for possible optimization */
+ }
+ else class_optcount = 10; /* \d, \s etc; make sure > 1 */
+ }
+
+ /* Check the syntax for POSIX stuff. The bits we actually handle are
+ checked during the real compile phase. */
+
+ else if (*ptr == '[' && check_posix_syntax(ptr, &ptr, &compile_block))
+ {
+ ptr++;
+ class_optcount = 10; /* Make sure > 1 */
+ }
+
+ /* Anything else just increments the possible optimization count. If
+ there are wide characters, we are going to have to use an XCLASS. */
+
+ else
+ {
+ NON_SPECIAL_CHARACTER:
+ class_optcount++;
+
+#ifdef SUPPORT_UTF8
+ if (1 /* utf8 */)
+ {
+ int ch;
+ int extra = 0;
+ GETCHARLEN(ch, ptr, extra);
+ if (ch > 127) class_optcount = 10; /* No optimization possible */
+ if (ch > 255)
+ {
+ if (!class_utf8)
+ {
+ class_utf8 = TRUE;
+ length += LINK_SIZE + 1 + 1;
+ }
+ length += 2 + extra;
+
+ /* If this wide character is preceded by '-', add an extra 2 to
+ the length in case the previous character was < 128, because in
+ this case the whole range will be put into the list. */
+
+ if (ptr[-1] == '-') length += 2;
+
+ /* Advance to the end of this character */
+
+ ptr += extra;
+ }
+ }
+#endif
+ }
+ }
+ while (*(++ptr) != 0 && (inescq || *ptr != ']')); /* Concludes "do" above */
+
+ if (*ptr == 0) /* Missing terminating ']' */
+ {
+ *errorptr = ERR6;
+ goto PCRE_ERROR_RETURN;
+ }
+
+ /* We can optimize when there was only one optimizable character. Repeats
+ for positive and negated single one-byte chars are handled by the general
+ code. Here, we handle repeats for the class opcodes. */
+
+ if (class_optcount == 1) length += 3; else
+ {
+ length += 33;
+
+ /* A repeat needs either 1 or 5 bytes. If it is a possessive quantifier,
+ we also need extra for wrapping the whole thing in a sub-pattern. */
+
+ if (*ptr != 0 && ptr[1] == '{' && is_counted_repeat(ptr+2))
+ {
+ ptr = read_repeat_counts(ptr+2, &min, &max, errorptr);
+ if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+ if ((min == 0 && (max == 1 || max == -1)) ||
+ (min == 1 && max == -1))
+ length++;
+ else length += 5;
+ if (ptr[1] == '+')
+ {
+ ptr++;
+ length += 2 + 2*LINK_SIZE;
+ }
+ else if (ptr[1] == '?') ptr++;
+ }
+ }
+ continue;
+
+ /* Brackets may be genuine groups or special things */
+
+ case '(':
+ branch_newextra = 0;
+ bracket_length = 1 + LINK_SIZE;
+
+ /* Handle special forms of bracket, which all start (? */
+
+ if (ptr[1] == '?')
+ {
+ int set, unset;
+ int *optset;
+
+ switch (c = ptr[2])
+ {
+ /* Skip over comments entirely */
+ case '#':
+ ptr += 3;
+ while (*ptr != 0 && *ptr != ')') ptr++;
+ if (*ptr == 0)
+ {
+ *errorptr = ERR18;
+ goto PCRE_ERROR_RETURN;
+ }
+ continue;
+
+ /* Non-referencing groups and lookaheads just move the pointer on, and
+ then behave like a non-special bracket, except that they don't increment
+ the count of extracting brackets. Ditto for the "once only" bracket,
+ which is in Perl from version 5.005. */
+
+ case ':':
+ case '=':
+ case '!':
+ case '>':
+ ptr += 2;
+ break;
+
+ /* (?R) specifies a recursive call to the regex, which is an extension
+ to provide the facility which can be obtained by (?p{perl-code}) in
+ Perl 5.6. In Perl 5.8 this has become (??{perl-code}).
+
+ From PCRE 4.00, items such as (?3) specify subroutine-like "calls" to
+ the appropriate numbered brackets. This includes both recursive and
+ non-recursive calls. (?R) is now synonymous with (?0). */
+
+ case 'R':
+ ptr++;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ ptr += 2;
+ if (c != 'R')
+ while ((digitab[*(++ptr)] & ctype_digit) != 0);
+ if (*ptr != ')')
+ {
+ *errorptr = ERR29;
+ goto PCRE_ERROR_RETURN;
+ }
+ length += 1 + LINK_SIZE;
+
+ /* If this item is quantified, it will get wrapped inside brackets so
+ as to use the code for quantified brackets. We jump down and use the
+ code that handles this for real brackets. */
+
+ if (ptr[1] == '+' || ptr[1] == '*' || ptr[1] == '?' || ptr[1] == '{')
+ {
+ length += 2 + 2 * LINK_SIZE; /* to make bracketed */
+ duplength = 5 + 3 * LINK_SIZE;
+ goto HANDLE_QUANTIFIED_BRACKETS;
+ }
+ continue;
+
+ /* (?C) is an extension which provides "callout" - to provide a bit of
+ the functionality of the Perl (?{...}) feature. An optional number may
+ follow (default is zero). */
+
+ case 'C':
+ ptr += 2;
+ while ((digitab[*(++ptr)] & ctype_digit) != 0);
+ if (*ptr != ')')
+ {
+ *errorptr = ERR39;
+ goto PCRE_ERROR_RETURN;
+ }
+ length += 2;
+ continue;
+
+ /* Named subpatterns are an extension copied from Python */
+
+ case 'P':
+ ptr += 3;
+ if (*ptr == '<')
+ {
+ const uschar *p; /* Don't amalgamate; some compilers */
+ p = ++ptr; /* grumble at autoincrement in declaration */
+ while ((compile_block.ctypes[*ptr] & ctype_word) != 0) ptr++;
+ if (*ptr != '>')
+ {
+ *errorptr = ERR42;
+ goto PCRE_ERROR_RETURN;
+ }
+ name_count++;
+ if (ptr - p > max_name_size) max_name_size = (ptr - p);
+ break;
+ }
+
+ if (*ptr == '=' || *ptr == '>')
+ {
+ while ((compile_block.ctypes[*(++ptr)] & ctype_word) != 0);
+ if (*ptr != ')')
+ {
+ *errorptr = ERR42;
+ goto PCRE_ERROR_RETURN;
+ }
+ break;
+ }
+
+ /* Unknown character after (?P */
+
+ *errorptr = ERR41;
+ goto PCRE_ERROR_RETURN;
+
+ /* Lookbehinds are in Perl from version 5.005 */
+
+ case '<':
+ ptr += 3;
+ if (*ptr == '=' || *ptr == '!')
+ {
+ branch_newextra = 1 + LINK_SIZE;
+ length += 1 + LINK_SIZE; /* For the first branch */
+ break;
+ }
+ *errorptr = ERR24;
+ goto PCRE_ERROR_RETURN;
+
+ /* Conditionals are in Perl from version 5.005. The bracket must either
+ be followed by a number (for bracket reference) or by an assertion
+ group, or (a PCRE extension) by 'R' for a recursion test. */
+
+ case '(':
+ if (ptr[3] == 'R' && ptr[4] == ')')
+ {
+ ptr += 4;
+ length += 3;
+ }
+ else if ((digitab[ptr[3]] & ctype_digit) != 0)
+ {
+ ptr += 4;
+ length += 3;
+ while ((digitab[*ptr] & ctype_digit) != 0) ptr++;
+ if (*ptr != ')')
+ {
+ *errorptr = ERR26;
+ goto PCRE_ERROR_RETURN;
+ }
+ }
+ else /* An assertion must follow */
+ {
+ ptr++; /* Can treat like ':' as far as spacing is concerned */
+ if (ptr[2] != '?' ||
+ (ptr[3] != '=' && ptr[3] != '!' && ptr[3] != '<') )
+ {
+ ptr += 2; /* To get right offset in message */
+ *errorptr = ERR28;
+ goto PCRE_ERROR_RETURN;
+ }
+ }
+ break;
+
+ /* Else loop checking valid options until ) is met. Anything else is an
+ error. If we are without any brackets, i.e. at top level, the settings
+ act as if specified in the options, so massage the options immediately.
+ This is for backward compatibility with Perl 5.004. */
+
+ default:
+ set = unset = 0;
+ optset = &set;
+ ptr += 2;
+
+ for (;; ptr++)
+ {
+ c = *ptr;
+ switch (c)
+ {
+ case 'i':
+ *optset |= PCRE_CASELESS;
+ continue;
+
+ case 'm':
+ *optset |= PCRE_MULTILINE;
+ continue;
+
+ case 's':
+ *optset |= PCRE_DOTALL;
+ continue;
+
+ case 'x':
+ *optset |= PCRE_EXTENDED;
+ continue;
+
+ case 'X':
+ *optset |= PCRE_EXTRA;
+ continue;
+
+ case 'U':
+ *optset |= PCRE_UNGREEDY;
+ continue;
+
+ case '-':
+ optset = &unset;
+ continue;
+
+ /* A termination by ')' indicates an options-setting-only item; if
+ this is at the very start of the pattern (indicated by item_count
+ being zero), we use it to set the global options. This is helpful
+ when analyzing the pattern for first characters, etc. Otherwise
+ nothing is done here and it is handled during the compiling
+ process.
+
+ [Historical note: Up to Perl 5.8, options settings at top level
+ were always global settings, wherever they appeared in the pattern.
+ That is, they were equivalent to an external setting. From 5.8
+ onwards, they apply only to what follows (which is what you might
+ expect).] */
+
+ case ')':
+ if (item_count == 0)
+ {
+ options = (options | set) & (~unset);
+ set = unset = 0; /* To save length */
+ item_count--; /* To allow for several */
+ }
+
+ /* Fall through */
+
+ /* A termination by ':' indicates the start of a nested group with
+ the given options set. This is again handled at compile time, but
+ we must allow for compiled space if any of the ims options are
+ set. We also have to allow for resetting space at the end of
+ the group, which is why 4 is added to the length and not just 2.
+ If there are several changes of options within the same group, this
+ will lead to an over-estimate on the length, but this shouldn't
+ matter very much. We also have to allow for resetting options at
+ the start of any alternations, which we do by setting
+ branch_newextra to 2. Finally, we record whether the case-dependent
+ flag ever changes within the regex. This is used by the "required
+ character" code. */
+
+ case ':':
+ if (((set|unset) & PCRE_IMS) != 0)
+ {
+ length += 4;
+ branch_newextra = 2;
+ if (((set|unset) & PCRE_CASELESS) != 0) options |= PCRE_ICHANGED;
+ }
+ goto END_OPTIONS;
+
+ /* Unrecognized option character */
+
+ default:
+ *errorptr = ERR12;
+ goto PCRE_ERROR_RETURN;
+ }
+ }
+
+ /* If we hit a closing bracket, that's it - this is a freestanding
+ option-setting. We need to ensure that branch_extra is updated if
+ necessary. The only values branch_newextra can have here are 0 or 2.
+ If the value is 2, then branch_extra must either be 2 or 5, depending
+ on whether this is a lookbehind group or not. */
+
+ END_OPTIONS:
+ if (c == ')')
+ {
+ if (branch_newextra == 2 &&
+ (branch_extra == 0 || branch_extra == 1+LINK_SIZE))
+ branch_extra += branch_newextra;
+ continue;
+ }
+
+ /* If options were terminated by ':' control comes here. Fall through
+ to handle the group below. */
+ }
+ }
+
+ /* Extracting brackets must be counted so we can process escapes in a
+ Perlish way. If the number exceeds EXTRACT_BASIC_MAX we are going to
+ need an additional 3 bytes of store per extracting bracket. However, if
+ PCRE_NO_AUTO)CAPTURE is set, unadorned brackets become non-capturing, so we
+ must leave the count alone (it will aways be zero). */
+
+ else if ((options & PCRE_NO_AUTO_CAPTURE) == 0)
+ {
+ bracount++;
+ if (bracount > EXTRACT_BASIC_MAX) bracket_length += 3;
+ }
+
+ /* Save length for computing whole length at end if there's a repeat that
+ requires duplication of the group. Also save the current value of
+ branch_extra, and start the new group with the new value. If non-zero, this
+ will either be 2 for a (?imsx: group, or 3 for a lookbehind assertion. */
+
+ if (brastackptr >= sizeof(brastack)/sizeof(int))
+ {
+ *errorptr = ERR19;
+ goto PCRE_ERROR_RETURN;
+ }
+
+ bralenstack[brastackptr] = branch_extra;
+ branch_extra = branch_newextra;
+
+ brastack[brastackptr++] = length;
+ length += bracket_length;
+ continue;
+
+ /* Handle ket. Look for subsequent max/min; for certain sets of values we
+ have to replicate this bracket up to that many times. If brastackptr is
+ 0 this is an unmatched bracket which will generate an error, but take care
+ not to try to access brastack[-1] when computing the length and restoring
+ the branch_extra value. */
+
+ case ')':
+ length += 1 + LINK_SIZE;
+ if (brastackptr > 0)
+ {
+ duplength = length - brastack[--brastackptr];
+ branch_extra = bralenstack[brastackptr];
+ }
+ else duplength = 0;
+
+ /* The following code is also used when a recursion such as (?3) is
+ followed by a quantifier, because in that case, it has to be wrapped inside
+ brackets so that the quantifier works. The value of duplength must be
+ set before arrival. */
+
+ HANDLE_QUANTIFIED_BRACKETS:
+
+ /* Leave ptr at the final char; for read_repeat_counts this happens
+ automatically; for the others we need an increment. */
+
+ if ((c = ptr[1]) == '{' && is_counted_repeat(ptr+2))
+ {
+ ptr = read_repeat_counts(ptr+2, &min, &max, errorptr);
+ if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+ }
+ else if (c == '*') { min = 0; max = -1; ptr++; }
+ else if (c == '+') { min = 1; max = -1; ptr++; }
+ else if (c == '?') { min = 0; max = 1; ptr++; }
+ else { min = 1; max = 1; }
+
+ /* If the minimum is zero, we have to allow for an OP_BRAZERO before the
+ group, and if the maximum is greater than zero, we have to replicate
+ maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting
+ bracket set. */
+
+ if (min == 0)
+ {
+ length++;
+ if (max > 0) length += (max - 1) * (duplength + 3 + 2*LINK_SIZE);
+ }
+
+ /* When the minimum is greater than zero, we have to replicate up to
+ minval-1 times, with no additions required in the copies. Then, if there
+ is a limited maximum we have to replicate up to maxval-1 times allowing
+ for a BRAZERO item before each optional copy and nesting brackets for all
+ but one of the optional copies. */
+
+ else
+ {
+ length += (min - 1) * duplength;
+ if (max > min) /* Need this test as max=-1 means no limit */
+ length += (max - min) * (duplength + 3 + 2*LINK_SIZE)
+ - (2 + 2*LINK_SIZE);
+ }
+
+ /* Allow space for once brackets for "possessive quantifier" */
+
+ if (ptr[1] == '+')
+ {
+ ptr++;
+ length += 2 + 2*LINK_SIZE;
+ }
+ continue;
+
+ /* Non-special character. For a run of such characters the length required
+ is the number of characters + 2, except that the maximum run length is
+ MAXLIT. We won't get a skipped space or a non-data escape or the start of a
+ # comment as the first character, so the length can't be zero. */
+
+ NORMAL_CHAR:
+ default:
+ length += 2;
+ runlength = 0;
+ do
+ {
+#ifdef SUPPORT_UTF8
+ lastcharlength = 1; /* Need length of last char for UTF-8 repeats */
+#endif
+
+ /* If in a \Q...\E sequence, check for end; otherwise it's a literal */
+ if (inescq)
+ {
+ if (c == '\\' && ptr[1] == 'E')
+ {
+ inescq = FALSE;
+ ptr++;
+ }
+ else runlength++;
+ continue;
+ }
+
+ /* Skip whitespace and comments for /x */
+
+ if ((options & PCRE_EXTENDED) != 0)
+ {
+ if ((compile_block.ctypes[c] & ctype_space) != 0) continue;
+ if (c == '#')
+ {
+ /* The space before the ; is to avoid a warning on a silly compiler
+ on the Macintosh. */
+ while ((c = *(++ptr)) != 0 && c != NEWLINE) ;
+ continue;
+ }
+ }
+
+ /* Backslash may introduce a data char or a metacharacter; stop the
+ string before the latter. */
+
+ if (c == '\\')
+ {
+ const uschar *saveptr = ptr;
+ c = check_escape(&ptr, errorptr, bracount, options, FALSE);
+ if (*errorptr != NULL) goto PCRE_ERROR_RETURN;
+ if (c < 0) { ptr = saveptr; break; }
+
+ /* In UTF-8 mode, add on the number of additional bytes needed to
+ encode this character, and save the total length in case this is a
+ final char that is repeated. */
+
+#ifdef SUPPORT_UTF8
+ if (utf8 && c > 127)
+ {
+ int i;
+ for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++)
+ if (c <= utf8_table1[i]) break;
+ runlength += i;
+ lastcharlength += i;
+ }
+#endif
+ }
+
+ /* Ordinary character or single-char escape */
+
+ runlength++;
+ }
+
+ /* This "while" is the end of the "do" above. */
+
+ while (runlength < MAXLIT &&
+ (compile_block.ctypes[c = *(++ptr)] & ctype_meta) == 0);
+
+ /* If we hit a meta-character, back off to point to it */
+
+ if (runlength < MAXLIT) ptr--;
+
+ /* If the last char in the string is a UTF-8 multibyte character, we must
+ set lastcharlength correctly. If it was specified as an escape, this will
+ already have been done above. However, we also have to support in-line
+ UTF-8 characters, so check backwards from where we are. */
+
+#ifdef SUPPORT_UTF8
+ if (1 /* utf8 */ && ptr != (uschar *)pattern)
+ {
+ const uschar *lastptr = ptr - 1;
+ if ((*lastptr & 0x80) != 0)
+ {
+ while((*lastptr & 0xc0) == 0x80) lastptr--;
+ lastcharlength = ptr - lastptr;
+ }
+ }
+#endif
+
+ length += runlength;
+ continue;
+ }
+ }
+
+length += 2 + LINK_SIZE; /* For final KET and END */
+
+if (length > MAX_PATTERN_SIZE)
+ {
+ *errorptr = ERR20;
+ return NULL;
+ }
+
+/* Compute the size of data block needed and get it, either from malloc or
+externally provided function. */
+
+size = length + sizeof(real_pcre) + name_count * (max_name_size + 3);
+re = (real_pcre *)(pcre_malloc)(size);
+
+if (re == NULL)
+ {
+ *errorptr = ERR21;
+ return NULL;
+ }
+
+/* Put in the magic number, and save the size, options, and table pointer */
+
+re->magic_number = MAGIC_NUMBER;
+re->size = size;
+re->options = options;
+re->tables = tables;
+re->name_entry_size = max_name_size + 3;
+re->name_count = name_count;
+
+/* The starting points of the name/number translation table and of the code are
+passed around in the compile data block. */
+
+compile_block.names_found = 0;
+compile_block.name_entry_size = max_name_size + 3;
+compile_block.name_table = (uschar *)re + sizeof(real_pcre);
+codestart = compile_block.name_table + re->name_entry_size * re->name_count;
+compile_block.start_code = codestart;
+compile_block.req_varyopt = 0;
+
+/* Set up a starting, non-extracting bracket, then compile the expression. On
+error, *errorptr will be set non-NULL, so we don't need to look at the result
+of the function here. */
+
+ptr = (const uschar *)pattern;
+code = (uschar *)codestart;
+*code = OP_BRA;
+bracount = 0;
+(void)compile_regex(options, options & PCRE_IMS, &bracount, &code, &ptr,
+ errorptr, FALSE, 0, &firstbyte, &reqbyte, NULL, &compile_block);
+re->top_bracket = bracount;
+re->top_backref = compile_block.top_backref;
+
+/* If not reached end of pattern on success, there's an excess bracket. */
+
+if (*errorptr == NULL && *ptr != 0) *errorptr = ERR22;
+
+/* Fill in the terminating state and check for disastrous overflow, but
+if debugging, leave the test till after things are printed out. */
+
+*code++ = OP_END;
+
+#ifndef DEBUG
+if (code - codestart > length) *errorptr = ERR23;
+#endif
+
+/* Give an error if there's back reference to a non-existent capturing
+subpattern. */
+
+if (re->top_backref > re->top_bracket) *errorptr = ERR15;
+
+/* Failed to compile, or error while post-processing */
+
+if (*errorptr != NULL)
+ {
+ (pcre_free)(re);
+ PCRE_ERROR_RETURN:
+ *erroroffset = ptr - (const uschar *)pattern;
+ return NULL;
+ }
+
+/* If the anchored option was not passed, set the flag if we can determine that
+the pattern is anchored by virtue of ^ characters or \A or anything else (such
+as starting with .* when DOTALL is set).
+
+Otherwise, if we know what the first character has to be, save it, because that
+speeds up unanchored matches no end. If not, see if we can set the
+PCRE_STARTLINE flag. This is helpful for multiline matches when all branches
+start with ^. and also when all branches start with .* for non-DOTALL matches.
+*/
+
+if ((options & PCRE_ANCHORED) == 0)
+ {
+ int temp_options = options;
+ if (is_anchored(codestart, &temp_options, 0, compile_block.backref_map))
+ re->options |= PCRE_ANCHORED;
+ else
+ {
+ if (firstbyte < 0)
+ firstbyte = find_firstassertedchar(codestart, &temp_options, FALSE);
+ if (firstbyte >= 0) /* Remove caseless flag for non-caseable chars */
+ {
+ int ch = firstbyte & 255;
+ re->first_byte = ((firstbyte & REQ_CASELESS) != 0 &&
+ compile_block.fcc[ch] == ch)? ch : firstbyte;
+ re->options |= PCRE_FIRSTSET;
+ }
+ else if (is_startline(codestart, 0, compile_block.backref_map))
+ re->options |= PCRE_STARTLINE;
+ }
+ }
+
+/* For an anchored pattern, we use the "required byte" only if it follows a
+variable length item in the regex. Remove the caseless flag for non-caseable
+chars. */
+
+if (reqbyte >= 0 &&
+ ((re->options & PCRE_ANCHORED) == 0 || (reqbyte & REQ_VARY) != 0))
+ {
+ int ch = reqbyte & 255;
+ re->req_byte = ((reqbyte & REQ_CASELESS) != 0 &&
+ compile_block.fcc[ch] == ch)? (reqbyte & ~REQ_CASELESS) : reqbyte;
+ re->options |= PCRE_REQCHSET;
+ }
+
+/* Print out the compiled data for debugging */
+
+#ifdef DEBUG
+
+printf("Length = %d top_bracket = %d top_backref = %d\n",
+ length, re->top_bracket, re->top_backref);
+
+if (re->options != 0)
+ {
+ printf("%s%s%s%s%s%s%s%s%s\n",
+ ((re->options & PCRE_ANCHORED) != 0)? "anchored " : "",
+ ((re->options & PCRE_CASELESS) != 0)? "caseless " : "",
+ ((re->options & PCRE_ICHANGED) != 0)? "case state changed " : "",
+ ((re->options & PCRE_EXTENDED) != 0)? "extended " : "",
+ ((re->options & PCRE_MULTILINE) != 0)? "multiline " : "",
+ ((re->options & PCRE_DOTALL) != 0)? "dotall " : "",
+ ((re->options & PCRE_DOLLAR_ENDONLY) != 0)? "endonly " : "",
+ ((re->options & PCRE_EXTRA) != 0)? "extra " : "",
+ ((re->options & PCRE_UNGREEDY) != 0)? "ungreedy " : "");
+ }
+
+if ((re->options & PCRE_FIRSTSET) != 0)
+ {
+ int ch = re->first_byte & 255;
+ const char *caseless = ((re->first_byte & REQ_CASELESS) == 0)? "" : " (caseless)";
+ if (g_unichar_isprint(ch)) printf("First char = %c%s\n", ch, caseless);
+ else printf("First char = \\x%02x%s\n", ch, caseless);
+ }
+
+if ((re->options & PCRE_REQCHSET) != 0)
+ {
+ int ch = re->req_byte & 255;
+ const char *caseless = ((re->req_byte & REQ_CASELESS) == 0)? "" : " (caseless)";
+ if (g_unichar_isprint(ch)) printf("Req char = %c%s\n", ch, caseless);
+ else printf("Req char = \\x%02x%s\n", ch, caseless);
+ }
+
+print_internals(re, stdout);
+
+/* This check is done here in the debugging case so that the code that
+was compiled can be seen. */
+
+if (code - codestart > length)
+ {
+ *errorptr = ERR23;
+ (pcre_free)(re);
+ *erroroffset = ptr - (uschar *)pattern;
+ return NULL;
+ }
+#endif
+
+return (pcre *)re;
+}
+
+
+
+/*************************************************
+* Match a back-reference *
+*************************************************/
+
+/* If a back reference hasn't been set, the length that is passed is greater
+than the number of characters left in the string, so the match fails.
+
+Arguments:
+ offset index into the offset vector
+ eptr points into the subject
+ length length to be matched
+ md points to match data block
+ ims the ims flags
+
+Returns: TRUE if matched
+*/
+
+static BOOL
+match_ref(int offset, register const uschar *eptr, int length, match_data *md,
+ unsigned long int ims)
+{
+const uschar *p = md->start_subject + md->offset_vector[offset];
+
+#ifdef DEBUG
+if (eptr >= md->end_subject)
+ printf("matching subject <null>");
+else
+ {
+ printf("matching subject ");
+ pchars(eptr, length, TRUE, md);
+ }
+printf(" against backref ");
+pchars(p, length, FALSE, md);
+printf("\n");
+#endif
+
+/* Always fail if not enough characters left */
+
+if (length > md->end_subject - eptr) return FALSE;
+
+/* Separate the caselesss case for speed */
+
+if ((ims & PCRE_CASELESS) != 0)
+ {
+ while (length-- > 0)
+ if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE;
+ }
+else
+ { while (length-- > 0) if (*p++ != *eptr++) return FALSE; }
+
+return TRUE;
+}
+
+
+#ifdef SUPPORT_UTF8
+/*************************************************
+* Match character against an XCLASS *
+*************************************************/
+
+/* This function is called from within the XCLASS code below, to match a
+character against an extended class which might match values > 255.
+
+Arguments:
+ c the character
+ data points to the flag byte of the XCLASS data
+
+Returns: TRUE if character matches, else FALSE
+*/
+
+static BOOL
+match_xclass(int c, const uschar *data)
+{
+int t;
+BOOL negated = (*data & XCL_NOT) != 0;
+
+/* Character values < 256 are matched against a bitmap, if one is present. If
+not, we still carry on, because there may be ranges that start below 256 in the
+additional data. */
+
+if (c < 256)
+ {
+ if ((*data & XCL_MAP) != 0 && (data[1 + c/8] & (1 << (c&7))) != 0)
+ return !negated; /* char found */
+ }
+
+/* Now match against the list of large chars or ranges that end with a large
+char. First skip the bit map if present. */
+
+if ((*data++ & XCL_MAP) != 0) data += 32;
+
+while ((t = *data++) != XCL_END)
+ {
+ int x, y;
+ GETCHARINC(x, data);
+ if (t == XCL_SINGLE)
+ {
+ if (c == x) return !negated;
+ }
+ else
+ {
+ GETCHARINC(y, data);
+ if (c >= x && c <= y) return !negated;
+ }
+ }
+
+return negated; /* char was not found */
+}
+#endif
+
+
+/***************************************************************************
+****************************************************************************
+ RECURSION IN THE match() FUNCTION
+
+The match() function is highly recursive. Some regular expressions can cause
+it to recurse thousands of times. I was writing for Unix, so I just let it
+call itself recursively. This uses the stack for saving everything that has
+to be saved for a recursive call. On Unix, the stack can be large, and this
+works fine.
+
+It turns out that on non-Unix systems there are problems with programs that
+use a lot of stack. (This despite the fact that every last chip has oodles
+of memory these days, and techniques for extending the stack have been known
+for decades.) So....
+
+There is a fudge, triggered by defining NO_RECURSE, which avoids recursive
+calls by keeping local variables that need to be preserved in blocks of memory
+obtained from malloc instead instead of on the stack. Macros are used to
+achieve this so that the actual code doesn't look very different to what it
+always used to.
+****************************************************************************
+***************************************************************************/
+
+
+/* These versions of the macros use the stack, as normal */
+
+#ifndef NO_RECURSE
+#define REGISTER register
+#define RMATCH(rx,ra,rb,rc,rd,re,rf,rg) rx = match(ra,rb,rc,rd,re,rf,rg)
+#define RRETURN(ra) return ra
+#else
+
+
+/* These versions of the macros manage a private stack on the heap. Note
+that the rd argument of RMATCH isn't actually used. It's the md argument of
+match(), which never actually changes. */
+
+#define REGISTER
+
+#define RMATCH(rx,ra,rb,rc,rd,re,rf,rg)\
+ {\
+ heapframe *newframe = (pcre_stack_malloc)(sizeof(heapframe));\
+ if (setjmp(frame->Xwhere) == 0)\
+ {\
+ newframe->Xeptr = ra;\
+ newframe->Xecode = rb;\
+ newframe->Xoffset_top = rc;\
+ newframe->Xims = re;\
+ newframe->Xeptrb = rf;\
+ newframe->Xflags = rg;\
+ newframe->Xprevframe = frame;\
+ frame = newframe;\
+ DPRINTF(("restarting from line %d\n", __LINE__));\
+ goto HEAP_RECURSE;\
+ }\
+ else\
+ {\
+ DPRINTF(("longjumped back to line %d\n", __LINE__));\
+ frame = md->thisframe;\
+ rx = frame->Xresult;\
+ }\
+ }
+
+#define RRETURN(ra)\
+ {\
+ heapframe *newframe = frame;\
+ frame = newframe->Xprevframe;\
+ (pcre_stack_free)(newframe);\
+ if (frame != NULL)\
+ {\
+ frame->Xresult = ra;\
+ md->thisframe = frame;\
+ longjmp(frame->Xwhere, 1);\
+ }\
+ return ra;\
+ }
+
+
+/* Structure for remembering the local variables in a private frame */
+
+typedef struct heapframe {
+ struct heapframe *Xprevframe;
+
+ /* Function arguments that may change */
+
+ const uschar *Xeptr;
+ const uschar *Xecode;
+ int Xoffset_top;
+ long int Xims;
+ eptrblock *Xeptrb;
+ int Xflags;
+
+ /* Function local variables */
+
+ const uschar *Xcallpat;
+ const uschar *Xcharptr;
+ const uschar *Xdata;
+ const uschar *Xlastptr;
+ const uschar *Xnext;
+ const uschar *Xpp;
+ const uschar *Xprev;
+ const uschar *Xsaved_eptr;
+
+ recursion_info Xnew_recursive;
+
+ BOOL Xcur_is_word;
+ BOOL Xcondition;
+ BOOL Xminimize;
+ BOOL Xprev_is_word;
+
+ unsigned long int Xoriginal_ims;
+
+ int Xctype;
+ int Xfc;
+ int Xfi;
+ int Xlength;
+ int Xmax;
+ int Xmin;
+ int Xnumber;
+ int Xoffset;
+ int Xop;
+ int Xsave_capture_last;
+ int Xsave_offset1, Xsave_offset2, Xsave_offset3;
+ int Xstacksave[REC_STACK_SAVE_MAX];
+
+ eptrblock Xnewptrb;
+
+ /* Place to pass back result, and where to jump back to */
+
+ int Xresult;
+ jmp_buf Xwhere;
+
+} heapframe;
+
+#endif
+
+
+/***************************************************************************
+***************************************************************************/
+
+
+
+/*************************************************
+* Match from current position *
+*************************************************/
+
+/* On entry ecode points to the first opcode, and eptr to the first character
+in the subject string, while eptrb holds the value of eptr at the start of the
+last bracketed group - used for breaking infinite loops matching zero-length
+strings. This function is called recursively in many circumstances. Whenever it
+returns a negative (error) response, the outer incarnation must also return the
+same response.
+
+Performance note: It might be tempting to extract commonly used fields from the
+md structure (e.g. utf8, end_subject) into individual variables to improve
+performance. Tests using gcc on a SPARC disproved this; in the first case, it
+made performance worse.
+
+Arguments:
+ eptr pointer in subject
+ ecode position in code
+ offset_top current top pointer
+ md pointer to "static" info for the match
+ ims current /i, /m, and /s options
+ eptrb pointer to chain of blocks containing eptr at start of
+ brackets - for testing for empty matches
+ flags can contain
+ match_condassert - this is an assertion condition
+ match_isgroup - this is the start of a bracketed group
+
+Returns: MATCH_MATCH if matched ) these values are >= 0
+ MATCH_NOMATCH if failed to match )
+ a negative PCRE_ERROR_xxx value if aborted by an error condition
+ (e.g. stopped by recursion limit)
+*/
+
+static int
+match(REGISTER const uschar *eptr, REGISTER const uschar *ecode,
+ int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb,
+ int flags)
+{
+/* These variables do not need to be preserved over recursion in this function,
+so they can be ordinary variables in all cases. Mark them with "register"
+because they are used a lot in loops. */
+
+register int rrc; /* Returns from recursive calls */
+register int i; /* Used for loops not involving calls to RMATCH() */
+register int c; /* Character values not kept over RMATCH() calls */
+
+/* When recursion is not being used, all "local" variables that have to be
+preserved over calls to RMATCH() are part of a "frame" which is obtained from
+heap storage. Set up the top-level frame here; others are obtained from the
+heap whenever RMATCH() does a "recursion". See the macro definitions above. */
+
+#ifdef NO_RECURSE
+heapframe *frame = (pcre_stack_malloc)(sizeof(heapframe));
+frame->Xprevframe = NULL; /* Marks the top level */
+
+/* Copy in the original argument variables */
+
+frame->Xeptr = eptr;
+frame->Xecode = ecode;
+frame->Xoffset_top = offset_top;
+frame->Xims = ims;
+frame->Xeptrb = eptrb;
+frame->Xflags = flags;
+
+/* This is where control jumps back to to effect "recursion" */
+
+HEAP_RECURSE:
+
+/* Macros make the argument variables come from the current frame */
+
+#define eptr frame->Xeptr
+#define ecode frame->Xecode
+#define offset_top frame->Xoffset_top
+#define ims frame->Xims
+#define eptrb frame->Xeptrb
+#define flags frame->Xflags
+
+/* Ditto for the local variables */
+
+#define callpat frame->Xcallpat
+#define charptr frame->Xcharptr
+#define data frame->Xdata
+#define lastptr frame->Xlastptr
+#define next frame->Xnext
+#define pp frame->Xpp
+#define prev frame->Xprev
+#define saved_eptr frame->Xsaved_eptr
+
+#define new_recursive frame->Xnew_recursive
+
+#define cur_is_word frame->Xcur_is_word
+#define condition frame->Xcondition
+#define minimize frame->Xminimize
+#define prev_is_word frame->Xprev_is_word
+
+#define original_ims frame->Xoriginal_ims
+
+#define ctype frame->Xctype
+#define fc frame->Xfc
+#define fi frame->Xfi
+#define length frame->Xlength
+#define max frame->Xmax
+#define min frame->Xmin
+#define number frame->Xnumber
+#define offset frame->Xoffset
+#define op frame->Xop
+#define save_capture_last frame->Xsave_capture_last
+#define save_offset1 frame->Xsave_offset1
+#define save_offset2 frame->Xsave_offset2
+#define save_offset3 frame->Xsave_offset3
+#define stacksave frame->Xstacksave
+
+#define newptrb frame->Xnewptrb
+
+/* When recursion is being used, local variables are allocated on the stack and
+get preserved during recursion in the normal way. In this environment, fi and
+i, and fc and c, can be the same variables. */
+
+#else
+#define fi i
+#define fc c
+
+const uschar *callpat; /* Many of these variables are used ony */
+const uschar *charptr; /* small blocks of the code. My normal */
+const uschar *data; /* style of coding would have declared */
+const uschar *lastptr; /* them within each of those blocks. */
+const uschar *next; /* However, in order to accommodate the */
+const uschar *pp; /* version of this code that uses an */
+const uschar *prev; /* external "stack" implemented on the */
+const uschar *saved_eptr; /* heap, it is easier to declare them */
+ /* all here, so the declarations can */
+recursion_info new_recursive; /* be cut out in a block. The only */
+ /* declarations within blocks below are */
+BOOL cur_is_word; /* for variables that do not have to */
+BOOL condition; /* be preserved over a recursive call */
+BOOL minimize; /* to RMATCH(). */
+BOOL prev_is_word;
+
+unsigned long int original_ims;
+
+int ctype;
+int length;
+int max;
+int min;
+int number;
+int offset;
+int op;
+int save_capture_last;
+int save_offset1, save_offset2, save_offset3;
+int stacksave[REC_STACK_SAVE_MAX];
+
+eptrblock newptrb;
+#endif
+
+
+/* OK, now we can get on with the real code of the function. Recursion is
+specified by the macros RMATCH and RRETURN. When NO_RECURSE is *not* defined,
+these just turn into a recursive call to match() and a "return", respectively.
+However, RMATCH isn't like a function call because it's quite a complicated
+macro. It has to be used in one particular way. This shouldn't, however, impact
+performance when true recursion is being used. */
+
+if (md->match_call_count++ >= md->match_limit) RRETURN(PCRE_ERROR_MATCHLIMIT);
+
+original_ims = ims; /* Save for resetting on ')' */
+
+/* At the start of a bracketed group, add the current subject pointer to the
+stack of such pointers, to be re-instated at the end of the group when we hit
+the closing ket. When match() is called in other circumstances, we don't add to
+this stack. */
+
+if ((flags & match_isgroup) != 0)
+ {
+ newptrb.epb_prev = eptrb;
+ newptrb.epb_saved_eptr = eptr;
+ eptrb = &newptrb;
+ }
+
+/* Now start processing the operations. */
+
+for (;;)
+ {
+ op = *ecode;
+ minimize = FALSE;
+
+ /* Opening capturing bracket. If there is space in the offset vector, save
+ the current subject position in the working slot at the top of the vector. We
+ mustn't change the current values of the data slot, because they may be set
+ from a previous iteration of this group, and be referred to by a reference
+ inside the group.
+
+ If the bracket fails to match, we need to restore this value and also the
+ values of the final offsets, in case they were set by a previous iteration of
+ the same bracket.
+
+ If there isn't enough space in the offset vector, treat this as if it were a
+ non-capturing bracket. Don't worry about setting the flag for the error case
+ here; that is handled in the code for KET. */
+
+ if (op > OP_BRA)
+ {
+ number = op - OP_BRA;
+
+ /* For extended extraction brackets (large number), we have to fish out the
+ number from a dummy opcode at the start. */
+
+ if (number > EXTRACT_BASIC_MAX)
+ number = GET2(ecode, 2+LINK_SIZE);
+ offset = number << 1;
+
+#ifdef DEBUG
+ printf("start bracket %d subject=", number);
+ pchars(eptr, 16, TRUE, md);
+ printf("\n");
+#endif
+
+ if (offset < md->offset_max)
+ {
+ save_offset1 = md->offset_vector[offset];
+ save_offset2 = md->offset_vector[offset+1];
+ save_offset3 = md->offset_vector[md->offset_end - number];
+ save_capture_last = md->capture_last;
+
+ DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3));
+ md->offset_vector[md->offset_end - number] = eptr - md->start_subject;
+
+ do
+ {
+ RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb,
+ match_isgroup);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ md->capture_last = save_capture_last;
+ ecode += GET(ecode, 1);
+ }
+ while (*ecode == OP_ALT);
+
+ DPRINTF(("bracket %d failed\n", number));
+
+ md->offset_vector[offset] = save_offset1;
+ md->offset_vector[offset+1] = save_offset2;
+ md->offset_vector[md->offset_end - number] = save_offset3;
+
+ RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Insufficient room for saving captured contents */
+
+ else op = OP_BRA;
+ }
+
+ /* Other types of node can be handled by a switch */
+
+ switch(op)
+ {
+ case OP_BRA: /* Non-capturing bracket: optimized */
+ DPRINTF(("start bracket 0\n"));
+ do
+ {
+ RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb,
+ match_isgroup);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ ecode += GET(ecode, 1);
+ }
+ while (*ecode == OP_ALT);
+ DPRINTF(("bracket 0 failed\n"));
+ RRETURN(MATCH_NOMATCH);
+
+ /* Conditional group: compilation checked that there are no more than
+ two branches. If the condition is false, skipping the first branch takes us
+ past the end if there is only one branch, but that's OK because that is
+ exactly what going to the ket would do. */
+
+ case OP_COND:
+ if (ecode[LINK_SIZE+1] == OP_CREF) /* Condition extract or recurse test */
+ {
+ offset = GET2(ecode, LINK_SIZE+2) << 1; /* Doubled ref number */
+ condition = (offset == CREF_RECURSE * 2)?
+ (md->recursive != NULL) :
+ (offset < offset_top && md->offset_vector[offset] >= 0);
+ RMATCH(rrc, eptr, ecode + (condition?
+ (LINK_SIZE + 4) : (LINK_SIZE + 1 + GET(ecode, 1))),
+ offset_top, md, ims, eptrb, match_isgroup);
+ RRETURN(rrc);
+ }
+
+ /* The condition is an assertion. Call match() to evaluate it - setting
+ the final argument TRUE causes it to stop at the end of an assertion. */
+
+ else
+ {
+ RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
+ match_condassert | match_isgroup);
+ if (rrc == MATCH_MATCH)
+ {
+ ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE+2);
+ while (*ecode == OP_ALT) ecode += GET(ecode, 1);
+ }
+ else if (rrc != MATCH_NOMATCH)
+ {
+ RRETURN(rrc); /* Need braces because of following else */
+ }
+ else ecode += GET(ecode, 1);
+ RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb,
+ match_isgroup);
+ RRETURN(rrc);
+ }
+ /* Control never reaches here */
+
+ /* Skip over conditional reference or large extraction number data if
+ encountered. */
+
+ case OP_CREF:
+ case OP_BRANUMBER:
+ ecode += 3;
+ break;
+
+ /* End of the pattern. If we are in a recursion, we should restore the
+ offsets appropriately and continue from after the call. */
+
+ case OP_END:
+ if (md->recursive != NULL && md->recursive->group_num == 0)
+ {
+ recursion_info *rec = md->recursive;
+ DPRINTF(("Hit the end in a (?0) recursion\n"));
+ md->recursive = rec->prevrec;
+ memmove(md->offset_vector, rec->offset_save,
+ rec->saved_max * sizeof(int));
+ md->start_match = rec->save_start;
+ ims = original_ims;
+ ecode = rec->after_call;
+ break;
+ }
+
+ /* Otherwise, if PCRE_NOTEMPTY is set, fail if we have matched an empty
+ string - backtracking will then try other alternatives, if any. */
+
+ if (md->notempty && eptr == md->start_match) RRETURN(MATCH_NOMATCH);
+ md->end_match_ptr = eptr; /* Record where we ended */
+ md->end_offset_top = offset_top; /* and how many extracts were taken */
+ RRETURN(MATCH_MATCH);
+
+ /* Change option settings */
+
+ case OP_OPT:
+ ims = ecode[1];
+ ecode += 2;
+ DPRINTF(("ims set to %02lx\n", ims));
+ break;
+
+ /* Assertion brackets. Check the alternative branches in turn - the
+ matching won't pass the KET for an assertion. If any one branch matches,
+ the assertion is true. Lookbehind assertions have an OP_REVERSE item at the
+ start of each branch to move the current point backwards, so the code at
+ this level is identical to the lookahead case. */
+
+ case OP_ASSERT:
+ case OP_ASSERTBACK:
+ do
+ {
+ RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
+ match_isgroup);
+ if (rrc == MATCH_MATCH) break;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ ecode += GET(ecode, 1);
+ }
+ while (*ecode == OP_ALT);
+ if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH);
+
+ /* If checking an assertion for a condition, return MATCH_MATCH. */
+
+ if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH);
+
+ /* Continue from after the assertion, updating the offsets high water
+ mark, since extracts may have been taken during the assertion. */
+
+ do ecode += GET(ecode,1); while (*ecode == OP_ALT);
+ ecode += 1 + LINK_SIZE;
+ offset_top = md->end_offset_top;
+ continue;
+
+ /* Negative assertion: all branches must fail to match */
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK_NOT:
+ do
+ {
+ RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL,
+ match_isgroup);
+ if (rrc == MATCH_MATCH) RRETURN(MATCH_NOMATCH);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ ecode += GET(ecode,1);
+ }
+ while (*ecode == OP_ALT);
+
+ if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH);
+
+ ecode += 1 + LINK_SIZE;
+ continue;
+
+ /* Move the subject pointer back. This occurs only at the start of
+ each branch of a lookbehind assertion. If we are too close to the start to
+ move back, this match function fails. When working with UTF-8 we move
+ back a number of characters, not bytes. */
+
+ case OP_REVERSE:
+#ifdef SUPPORT_UTF8
+ if (1 /* md->utf8 */)
+ {
+ c = GET(ecode,1);
+ for (i = 0; i < c; i++)
+ {
+ eptr--;
+ if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH);
+ BACKCHAR(eptr)
+ }
+ }
+ else
+#endif
+
+ /* No UTF-8 support, or not in UTF-8 mode: count is byte count */
+
+ {
+ eptr -= GET(ecode,1);
+ if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Skip to next op code */
+
+ ecode += 1 + LINK_SIZE;
+ break;
+
+ /* The callout item calls an external function, if one is provided, passing
+ details of the match so far. This is mainly for debugging, though the
+ function is able to force a failure. */
+
+ case OP_CALLOUT:
+ if (pcre_callout != NULL)
+ {
+ pcre_callout_block cb;
+ cb.version = 0; /* Version 0 of the callout block */
+ cb.callout_number = ecode[1];
+ cb.offset_vector = md->offset_vector;
+ cb.subject = (const char *)md->start_subject;
+ cb.subject_length = md->end_subject - md->start_subject;
+ cb.start_match = md->start_match - md->start_subject;
+ cb.current_position = eptr - md->start_subject;
+ cb.capture_top = offset_top/2;
+ cb.capture_last = md->capture_last;
+ cb.callout_data = md->callout_data;
+ if ((rrc = (*pcre_callout)(&cb)) > 0) RRETURN(MATCH_NOMATCH);
+ if (rrc < 0) RRETURN(rrc);
+ }
+ ecode += 2;
+ break;
+
+ /* Recursion either matches the current regex, or some subexpression. The
+ offset data is the offset to the starting bracket from the start of the
+ whole pattern. (This is so that it works from duplicated subpatterns.)
+
+ If there are any capturing brackets started but not finished, we have to
+ save their starting points and reinstate them after the recursion. However,
+ we don't know how many such there are (offset_top records the completed
+ total) so we just have to save all the potential data. There may be up to
+ 65535 such values, which is too large to put on the stack, but using malloc
+ for small numbers seems expensive. As a compromise, the stack is used when
+ there are no more than REC_STACK_SAVE_MAX values to store; otherwise malloc
+ is used. A problem is what to do if the malloc fails ... there is no way of
+ returning to the top level with an error. Save the top REC_STACK_SAVE_MAX
+ values on the stack, and accept that the rest may be wrong.
+
+ There are also other values that have to be saved. We use a chained
+ sequence of blocks that actually live on the stack. Thanks to Robin Houston
+ for the original version of this logic. */
+
+ case OP_RECURSE:
+ {
+ callpat = md->start_code + GET(ecode, 1);
+ new_recursive.group_num = *callpat - OP_BRA;
+
+ /* For extended extraction brackets (large number), we have to fish out
+ the number from a dummy opcode at the start. */
+
+ if (new_recursive.group_num > EXTRACT_BASIC_MAX)
+ new_recursive.group_num = GET2(callpat, 2+LINK_SIZE);
+
+ /* Add to "recursing stack" */
+
+ new_recursive.prevrec = md->recursive;
+ md->recursive = &new_recursive;
+
+ /* Find where to continue from afterwards */
+
+ ecode += 1 + LINK_SIZE;
+ new_recursive.after_call = ecode;
+
+ /* Now save the offset data. */
+
+ new_recursive.saved_max = md->offset_end;
+ if (new_recursive.saved_max <= REC_STACK_SAVE_MAX)
+ new_recursive.offset_save = stacksave;
+ else
+ {
+ new_recursive.offset_save =
+ (int *)(pcre_malloc)(new_recursive.saved_max * sizeof(int));
+ if (new_recursive.offset_save == NULL) RRETURN(PCRE_ERROR_NOMEMORY);
+ }
+
+ memcpy(new_recursive.offset_save, md->offset_vector,
+ new_recursive.saved_max * sizeof(int));
+ new_recursive.save_start = md->start_match;
+ md->start_match = eptr;
+
+ /* OK, now we can do the recursion. For each top-level alternative we
+ restore the offset and recursion data. */
+
+ DPRINTF(("Recursing into group %d\n", new_recursive.group_num));
+ do
+ {
+ RMATCH(rrc, eptr, callpat + 1 + LINK_SIZE, offset_top, md, ims,
+ eptrb, match_isgroup);
+ if (rrc == MATCH_MATCH)
+ {
+ md->recursive = new_recursive.prevrec;
+ if (new_recursive.offset_save != stacksave)
+ (pcre_free)(new_recursive.offset_save);
+ RRETURN(MATCH_MATCH);
+ }
+ else if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+
+ md->recursive = &new_recursive;
+ memcpy(md->offset_vector, new_recursive.offset_save,
+ new_recursive.saved_max * sizeof(int));
+ callpat += GET(callpat, 1);
+ }
+ while (*callpat == OP_ALT);
+
+ DPRINTF(("Recursion didn't match\n"));
+ md->recursive = new_recursive.prevrec;
+ if (new_recursive.offset_save != stacksave)
+ (pcre_free)(new_recursive.offset_save);
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never reaches here */
+
+ /* "Once" brackets are like assertion brackets except that after a match,
+ the point in the subject string is not moved back. Thus there can never be
+ a move back into the brackets. Friedl calls these "atomic" subpatterns.
+ Check the alternative branches in turn - the matching won't pass the KET
+ for this kind of subpattern. If any one branch matches, we carry on as at
+ the end of a normal bracket, leaving the subject pointer. */
+
+ case OP_ONCE:
+ {
+ prev = ecode;
+ saved_eptr = eptr;
+
+ do
+ {
+ RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims,
+ eptrb, match_isgroup);
+ if (rrc == MATCH_MATCH) break;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ ecode += GET(ecode,1);
+ }
+ while (*ecode == OP_ALT);
+
+ /* If hit the end of the group (which could be repeated), fail */
+
+ if (*ecode != OP_ONCE && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH);
+
+ /* Continue as from after the assertion, updating the offsets high water
+ mark, since extracts may have been taken. */
+
+ do ecode += GET(ecode,1); while (*ecode == OP_ALT);
+
+ offset_top = md->end_offset_top;
+ eptr = md->end_match_ptr;
+
+ /* For a non-repeating ket, just continue at this level. This also
+ happens for a repeating ket if no characters were matched in the group.
+ This is the forcible breaking of infinite loops as implemented in Perl
+ 5.005. If there is an options reset, it will get obeyed in the normal
+ course of events. */
+
+ if (*ecode == OP_KET || eptr == saved_eptr)
+ {
+ ecode += 1+LINK_SIZE;
+ break;
+ }
+
+ /* The repeating kets try the rest of the pattern or restart from the
+ preceding bracket, in the appropriate order. We need to reset any options
+ that changed within the bracket before re-running it, so check the next
+ opcode. */
+
+ if (ecode[1+LINK_SIZE] == OP_OPT)
+ {
+ ims = (ims & ~PCRE_IMS) | ecode[4];
+ DPRINTF(("ims set to %02lx at group repeat\n", ims));
+ }
+
+ if (*ecode == OP_KETRMIN)
+ {
+ RMATCH(rrc, eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ else /* OP_KETRMAX */
+ {
+ RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ RMATCH(rrc, eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ }
+ RRETURN(MATCH_NOMATCH);
+
+ /* An alternation is the end of a branch; scan along to find the end of the
+ bracketed group and go to there. */
+
+ case OP_ALT:
+ do ecode += GET(ecode,1); while (*ecode == OP_ALT);
+ break;
+
+ /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating
+ that it may occur zero times. It may repeat infinitely, or not at all -
+ i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper
+ repeat limits are compiled as a number of copies, with the optional ones
+ preceded by BRAZERO or BRAMINZERO. */
+
+ case OP_BRAZERO:
+ {
+ next = ecode+1;
+ RMATCH(rrc, eptr, next, offset_top, md, ims, eptrb, match_isgroup);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ do next += GET(next,1); while (*next == OP_ALT);
+ ecode = next + 1+LINK_SIZE;
+ }
+ break;
+
+ case OP_BRAMINZERO:
+ {
+ next = ecode+1;
+ do next += GET(next,1); while (*next == OP_ALT);
+ RMATCH(rrc, eptr, next + 1+LINK_SIZE, offset_top, md, ims, eptrb,
+ match_isgroup);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ ecode++;
+ }
+ break;
+
+ /* End of a group, repeated or non-repeating. If we are at the end of
+ an assertion "group", stop matching and return MATCH_MATCH, but record the
+ current high water mark for use by positive assertions. Do this also
+ for the "once" (not-backup up) groups. */
+
+ case OP_KET:
+ case OP_KETRMIN:
+ case OP_KETRMAX:
+ {
+ prev = ecode - GET(ecode, 1);
+ saved_eptr = eptrb->epb_saved_eptr;
+
+ /* Back up the stack of bracket start pointers. */
+
+ eptrb = eptrb->epb_prev;
+
+ if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT ||
+ *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT ||
+ *prev == OP_ONCE)
+ {
+ md->end_match_ptr = eptr; /* For ONCE */
+ md->end_offset_top = offset_top;
+ RRETURN(MATCH_MATCH);
+ }
+
+ /* In all other cases except a conditional group we have to check the
+ group number back at the start and if necessary complete handling an
+ extraction by setting the offsets and bumping the high water mark. */
+
+ if (*prev != OP_COND)
+ {
+ number = *prev - OP_BRA;
+
+ /* For extended extraction brackets (large number), we have to fish out
+ the number from a dummy opcode at the start. */
+
+ if (number > EXTRACT_BASIC_MAX) number = GET2(prev, 2+LINK_SIZE);
+ offset = number << 1;
+
+#ifdef DEBUG
+ printf("end bracket %d", number);
+ printf("\n");
+#endif
+
+ /* Test for a numbered group. This includes groups called as a result
+ of recursion. Note that whole-pattern recursion is coded as a recurse
+ into group 0, so it won't be picked up here. Instead, we catch it when
+ the OP_END is reached. */
+
+ if (number > 0)
+ {
+ md->capture_last = number;
+ if (offset >= md->offset_max) md->offset_overflow = TRUE; else
+ {
+ md->offset_vector[offset] =
+ md->offset_vector[md->offset_end - number];
+ md->offset_vector[offset+1] = eptr - md->start_subject;
+ if (offset_top <= offset) offset_top = offset + 2;
+ }
+
+ /* Handle a recursively called group. Restore the offsets
+ appropriately and continue from after the call. */
+
+ if (md->recursive != NULL && md->recursive->group_num == number)
+ {
+ recursion_info *rec = md->recursive;
+ DPRINTF(("Recursion (%d) succeeded - continuing\n", number));
+ md->recursive = rec->prevrec;
+ md->start_match = rec->save_start;
+ memcpy(md->offset_vector, rec->offset_save,
+ rec->saved_max * sizeof(int));
+ ecode = rec->after_call;
+ ims = original_ims;
+ break;
+ }
+ }
+ }
+
+ /* Reset the value of the ims flags, in case they got changed during
+ the group. */
+
+ ims = original_ims;
+ DPRINTF(("ims reset to %02lx\n", ims));
+
+ /* For a non-repeating ket, just continue at this level. This also
+ happens for a repeating ket if no characters were matched in the group.
+ This is the forcible breaking of infinite loops as implemented in Perl
+ 5.005. If there is an options reset, it will get obeyed in the normal
+ course of events. */
+
+ if (*ecode == OP_KET || eptr == saved_eptr)
+ {
+ ecode += 1 + LINK_SIZE;
+ break;
+ }
+
+ /* The repeating kets try the rest of the pattern or restart from the
+ preceding bracket, in the appropriate order. */
+
+ if (*ecode == OP_KETRMIN)
+ {
+ RMATCH(rrc, eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ else /* OP_KETRMAX */
+ {
+ RMATCH(rrc, eptr, prev, offset_top, md, ims, eptrb, match_isgroup);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ RMATCH(rrc, eptr, ecode + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ }
+
+ RRETURN(MATCH_NOMATCH);
+
+ /* Start of subject unless notbol, or after internal newline if multiline */
+
+ case OP_CIRC:
+ if (md->notbol && eptr == md->start_subject) RRETURN(MATCH_NOMATCH);
+ if ((ims & PCRE_MULTILINE) != 0)
+ {
+ if (eptr != md->start_subject && eptr[-1] != NEWLINE)
+ RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+ }
+ /* ... else fall through */
+
+ /* Start of subject assertion */
+
+ case OP_SOD:
+ if (eptr != md->start_subject) RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ /* Start of match assertion */
+
+ case OP_SOM:
+ if (eptr != md->start_subject + md->start_offset) RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ /* Assert before internal newline if multiline, or before a terminating
+ newline unless endonly is set, else end of subject unless noteol is set. */
+
+ case OP_DOLL:
+ if ((ims & PCRE_MULTILINE) != 0)
+ {
+ if (eptr < md->end_subject)
+ { if (*eptr != NEWLINE) RRETURN(MATCH_NOMATCH); }
+ else
+ { if (md->noteol) RRETURN(MATCH_NOMATCH); }
+ ecode++;
+ break;
+ }
+ else
+ {
+ if (md->noteol) RRETURN(MATCH_NOMATCH);
+ if (!md->endonly)
+ {
+ if (eptr < md->end_subject - 1 ||
+ (eptr == md->end_subject - 1 && *eptr != NEWLINE))
+ RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+ }
+ }
+ /* ... else fall through */
+
+ /* End of subject assertion (\z) */
+
+ case OP_EOD:
+ if (eptr < md->end_subject) RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ /* End of subject or ending \n assertion (\Z) */
+
+ case OP_EODN:
+ if (eptr < md->end_subject - 1 ||
+ (eptr == md->end_subject - 1 && *eptr != NEWLINE)) RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ /* Word boundary assertions */
+
+ case OP_NOT_WORD_BOUNDARY:
+ case OP_WORD_BOUNDARY:
+ {
+
+ /* Find out if the previous and current characters are "word" characters.
+ It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to
+ be "non-word" characters. */
+
+#ifdef SUPPORT_UTF8
+ if (1 /* md->utf8 */)
+ {
+ if (eptr == md->start_subject) prev_is_word = FALSE; else
+ {
+ lastptr = eptr - 1;
+ while((*lastptr & 0xc0) == 0x80) lastptr--;
+ GETCHAR(c, lastptr);
+ prev_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0;
+ }
+ if (eptr >= md->end_subject) cur_is_word = FALSE; else
+ {
+ GETCHAR(c, eptr);
+ cur_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0;
+ }
+ }
+ else
+#endif
+
+ /* More streamlined when not in UTF-8 mode */
+
+ {
+ prev_is_word = (eptr != md->start_subject) &&
+ ((md->ctypes[eptr[-1]] & ctype_word) != 0);
+ cur_is_word = (eptr < md->end_subject) &&
+ ((md->ctypes[*eptr] & ctype_word) != 0);
+ }
+
+ /* Now see if the situation is what we want */
+
+ if ((*ecode++ == OP_WORD_BOUNDARY)?
+ cur_is_word == prev_is_word : cur_is_word != prev_is_word)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* Match a single character type; inline for speed */
+
+ case OP_ANY:
+ if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == NEWLINE)
+ RRETURN(MATCH_NOMATCH);
+ if (eptr++ >= md->end_subject) RRETURN(MATCH_NOMATCH);
+#ifdef SUPPORT_UTF8
+ if (1 /* md->utf8 */)
+ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+#endif
+ ecode++;
+ break;
+
+ /* Match a single byte, even in UTF-8 mode. This opcode really does match
+ any byte, even newline, independent of the setting of PCRE_DOTALL. */
+
+ case OP_ANYBYTE:
+ if (eptr++ >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ case OP_NOT_DIGIT:
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINCTEST(c, eptr);
+ if (
+#ifdef SUPPORT_UTF8
+ c < 256 &&
+#endif
+ (md->ctypes[c] & ctype_digit) != 0
+ )
+ RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ case OP_DIGIT:
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINCTEST(c, eptr);
+ if (
+#ifdef SUPPORT_UTF8
+ c >= 256 ||
+#endif
+ (md->ctypes[c] & ctype_digit) == 0
+ )
+ RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINCTEST(c, eptr);
+ if (
+#ifdef SUPPORT_UTF8
+ c < 256 &&
+#endif
+ (md->ctypes[c] & ctype_space) != 0
+ )
+ RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ case OP_WHITESPACE:
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINCTEST(c, eptr);
+ if (
+#ifdef SUPPORT_UTF8
+ c >= 256 ||
+#endif
+ (md->ctypes[c] & ctype_space) == 0
+ )
+ RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINCTEST(c, eptr);
+ if (
+#ifdef SUPPORT_UTF8
+ c < 256 &&
+#endif
+ (md->ctypes[c] & ctype_word) != 0
+ )
+ RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ case OP_WORDCHAR:
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINCTEST(c, eptr);
+ if (
+#ifdef SUPPORT_UTF8
+ c >= 256 ||
+#endif
+ (md->ctypes[c] & ctype_word) == 0
+ )
+ RRETURN(MATCH_NOMATCH);
+ ecode++;
+ break;
+
+ /* Match a back reference, possibly repeatedly. Look past the end of the
+ item to see if there is repeat information following. The code is similar
+ to that for character classes, but repeated for efficiency. Then obey
+ similar code to character type repeats - written out again for speed.
+ However, if the referenced string is the empty string, always treat
+ it as matched, any number of times (otherwise there could be infinite
+ loops). */
+
+ case OP_REF:
+ {
+ offset = GET2(ecode, 1) << 1; /* Doubled ref number */
+ ecode += 3; /* Advance past item */
+
+ /* If the reference is unset, set the length to be longer than the amount
+ of subject left; this ensures that every attempt at a match fails. We
+ can't just fail here, because of the possibility of quantifiers with zero
+ minima. */
+
+ length = (offset >= offset_top || md->offset_vector[offset] < 0)?
+ md->end_subject - eptr + 1 :
+ md->offset_vector[offset+1] - md->offset_vector[offset];
+
+ /* Set up for repetition, or handle the non-repeated case */
+
+ switch (*ecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ c = *ecode++ - OP_CRSTAR;
+ minimize = (c & 1) != 0;
+ min = rep_min[c]; /* Pick up values from tables; */
+ max = rep_max[c]; /* zero for max => infinity */
+ if (max == 0) max = INT_MAX;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ minimize = (*ecode == OP_CRMINRANGE);
+ min = GET2(ecode, 1);
+ max = GET2(ecode, 3);
+ if (max == 0) max = INT_MAX;
+ ecode += 5;
+ break;
+
+ default: /* No repeat follows */
+ if (!match_ref(offset, eptr, length, md, ims)) RRETURN(MATCH_NOMATCH);
+ eptr += length;
+ continue; /* With the main loop */
+ }
+
+ /* If the length of the reference is zero, just continue with the
+ main loop. */
+
+ if (length == 0) continue;
+
+ /* First, ensure the minimum number of matches are present. We get back
+ the length of the reference string explicitly rather than passing the
+ address of eptr, so that eptr can be a register variable. */
+
+ for (i = 1; i <= min; i++)
+ {
+ if (!match_ref(offset, eptr, length, md, ims)) RRETURN(MATCH_NOMATCH);
+ eptr += length;
+ }
+
+ /* If min = max, continue at the same level without recursion.
+ They are not both allowed to be zero. */
+
+ if (min == max) continue;
+
+ /* If minimizing, keep trying and advancing the pointer */
+
+ if (minimize)
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || !match_ref(offset, eptr, length, md, ims))
+ RRETURN(MATCH_NOMATCH);
+ eptr += length;
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, find the longest string and work backwards */
+
+ else
+ {
+ pp = eptr;
+ for (i = min; i < max; i++)
+ {
+ if (!match_ref(offset, eptr, length, md, ims)) break;
+ eptr += length;
+ }
+ while (eptr >= pp)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ eptr -= length;
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+
+
+
+ /* Match a bit-mapped character class, possibly repeatedly. This op code is
+ used when all the characters in the class have values in the range 0-255.
+ The only difference between OP_CLASS and OP_NCLASS occurs when a data
+ character outside the range is encountered.
+
+ First, look past the end of the item to see if there is repeat information
+ following. Then obey similar code to character type repeats - written out
+ again for speed. */
+
+ case OP_NCLASS:
+ case OP_CLASS:
+ {
+ data = ecode + 1; /* Save for matching */
+ ecode += 33; /* Advance past the item */
+
+ switch (*ecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ c = *ecode++ - OP_CRSTAR;
+ minimize = (c & 1) != 0;
+ min = rep_min[c]; /* Pick up values from tables; */
+ max = rep_max[c]; /* zero for max => infinity */
+ if (max == 0) max = INT_MAX;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ minimize = (*ecode == OP_CRMINRANGE);
+ min = GET2(ecode, 1);
+ max = GET2(ecode, 3);
+ if (max == 0) max = INT_MAX;
+ ecode += 5;
+ break;
+
+ default: /* No repeat follows */
+ min = max = 1;
+ break;
+ }
+
+ /* First, ensure the minimum number of matches are present. */
+
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINC(c, eptr);
+ if (c > 255)
+ {
+ if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ }
+ else
+#endif
+ /* Not UTF-8 mode */
+ {
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ c = *eptr++;
+ if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+
+ /* If max == min we can continue with the main loop without the
+ need to recurse. */
+
+ if (min == max) continue;
+
+ /* If minimizing, keep testing the rest of the expression and advancing
+ the pointer while it matches the class. */
+
+ if (minimize)
+ {
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINC(c, eptr);
+ if (c > 255)
+ {
+ if (op == OP_CLASS) RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ }
+ else
+#endif
+ /* Not UTF-8 mode */
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ c = *eptr++;
+ if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, find the longest possible run, then work backwards. */
+
+ else
+ {
+ pp = eptr;
+
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(c, eptr, len);
+ if (c > 255)
+ {
+ if (op == OP_CLASS) break;
+ }
+ else
+ {
+ if ((data[c/8] & (1 << (c&7))) == 0) break;
+ }
+ eptr += len;
+ }
+ for (;;)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (eptr-- == pp) break; /* Stop if tried at original pos */
+ BACKCHAR(eptr);
+ }
+ }
+ else
+#endif
+ /* Not UTF-8 mode */
+ {
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject) break;
+ c = *eptr;
+ if ((data[c/8] & (1 << (c&7))) == 0) break;
+ eptr++;
+ }
+ while (eptr >= pp)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ eptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ }
+
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+
+
+ /* Match an extended character class. This opcode is encountered only
+ in UTF-8 mode, because that's the only time it is compiled. */
+
+#ifdef SUPPORT_UTF8
+ case OP_XCLASS:
+ {
+ data = ecode + 1 + LINK_SIZE; /* Save for matching */
+ ecode += GET(ecode, 1); /* Advance past the item */
+
+ switch (*ecode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ c = *ecode++ - OP_CRSTAR;
+ minimize = (c & 1) != 0;
+ min = rep_min[c]; /* Pick up values from tables; */
+ max = rep_max[c]; /* zero for max => infinity */
+ if (max == 0) max = INT_MAX;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ minimize = (*ecode == OP_CRMINRANGE);
+ min = GET2(ecode, 1);
+ max = GET2(ecode, 3);
+ if (max == 0) max = INT_MAX;
+ ecode += 5;
+ break;
+
+ default: /* No repeat follows */
+ min = max = 1;
+ break;
+ }
+
+ /* First, ensure the minimum number of matches are present. */
+
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINC(c, eptr);
+ if (!match_xclass(c, data)) RRETURN(MATCH_NOMATCH);
+ }
+
+ /* If max == min we can continue with the main loop without the
+ need to recurse. */
+
+ if (min == max) continue;
+
+ /* If minimizing, keep testing the rest of the expression and advancing
+ the pointer while it matches the class. */
+
+ if (minimize)
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINC(c, eptr);
+ if (!match_xclass(c, data)) RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing, find the longest possible run, then work backwards. */
+
+ else
+ {
+ pp = eptr;
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(c, eptr, len);
+ if (!match_xclass(c, data)) break;
+ eptr += len;
+ }
+ for(;;)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (eptr-- == pp) break; /* Stop if tried at original pos */
+ BACKCHAR(eptr)
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+
+ /* Control never gets here */
+ }
+#endif /* End of XCLASS */
+
+ /* Match a run of characters */
+
+ case OP_CHARS:
+ {
+ register int slen = ecode[1];
+ ecode += 2;
+
+#ifdef DEBUG /* Sigh. Some compilers never learn. */
+ if (eptr >= md->end_subject)
+ printf("matching subject <null> against pattern ");
+ else
+ {
+ printf("matching subject ");
+ pchars(eptr, slen, TRUE, md);
+ printf(" against pattern ");
+ }
+ pchars(ecode, slen, FALSE, md);
+ printf("\n");
+#endif
+
+ if (slen > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+ if ((ims & PCRE_CASELESS) != 0)
+ {
+ while (slen-- > 0)
+ if (md->lcc[*ecode++] != md->lcc[*eptr++])
+ RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ while (slen-- > 0) if (*ecode++ != *eptr++) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ break;
+
+ /* Match a single character repeatedly; different opcodes share code. */
+
+ case OP_EXACT:
+ min = max = GET2(ecode, 1);
+ ecode += 3;
+ goto REPEATCHAR;
+
+ case OP_UPTO:
+ case OP_MINUPTO:
+ min = 0;
+ max = GET2(ecode, 1);
+ minimize = *ecode == OP_MINUPTO;
+ ecode += 3;
+ goto REPEATCHAR;
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ c = *ecode++ - OP_STAR;
+ minimize = (c & 1) != 0;
+ min = rep_min[c]; /* Pick up values from tables; */
+ max = rep_max[c]; /* zero for max => infinity */
+ if (max == 0) max = INT_MAX;
+
+ /* Common code for all repeated single-character matches. We can give
+ up quickly if there are fewer than the minimum number of characters left in
+ the subject. */
+
+ REPEATCHAR:
+#ifdef SUPPORT_UTF8
+ if (1 /* md->utf8 */)
+ {
+ length = 1;
+ charptr = ecode;
+ GETCHARLEN(fc, ecode, length);
+ if (min * length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+ ecode += length;
+
+ /* Handle multibyte character matching specially here. There is no
+ support for any kind of casing for multibyte characters. */
+
+ if (length > 1)
+ {
+ for (i = 1; i <= min; i++)
+ {
+ if (memcmp(eptr, charptr, length) != 0) RRETURN(MATCH_NOMATCH);
+ eptr += length;
+ }
+
+ if (min == max) continue;
+
+ if (minimize)
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max ||
+ eptr >= md->end_subject ||
+ memcmp(eptr, charptr, length) != 0)
+ RRETURN(MATCH_NOMATCH);
+ eptr += length;
+ }
+ /* Control never gets here */
+ }
+ else
+ {
+ pp = eptr;
+ for (i = min; i < max; i++)
+ {
+ if (eptr > md->end_subject - length ||
+ memcmp(eptr, charptr, length) != 0)
+ break;
+ eptr += length;
+ }
+ while (eptr >= pp)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ eptr -= length;
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+
+ /* If the length of a UTF-8 character is 1, we fall through here, and
+ obey the code as for non-UTF-8 characters below, though in this case the
+ value of fc will always be < 128. */
+ }
+ else
+#endif
+
+ /* When not in UTF-8 mode, load a single-byte character. */
+ {
+ if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+ fc = *ecode++;
+ }
+
+ /* The value of fc at this point is always less than 256, though we may or
+ may not be in UTF-8 mode. The code is duplicated for the caseless and
+ caseful cases, for speed, since matching characters is likely to be quite
+ common. First, ensure the minimum number of matches are present. If min =
+ max, continue at the same level without recursing. Otherwise, if
+ minimizing, keep trying the rest of the expression and advancing one
+ matching character if failing, up to the maximum. Alternatively, if
+ maximizing, find the maximum number of characters and work backwards. */
+
+ DPRINTF(("matching %c{%d,%d} against subject %.*s\n", fc, min, max,
+ max, eptr));
+
+ if ((ims & PCRE_CASELESS) != 0)
+ {
+ fc = md->lcc[fc];
+ for (i = 1; i <= min; i++)
+ if (fc != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
+ if (min == max) continue;
+ if (minimize)
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || eptr >= md->end_subject ||
+ fc != md->lcc[*eptr++])
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+ else
+ {
+ pp = eptr;
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || fc != md->lcc[*eptr]) break;
+ eptr++;
+ }
+ while (eptr >= pp)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ eptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+
+ /* Caseful comparisons (includes all multi-byte characters) */
+
+ else
+ {
+ for (i = 1; i <= min; i++) if (fc != *eptr++) RRETURN(MATCH_NOMATCH);
+ if (min == max) continue;
+ if (minimize)
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || eptr >= md->end_subject || fc != *eptr++)
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+ else
+ {
+ pp = eptr;
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || fc != *eptr) break;
+ eptr++;
+ }
+ while (eptr >= pp)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ eptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+
+ /* Match a negated single one-byte character. The character we are
+ checking can be multibyte. */
+
+ case OP_NOT:
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ ecode++;
+ GETCHARINCTEST(c, eptr);
+ if ((ims & PCRE_CASELESS) != 0)
+ {
+#ifdef SUPPORT_UTF8
+ if (c < 256)
+#endif
+ c = md->lcc[c];
+ if (md->lcc[*ecode++] == c) RRETURN(MATCH_NOMATCH);
+ }
+ else
+ {
+ if (*ecode++ == c) RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ /* Match a negated single one-byte character repeatedly. This is almost a
+ repeat of the code for a repeated single character, but I haven't found a
+ nice way of commoning these up that doesn't require a test of the
+ positive/negative option for each character match. Maybe that wouldn't add
+ very much to the time taken, but character matching *is* what this is all
+ about... */
+
+ case OP_NOTEXACT:
+ min = max = GET2(ecode, 1);
+ ecode += 3;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ min = 0;
+ max = GET2(ecode, 1);
+ minimize = *ecode == OP_NOTMINUPTO;
+ ecode += 3;
+ goto REPEATNOTCHAR;
+
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ c = *ecode++ - OP_NOTSTAR;
+ minimize = (c & 1) != 0;
+ min = rep_min[c]; /* Pick up values from tables; */
+ max = rep_max[c]; /* zero for max => infinity */
+ if (max == 0) max = INT_MAX;
+
+ /* Common code for all repeated single-character (less than 255) matches.
+ We can give up quickly if there are fewer than the minimum number of
+ characters left in the subject. */
+
+ REPEATNOTCHAR:
+ if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+ fc = *ecode++;
+
+ /* The code is duplicated for the caseless and caseful cases, for speed,
+ since matching characters is likely to be quite common. First, ensure the
+ minimum number of matches are present. If min = max, continue at the same
+ level without recursing. Otherwise, if minimizing, keep trying the rest of
+ the expression and advancing one matching character if failing, up to the
+ maximum. Alternatively, if maximizing, find the maximum number of
+ characters and work backwards. */
+
+ DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", fc, min, max,
+ max, eptr));
+
+ if ((ims & PCRE_CASELESS) != 0)
+ {
+ fc = md->lcc[fc];
+
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ register int d;
+ for (i = 1; i <= min; i++)
+ {
+ GETCHARINC(d, eptr);
+ if (d < 256) d = md->lcc[d];
+ if (fc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+
+ /* Not UTF-8 mode */
+ {
+ for (i = 1; i <= min; i++)
+ if (fc == md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH);
+ }
+
+ if (min == max) continue;
+
+ if (minimize)
+ {
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ register int d;
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ GETCHARINC(d, eptr);
+ if (d < 256) d = md->lcc[d];
+ if (fi >= max || eptr >= md->end_subject || fc == d)
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF-8 mode */
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || eptr >= md->end_subject || fc == md->lcc[*eptr++])
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* Maximize case */
+
+ else
+ {
+ pp = eptr;
+
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ register int d;
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(d, eptr, len);
+ if (d < 256) d = md->lcc[d];
+ if (fc == d) break;
+ eptr += len;
+ }
+ for(;;)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (eptr-- == pp) break; /* Stop if tried at original pos */
+ BACKCHAR(eptr);
+ }
+ }
+ else
+#endif
+ /* Not UTF-8 mode */
+ {
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || fc == md->lcc[*eptr]) break;
+ eptr++;
+ }
+ while (eptr >= pp)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ eptr--;
+ }
+ }
+
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+ }
+
+ /* Caseful comparisons */
+
+ else
+ {
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ register int d;
+ for (i = 1; i <= min; i++)
+ {
+ GETCHARINC(d, eptr);
+ if (fc == d) RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF-8 mode */
+ {
+ for (i = 1; i <= min; i++)
+ if (fc == *eptr++) RRETURN(MATCH_NOMATCH);
+ }
+
+ if (min == max) continue;
+
+ if (minimize)
+ {
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ register int d;
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ GETCHARINC(d, eptr);
+ if (fi >= max || eptr >= md->end_subject || fc == d)
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ else
+#endif
+ /* Not UTF-8 mode */
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || eptr >= md->end_subject || fc == *eptr++)
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* Maximize case */
+
+ else
+ {
+ pp = eptr;
+
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ register int d;
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(d, eptr, len);
+ if (fc == d) break;
+ eptr += len;
+ }
+ for(;;)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (eptr-- == pp) break; /* Stop if tried at original pos */
+ BACKCHAR(eptr);
+ }
+ }
+ else
+#endif
+ /* Not UTF-8 mode */
+ {
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || fc == *eptr) break;
+ eptr++;
+ }
+ while (eptr >= pp)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ eptr--;
+ }
+ }
+
+ RRETURN(MATCH_NOMATCH);
+ }
+ }
+ /* Control never gets here */
+
+ /* Match a single character type repeatedly; several different opcodes
+ share code. This is very similar to the code for single characters, but we
+ repeat it in the interests of efficiency. */
+
+ case OP_TYPEEXACT:
+ min = max = GET2(ecode, 1);
+ minimize = TRUE;
+ ecode += 3;
+ goto REPEATTYPE;
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ min = 0;
+ max = GET2(ecode, 1);
+ minimize = *ecode == OP_TYPEMINUPTO;
+ ecode += 3;
+ goto REPEATTYPE;
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ c = *ecode++ - OP_TYPESTAR;
+ minimize = (c & 1) != 0;
+ min = rep_min[c]; /* Pick up values from tables; */
+ max = rep_max[c]; /* zero for max => infinity */
+ if (max == 0) max = INT_MAX;
+
+ /* Common code for all repeated single character type matches. Note that
+ in UTF-8 mode, '.' matches a character of any length, but for the other
+ character types, the valid characters are all one-byte long. */
+
+ REPEATTYPE:
+ ctype = *ecode++; /* Code for the character type */
+
+ /* First, ensure the minimum number of matches are present. Use inline
+ code for maximizing the speed, and do the type test once at the start
+ (i.e. keep it out of the loop). Also we can test that there are at least
+ the minimum number of bytes before we start. This isn't as effective in
+ UTF-8 mode, but it does no harm. Separate the UTF-8 code completely as that
+ is tidier. */
+
+ if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH);
+ if (min > 0)
+ {
+#ifdef SUPPORT_UTF8
+ if (1 /* md->utf8 */) switch(ctype)
+ {
+ case OP_ANY:
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject ||
+ (*eptr++ == NEWLINE && (ims & PCRE_DOTALL) == 0))
+ RRETURN(MATCH_NOMATCH);
+ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+ }
+ break;
+
+ case OP_ANYBYTE:
+ eptr += min;
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ GETCHARINC(c, eptr);
+ if (c < 256 && (md->ctypes[c] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject ||
+ *eptr >= 128 || (md->ctypes[*eptr++] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ /* No need to skip more bytes - we know it's a 1-byte character */
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject ||
+ (*eptr < 128 && (md->ctypes[*eptr++] & ctype_space) != 0))
+ RRETURN(MATCH_NOMATCH);
+ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject ||
+ *eptr >= 128 || (md->ctypes[*eptr++] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ /* No need to skip more bytes - we know it's a 1-byte character */
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject ||
+ (*eptr < 128 && (md->ctypes[*eptr++] & ctype_word) != 0))
+ RRETURN(MATCH_NOMATCH);
+ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = 1; i <= min; i++)
+ {
+ if (eptr >= md->end_subject ||
+ *eptr >= 128 || (md->ctypes[*eptr++] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ /* No need to skip more bytes - we know it's a 1-byte character */
+ }
+ break;
+ }
+ else
+#endif
+
+ /* Code for the non-UTF-8 case for minimum matching */
+
+ switch(ctype)
+ {
+ case OP_ANY:
+ if ((ims & PCRE_DOTALL) == 0)
+ {
+ for (i = 1; i <= min; i++)
+ if (*eptr++ == NEWLINE) RRETURN(MATCH_NOMATCH);
+ }
+ else eptr += min;
+ break;
+
+ case OP_ANYBYTE:
+ eptr += min;
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = 1; i <= min; i++)
+ if ((md->ctypes[*eptr++] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_DIGIT:
+ for (i = 1; i <= min; i++)
+ if ((md->ctypes[*eptr++] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = 1; i <= min; i++)
+ if ((md->ctypes[*eptr++] & ctype_space) != 0) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WHITESPACE:
+ for (i = 1; i <= min; i++)
+ if ((md->ctypes[*eptr++] & ctype_space) == 0) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = 1; i <= min; i++)
+ if ((md->ctypes[*eptr++] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WORDCHAR:
+ for (i = 1; i <= min; i++)
+ if ((md->ctypes[*eptr++] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+
+ /* If min = max, continue at the same level without recursing */
+
+ if (min == max) continue;
+
+ /* If minimizing, we have to test the rest of the pattern before each
+ subsequent match. Again, separate the UTF-8 case for speed. */
+
+ if (minimize)
+ {
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+ if (1 /* md->utf8 */)
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+
+ GETCHARINC(c, eptr);
+ switch(ctype)
+ {
+ case OP_ANY:
+ if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_ANYBYTE:
+ break;
+
+ case OP_NOT_DIGIT:
+ if (c < 256 && (md->ctypes[c] & ctype_digit) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_DIGIT:
+ if (c >= 256 || (md->ctypes[c] & ctype_digit) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if (c < 256 && (md->ctypes[c] & ctype_space) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WHITESPACE:
+ if (c >= 256 || (md->ctypes[c] & ctype_space) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if (c < 256 && (md->ctypes[c] & ctype_word) != 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WORDCHAR:
+ if (c >= 256 && (md->ctypes[c] & ctype_word) == 0)
+ RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ }
+ else
+#endif
+ /* Not UTF-8 mode */
+ {
+ for (fi = min;; fi++)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH);
+ c = *eptr++;
+ switch(ctype)
+ {
+ case OP_ANY:
+ if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_ANYBYTE:
+ break;
+
+ case OP_NOT_DIGIT:
+ if ((md->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_DIGIT:
+ if ((md->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WHITESPACE:
+ if ((md->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WHITESPACE:
+ if ((md->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_NOT_WORDCHAR:
+ if ((md->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH);
+ break;
+
+ case OP_WORDCHAR:
+ if ((md->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH);
+ break;
+ }
+ }
+ }
+ /* Control never gets here */
+ }
+
+ /* If maximizing it is worth using inline code for speed, doing the type
+ test once at the start (i.e. keep it out of the loop). Again, keep the
+ UTF-8 stuff separate. */
+
+ else
+ {
+ pp = eptr;
+
+#ifdef SUPPORT_UTF8
+ /* UTF-8 mode */
+
+ if (1 /* md->utf8 */)
+ {
+ switch(ctype)
+ {
+ case OP_ANY:
+
+ /* Special code is required for UTF8, but when the maximum is unlimited
+ we don't need it, so we repeat the non-UTF8 code. This is probably
+ worth it, because .* is quite a common idiom. */
+
+ if (max < INT_MAX)
+ {
+ if ((ims & PCRE_DOTALL) == 0)
+ {
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || *eptr == NEWLINE) break;
+ eptr++;
+ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+ }
+ }
+ else
+ {
+ for (i = min; i < max; i++)
+ {
+ eptr++;
+ while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++;
+ }
+ }
+ }
+
+ /* Handle unlimited UTF-8 repeat */
+
+ else
+ {
+ if ((ims & PCRE_DOTALL) == 0)
+ {
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || *eptr == NEWLINE) break;
+ eptr++;
+ }
+ break;
+ }
+ else
+ {
+ c = max - min;
+ if (c > md->end_subject - eptr) c = md->end_subject - eptr;
+ eptr += c;
+ }
+ }
+ break;
+
+ /* The byte case is the same as non-UTF8 */
+
+ case OP_ANYBYTE:
+ c = max - min;
+ if (c > md->end_subject - eptr) c = md->end_subject - eptr;
+ eptr += c;
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(c, eptr, len);
+ if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) break;
+ eptr+= len;
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(c, eptr, len);
+ if (c >= 256 ||(md->ctypes[c] & ctype_digit) == 0) break;
+ eptr+= len;
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(c, eptr, len);
+ if (c < 256 && (md->ctypes[c] & ctype_space) != 0) break;
+ eptr+= len;
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(c, eptr, len);
+ if (c >= 256 ||(md->ctypes[c] & ctype_space) == 0) break;
+ eptr+= len;
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(c, eptr, len);
+ if (c < 256 && (md->ctypes[c] & ctype_word) != 0) break;
+ eptr+= len;
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = min; i < max; i++)
+ {
+ int len = 1;
+ if (eptr >= md->end_subject) break;
+ GETCHARLEN(c, eptr, len);
+ if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) break;
+ eptr+= len;
+ }
+ break;
+ }
+
+ /* eptr is now past the end of the maximum run */
+
+ for(;;)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ if (eptr-- == pp) break; /* Stop if tried at original pos */
+ BACKCHAR(eptr);
+ }
+ }
+ else
+#endif
+
+ /* Not UTF-8 mode */
+ {
+ switch(ctype)
+ {
+ case OP_ANY:
+ if ((ims & PCRE_DOTALL) == 0)
+ {
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || *eptr == NEWLINE) break;
+ eptr++;
+ }
+ break;
+ }
+ /* For DOTALL case, fall through and treat as \C */
+
+ case OP_ANYBYTE:
+ c = max - min;
+ if (c > md->end_subject - eptr) c = md->end_subject - eptr;
+ eptr += c;
+ break;
+
+ case OP_NOT_DIGIT:
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0)
+ break;
+ eptr++;
+ }
+ break;
+
+ case OP_DIGIT:
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0)
+ break;
+ eptr++;
+ }
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0)
+ break;
+ eptr++;
+ }
+ break;
+
+ case OP_WHITESPACE:
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0)
+ break;
+ eptr++;
+ }
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0)
+ break;
+ eptr++;
+ }
+ break;
+
+ case OP_WORDCHAR:
+ for (i = min; i < max; i++)
+ {
+ if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0)
+ break;
+ eptr++;
+ }
+ break;
+ }
+
+ /* eptr is now past the end of the maximum run */
+
+ while (eptr >= pp)
+ {
+ RMATCH(rrc, eptr, ecode, offset_top, md, ims, eptrb, 0);
+ eptr--;
+ if (rrc != MATCH_NOMATCH) RRETURN(rrc);
+ }
+ }
+
+ /* Get here if we can't make it match with any permitted repetitions */
+
+ RRETURN(MATCH_NOMATCH);
+ }
+ /* Control never gets here */
+
+ /* There's been some horrible disaster. Since all codes > OP_BRA are
+ for capturing brackets, and there shouldn't be any gaps between 0 and
+ OP_BRA, arrival here can only mean there is something seriously wrong
+ in the code above or the OP_xxx definitions. */
+
+ default:
+ DPRINTF(("Unknown opcode %d\n", *ecode));
+ RRETURN(PCRE_ERROR_UNKNOWN_NODE);
+ }
+
+ /* Do not stick any code in here without much thought; it is assumed
+ that "continue" in the code above comes out to here to repeat the main
+ loop. */
+
+ } /* End of main loop */
+/* Control never reaches here */
+}
+
+
+/***************************************************************************
+****************************************************************************
+ RECURSION IN THE match() FUNCTION
+
+Undefine all the macros that were defined above to handle this. */
+
+#ifdef NO_RECURSE
+#undef eptr
+#undef ecode
+#undef offset_top
+#undef ims
+#undef eptrb
+#undef flags
+
+#undef callpat
+#undef charptr
+#undef data
+#undef lastptr
+#undef next
+#undef pp
+#undef prev
+#undef saved_eptr
+
+#undef new_recursive
+
+#undef cur_is_word
+#undef condition
+#undef minimize
+#undef prev_is_word
+
+#undef original_ims
+
+#undef ctype
+#undef length
+#undef max
+#undef min
+#undef number
+#undef offset
+#undef op
+#undef save_capture_last
+#undef save_offset1
+#undef save_offset2
+#undef save_offset3
+#undef stacksave
+
+#undef newptrb
+
+#endif
+
+/* These two are defined as macros in both cases */
+
+#undef fc
+#undef fi
+
+/***************************************************************************
+***************************************************************************/
+
+
+
+/*************************************************
+* Execute a Regular Expression *
+*************************************************/
+
+/* This function applies a compiled re to a subject string and picks out
+portions of the string if it matches. Two elements in the vector are set for
+each substring: the offsets to the start and end of the substring.
+
+Arguments:
+ external_re points to the compiled expression
+ extra_data points to extra data or is NULL
+ subject points to the subject string
+ length length of subject string (may contain binary zeros)
+ start_offset where to start in the subject string
+ options option bits
+ offsets points to a vector of ints to be filled in with offsets
+ offsetcount the number of elements in the vector
+
+Returns: > 0 => success; value is the number of elements filled in
+ = 0 => success, but offsets is not big enough
+ -1 => failed to match
+ < -1 => some kind of unexpected problem
+*/
+
+int
+pcre_exec(const pcre *external_re, const pcre_extra *extra_data,
+ const char *subject, int length, int start_offset, int options, int *offsets,
+ int offsetcount)
+{
+int rc, resetcount, ocount;
+int first_byte = -1;
+int req_byte = -1;
+int req_byte2 = -1;
+unsigned long int ims = 0;
+BOOL using_temporary_offsets = FALSE;
+BOOL anchored;
+BOOL startline;
+BOOL first_byte_caseless = FALSE;
+BOOL req_byte_caseless = FALSE;
+match_data match_block;
+const uschar *start_bits = NULL;
+const uschar *start_match = (const uschar *)subject + start_offset;
+const uschar *end_subject;
+const uschar *req_byte_ptr = start_match - 1;
+const pcre_study_data *study;
+const real_pcre *re = (const real_pcre *)external_re;
+
+/* Plausibility checks */
+
+if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION;
+if (re == NULL || subject == NULL ||
+ (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL;
+
+/* Fish out the optional data from the extra_data structure, first setting
+the default values. */
+
+study = NULL;
+match_block.match_limit = MATCH_LIMIT;
+match_block.callout_data = NULL;
+
+if (extra_data != NULL)
+ {
+ register unsigned int flags = extra_data->flags;
+ if ((flags & PCRE_EXTRA_STUDY_DATA) != 0)
+ study = (const pcre_study_data *)extra_data->study_data;
+ if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0)
+ match_block.match_limit = extra_data->match_limit;
+ if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0)
+ match_block.callout_data = extra_data->callout_data;
+ }
+
+/* Now we have re supposedly pointing to the regex */
+
+if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC;
+
+anchored = ((re->options | options) & PCRE_ANCHORED) != 0;
+startline = (re->options & PCRE_STARTLINE) != 0;
+
+match_block.start_code =
+ (const uschar *)re + sizeof(real_pcre) + re->name_count * re->name_entry_size;
+match_block.start_subject = (const uschar *)subject;
+match_block.start_offset = start_offset;
+match_block.end_subject = match_block.start_subject + length;
+end_subject = match_block.end_subject;
+
+match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0;
+match_block.utf8 = (re->options & PCRE_UTF8) != 0;
+
+match_block.notbol = (options & PCRE_NOTBOL) != 0;
+match_block.noteol = (options & PCRE_NOTEOL) != 0;
+match_block.notempty = (options & PCRE_NOTEMPTY) != 0;
+
+match_block.recursive = NULL; /* No recursion at top level */
+
+match_block.lcc = re->tables + lcc_offset;
+match_block.ctypes = re->tables + ctypes_offset;
+
+/* Check a UTF-8 string if required. Unfortunately there's no way of passing
+back the character offset. */
+
+#ifdef SUPPORT_UTF8
+if (match_block.utf8 && (options & PCRE_NO_UTF8_CHECK) == 0)
+ {
+ if (valid_utf8((uschar *)subject, length) >= 0)
+ return PCRE_ERROR_BADUTF8;
+ if (start_offset > 0 && start_offset < length)
+ {
+ int tb = ((uschar *)subject)[start_offset];
+ if (tb > 127)
+ {
+ tb &= 0xc0;
+ if (tb != 0 && tb != 0xc0) return PCRE_ERROR_BADUTF8_OFFSET;
+ }
+ }
+ }
+#endif
+
+/* The ims options can vary during the matching as a result of the presence
+of (?ims) items in the pattern. They are kept in a local variable so that
+restoring at the exit of a group is easy. */
+
+ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL);
+
+/* If the expression has got more back references than the offsets supplied can
+hold, we get a temporary bit of working store to use during the matching.
+Otherwise, we can use the vector supplied, rounding down its size to a multiple
+of 3. */
+
+ocount = offsetcount - (offsetcount % 3);
+
+if (re->top_backref > 0 && re->top_backref >= ocount/3)
+ {
+ ocount = re->top_backref * 3 + 3;
+ match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int));
+ if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY;
+ using_temporary_offsets = TRUE;
+ DPRINTF(("Got memory to hold back references\n"));
+ }
+else match_block.offset_vector = offsets;
+
+match_block.offset_end = ocount;
+match_block.offset_max = (2*ocount)/3;
+match_block.offset_overflow = FALSE;
+match_block.capture_last = -1;
+
+/* Compute the minimum number of offsets that we need to reset each time. Doing
+this makes a huge difference to execution time when there aren't many brackets
+in the pattern. */
+
+resetcount = 2 + re->top_bracket * 2;
+if (resetcount > offsetcount) resetcount = ocount;
+
+/* Reset the working variable associated with each extraction. These should
+never be used unless previously set, but they get saved and restored, and so we
+initialize them to avoid reading uninitialized locations. */
+
+if (match_block.offset_vector != NULL)
+ {
+ register int *iptr = match_block.offset_vector + ocount;
+ register int *iend = iptr - resetcount/2 + 1;
+ while (--iptr >= iend) *iptr = -1;
+ }
+
+/* Set up the first character to match, if available. The first_byte value is
+never set for an anchored regular expression, but the anchoring may be forced
+at run time, so we have to test for anchoring. The first char may be unset for
+an unanchored pattern, of course. If there's no first char and the pattern was
+studied, there may be a bitmap of possible first characters. */
+
+if (!anchored)
+ {
+ if ((re->options & PCRE_FIRSTSET) != 0)
+ {
+ first_byte = re->first_byte & 255;
+ if ((first_byte_caseless = ((re->first_byte & REQ_CASELESS) != 0)) == TRUE)
+ first_byte = match_block.lcc[first_byte];
+ }
+ else
+ if (!startline && study != NULL &&
+ (study->options & PCRE_STUDY_MAPPED) != 0)
+ start_bits = study->start_bits;
+ }
+
+/* For anchored or unanchored matches, there may be a "last known required
+character" set. */
+
+if ((re->options & PCRE_REQCHSET) != 0)
+ {
+ req_byte = re->req_byte & 255;
+ req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0;
+ req_byte2 = (re->tables + fcc_offset)[req_byte]; /* case flipped */
+ }
+
+/* Loop for handling unanchored repeated matching attempts; for anchored regexs
+the loop runs just once. */
+
+do
+ {
+ register int *iptr = match_block.offset_vector;
+ register int *iend = iptr + resetcount;
+
+ /* Reset the maximum number of extractions we might see. */
+
+ while (iptr < iend) *iptr++ = -1;
+
+ /* Advance to a unique first char if possible */
+
+ if (first_byte >= 0)
+ {
+ if (first_byte_caseless)
+ while (start_match < end_subject &&
+ match_block.lcc[*start_match] != first_byte)
+ start_match++;
+ else
+ while (start_match < end_subject && *start_match != first_byte)
+ start_match++;
+ }
+
+ /* Or to just after \n for a multiline match if possible */
+
+ else if (startline)
+ {
+ if (start_match > match_block.start_subject + start_offset)
+ {
+ while (start_match < end_subject && start_match[-1] != NEWLINE)
+ start_match++;
+ }
+ }
+
+ /* Or to a non-unique first char after study */
+
+ else if (start_bits != NULL)
+ {
+ while (start_match < end_subject)
+ {
+ register int c = *start_match;
+ if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break;
+ }
+ }
+
+#ifdef DEBUG /* Sigh. Some compilers never learn. */
+ printf(">>>> Match against: ");
+ pchars(start_match, end_subject - start_match, TRUE, &match_block);
+ printf("\n");
+#endif
+
+ /* If req_byte is set, we know that that character must appear in the subject
+ for the match to succeed. If the first character is set, req_byte must be
+ later in the subject; otherwise the test starts at the match point. This
+ optimization can save a huge amount of backtracking in patterns with nested
+ unlimited repeats that aren't going to match. Writing separate code for
+ cased/caseless versions makes it go faster, as does using an autoincrement
+ and backing off on a match.
+
+ HOWEVER: when the subject string is very, very long, searching to its end can
+ take a long time, and give bad performance on quite ordinary patterns. This
+ showed up when somebody was matching /^C/ on a 32-megabyte string... so we
+ don't do this when the string is sufficiently long. */
+
+ if (req_byte >= 0 && end_subject - start_match < REQ_BYTE_MAX)
+ {
+ register const uschar *p = start_match + ((first_byte >= 0)? 1 : 0);
+
+ /* We don't need to repeat the search if we haven't yet reached the
+ place we found it at last time. */
+
+ if (p > req_byte_ptr)
+ {
+ if (req_byte_caseless)
+ {
+ while (p < end_subject)
+ {
+ register int pp = *p++;
+ if (pp == req_byte || pp == req_byte2) { p--; break; }
+ }
+ }
+ else
+ {
+ while (p < end_subject)
+ {
+ if (*p++ == req_byte) { p--; break; }
+ }
+ }
+
+ /* If we can't find the required character, break the matching loop */
+
+ if (p >= end_subject) break;
+
+ /* If we have found the required character, save the point where we
+ found it, so that we don't search again next time round the loop if
+ the start hasn't passed this character yet. */
+
+ req_byte_ptr = p;
+ }
+ }
+
+ /* When a match occurs, substrings will be set for all internal extractions;
+ we just need to set up the whole thing as substring 0 before returning. If
+ there were too many extractions, set the return code to zero. In the case
+ where we had to get some local store to hold offsets for backreferences, copy
+ those back references that we can. In this case there need not be overflow
+ if certain parts of the pattern were not used. */
+
+ match_block.start_match = start_match;
+ match_block.match_call_count = 0;
+
+ rc = match(start_match, match_block.start_code, 2, &match_block, ims, NULL,
+ match_isgroup);
+
+ if (rc == MATCH_NOMATCH)
+ {
+ start_match++;
+#ifdef SUPPORT_UTF8
+ if (match_block.utf8)
+ while((*start_match & 0xc0) == 0x80) start_match++;
+#endif
+ continue;
+ }
+
+ if (rc != MATCH_MATCH)
+ {
+ DPRINTF((">>>> error: returning %d\n", rc));
+ return rc;
+ }
+
+ /* We have a match! Copy the offset information from temporary store if
+ necessary */
+
+ if (using_temporary_offsets)
+ {
+ if (offsetcount >= 4)
+ {
+ memcpy(offsets + 2, match_block.offset_vector + 2,
+ (offsetcount - 2) * sizeof(int));
+ DPRINTF(("Copied offsets from temporary memory\n"));
+ }
+ if (match_block.end_offset_top > offsetcount)
+ match_block.offset_overflow = TRUE;
+
+ DPRINTF(("Freeing temporary memory\n"));
+ (pcre_free)(match_block.offset_vector);
+ }
+
+ rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2;
+
+ if (offsetcount < 2) rc = 0; else
+ {
+ offsets[0] = start_match - match_block.start_subject;
+ offsets[1] = match_block.end_match_ptr - match_block.start_subject;
+ }
+
+ DPRINTF((">>>> returning %d\n", rc));
+ return rc;
+ }
+
+/* This "while" is the end of the "do" above */
+
+while (!anchored && start_match <= end_subject);
+
+if (using_temporary_offsets)
+ {
+ DPRINTF(("Freeing temporary memory\n"));
+ (pcre_free)(match_block.offset_vector);
+ }
+
+DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n"));
+
+return PCRE_ERROR_NOMATCH;
+}
+
+/* End of pcre.c */
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/study.c
@@ -0,0 +1,477 @@
+/* File import from pcre to goffice by import-pcre. Do not edit. */
+
+/* This file has been programatically changed. */
+/* This makes the following file fall under GPL license, see below. */
+
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10 at cam.ac.uk>
+
+ Copyright (c) 1997-2003 University of Cambridge
+
+-----------------------------------------------------------------------------
+Permission is granted to anyone to use this software for any purpose on any
+computer system, and to redistribute it freely, subject to the following
+restrictions:
+
+1. This software 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.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+4. If PCRE is embedded in any software that is released under the GNU
+ General Purpose Licence (GPL), then the terms of that licence shall
+ supersede any condition above with which it is incompatible.
+-----------------------------------------------------------------------------
+*/
+
+
+/* Include the internals header, which itself includes Standard C headers plus
+the external pcre header. */
+
+#include "internal.h"
+
+
+
+/*************************************************
+* Set a bit and maybe its alternate case *
+*************************************************/
+
+/* Given a character, set its bit in the table, and also the bit for the other
+version of a letter if we are caseless.
+
+Arguments:
+ start_bits points to the bit map
+ c is the character
+ caseless the caseless flag
+ cd the block with char table pointers
+
+Returns: nothing
+*/
+
+static void
+set_bit(uschar *start_bits, int c, BOOL caseless, compile_data *cd)
+{
+start_bits[c/8] |= (1 << (c&7));
+if (caseless && (cd->ctypes[c] & ctype_letter) != 0)
+ start_bits[cd->fcc[c]/8] |= (1 << (cd->fcc[c]&7));
+}
+
+
+
+/*************************************************
+* Create bitmap of starting chars *
+*************************************************/
+
+/* This function scans a compiled unanchored expression and attempts to build a
+bitmap of the set of initial characters. If it can't, it returns FALSE. As time
+goes by, we may be able to get more clever at doing this.
+
+Arguments:
+ code points to an expression
+ start_bits points to a 32-byte table, initialized to 0
+ caseless the current state of the caseless flag
+ utf8 TRUE if in UTF-8 mode
+ cd the block with char table pointers
+
+Returns: TRUE if table built, FALSE otherwise
+*/
+
+static BOOL
+set_start_bits(const uschar *code, uschar *start_bits, BOOL caseless,
+ BOOL utf8, compile_data *cd)
+{
+register int c;
+
+/* This next statement and the later reference to dummy are here in order to
+trick the optimizer of the IBM C compiler for OS/2 into generating correct
+code. Apparently IBM isn't going to fix the problem, and we would rather not
+disable optimization (in this module it actually makes a big difference, and
+the pcre module can use all the optimization it can get). */
+
+volatile int dummy;
+
+do
+ {
+ const uschar *tcode = code + 1 + LINK_SIZE;
+ BOOL try_next = TRUE;
+
+ while (try_next)
+ {
+ /* If a branch starts with a bracket or a positive lookahead assertion,
+ recurse to set bits from within them. That's all for this branch. */
+
+ if ((int)*tcode >= OP_BRA || *tcode == OP_ASSERT)
+ {
+ if (!set_start_bits(tcode, start_bits, caseless, utf8, cd))
+ return FALSE;
+ try_next = FALSE;
+ }
+
+ else switch(*tcode)
+ {
+ default:
+ return FALSE;
+
+ /* Skip over callout */
+
+ case OP_CALLOUT:
+ tcode += 2;
+ break;
+
+ /* Skip over extended extraction bracket number */
+
+ case OP_BRANUMBER:
+ tcode += 3;
+ break;
+
+ /* Skip over lookbehind and negative lookahead assertions */
+
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
+ tcode += 1+LINK_SIZE;
+ break;
+
+ /* Skip over an option setting, changing the caseless flag */
+
+ case OP_OPT:
+ caseless = (tcode[1] & PCRE_CASELESS) != 0;
+ tcode += 2;
+ break;
+
+ /* BRAZERO does the bracket, but carries on. */
+
+ case OP_BRAZERO:
+ case OP_BRAMINZERO:
+ if (!set_start_bits(++tcode, start_bits, caseless, utf8, cd))
+ return FALSE;
+ dummy = 1;
+ do tcode += GET(tcode,1); while (*tcode == OP_ALT);
+ tcode += 1+LINK_SIZE;
+ break;
+
+ /* Single-char * or ? sets the bit and tries the next item */
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ set_bit(start_bits, tcode[1], caseless, cd);
+ tcode += 2;
+#ifdef SUPPORT_UTF8
+ if (1 /* utf8 */) while ((*tcode & 0xc0) == 0x80) tcode++;
+#endif
+ break;
+
+ /* Single-char upto sets the bit and tries the next */
+
+ case OP_UPTO:
+ case OP_MINUPTO:
+ set_bit(start_bits, tcode[3], caseless, cd);
+ tcode += 4;
+#ifdef SUPPORT_UTF8
+ if (1 /* utf8 */) while ((*tcode & 0xc0) == 0x80) tcode++;
+#endif
+ break;
+
+ /* At least one single char sets the bit and stops */
+
+ case OP_EXACT: /* Fall through */
+ tcode++;
+
+ case OP_CHARS: /* Fall through */
+ tcode++;
+
+ case OP_PLUS:
+ case OP_MINPLUS:
+ set_bit(start_bits, tcode[1], caseless, cd);
+ try_next = FALSE;
+ break;
+
+ /* Single character type sets the bits and stops */
+
+ case OP_NOT_DIGIT:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= ~cd->cbits[c+cbit_digit];
+ try_next = FALSE;
+ break;
+
+ case OP_DIGIT:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= cd->cbits[c+cbit_digit];
+ try_next = FALSE;
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= ~cd->cbits[c+cbit_space];
+ try_next = FALSE;
+ break;
+
+ case OP_WHITESPACE:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= cd->cbits[c+cbit_space];
+ try_next = FALSE;
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= ~cd->cbits[c+cbit_word];
+ try_next = FALSE;
+ break;
+
+ case OP_WORDCHAR:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= cd->cbits[c+cbit_word];
+ try_next = FALSE;
+ break;
+
+ /* One or more character type fudges the pointer and restarts, knowing
+ it will hit a single character type and stop there. */
+
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ tcode++;
+ break;
+
+ case OP_TYPEEXACT:
+ tcode += 3;
+ break;
+
+ /* Zero or more repeats of character types set the bits and then
+ try again. */
+
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ tcode += 2; /* Fall through */
+
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ switch(tcode[1])
+ {
+ case OP_ANY:
+ return FALSE;
+
+ case OP_NOT_DIGIT:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= ~cd->cbits[c+cbit_digit];
+ break;
+
+ case OP_DIGIT:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= cd->cbits[c+cbit_digit];
+ break;
+
+ case OP_NOT_WHITESPACE:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= ~cd->cbits[c+cbit_space];
+ break;
+
+ case OP_WHITESPACE:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= cd->cbits[c+cbit_space];
+ break;
+
+ case OP_NOT_WORDCHAR:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= ~cd->cbits[c+cbit_word];
+ break;
+
+ case OP_WORDCHAR:
+ for (c = 0; c < 32; c++)
+ start_bits[c] |= cd->cbits[c+cbit_word];
+ break;
+ }
+
+ tcode += 2;
+ break;
+
+ /* Character class where all the information is in a bit map: set the
+ bits and either carry on or not, according to the repeat count. If it was
+ a negative class, and we are operating with UTF-8 characters, any byte
+ with a value >= 0xc4 is a potentially valid starter because it starts a
+ character with a value > 255. */
+
+ case OP_NCLASS:
+ if (1 /* utf8 */)
+ {
+ start_bits[24] |= 0xf0; /* Bits for 0xc4 - 0xc8 */
+ memset(start_bits+25, 0xff, 7); /* Bits for 0xc9 - 0xff */
+ }
+ /* Fall through */
+
+ case OP_CLASS:
+ {
+ tcode++;
+
+ /* In UTF-8 mode, the bits in a bit map correspond to character
+ values, not to byte values. However, the bit map we are constructing is
+ for byte values. So we have to do a conversion for characters whose
+ value is > 127. In fact, there are only two possible starting bytes for
+ characters in the range 128 - 255. */
+
+ if (1 /* utf8 */)
+ {
+ for (c = 0; c < 16; c++) start_bits[c] |= tcode[c];
+ for (c = 128; c < 256; c++)
+ {
+ if ((tcode[c/8] && (1 << (c&7))) != 0)
+ {
+ int d = (c >> 6) | 0xc0; /* Set bit for this starter */
+ start_bits[d/8] |= (1 << (d&7)); /* and then skip on to the */
+ c = (c & 0xc0) + 0x40 - 1; /* next relevant character. */
+ }
+ }
+ }
+
+ /* In non-UTF-8 mode, the two bit maps are completely compatible. */
+
+ else
+ {
+ for (c = 0; c < 32; c++) start_bits[c] |= tcode[c];
+ }
+
+ /* Advance past the bit map, and act on what follows */
+
+ tcode += 32;
+ switch (*tcode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ tcode++;
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ if (((tcode[1] << 8) + tcode[2]) == 0) tcode += 5;
+ else try_next = FALSE;
+ break;
+
+ default:
+ try_next = FALSE;
+ break;
+ }
+ }
+ break; /* End of bitmap class handling */
+
+ } /* End of switch */
+ } /* End of try_next loop */
+
+ code += GET(code, 1); /* Advance to next branch */
+ }
+while (*code == OP_ALT);
+return TRUE;
+}
+
+
+
+/*************************************************
+* Study a compiled expression *
+*************************************************/
+
+/* This function is handed a compiled expression that it must study to produce
+information that will speed up the matching. It returns a pcre_extra block
+which then gets handed back to pcre_exec().
+
+Arguments:
+ re points to the compiled expression
+ options contains option bits
+ errorptr points to where to place error messages;
+ set NULL unless error
+
+Returns: pointer to a pcre_extra block, with study_data filled in and the
+ appropriate flag set;
+ NULL on error or if no optimization possible
+*/
+
+pcre_extra *
+pcre_study(const pcre *external_re, int options, const char **errorptr)
+{
+uschar start_bits[32];
+pcre_extra *extra;
+pcre_study_data *study;
+const real_pcre *re = (const real_pcre *)external_re;
+uschar *code = (uschar *)re + sizeof(real_pcre) +
+ (re->name_count * re->name_entry_size);
+compile_data compile_block;
+
+*errorptr = NULL;
+
+if (re == NULL || re->magic_number != MAGIC_NUMBER)
+ {
+ *errorptr = "argument is not a compiled regular expression";
+ return NULL;
+ }
+
+if ((options & ~PUBLIC_STUDY_OPTIONS) != 0)
+ {
+ *errorptr = "unknown or incorrect option bit(s) set";
+ return NULL;
+ }
+
+/* For an anchored pattern, or an unanchored pattern that has a first char, or
+a multiline pattern that matches only at "line starts", no further processing
+at present. */
+
+if ((re->options & (PCRE_ANCHORED|PCRE_FIRSTSET|PCRE_STARTLINE)) != 0)
+ return NULL;
+
+/* Set the character tables in the block which is passed around */
+
+compile_block.lcc = re->tables + lcc_offset;
+compile_block.fcc = re->tables + fcc_offset;
+compile_block.cbits = re->tables + cbits_offset;
+compile_block.ctypes = re->tables + ctypes_offset;
+
+/* See if we can find a fixed set of initial characters for the pattern. */
+
+memset(start_bits, 0, 32 * sizeof(uschar));
+if (!set_start_bits(code, start_bits, (re->options & PCRE_CASELESS) != 0,
+ (re->options & PCRE_UTF8) != 0, &compile_block)) return NULL;
+
+/* Get a pcre_extra block and a pcre_study_data block. The study data is put in
+the latter, which is pointed to by the former, which may also get additional
+data set later by the calling program. At the moment, the size of
+pcre_study_data is fixed. We nevertheless save it in a field for returning via
+the pcre_fullinfo() function so that if it becomes variable in the future, we
+don't have to change that code. */
+
+extra = (pcre_extra *)(pcre_malloc)
+ (sizeof(pcre_extra) + sizeof(pcre_study_data));
+
+if (extra == NULL)
+ {
+ *errorptr = "failed to get memory";
+ return NULL;
+ }
+
+study = (pcre_study_data *)((char *)extra + sizeof(pcre_extra));
+extra->flags = PCRE_EXTRA_STUDY_DATA;
+extra->study_data = study;
+
+study->size = sizeof(pcre_study_data);
+study->options = PCRE_STUDY_MAPPED;
+memcpy(study->start_bits, start_bits, sizeof(start_bits));
+
+return extra;
+}
+
+/* End of study.c */
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/printint.c
@@ -0,0 +1,365 @@
+/* File import from pcre to goffice by import-pcre. Do not edit. */
+
+/* This file has been programatically changed. */
+/* This makes the following file fall under GPL license, see below. */
+
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+Written by: Philip Hazel <ph10 at cam.ac.uk>
+
+ Copyright (c) 1997-2003 University of Cambridge
+
+-----------------------------------------------------------------------------
+Permission is granted to anyone to use this software for any purpose on any
+computer system, and to redistribute it freely, subject to the following
+restrictions:
+
+1. This software 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.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+4. If PCRE is embedded in any software that is released under the GNU
+ General Purpose Licence (GPL), then the terms of that licence shall
+ supersede any condition above with which it is incompatible.
+-----------------------------------------------------------------------------
+*/
+
+
+/* This module contains a debugging function for printing out the internal form
+of a compiled regular expression. It is kept in a separate file so that it can
+be #included both in the pcretest program, and in the library itself when
+compiled with the debugging switch. */
+
+
+static const char *OP_names[] = { OP_NAME_LIST };
+
+
+/*************************************************
+* Print single- or multi-byte character *
+*************************************************/
+
+/* These tables are actually copies of ones in pcre.c. If we compile the
+library with debugging, they are included twice, but that isn't really a
+problem - compiling with debugging is pretty rare and these are very small. */
+
+static const int utf8_t3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01};
+
+static const uschar utf8_t4[] = {
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 };
+
+static int
+print_char(FILE *f, uschar *ptr, BOOL utf8)
+{
+int c = *ptr;
+
+if (!utf8 || (c & 0xc0) != 0xc0)
+ {
+ if (g_unichar_isprint(c)) fprintf(f, "%c", c); else fprintf(f, "\\x%02x", c);
+ return 0;
+ }
+else
+ {
+ int i;
+ int a = utf8_t4[c & 0x3f]; /* Number of additional bytes */
+ int s = 6*a;
+ c = (c & utf8_t3[a]) << s;
+ for (i = 1; i <= a; i++)
+ {
+ s -= 6;
+ c |= (ptr[i] & 0x3f) << s;
+ }
+ if (c < 128) fprintf(f, "\\x%02x", c); else fprintf(f, "\\x{%x}", c);
+ return a;
+ }
+}
+
+
+
+
+/*************************************************
+* Print compiled regex *
+*************************************************/
+
+static void
+print_internals(pcre *external_re, FILE *f)
+{
+real_pcre *re = (real_pcre *)external_re;
+uschar *codestart =
+ (uschar *)re + sizeof(real_pcre) + re->name_count * re->name_entry_size;
+uschar *code = codestart;
+BOOL utf8 = (re->options & PCRE_UTF8) != 0;
+
+for(;;)
+ {
+ uschar *ccode;
+ int c;
+ int extra = 0;
+
+ fprintf(f, "%3d ", code - codestart);
+
+ if (*code >= OP_BRA)
+ {
+ if (*code - OP_BRA > EXTRACT_BASIC_MAX)
+ fprintf(f, "%3d Bra extra\n", GET(code, 1));
+ else
+ fprintf(f, "%3d Bra %d\n", GET(code, 1), *code - OP_BRA);
+ code += OP_lengths[OP_BRA];
+ continue;
+ }
+
+ switch(*code)
+ {
+ case OP_END:
+ fprintf(f, " %s\n", OP_names[*code]);
+ fprintf(f, "------------------------------------------------------------------\n");
+ return;
+
+ case OP_OPT:
+ fprintf(f, " %.2x %s", code[1], OP_names[*code]);
+ break;
+
+ case OP_CHARS:
+ {
+ int charlength = code[1];
+ ccode = code + 2;
+ extra = charlength;
+ fprintf(f, "%3d ", charlength);
+ while (charlength > 0)
+ {
+ int extrabytes = print_char(f, ccode, utf8);
+ ccode += 1 + extrabytes;
+ charlength -= 1 + extrabytes;
+ }
+ }
+ break;
+
+ case OP_KETRMAX:
+ case OP_KETRMIN:
+ case OP_ALT:
+ case OP_KET:
+ case OP_ASSERT:
+ case OP_ASSERT_NOT:
+ case OP_ASSERTBACK:
+ case OP_ASSERTBACK_NOT:
+ case OP_ONCE:
+ case OP_COND:
+ case OP_REVERSE:
+ fprintf(f, "%3d %s", GET(code, 1), OP_names[*code]);
+ break;
+
+ case OP_BRANUMBER:
+ printf("%3d %s", GET2(code, 1), OP_names[*code]);
+ break;
+
+ case OP_CREF:
+ if (GET2(code, 1) == CREF_RECURSE)
+ fprintf(f, " Cond recurse");
+ else
+ fprintf(f, "%3d %s", GET2(code,1), OP_names[*code]);
+ break;
+
+ case OP_STAR:
+ case OP_MINSTAR:
+ case OP_PLUS:
+ case OP_MINPLUS:
+ case OP_QUERY:
+ case OP_MINQUERY:
+ case OP_TYPESTAR:
+ case OP_TYPEMINSTAR:
+ case OP_TYPEPLUS:
+ case OP_TYPEMINPLUS:
+ case OP_TYPEQUERY:
+ case OP_TYPEMINQUERY:
+ fprintf(f, " ");
+ if (*code >= OP_TYPESTAR) fprintf(f, "%s", OP_names[code[1]]);
+ else extra = print_char(f, code+1, utf8);
+ fprintf(f, "%s", OP_names[*code]);
+ break;
+
+ case OP_EXACT:
+ case OP_UPTO:
+ case OP_MINUPTO:
+ fprintf(f, " ");
+ extra = print_char(f, code+3, utf8);
+ fprintf(f, "{");
+ if (*code != OP_EXACT) fprintf(f, ",");
+ fprintf(f, "%d}", GET2(code,1));
+ if (*code == OP_MINUPTO) fprintf(f, "?");
+ break;
+
+ case OP_TYPEEXACT:
+ case OP_TYPEUPTO:
+ case OP_TYPEMINUPTO:
+ fprintf(f, " %s{", OP_names[code[3]]);
+ if (*code != OP_TYPEEXACT) fprintf(f, "0,");
+ fprintf(f, "%d}", GET2(code,1));
+ if (*code == OP_TYPEMINUPTO) fprintf(f, "?");
+ break;
+
+ case OP_NOT:
+ if (g_unichar_isprint(c = code[1])) fprintf(f, " [^%c]", c);
+ else fprintf(f, " [^\\x%02x]", c);
+ break;
+
+ case OP_NOTSTAR:
+ case OP_NOTMINSTAR:
+ case OP_NOTPLUS:
+ case OP_NOTMINPLUS:
+ case OP_NOTQUERY:
+ case OP_NOTMINQUERY:
+ if (g_unichar_isprint(c = code[1])) fprintf(f, " [^%c]", c);
+ else fprintf(f, " [^\\x%02x]", c);
+ fprintf(f, "%s", OP_names[*code]);
+ break;
+
+ case OP_NOTEXACT:
+ case OP_NOTUPTO:
+ case OP_NOTMINUPTO:
+ if (g_unichar_isprint(c = code[3])) fprintf(f, " [^%c]{", c);
+ else fprintf(f, " [^\\x%02x]{", c);
+ if (*code != OP_NOTEXACT) fprintf(f, ",");
+ fprintf(f, "%d}", GET2(code,1));
+ if (*code == OP_NOTMINUPTO) fprintf(f, "?");
+ break;
+
+ case OP_RECURSE:
+ fprintf(f, "%3d %s", GET(code, 1), OP_names[*code]);
+ break;
+
+ case OP_REF:
+ fprintf(f, " \\%d", GET2(code,1));
+ ccode = code + OP_lengths[*code];
+ goto CLASS_REF_REPEAT;
+
+ case OP_CALLOUT:
+ fprintf(f, " %s %d", OP_names[*code], code[1]);
+ break;
+
+ /* OP_XCLASS can only occur in UTF-8 mode. However, there's no harm in
+ having this code always here, and it makes it less messy without all those
+ #ifdefs. */
+
+ case OP_CLASS:
+ case OP_NCLASS:
+ case OP_XCLASS:
+ {
+ int i, min, max;
+ BOOL printmap;
+
+ fprintf(f, " [");
+
+ if (*code == OP_XCLASS)
+ {
+ extra = GET(code, 1);
+ ccode = code + LINK_SIZE + 1;
+ printmap = (*ccode & XCL_MAP) != 0;
+ if ((*ccode++ & XCL_NOT) != 0) fprintf(f, "^");
+ }
+ else
+ {
+ printmap = TRUE;
+ ccode = code + 1;
+ }
+
+ /* Print a bit map */
+
+ if (printmap)
+ {
+ for (i = 0; i < 256; i++)
+ {
+ if ((ccode[i/8] & (1 << (i&7))) != 0)
+ {
+ int j;
+ for (j = i+1; j < 256; j++)
+ if ((ccode[j/8] & (1 << (j&7))) == 0) break;
+ if (i == '-' || i == ']') fprintf(f, "\\");
+ if (g_unichar_isprint(i)) fprintf(f, "%c", i); else fprintf(f, "\\x%02x", i);
+ if (--j > i)
+ {
+ fprintf(f, "-");
+ if (j == '-' || j == ']') fprintf(f, "\\");
+ if (g_unichar_isprint(j)) fprintf(f, "%c", j); else fprintf(f, "\\x%02x", j);
+ }
+ i = j;
+ }
+ }
+ ccode += 32;
+ }
+
+ /* For an XCLASS there is always some additional data */
+
+ if (*code == OP_XCLASS)
+ {
+ int ch;
+ while ((ch = *ccode++) != XCL_END)
+ {
+ ccode += 1 + print_char(f, ccode, TRUE);
+ if (ch == XCL_RANGE)
+ {
+ fprintf(f, "-");
+ ccode += 1 + print_char(f, ccode, TRUE);
+ }
+ }
+ }
+
+ /* Indicate a non-UTF8 class which was created by negation */
+
+ fprintf(f, "]%s", (*code == OP_NCLASS)? " (neg)" : "");
+
+ /* Handle repeats after a class or a back reference */
+
+ CLASS_REF_REPEAT:
+ switch(*ccode)
+ {
+ case OP_CRSTAR:
+ case OP_CRMINSTAR:
+ case OP_CRPLUS:
+ case OP_CRMINPLUS:
+ case OP_CRQUERY:
+ case OP_CRMINQUERY:
+ fprintf(f, "%s", OP_names[*ccode]);
+ extra = OP_lengths[*ccode];
+ break;
+
+ case OP_CRRANGE:
+ case OP_CRMINRANGE:
+ min = GET2(ccode,1);
+ max = GET2(ccode,3);
+ if (max == 0) fprintf(f, "{%d,}", min);
+ else fprintf(f, "{%d,%d}", min, max);
+ if (*ccode == OP_CRMINRANGE) fprintf(f, "?");
+ extra = OP_lengths[*ccode];
+ break;
+ }
+ }
+ break;
+
+ /* Anything else is just an item with no data*/
+
+ default:
+ fprintf(f, " %s", OP_names[*code]);
+ break;
+ }
+
+ code += OP_lengths[*code] + extra;
+ fprintf(f, "\n");
+ }
+}
+
+/* End of printint.c */
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES = \
+ $(GNUMERIC_CFLAGS) -I$(top_srcdir) \
+ -DSUPPORT_UTF8 \
+ -DNEWLINE=10 \
+ -DPOSIX_MALLOC_THRESHOLD=100 \
+ -DLINK_SIZE=2 \
+ -DMATCH_LIMIT=10000000
+
+AM_CFLAGS = $(GNOME_CFLAGS)
+
+noinst_LTLIBRARIES = libpcre.la
+
+libpcre_la_SOURCES = maketables.c get.c study.c pcre.c pcreposix.c
+
+noinst_HEADERS = internal.h pcreposix.h pcre.h
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/pcreposix.c
@@ -0,0 +1,310 @@
+/* File import from pcre to goffice by import-pcre. Do not edit. */
+
+/* This file has been programatically changed. */
+/* This makes the following file fall under GPL license, see below. */
+
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/*
+This is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language. See
+the file Tech.Notes for some information on the internals.
+
+This module is a wrapper that provides a POSIX API to the underlying PCRE
+functions.
+
+Written by: Philip Hazel <ph10 at cam.ac.uk>
+
+ Copyright (c) 1997-2003 University of Cambridge
+
+-----------------------------------------------------------------------------
+Permission is granted to anyone to use this software for any purpose on any
+computer system, and to redistribute it freely, subject to the following
+restrictions:
+
+1. This software 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.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+4. If PCRE is embedded in any software that is released under the GNU
+ General Purpose Licence (GPL), then the terms of that licence shall
+ supersede any condition above with which it is incompatible.
+-----------------------------------------------------------------------------
+*/
+
+#include "internal.h"
+#include "pcreposix.h"
+#include "stdlib.h"
+
+
+
+/* Corresponding tables of PCRE error messages and POSIX error codes. */
+
+static const char *const estring[] = {
+ ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, ERR10,
+ ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, ERR20,
+ ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR29, ERR29, ERR30,
+ ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, ERR40,
+ ERR41, ERR42, ERR43, ERR44 };
+
+static const int eint[] = {
+ REG_EESCAPE, /* "\\ at end of pattern" */
+ REG_EESCAPE, /* "\\c at end of pattern" */
+ REG_EESCAPE, /* "unrecognized character follows \\" */
+ REG_BADBR, /* "numbers out of order in {} quantifier" */
+ REG_BADBR, /* "number too big in {} quantifier" */
+ REG_EBRACK, /* "missing terminating ] for character class" */
+ REG_ECTYPE, /* "invalid escape sequence in character class" */
+ REG_ERANGE, /* "range out of order in character class" */
+ REG_BADRPT, /* "nothing to repeat" */
+ REG_BADRPT, /* "operand of unlimited repeat could match the empty string" */
+ REG_ASSERT, /* "internal error: unexpected repeat" */
+ REG_BADPAT, /* "unrecognized character after (?" */
+ REG_BADPAT, /* "POSIX named classes are supported only within a class" */
+ REG_EPAREN, /* "missing )" */
+ REG_ESUBREG, /* "reference to non-existent subpattern" */
+ REG_INVARG, /* "erroffset passed as NULL" */
+ REG_INVARG, /* "unknown option bit(s) set" */
+ REG_EPAREN, /* "missing ) after comment" */
+ REG_ESIZE, /* "parentheses nested too deeply" */
+ REG_ESIZE, /* "regular expression too large" */
+ REG_ESPACE, /* "failed to get memory" */
+ REG_EPAREN, /* "unmatched brackets" */
+ REG_ASSERT, /* "internal error: code overflow" */
+ REG_BADPAT, /* "unrecognized character after (?<" */
+ REG_BADPAT, /* "lookbehind assertion is not fixed length" */
+ REG_BADPAT, /* "malformed number after (?(" */
+ REG_BADPAT, /* "conditional group containe more than two branches" */
+ REG_BADPAT, /* "assertion expected after (?(" */
+ REG_BADPAT, /* "(?R or (?digits must be followed by )" */
+ REG_ECTYPE, /* "unknown POSIX class name" */
+ REG_BADPAT, /* "POSIX collating elements are not supported" */
+ REG_INVARG, /* "this version of PCRE is not compiled with PCRE_UTF8 support" */
+ REG_BADPAT, /* "spare error" */
+ REG_BADPAT, /* "character value in \x{...} sequence is too large" */
+ REG_BADPAT, /* "invalid condition (?(0)" */
+ REG_BADPAT, /* "\\C not allowed in lookbehind assertion" */
+ REG_EESCAPE, /* "PCRE does not support \\L, \\l, \\N, \\P, \\p, \\U, \\u, or \\X" */
+ REG_BADPAT, /* "number after (?C is > 255" */
+ REG_BADPAT, /* "closing ) for (?C expected" */
+ REG_BADPAT, /* "recursive call could loop indefinitely" */
+ REG_BADPAT, /* "unrecognized character after (?P" */
+ REG_BADPAT, /* "syntax error after (?P" */
+ REG_BADPAT, /* "two named groups have the same name" */
+ REG_BADPAT /* "invalid UTF-8 string" */
+};
+
+/* Table of texts corresponding to POSIX error codes */
+
+static const char *const pstring[] = {
+ "", /* Dummy for value 0 */
+ "internal error", /* REG_ASSERT */
+ "invalid repeat counts in {}", /* BADBR */
+ "pattern error", /* BADPAT */
+ "? * + invalid", /* BADRPT */
+ "unbalanced {}", /* EBRACE */
+ "unbalanced []", /* EBRACK */
+ "collation error - not relevant", /* ECOLLATE */
+ "bad class", /* ECTYPE */
+ "bad escape sequence", /* EESCAPE */
+ "empty expression", /* EMPTY */
+ "unbalanced ()", /* EPAREN */
+ "bad range inside []", /* ERANGE */
+ "expression too big", /* ESIZE */
+ "failed to get memory", /* ESPACE */
+ "bad back reference", /* ESUBREG */
+ "bad argument", /* INVARG */
+ "match failed" /* NOMATCH */
+};
+
+
+
+
+/*************************************************
+* Translate PCRE text code to int *
+*************************************************/
+
+/* PCRE compile-time errors are given as strings defined as macros. We can just
+look them up in a table to turn them into POSIX-style error codes. */
+
+static int
+pcre_posix_error_code(const char *s)
+{
+size_t i;
+for (i = 0; i < sizeof(estring)/sizeof(char *); i++)
+ if (strcmp(s, estring[i]) == 0) return eint[i];
+return REG_ASSERT;
+}
+
+
+
+/*************************************************
+* Translate error code to string *
+*************************************************/
+
+size_t
+go_regerror(int errcode, const go_regex_t *preg, char *errbuf, size_t errbuf_size)
+{
+const char *message, *addmessage;
+size_t length, addlength;
+
+message = (errcode >= (int)(sizeof(pstring)/sizeof(char *)))?
+ "unknown error code" : pstring[errcode];
+length = strlen(message) + 1;
+
+addmessage = " at offset ";
+addlength = (preg != NULL && (int)preg->re_erroffset != -1)?
+ strlen(addmessage) + 6 : 0;
+
+if (errbuf_size > 0)
+ {
+ if (addlength > 0 && errbuf_size >= length + addlength)
+ sprintf(errbuf, "%s%s%-6d", message, addmessage, (int)preg->re_erroffset);
+ else
+ {
+ strncpy(errbuf, message, errbuf_size - 1);
+ errbuf[errbuf_size-1] = 0;
+ }
+ }
+
+return length + addlength;
+}
+
+
+
+
+/*************************************************
+* Free store held by a regex *
+*************************************************/
+
+void
+go_regfree(go_regex_t *preg)
+{
+(pcre_free)(preg->re_pcre);
+}
+
+
+
+
+/*************************************************
+* Compile a regular expression *
+*************************************************/
+
+/*
+Arguments:
+ preg points to a structure for recording the compiled expression
+ pattern the pattern to compile
+ cflags compilation flags
+
+Returns: 0 on success
+ various non-zero codes on failure
+*/
+
+int
+go_regcomp(go_regex_t *preg, const char *pattern, int cflags)
+{
+const char *errorptr;
+int erroffset;
+int options = 0;
+
+if ((cflags & REG_ICASE) != 0) options |= PCRE_CASELESS;
+if ((cflags & REG_NEWLINE) != 0) options |= PCRE_MULTILINE;
+
+preg->re_pcre = pcre_compile(pattern, options | PCRE_UTF8 | PCRE_NO_UTF8_CHECK, &errorptr, &erroffset, NULL);
+preg->re_erroffset = erroffset;
+
+if (preg->re_pcre == NULL) return pcre_posix_error_code(errorptr);
+
+preg->re_nsub = pcre_info((const pcre *)preg->re_pcre, NULL, NULL);
+return 0;
+}
+
+
+
+
+/*************************************************
+* Match a regular expression *
+*************************************************/
+
+/* Unfortunately, PCRE requires 3 ints of working space for each captured
+substring, so we have to get and release working store instead of just using
+the POSIX structures as was done in earlier releases when PCRE needed only 2
+ints. However, if the number of possible capturing brackets is small, use a
+block of store on the stack, to reduce the use of malloc/free. The threshold is
+in a macro that can be changed at configure time. */
+
+int
+go_regexec(const go_regex_t *preg, const char *string, size_t nmatch,
+ regmatch_t pmatch[], int eflags)
+{
+int rc;
+int options = 0;
+int *ovector = NULL;
+int small_ovector[POSIX_MALLOC_THRESHOLD * 3];
+BOOL allocated_ovector = FALSE;
+
+if ((eflags & REG_NOTBOL) != 0) options |= PCRE_NOTBOL;
+if ((eflags & REG_NOTEOL) != 0) options |= PCRE_NOTEOL;
+
+((go_regex_t *)preg)->re_erroffset = (size_t)(-1); /* Only has meaning after compile */
+
+if (nmatch > 0)
+ {
+ if (nmatch <= POSIX_MALLOC_THRESHOLD)
+ {
+ ovector = &(small_ovector[0]);
+ }
+ else
+ {
+ ovector = (int *)malloc(sizeof(int) * nmatch * 3);
+ if (ovector == NULL) return REG_ESPACE;
+ allocated_ovector = TRUE;
+ }
+ }
+
+rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string, (int)strlen(string),
+ 0, options, ovector, nmatch * 3);
+
+if (rc == 0) rc = nmatch; /* All captured slots were filled in */
+
+if (rc >= 0)
+ {
+ size_t i;
+ for (i = 0; i < (size_t)rc; i++)
+ {
+ pmatch[i].rm_so = ovector[i*2];
+ pmatch[i].rm_eo = ovector[i*2+1];
+ }
+ if (allocated_ovector) free(ovector);
+ for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+ return 0;
+ }
+
+else
+ {
+ if (allocated_ovector) free(ovector);
+ switch(rc)
+ {
+ case PCRE_ERROR_NOMATCH: return REG_NOMATCH;
+ case PCRE_ERROR_NULL: return REG_INVARG;
+ case PCRE_ERROR_BADOPTION: return REG_INVARG;
+ case PCRE_ERROR_BADMAGIC: return REG_INVARG;
+ case PCRE_ERROR_UNKNOWN_NODE: return REG_ASSERT;
+ case PCRE_ERROR_NOMEMORY: return REG_ESPACE;
+ case PCRE_ERROR_MATCHLIMIT: return REG_ESPACE;
+ case PCRE_ERROR_BADUTF8: return REG_INVARG;
+ case PCRE_ERROR_BADUTF8_OFFSET: return REG_INVARG;
+ default: return REG_ASSERT;
+ }
+ }
+}
+
+/* End of pcreposix.c */
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/maketables.c
@@ -0,0 +1,146 @@
+/* File import from pcre to goffice by import-pcre. Do not edit. */
+
+/* This file has been programatically changed. */
+/* This makes the following file fall under GPL license, see below. */
+
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/*
+PCRE is a library of functions to support regular expressions whose syntax
+and semantics are as close as possible to those of the Perl 5 language.
+
+Written by: Philip Hazel <ph10 at cam.ac.uk>
+
+ Copyright (c) 1997-2003 University of Cambridge
+
+-----------------------------------------------------------------------------
+Permission is granted to anyone to use this software for any purpose on any
+computer system, and to redistribute it freely, subject to the following
+restrictions:
+
+1. This software 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.
+
+2. The origin of this software must not be misrepresented, either by
+ explicit claim or by omission.
+
+3. Altered versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+4. If PCRE is embedded in any software that is released under the GNU
+ General Purpose Licence (GPL), then the terms of that licence shall
+ supersede any condition above with which it is incompatible.
+-----------------------------------------------------------------------------
+
+See the file Tech.Notes for some information on the internals.
+*/
+
+
+/* This file is compiled on its own as part of the PCRE library. However,
+it is also included in the compilation of dftables.c, in which case the macro
+DFTABLES is defined. */
+
+#ifndef DFTABLES
+#include "internal.h"
+#include <glib.h>
+#endif
+
+
+
+/*************************************************
+* Create PCRE character tables *
+*************************************************/
+
+/* This function builds a set of character tables for use by PCRE and returns
+a pointer to them. They are build using the ctype functions, and consequently
+their contents will depend upon the current locale setting. When compiled as
+part of the library, the store is obtained via pcre_malloc(), but when compiled
+inside dftables, use malloc().
+
+Arguments: none
+Returns: pointer to the contiguous block of data
+*/
+
+const unsigned char *
+pcre_maketables(void)
+{
+unsigned char *yield, *p;
+int i;
+
+#ifndef DFTABLES
+yield = (unsigned char*)(pcre_malloc)(tables_length);
+#else
+yield = (unsigned char*)malloc(tables_length);
+#endif
+
+if (yield == NULL) return NULL;
+p = yield;
+
+/* First comes the lower casing table */
+
+for (i = 0; i < 256; i++) *p++ = g_unichar_tolower(i);
+
+/* Next the case-flipping table */
+
+for (i = 0; i < 256; i++) *p++ = g_unichar_islower(i)? g_unichar_toupper(i) : g_unichar_tolower(i);
+
+/* Then the character class tables. Don't try to be clever and save effort
+on exclusive ones - in some locales things may be different. Note that the
+table for "space" includes everything "g_unichar_isspace" gives, including VT in the
+default locale. This makes it work for the POSIX class [:space:]. */
+
+memset(p, 0, cbit_length);
+for (i = 0; i < 256; i++)
+ {
+ if (g_unichar_isdigit(i))
+ {
+ p[cbit_digit + i/8] |= 1 << (i&7);
+ p[cbit_word + i/8] |= 1 << (i&7);
+ }
+ if (g_unichar_isupper(i))
+ {
+ p[cbit_upper + i/8] |= 1 << (i&7);
+ p[cbit_word + i/8] |= 1 << (i&7);
+ }
+ if (g_unichar_islower(i))
+ {
+ p[cbit_lower + i/8] |= 1 << (i&7);
+ p[cbit_word + i/8] |= 1 << (i&7);
+ }
+ if (i == '_') p[cbit_word + i/8] |= 1 << (i&7);
+ if (g_unichar_isspace(i)) p[cbit_space + i/8] |= 1 << (i&7);
+ if (g_unichar_isxdigit(i))p[cbit_xdigit + i/8] |= 1 << (i&7);
+ if (g_unichar_isgraph(i)) p[cbit_graph + i/8] |= 1 << (i&7);
+ if (g_unichar_isprint(i)) p[cbit_print + i/8] |= 1 << (i&7);
+ if (g_unichar_ispunct(i)) p[cbit_punct + i/8] |= 1 << (i&7);
+ if (g_unichar_iscntrl(i)) p[cbit_cntrl + i/8] |= 1 << (i&7);
+ }
+p += cbit_length;
+
+/* Finally, the character type table. In this, we exclude VT from the white
+space chars, because Perl doesn't recognize it as such for \s and for comments
+within regexes. */
+
+for (i = 0; i < 256; i++)
+ {
+ int x = 0;
+ if (i != 0x0b && g_unichar_isspace(i)) x += ctype_space;
+ if (g_unichar_isalpha(i)) x += ctype_letter;
+ if (g_unichar_isdigit(i)) x += ctype_digit;
+ if (g_unichar_isxdigit(i)) x += ctype_xdigit;
+ if (g_unichar_isalnum(i) || i == '_') x += ctype_word;
+
+ /* Note: strchr includes the terminating zero in the characters it considers.
+ In this instance, that is ok because we want binary zero to be flagged as a
+ meta-character, which in this sense is any character that terminates a run
+ of data characters. */
+
+ if (strchr("*+?{^.$|()[", i) != 0) x += ctype_meta; *p++ = x; }
+
+return yield;
+}
+
+/* End of maketables.c */
--- /dev/null
+++ lib/goffice/cut-n-paste/pcre/pcre.h
@@ -0,0 +1,198 @@
+/* File import from pcre to goffice by import-pcre. Do not edit. */
+
+/* This file has been programatically changed. */
+/* This makes the following file fall under GPL license, see pcre.c. */
+
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* Copyright (c) 1997-2003 University of Cambridge */
+
+#ifndef _PCRE_H
+#define _PCRE_H
+
+/* The file pcre.h is build by "configure". Do not edit it; instead
+make changes to pcre.in. */
+
+#define PCRE_MAJOR 4
+#define PCRE_MINOR 5
+#define PCRE_DATE 01-December-2003
+
+/* Win32 uses DLL by default */
+
+#ifdef _WIN32
+# ifdef PCRE_DEFINITION
+# ifdef DLL_EXPORT
+# define PCRE_DATA_SCOPE __declspec(dllexport)
+# endif
+# else
+# ifndef PCRE_STATIC
+# define PCRE_DATA_SCOPE extern __declspec(dllimport)
+# endif
+# endif
+#endif
+#ifndef PCRE_DATA_SCOPE
+# define PCRE_DATA_SCOPE extern
+#endif
+
+/* Have to include stdlib.h in order to ensure that size_t is defined;
+it is needed here for malloc. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options */
+
+#define PCRE_CASELESS 0x0001
+#define PCRE_MULTILINE 0x0002
+#define PCRE_DOTALL 0x0004
+#define PCRE_EXTENDED 0x0008
+#define PCRE_ANCHORED 0x0010
+#define PCRE_DOLLAR_ENDONLY 0x0020
+#define PCRE_EXTRA 0x0040
+#define PCRE_NOTBOL 0x0080
+#define PCRE_NOTEOL 0x0100
+#define PCRE_UNGREEDY 0x0200
+#define PCRE_NOTEMPTY 0x0400
+#define PCRE_UTF8 0x0800
+#define PCRE_NO_AUTO_CAPTURE 0x1000
+#define PCRE_NO_UTF8_CHECK 0x2000
+
+/* Exec-time and get/set-time error codes */
+
+#define PCRE_ERROR_NOMATCH (-1)
+#define PCRE_ERROR_NULL (-2)
+#define PCRE_ERROR_BADOPTION (-3)
+#define PCRE_ERROR_BADMAGIC (-4)
+#define PCRE_ERROR_UNKNOWN_NODE (-5)
+#define PCRE_ERROR_NOMEMORY (-6)
+#define PCRE_ERROR_NOSUBSTRING (-7)
+#define PCRE_ERROR_MATCHLIMIT (-8)
+#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */
+#define PCRE_ERROR_BADUTF8 (-10)
+#define PCRE_ERROR_BADUTF8_OFFSET (-11)
+
+/* Request types for pcre_fullinfo() */
+
+#define PCRE_INFO_OPTIONS 0
+#define PCRE_INFO_SIZE 1
+#define PCRE_INFO_CAPTURECOUNT 2
+#define PCRE_INFO_BACKREFMAX 3
+#define PCRE_INFO_FIRSTBYTE 4
+#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */
+#define PCRE_INFO_FIRSTTABLE 5
+#define PCRE_INFO_LASTLITERAL 6
+#define PCRE_INFO_NAMEENTRYSIZE 7
+#define PCRE_INFO_NAMECOUNT 8
+#define PCRE_INFO_NAMETABLE 9
+#define PCRE_INFO_STUDYSIZE 10
+
+/* Request types for pcre_config() */
+
+#define PCRE_CONFIG_UTF8 0
+#define PCRE_CONFIG_NEWLINE 1
+#define PCRE_CONFIG_LINK_SIZE 2
+#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3
+#define PCRE_CONFIG_MATCH_LIMIT 4
+#define PCRE_CONFIG_STACKRECURSE 5
+
+/* Bit flags for the pcre_extra structure */
+
+#define PCRE_EXTRA_STUDY_DATA 0x0001
+#define PCRE_EXTRA_MATCH_LIMIT 0x0002
+#define PCRE_EXTRA_CALLOUT_DATA 0x0004
+
+/* Types */
+
+struct real_pcre; /* declaration; the definition is private */
+typedef struct real_pcre pcre;
+
+/* The structure for passing additional data to pcre_exec(). This is defined in
+such as way as to be extensible. */
+
+typedef struct pcre_extra {
+ unsigned long int flags; /* Bits for which fields are set */
+ void *study_data; /* Opaque data from pcre_study() */
+ unsigned long int match_limit; /* Maximum number of calls to match() */
+ void *callout_data; /* Data passed back in callouts */
+} pcre_extra;
+
+/* The structure for passing out data via the pcre_callout_function. We use a
+structure so that new fields can be added on the end in future versions,
+without changing the API of the function, thereby allowing old clients to work
+without modification. */
+
+typedef struct pcre_callout_block {
+ int version; /* Identifies version of block */
+ /* ------------------------ Version 0 ------------------------------- */
+ int callout_number; /* Number compiled into pattern */
+ int *offset_vector; /* The offset vector */
+ const char *subject; /* The subject being matched */
+ int subject_length; /* The length of the subject */
+ int start_match; /* Offset to start of this match attempt */
+ int current_position; /* Where we currently are */
+ int capture_top; /* Max current capture */
+ int capture_last; /* Most recently closed capture */
+ void *callout_data; /* Data passed in with the call */
+ /* ------------------------------------------------------------------ */
+} pcre_callout_block;
+
+/* Indirection for store get and free functions. These can be set to
+alternative malloc/free functions if required. Special ones are used in the
+non-recursive case for "frames". There is also an optional callout function
+that is triggered by the (?) regex item. Some magic is required for Win32 DLL;
+it is null on other OS. For Virtual Pascal, these have to be different again.
+*/
+
+#ifndef VPCOMPAT
+PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_free)(void *);
+PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_stack_free)(void *);
+PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *);
+#else /* VPCOMPAT */
+extern void *pcre_malloc(size_t);
+extern void pcre_free(void *);
+extern void *pcre_stack_malloc(size_t);
+extern void pcre_stack_free(void *);
+extern int pcre_callout(pcre_callout_block *);
+#endif /* VPCOMPAT */
+
+/* Exported PCRE functions */
+
+extern pcre *pcre_compile(const char *, int, const char **,
+ int *, const unsigned char *);
+extern int pcre_config(int, void *);
+extern int pcre_copy_named_substring(const pcre *, const char *,
+ int *, int, const char *, char *, int);
+extern int pcre_copy_substring(const char *, int *, int, int,
+ char *, int);
+extern int pcre_exec(const pcre *, const pcre_extra *,
+ const char *, int, int, int, int *, int);
+extern void pcre_free_substring(const char *);
+extern void pcre_free_substring_list(const char **);
+extern int pcre_fullinfo(const pcre *, const pcre_extra *, int,
+ void *);
+extern int pcre_get_named_substring(const pcre *, const char *,
+ int *, int, const char *, const char **);
+extern int pcre_get_stringnumber(const pcre *, const char *);
+extern int pcre_get_substring(const char *, int *, int, int,
+ const char **);
+extern int pcre_get_substring_list(const char *, int *, int,
+ const char ***);
+extern int pcre_info(const pcre *, int *, int *);
+extern const unsigned char *pcre_maketables(void);
+extern pcre_extra *pcre_study(const pcre *, int, const char **);
+extern const char *pcre_version(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* End of pcre.h */
--- /dev/null
+++ lib/goffice/pixmaps/chart_radar_1_1.svg
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_radar_1_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient6655">
+ <stop
+ style="stop-color:#ffda86;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6656" />
+ <stop
+ style="stop-color:#c68c0b;stop-opacity:1.0000000;"
+ offset="0.81471545"
+ id="stop6657" />
+ <stop
+ style="stop-color:#664805;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop6658" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6649">
+ <stop
+ style="stop-color:#b5e6b0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6650" />
+ <stop
+ style="stop-color:#6c8969;stop-opacity:1.0000000;"
+ offset="0.81471545"
+ id="stop6652" />
+ <stop
+ style="stop-color:#3b4b39;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop6651" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <g
+ id="g576"
+ style="stroke:#000000;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ transform="matrix(3.226160,0.000000,0.000000,3.226160,12.70631,11.39544)">
+ <path
+ style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke-linecap:butt;stroke-linejoin:miter;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 32.000000,8.0000000 L 32.000000,30.000000 L 54.825400,24.583600"
+ id="path571" />
+ <path
+ style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke-linecap:butt;stroke-linejoin:miter;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 46.106800,51.416400 L 32.000000,30.000000 L 17.893200,51.416400"
+ id="path572" />
+ <path
+ style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke-linecap:butt;stroke-linejoin:miter;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 9.1746400,24.583600 L 32.000000,30.000000"
+ id="path573" />
+ </g>
+ <path
+ style="font-size:12.000000;fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#83a67f;stroke-width:8.8582677;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000;"
+ d="M 114.88784,36.358436 L 147.67821,99.179587 L 153.71628,164.07348 L 82.894321,156.84558 L 57.340880,93.912237 L 114.88784,36.358436 z "
+ id="path575"
+ sodipodi:nodetypes="cccccc" />
+ <path
+ style="font-size:12.000000;fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#d1940c;stroke-width:8.8582677;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000;"
+ d="M 116.07507,65.055195 L 116.73352,65.055195 L 183.08564,90.891263 L 183.08564,90.891263 L 142.65992,147.61587 L 101.57832,129.72681 L 70.906555,96.272817 L 116.07507,65.055195 z "
+ id="path574"
+ sodipodi:nodetypes="cccccc" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/scatter.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char const * const scatter_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"g.gggggggggggggggg",
+"g.gggggggggg.ggggg",
+"g.gggggggggggggggg",
+"g.gggggggggggggggg",
+"g.ggggg#ggggggg.gg",
+"g.ggggggggggg#gggg",
+"g.gg.ggg.ggggggggg",
+"g.gggg.ggggggggggg",
+"g.ggggggggg.gggggg",
+"g.g#ggggg#gggggggg",
+"g.ggg.gggggggg#ggg",
+"g.gggggggg#ggggggg",
+"g.gggggggggggggggg",
+"g................g",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/chart_column_1_2.svg
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_column_1_2.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6936"
+ width="28.346457"
+ height="31.889763"
+ x="58.464565"
+ y="104.52755" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6938"
+ width="28.346457"
+ height="28.346460"
+ x="100.98425"
+ y="157.67715" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7854146;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6939"
+ width="28.346457"
+ height="95.669167"
+ x="100.98425"
+ y="62.007870" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6941"
+ width="28.346457"
+ height="60.236225"
+ x="143.50394"
+ y="125.78740" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6942"
+ width="28.346457"
+ height="38.976379"
+ x="143.50394"
+ y="86.811020" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6935"
+ width="28.346457"
+ height="49.606300"
+ x="58.464565"
+ y="136.41731" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/radar.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char * radar_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"gggggggg.ggggggggg",
+"ggggggg##ggggggggg",
+"gggggg#####ggggggg",
+"ggggg#gg.gg##ggggg",
+"g.gg#ggbbbggg#gg.g",
+"gg##ggbgbbbbgg##gg",
+"gg##.bbg.ggbb.##gg",
+"gg#ggbbg.g.bbg#ggg",
+"gg#ggbg...ggbg#ggg",
+"gg#ggbg.g.gbgg#ggg",
+"ggg#gbbgggbbg#gggg",
+"ggg#gbbbbbb.g#gggg",
+"ggg##ggggggg##gggg",
+"ggg###########gggg",
+"gg.ggggggggggg.ggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/linegraph.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char const * const linegraph_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"g.gggggggggggggggg",
+"g.gggggggggggggggg",
+"g.ggggggbbgggg##gg",
+"g.ggggggbbgggg##gg",
+"g.gggggcggcggg#ggg",
+"g.ggggcggggcg#gggg",
+"g.gggcgggggg#ggggg",
+"g.gbbgggggg#gcgggg",
+"g.gbbggggg#gggbbgg",
+"g.ggggggg#ggggbbgg",
+"g.g##gg##ggggggggg",
+"g.g######ggggggggg",
+"g.gggggggggggggggg",
+"g................g",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/column.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char const * const column_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"g.gggggggggg...ggg",
+"g.gggggggggg.b.ggg",
+"g.gggggggggg.b.ggg",
+"g.gggggggggg.b.ggg",
+"g.gggggggggg.b.ggg",
+"g.gggggggg...b.ggg",
+"g.gggggggg.#.b.ggg",
+"g.gggg...g.#.b.ggg",
+"g.gggg.b.g.#.b.ggg",
+"g.gg...b.g.#.b.ggg",
+"g.gg.#.b.g.#.b.ggg",
+"g.gg.#.b.g.#.b.ggg",
+"g.gg.#.b.g.#.b.ggg",
+"g................g",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/chart_area_1_2.svg
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/gnome/head/gnumeric/src/cut-n-paste-code/goffice/pixmaps"
+ sodipodi:docname="chart_area_1_2.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ d="M 115.20785,37.204718 L 70.375000,126.96875 L 30.000000,108.31250 L 30.000000,172.21875 C 30.000000,180.06587 37.846387,186.37500 47.593750,186.37500 L 182.78125,186.37500 C 191.94000,186.37500 199.34862,180.77699 200.21875,173.59375 L 200.37500,40.750000 C 200.37500,40.323253 200.26421,39.916928 200.21875,39.500000 L 158.12500,73.781250 L 115.20785,37.204718 z "
+ id="path4680"
+ sodipodi:nodetypes="ccccccccccc" />
+ <path
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ d="M 198.75000 94.593750 L 156.81250 144.87500 L 117.37500 121.96875 L 114.28125 120.15625 L 111.87500 122.84375 L 72.343750 166.90625 L 31.312500 128.06250 L 30.000000 129.43750 L 30.000000 172.21875 C 30.000000 180.06587 37.846387 186.37500 47.593750 186.37500 L 182.78125 186.37500 C 191.97959 186.37500 199.40235 180.72816 200.21875 173.50000 L 200.37500 95.968750 L 198.75000 94.593750 z "
+ id="path5302" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart-rings-2d.svg
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="us-ascii" ?>
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20000303 Stylable//EN" "http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd" [
+ <!ENTITY st0 "fill:url(#aigrd1);">
+ <!ENTITY st1 "fill:url(#aigrd6);stroke:none;">
+ <!ENTITY st2 "fill:url(#aigrd4);stroke-width:0.6888;">
+ <!ENTITY st3 "fill:url(#aigrd3);stroke:none;">
+ <!ENTITY st4 "fill:url(#aigrd5);stroke:none;">
+ <!ENTITY st5 "fill:url(#aigrd2);stroke:none;">
+ <!ENTITY st6 "fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
+]>
+<svg width="82.2pt" height="82.2pt" viewBox="0 0 82.2 82.2" xml:space="preserve">
+ <g id="Layer_x0020_1" style="&st6;">
+ <linearGradient id="aigrd1" gradientUnits="userSpaceOnUse" x1="19.6348" y1="17.5508" x2="69.4219" y2="72.1719">
+ <stop offset="0" style="stop-color:#993366"/>
+ <stop offset="1" style="stop-color:#7A2952"/>
+ </linearGradient>
+ <path style="&st0;" d="M81.7,41.1c0,22.4-18.2,40.6-40.6,40.6S0.5,63.5,0.5,41.1S18.7,0.5,41.1,0.5s40.6,18.2,40.6,40.6z"/>
+ <linearGradient id="aigrd2" gradientUnits="userSpaceOnUse" x1="21.6367" y1="49.5361" x2="81.5742" y2="88.6888">
+ <stop offset="0" style="stop-color:#9999FF"/>
+ <stop offset="1" style="stop-color:#9968FF"/>
+ </linearGradient>
+ <path style="&st5;" d="M41.1,41.1L23.2,77.5c5.4,2.7,11.5,4.2,17.9,4.2c16,0,29.8-9.2,36.4-22.7L41.1,41.1z"/>
+ <linearGradient id="aigrd3" gradientUnits="userSpaceOnUse" x1="10.77" y1="32.3887" x2="43.6398" y2="81.2097">
+ <stop offset="0" style="stop-color:#FFFFCC"/>
+ <stop offset="0.9888" style="stop-color:#FFFFB8"/>
+ </linearGradient>
+ <path style="&st3;" d="M41.1,41.1l-40.6,0c0,16,9.2,29.8,22.7,36.4l17.9-36.4z"/>
+ <linearGradient id="aigrd4" gradientUnits="userSpaceOnUse" x1="26.3159" y1="24.8804" x2="60.6082" y2="62.5022">
+ <stop offset="0" style="stop-color:#993366"/>
+ <stop offset="1" style="stop-color:#7A2952"/>
+ </linearGradient>
+ <path style="&st2;" d="M41.1,13.1c-15.4,0-28,12.5-28,28c0,15.4,12.5,28,28,28c15.4,0,28-12.5,28-28c0-15.4-12.5-28-28-28z M41.1,56.8c-8.7,0-15.7-7-15.7-15.7s7-15.7,15.7-15.7s15.7,7,15.7,15.7s-7,15.7-15.7,15.7z"/>
+ <linearGradient id="aigrd5" gradientUnits="userSpaceOnUse" x1="34.9023" y1="40.4746" x2="73.0145" y2="65.3705">
+ <stop offset="0" style="stop-color:#9999FF"/>
+ <stop offset="1" style="stop-color:#9968FF"/>
+ </linearGradient>
+ <path style="&st4;" d="M69.1,41.1c0-3.2-0.5-6.2-1.5-9L56,36c0.5,1.6,0.9,3.3,0.9,5.1c0,8.7-7,15.7-15.7,15.7c-2.5,0-4.9-0.6-6.9-1.6l-5.4,11c3.7,1.8,7.9,2.9,12.4,2.9c15.4,0,28-12.5,28-28z"/>
+ <linearGradient id="aigrd6" gradientUnits="userSpaceOnUse" x1="12.4072" y1="31.9868" x2="40.1898" y2="73.2519">
+ <stop offset="0" style="stop-color:#FFFFCC"/>
+ <stop offset="0.9888" style="stop-color:#FFFFB8"/>
+ </linearGradient>
+ <path style="&st1;" d="M28.7,66.2l5.4-11c-5.2-2.6-8.8-7.9-8.8-14.1c0-1.8,0.3-3.5,0.9-5.1l-11.6-4c-1,2.9-1.5,5.9-1.5,9.1c0,11,6.4,20.5,15.6,25.1z"/>
+ </g>
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_ring_1_2.svg
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_ring_1_2.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="57.631955"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#83a67f;stroke-width:13.884113;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M 127.30553,48.412091 C 159.45548,47.917488 186.65918,80.067430 173.30453,115.67957"
+ id="path161"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#83a67f;stroke-width:14.428587;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M 115.47835,70.822594 C 138.47784,71.069785 151.09050,89.865062 150.59568,106.18754"
+ id="path162"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#d1940c;stroke-width:13.339639;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M 163.72354,138.83635 C 149.54952,175.81294 101.54879,180.31391 78.739696,151.66136"
+ id="path163"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#d1940c;stroke-width:14.700828;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M 150.59055,106.43158 C 150.26585,128.56972 128.47964,148.40367 101.46643,139.04123"
+ id="path164"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#c5d2c8;stroke-width:13.884115;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M 61.279646,129.03097 C 36.965996,97.256086 59.200470,48.338816 100.44393,48.868523"
+ id="path165"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#c5d2c8;stroke-width:14.156352;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M 114.97839,71.066690 C 99.235804,71.748852 85.600668,81.049873 81.319898,97.762210 C 77.083686,114.54788 84.801423,131.85182 101.21919,138.54674"
+ id="path166"
+ sodipodi:nodetypes="csc" />
+ <path
+ style="font-size:12.000000;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 115.47814,63.156015 L 115.47814,78.489084 M 143.17647,106.43458 L 158.50953,106.43480 M 104.84390,131.16528 L 98.166672,146.25117 M 158.50953,106.18739 C 158.50953,129.94088 139.23164,149.21877 115.47814,149.21877 C 91.724880,149.21877 72.446545,129.94088 72.446545,106.18739 C 72.446545,82.434133 91.724880,63.156015 115.47814,63.156015 C 139.23164,63.156015 158.50953,82.434133 158.50953,106.18739 z M 143.17647,106.18739 C 143.17647,121.47690 130.76766,133.88570 115.47814,133.88570 C 100.18863,133.88570 87.779614,121.47690 87.779614,106.18739 C 87.779614,90.897888 100.18863,78.489084 115.47814,78.489084 C 130.76766,78.489084 143.17647,90.897888 143.17647,106.18739 z "
+ id="path149" />
+ <path
+ style="font-size:12.000000;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 101.10127,55.830802 L 101.08799,43.095182 C 88.227913,42.920102 77.035467,46.498935 66.712875,54.555423 C 42.391830,73.538000 38.059112,108.68298 56.937589,132.87401 L 67.623237,125.38725 C 52.965970,107.05588 55.872595,80.105457 74.266703,65.292688 C 82.205366,58.899680 90.908037,55.830802 101.10127,55.830802 z "
+ id="path197"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ style="font-size:12.000000;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 167.18770,112.95623 L 179.13369,117.85520 C 181.80075,111.43563 182.92085,105.42898 182.94937,98.346676 C 183.07374,67.494763 158.13570,42.354392 127.54208,42.353259 L 127.42905,55.212714 C 150.67508,55.336200 169.84234,74.503462 169.84234,98.120392 C 169.84234,103.44448 169.03391,107.96232 167.18770,112.95623 z "
+ id="path207"
+ sodipodi:nodetypes="ccccccc" />
+ <path
+ style="font-size:12.000000;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 169.91347,140.80372 L 158.22577,136.12188 C 148.46007,157.67743 124.43674,169.19286 102.39030,160.72562 C 94.800096,157.81050 89.024088,153.53658 84.180439,147.18888 L 74.042750,155.67464 C 80.405702,163.69868 87.889837,169.20244 97.712166,172.97021 C 126.51751,184.01978 158.86391,169.60906 169.91347,140.80372 z "
+ id="path212"
+ sodipodi:nodetypes="ccccccc" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_line_1_2.svg
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/gnome/head/gnumeric/src/cut-n-paste-code/goffice/pixmaps"
+ sodipodi:docname="chart_line_1_2.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 113.71875 29.531250 L 111.12500 35.406250 L 70.375000 126.96875 L 30.000000 108.31250 L 30.000000 118.06250 L 70.750000 136.90625 L 74.812500 138.78125 L 76.656250 134.68750 L 116.59375 44.906250 L 154.68750 82.875000 L 157.53125 85.687500 L 160.62500 83.156250 L 200.37500 50.781250 L 200.37500 40.750000 C 200.37500 40.323253 200.26421 39.916928 200.21875 39.500000 L 158.12500 73.781250 L 118.28125 34.062500 L 113.71875 29.531250 z "
+ id="path4680" />
+ <path
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 198.75000 94.593750 L 156.81250 144.87500 L 117.37500 121.96875 L 114.28125 120.15625 L 111.87500 122.84375 L 72.343750 166.90625 L 31.312500 128.06250 L 30.000000 129.43750 L 30.000000 139.03125 L 69.562500 176.46875 L 72.875000 179.59375 L 75.906250 176.21875 L 116.06250 131.43750 L 155.59375 154.40625 L 158.81250 156.31250 L 161.21875 153.43750 L 200.37500 106.46875 L 200.37500 95.968750 L 198.75000 94.593750 z "
+ id="path5302" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_line_2_3.svg
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/gnome/head/gnumeric/src/cut-n-paste-code/goffice/pixmaps"
+ sodipodi:docname="chart_line_2_3.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="57.631955"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 30.000000,39.875000 L 30.000000,48.718750 L 200.18750,48.718750 L 200.18750,39.875000 L 30.000000,39.875000 z "
+ id="path4680" />
+ <path
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 157.59375,63.285181 L 154.06250,69.003931 L 112.00000,136.94143 L 73.375000,159.28518 L 30.000000,105.59768 L 30.000000,119.69143 L 68.875000,167.81643 L 71.250000,170.75393 L 74.531250,168.84768 L 117.43750,144.06643 L 118.37500,143.50393 L 118.96875,142.56643 L 158.06250,79.378931 L 200.12500,139.22268 L 200.37500,139.03518 L 200.37500,124.16018 L 161.43750,68.785181 L 157.59375,63.285181 z "
+ id="path5302" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6059"
+ width="21.259842"
+ height="21.259842"
+ x="19.488190"
+ y="33.661411"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6060"
+ width="21.259842"
+ height="21.259842"
+ x="62.007874"
+ y="33.661411"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6061"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="33.661411"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6062"
+ width="21.259842"
+ height="21.259842"
+ x="147.04724"
+ y="33.661411"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6063"
+ width="21.259842"
+ height="21.259842"
+ x="186.02362"
+ y="33.661411"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6064"
+ width="21.259842"
+ height="21.259842"
+ x="19.488190"
+ y="101.24531"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6065"
+ width="21.259842"
+ height="21.259842"
+ x="62.007874"
+ y="154.39493"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6066"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="129.59177"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6067"
+ width="21.259842"
+ height="21.259842"
+ x="147.04724"
+ y="62.268932"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6068"
+ width="21.259842"
+ height="21.259842"
+ x="189.56693"
+ y="122.50515"
+ rx="0.0000000" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_bubble_1_2.svg
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_bubble_1_2.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient6655">
+ <stop
+ style="stop-color:#ffda86;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6656" />
+ <stop
+ style="stop-color:#c68c0b;stop-opacity:1.0000000;"
+ offset="0.81471545"
+ id="stop6657" />
+ <stop
+ style="stop-color:#664805;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop6658" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6649">
+ <stop
+ style="stop-color:#b5e6b0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6650" />
+ <stop
+ style="stop-color:#6c8969;stop-opacity:1.0000000;"
+ offset="0.81471545"
+ id="stop6652" />
+ <stop
+ style="stop-color:#3b4b39;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop6651" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6649"
+ id="radialGradient6648"
+ cx="0.50000000"
+ cy="0.50000000"
+ r="0.50000000"
+ fx="0.50000000"
+ fy="0.50000000" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6649"
+ id="radialGradient6653" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6655"
+ id="radialGradient6654"
+ cx="0.50000000"
+ cy="0.50000000"
+ r="0.50000000"
+ fx="0.50000000"
+ fy="0.50000000" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6655"
+ id="radialGradient6659" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#radialGradient6648);fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="path6611"
+ sodipodi:cx="92.862892"
+ sodipodi:cy="85.776276"
+ sodipodi:rx="27.311714"
+ sodipodi:ry="27.311714"
+ d="M 93.016367,58.464993 A 27.311714,27.311714 0 1 1 92.862912,58.464561"
+ sodipodi:start="4.7180084"
+ sodipodi:end="10.995575"
+ sodipodi:open="true"
+ transform="translate(1.165789e-5,-21.25984)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#radialGradient6653);fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:3.6828350;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="path6612"
+ sodipodi:cx="92.862892"
+ sodipodi:cy="85.776276"
+ sodipodi:rx="27.311714"
+ sodipodi:ry="27.311714"
+ d="M 93.016367,58.464993 A 27.311714,27.311714 0 1 1 92.862912,58.464561"
+ sodipodi:start="4.7180084"
+ sodipodi:end="10.995575"
+ sodipodi:open="true"
+ transform="matrix(0.481057,0.000000,0.000000,0.481057,110.4962,48.05631)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#radialGradient6659);fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:3.1816833;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="path6613"
+ sodipodi:cx="92.862892"
+ sodipodi:cy="85.776276"
+ sodipodi:rx="27.311714"
+ sodipodi:ry="27.311714"
+ d="M 93.016367,58.464993 A 27.311714,27.311714 0 1 1 92.862912,58.464561"
+ sodipodi:start="4.7180084"
+ sodipodi:end="10.995575"
+ sodipodi:open="true"
+ transform="matrix(0.556829,0.000000,0.000000,0.556829,19.89431,80.53325)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#radialGradient6654);fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7525421;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="path6614"
+ sodipodi:cx="92.862892"
+ sodipodi:cy="85.776276"
+ sodipodi:rx="27.311714"
+ sodipodi:ry="27.311714"
+ d="M 93.016367,58.464993 A 27.311714,27.311714 0 1 1 92.862912,58.464561"
+ sodipodi:start="4.7180084"
+ sodipodi:end="10.995575"
+ sodipodi:open="true"
+ transform="matrix(1.010905,0.000000,0.000000,1.010905,45.34817,59.59866)" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_area_1_1.svg
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/gnome/head/gnumeric/src/cut-n-paste-code/goffice/pixmaps"
+ sodipodi:docname="chart_area_1_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ d="M 115.20785,37.204718 L 69.187500,150.62500 L 30.000000,164.31250 L 30.000000,172.21875 C 30.000000,180.06587 37.846387,186.37500 47.593750,186.37500 L 182.78125,186.37500 C 191.94000,186.37500 199.34862,180.77699 200.21875,173.59375 L 200.37500,40.750000 C 200.37500,40.408602 200.27922,40.085212 200.25000,39.750000 L 200.18750,39.687500 L 158.00000,96.812500 L 115.20785,37.204718 z "
+ id="path4680"
+ sodipodi:nodetypes="cccccccccccc" />
+ <path
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ d="M 74.062500 53.937500 L 69.343750 59.000000 L 30.000000 101.37500 L 30.093750 173.09375 C 30.584987 179.48170 36.229908 184.69793 43.781250 186.03125 L 186.59375 186.03125 C 194.09417 184.70692 199.69238 179.54778 200.25000 173.21875 L 200.37500 149.96875 L 160.18750 122.15625 L 157.28125 120.12500 L 154.68750 122.53125 L 116.87500 157.21875 L 76.687500 60.312500 L 74.062500 53.937500 z "
+ id="path5302" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_area_1_3.svg
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/gnome/head/gnumeric/src/cut-n-paste-code/goffice/pixmaps"
+ sodipodi:docname="chart_area_1_3.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="102.97927"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="57.856204"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ d="M 200.18750 41.531250 L 30.000000 42.156250 L 30.000000 172.21875 C 30.000000 180.06587 37.846387 186.37500 47.593750 186.37500 L 182.78125 186.37500 C 191.85923 186.37500 199.20984 180.87276 200.18750 173.78125 L 200.18750 41.531250 z "
+ id="path4680" />
+ <path
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ d="M 157.59375 55.937500 L 154.06250 61.656250 L 112.00000 129.59375 L 73.375000 151.93750 L 30.000000 98.250000 L 30.000000 172.21875 C 30.000000 180.06587 37.846387 186.37500 47.593750 186.37500 L 182.78125 186.37500 C 192.00598 186.37500 199.43855 180.69559 200.21875 173.43750 L 200.37500 116.81250 L 161.43750 61.437500 L 157.59375 55.937500 z "
+ id="path5302" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_radar_1_3.svg
@@ -0,0 +1,241 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_radar_1_3.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient6655">
+ <stop
+ style="stop-color:#ffda86;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6656" />
+ <stop
+ style="stop-color:#c68c0b;stop-opacity:1.0000000;"
+ offset="0.81471545"
+ id="stop6657" />
+ <stop
+ style="stop-color:#664805;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop6658" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6649">
+ <stop
+ style="stop-color:#b5e6b0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6650" />
+ <stop
+ style="stop-color:#6c8969;stop-opacity:1.0000000;"
+ offset="0.81471545"
+ id="stop6652" />
+ <stop
+ style="stop-color:#3b4b39;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop6651" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <g
+ id="g576"
+ style="stroke:#000000;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ transform="matrix(3.226160,0.000000,0.000000,3.226160,12.70631,11.39544)">
+ <path
+ style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke-linecap:butt;stroke-linejoin:miter;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 32.000000,8.0000000 L 32.000000,30.000000 L 54.825400,24.583600"
+ id="path571" />
+ <path
+ style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke-linecap:butt;stroke-linejoin:miter;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 46.106800,51.416400 L 32.000000,30.000000 L 17.893200,51.416400"
+ id="path572" />
+ <path
+ style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke-linecap:butt;stroke-linejoin:miter;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 9.1746400,24.583600 L 32.000000,30.000000"
+ id="path573" />
+ </g>
+ <path
+ style="font-size:12.000000;fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000;"
+ d="M 114.88784,36.358436 L 147.67821,99.179587 L 153.71628,164.07348 L 82.894321,156.84558 L 57.340880,93.912237 L 114.88784,36.358436 z "
+ id="path575"
+ sodipodi:nodetypes="cccccc" />
+ <path
+ style="font-size:12.000000;fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000;"
+ d="M 116.07507,65.055195 L 116.73352,65.055195 L 183.08564,90.891263 L 183.08564,90.891263 L 142.65992,147.61587 L 101.57832,129.72681 L 70.906555,96.272817 L 116.07507,65.055195 z "
+ id="path574"
+ sodipodi:nodetypes="cccccc" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_line_1_1.svg
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_line_1_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 114.09375 31.125000 L 111.06250 39.187500 L 69.187500 150.62500 L 30.000000 164.31250 L 30.000000 172.21875 C 30.000000 172.70324 30.097823 173.15330 30.156250 173.62500 L 74.062500 158.31250 L 76.031250 157.62500 L 76.750000 155.68750 L 116.31250 50.375000 L 154.12500 107.00000 L 157.62500 112.21875 L 161.37500 107.15625 L 200.37500 54.375000 L 200.37500 40.750000 C 200.37500 40.408602 200.27922 40.085212 200.25000 39.750000 L 200.18750 39.687500 L 158.00000 96.812500 L 118.90625 38.281250 L 114.09375 31.125000 z "
+ id="path4680" />
+ <path
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 74.062500 53.937500 L 69.343750 59.000000 L 30.000000 101.37500 L 30.000000 114.43750 L 71.156250 70.093750 L 111.12500 166.46875 L 113.53125 172.31250 L 118.18750 168.03125 L 158.06250 131.46875 L 200.37500 160.71875 L 200.37500 149.96875 L 160.18750 122.15625 L 157.28125 120.12500 L 154.68750 122.53125 L 116.87500 157.21875 L 76.687500 60.312500 L 74.062500 53.937500 z "
+ id="path5302" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart-pie-2d.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="us-ascii" ?>
+<!-- Generator: Adobe Illustrator 9.0, SVG Export Plug-In -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20000303 Stylable//EN" "http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd" [
+ <!ENTITY st0 "fill:url(#aigrd1);">
+ <!ENTITY st1 "fill:url(#aigrd3);stroke:none;">
+ <!ENTITY st2 "fill:url(#aigrd2);stroke:none;">
+ <!ENTITY st3 "fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
+]>
+<svg width="82.2pt" height="82.2pt" viewBox="0 0 82.2 82.2" xml:space="preserve">
+ <g id="Layer_x0020_1" style="&st3;">
+ <linearGradient id="aigrd1" gradientUnits="userSpaceOnUse" x1="19.6328" y1="17.5483" x2="69.4199" y2="72.1693">
+ <stop offset="0" style="stop-color:#993366"/>
+ <stop offset="1" style="stop-color:#7A2952"/>
+ </linearGradient>
+ <path style="&st0;" d="M81.7,41.1c0,22.4-18.2,40.6-40.6,40.6S0.5,63.5,0.5,41.1S18.7,0.5,41.1,0.5s40.6,18.2,40.6,40.6z"/>
+ <linearGradient id="aigrd2" gradientUnits="userSpaceOnUse" x1="21.6367" y1="49.5361" x2="81.5742" y2="88.6888">
+ <stop offset="0" style="stop-color:#9999FF"/>
+ <stop offset="1" style="stop-color:#9968FF"/>
+ </linearGradient>
+ <path style="&st2;" d="M41.1,41.1L23.2,77.5c5.4,2.7,11.5,4.2,17.9,4.2c16,0,29.8-9.2,36.4-22.7L41.1,41.1z"/>
+ <linearGradient id="aigrd3" gradientUnits="userSpaceOnUse" x1="10.77" y1="32.3887" x2="43.6398" y2="81.2097">
+ <stop offset="0" style="stop-color:#FFFFCC"/>
+ <stop offset="0.9888" style="stop-color:#FFFFB8"/>
+ </linearGradient>
+ <path style="&st1;" d="M41.1,41.1l-40.6,0c0,16,9.2,29.8,22.7,36.4l17.9-36.4z"/>
+ </g>
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_bar_1_3.svg
@@ -0,0 +1,252 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_bar_1_3.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <g
+ id="g7033"
+ transform="matrix(8.828116e-17,1.000000,-1.000000,8.828116e-17,216.0169,-10.62993)">
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6936"
+ width="28.346457"
+ height="31.889763"
+ x="58.464565"
+ y="104.52755" />
+ <rect
+ style="fill:#c5d2c8;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7798914;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6937"
+ width="28.346457"
+ height="67.314575"
+ x="58.464565"
+ y="37.212978"
+ ry="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6938"
+ width="28.346457"
+ height="28.346460"
+ x="100.98425"
+ y="157.67715" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7854146;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6939"
+ width="28.346457"
+ height="95.669167"
+ x="100.98425"
+ y="62.007870" />
+ <rect
+ style="fill:#c5d2c8;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7805604;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6940"
+ width="28.346457"
+ height="25.249683"
+ x="100.98425"
+ y="36.758186" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6941"
+ width="28.346457"
+ height="60.236225"
+ x="143.50394"
+ y="125.78740" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6942"
+ width="28.346457"
+ height="38.976379"
+ x="143.50394"
+ y="86.811020" />
+ <rect
+ style="fill:#c5d2c8;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6943"
+ width="28.346457"
+ height="49.664383"
+ x="143.50394"
+ y="37.204720" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6935"
+ width="28.346457"
+ height="49.606300"
+ x="58.464565"
+ y="136.41731" />
+ </g>
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_bubble_1_1.svg
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_bubble_1_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="path6611"
+ sodipodi:cx="92.862892"
+ sodipodi:cy="85.776276"
+ sodipodi:rx="27.311714"
+ sodipodi:ry="27.311714"
+ d="M 93.016367,58.464993 A 27.311714,27.311714 0 1 1 92.862912,58.464561"
+ sodipodi:start="4.7180084"
+ sodipodi:end="10.995575"
+ sodipodi:open="true"
+ transform="translate(1.165789e-5,-21.25984)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:3.6828350;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="path6612"
+ sodipodi:cx="92.862892"
+ sodipodi:cy="85.776276"
+ sodipodi:rx="27.311714"
+ sodipodi:ry="27.311714"
+ d="M 93.016367,58.464993 A 27.311714,27.311714 0 1 1 92.862912,58.464561"
+ sodipodi:start="4.7180084"
+ sodipodi:end="10.995575"
+ sodipodi:open="true"
+ transform="matrix(0.481057,0.000000,0.000000,0.481057,110.4962,48.05631)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:3.1816833;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="path6613"
+ sodipodi:cx="92.862892"
+ sodipodi:cy="85.776276"
+ sodipodi:rx="27.311714"
+ sodipodi:ry="27.311714"
+ d="M 93.016367,58.464993 A 27.311714,27.311714 0 1 1 92.862912,58.464561"
+ sodipodi:start="4.7180084"
+ sodipodi:end="10.995575"
+ sodipodi:open="true"
+ transform="matrix(0.556829,0.000000,0.000000,0.556829,19.89431,80.53325)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7525421;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="path6614"
+ sodipodi:cx="92.862892"
+ sodipodi:cy="85.776276"
+ sodipodi:rx="27.311714"
+ sodipodi:ry="27.311714"
+ d="M 93.016367,58.464993 A 27.311714,27.311714 0 1 1 92.862912,58.464561"
+ sodipodi:start="4.7180084"
+ sodipodi:end="10.995575"
+ sodipodi:open="true"
+ transform="matrix(1.010905,0.000000,0.000000,1.010905,45.34817,59.59866)" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/surface.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char * surface_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggg..........gg",
+"ggggg.g.ggggggg.gg",
+"gggg.gg.ggggggg.gg",
+"ggg.ggg.....dgg.gg",
+"gg.ggg.bbb.b..g.gg",
+"g.ggg.bbb.bbb...gg",
+"g.gg.........gg.gg",
+"g.g.#bb.bbb.ggg.gg",
+"g.g.##b.b##.....gg",
+"g..###.###.gggg.gg",
+"g..###.###.ggg.ggg",
+"g.........ggg.gggg",
+"g.gggggggggg.ggggg",
+"g.ggggggggg.gggggg",
+"g..........ggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/chart_line_2_2.svg
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/gnome/head/gnumeric/src/cut-n-paste-code/goffice/pixmaps"
+ sodipodi:docname="chart_line_2_2.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 113.71875 29.531250 L 111.12500 35.406250 L 70.375000 126.96875 L 30.000000 108.31250 L 30.000000 118.06250 L 70.750000 136.90625 L 74.812500 138.78125 L 76.656250 134.68750 L 116.59375 44.906250 L 154.68750 82.875000 L 157.53125 85.687500 L 160.62500 83.156250 L 200.37500 50.781250 L 200.37500 40.750000 C 200.37500 40.323253 200.26421 39.916928 200.21875 39.500000 L 158.12500 73.781250 L 118.28125 34.062500 L 113.71875 29.531250 z "
+ id="path4680" />
+ <path
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 198.75000 94.593750 L 156.81250 144.87500 L 117.37500 121.96875 L 114.28125 120.15625 L 111.87500 122.84375 L 72.343750 166.90625 L 31.312500 128.06250 L 30.000000 129.43750 L 30.000000 139.03125 L 69.562500 176.46875 L 72.875000 179.59375 L 75.906250 176.21875 L 116.06250 131.43750 L 155.59375 154.40625 L 158.81250 156.31250 L 161.21875 153.43750 L 200.37500 106.46875 L 200.37500 95.968750 L 198.75000 94.593750 z "
+ id="path5302" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6060"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="26.574797"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6103"
+ width="21.259842"
+ height="21.259842"
+ x="147.04724"
+ y="69.094482"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6104"
+ width="21.259842"
+ height="21.259842"
+ x="189.56693"
+ y="37.204720"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6105"
+ width="21.259842"
+ height="21.259842"
+ x="60.236221"
+ y="124.01574"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6106"
+ width="21.259842"
+ height="21.259842"
+ x="19.488190"
+ y="102.38581"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6107"
+ width="21.259842"
+ height="21.259842"
+ x="19.488190"
+ y="125.78740"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6108"
+ width="21.259842"
+ height="21.259842"
+ x="62.007874"
+ y="161.22046"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6109"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="115.15748"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6110"
+ width="21.259842"
+ height="21.259842"
+ x="147.04724"
+ y="136.41731"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6111"
+ width="21.259842"
+ height="21.259842"
+ x="189.56693"
+ y="90.354324"
+ rx="0.0000000" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_pie_2_1.svg
@@ -0,0 +1,220 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_pie_2_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="73.724767"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="1"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="57.631955"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ sodipodi:type="arc"
+ style="font-size:12.000000;fill:#c5d2c8;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.74652702;stroke-opacity:1.0000000;"
+ id="path6489"
+ d="M 28.807579,46.456982 A 15.457470,15.457470 0 0 1 35.108841,16.884708 L 35.108826,32.342178 z"
+ sodipodi:cx="35.108826"
+ sodipodi:cy="32.342178"
+ sodipodi:rx="15.457470"
+ sodipodi:ry="15.457470"
+ transform="matrix(3.782506,0.000000,0.000000,3.782506,-25.07554,-16.75996)"
+ sodipodi:start="1.9906760"
+ sodipodi:end="4.7123900" />
+ <path
+ sodipodi:type="arc"
+ style="font-size:12.000000;fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.74652702;stroke-opacity:1.0000000;"
+ id="path6490"
+ d="M 50.566090,32.421815 A 15.457470,15.457470 0 0 1 28.760584,46.435907 L 35.108826,32.342178 z"
+ sodipodi:cx="35.108826"
+ sodipodi:cy="32.342178"
+ sodipodi:rx="15.457470"
+ sodipodi:ry="15.457470"
+ transform="matrix(3.782506,0.000000,0.000000,3.782506,-13.44555,-8.376378)"
+ sodipodi:start="0.0051520000"
+ sodipodi:end="1.9940080" />
+ <path
+ sodipodi:type="arc"
+ style="font-size:12.000000;fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.74652702;stroke-opacity:1.0000000;"
+ id="path6491"
+ d="M 35.062840,16.884777 A 15.457470,15.457470 0 0 1 50.565268,32.520394 L 35.108826,32.342178 z"
+ sodipodi:cx="35.108826"
+ sodipodi:cy="32.342178"
+ sodipodi:rx="15.457470"
+ sodipodi:ry="15.457470"
+ transform="matrix(3.782506,0.000000,0.000000,3.782506,-12.09422,-21.35024)"
+ sodipodi:start="4.7094140"
+ sodipodi:end="6.2947150" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_pie_1_1.svg
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_pie_1_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="73.724767"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="1"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="57.631955"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="font-size:12.000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.6243682;"
+ d="M 155.04923,121.56082 L 167.34590,126.47962 M 82.006367,132.87401 L 71.185042,141.48186 M 115.20759,79.260101 L 115.20759,50.731431 M 171.28142,106.55901 C 171.28142,137.51168 146.16047,162.63265 115.20780,162.63265 C 84.255359,162.63265 59.134179,137.51168 59.134179,106.55901 C 59.134179,75.606355 84.255359,50.485609 115.20780,50.485609 C 146.16047,50.485609 171.28142,75.606355 171.28142,106.55901 z M 158.00102,106.55901 C 158.00102,130.18080 138.82981,149.35224 115.20802,149.35224 C 91.586240,149.35224 72.415013,130.18080 72.415013,106.55901 C 72.415013,82.937454 91.586240,63.766227 115.20802,63.766227 C 138.82981,63.766227 158.00102,82.937454 158.00102,106.55901 z "
+ id="path6470" />
+ <path
+ style="font-size:12.000000;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.6243682;"
+ d="M 115.20792,63.766340 L 115.20792,79.014392 M 142.75266,106.80495 L 158.00072,106.80517 M 104.63264,131.39853 L 97.992427,146.40077 M 158.00072,106.55913 C 158.00072,130.18091 138.82970,149.35192 115.20792,149.35192 C 91.586353,149.35192 72.414910,130.18091 72.414910,106.55913 C 72.414910,82.937566 91.586353,63.766340 115.20792,63.766340 C 138.82970,63.766340 158.00072,82.937566 158.00072,106.55913 z M 142.75266,106.55913 C 142.75266,121.76386 130.41266,134.10387 115.20792,134.10387 C 100.00318,134.10387 87.662962,121.76386 87.662962,106.55913 C 87.662962,91.354393 100.00318,79.014392 115.20792,79.014392 C 130.41266,79.014392 142.75266,91.354393 142.75266,106.55913 z "
+ id="path149" />
+ <path
+ sodipodi:type="arc"
+ style="font-size:12.000000;fill:#c5d2c8;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.74652702;stroke-opacity:1.0000000;"
+ id="path113"
+ d="M 28.807579,46.456982 A 15.457470,15.457470 0 0 1 35.108841,16.884708 L 35.108826,32.342178 z"
+ sodipodi:cx="35.108826"
+ sodipodi:cy="32.342178"
+ sodipodi:rx="15.457470"
+ sodipodi:ry="15.457470"
+ transform="matrix(3.626499,0.000000,0.000000,3.626499,-12.11427,-10.72977)"
+ sodipodi:start="1.9906760"
+ sodipodi:end="4.7123900" />
+ <path
+ sodipodi:type="arc"
+ style="font-size:12.000000;fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.74652702;stroke-opacity:1.0000000;"
+ id="path114"
+ d="M 50.566090,32.421815 A 15.457470,15.457470 0 0 1 28.760584,46.435907 L 35.108826,32.342178 z"
+ sodipodi:cx="35.108826"
+ sodipodi:cy="32.342178"
+ sodipodi:rx="15.457470"
+ sodipodi:ry="15.457470"
+ transform="matrix(3.626499,0.000000,0.000000,3.626499,-12.11427,-10.72977)"
+ sodipodi:start="0.0051520000"
+ sodipodi:end="1.9940080" />
+ <path
+ sodipodi:type="arc"
+ style="font-size:12.000000;fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.74652702;stroke-opacity:1.0000000;"
+ id="path115"
+ d="M 35.062840,16.884777 A 15.457470,15.457470 0 0 1 50.565268,32.520394 L 35.108826,32.342178 z"
+ sodipodi:cx="35.108826"
+ sodipodi:cy="32.342178"
+ sodipodi:rx="15.457470"
+ sodipodi:ry="15.457470"
+ transform="matrix(3.626499,0.000000,0.000000,3.626499,-12.11427,-10.72977)"
+ sodipodi:start="4.7094140"
+ sodipodi:end="6.2947150" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_scatter_3_1.svg
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_scatter_3_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#83a67f;stroke-width:8.8582677;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 37.204724,86.811018 L 83.267717,47.834640 L 122.24409,97.440939 L 175.39370,74.420392 L 111.61417,171.85039"
+ id="path4680"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#d1940c;stroke-width:8.8582677;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 125.78740,42.994894 L 51.377953,157.67716 L 108.07087,136.41732 L 157.81512,150.59055 L 193.11024,129.33070"
+ id="path5302"
+ sodipodi:nodetypes="ccccc" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6150"
+ width="21.259842"
+ height="21.259842"
+ x="19.488190"
+ y="79.724403"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6602"
+ width="21.259842"
+ height="21.259842"
+ x="72.600563"
+ y="37.204720"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6603"
+ width="21.259842"
+ height="21.259842"
+ x="111.61417"
+ y="86.811020"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6604"
+ width="21.259842"
+ height="21.259842"
+ x="168.30708"
+ y="62.007870"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6605"
+ width="21.259842"
+ height="21.259842"
+ x="93.948006"
+ y="168.30708"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6606"
+ width="21.259842"
+ height="21.259842"
+ x="147.04724"
+ y="139.96062"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6607"
+ width="21.259842"
+ height="21.259842"
+ x="189.56693"
+ y="115.15748"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6608"
+ width="21.259842"
+ height="21.259842"
+ x="40.748032"
+ y="147.04724"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6609"
+ width="21.259842"
+ height="21.259842"
+ x="100.98425"
+ y="125.78740"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6610"
+ width="21.259842"
+ height="21.259842"
+ x="109.84252"
+ y="31.889755"
+ rx="0.0000000" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/bar.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char const * const bar_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"g.gggggggggggggggg",
+"g........ggggggggg",
+"g.bbbbbb.ggggggggg",
+"g..........ggggggg",
+"g.########.ggggggg",
+"g...............gg",
+"g.gggggggggggggggg",
+"g...............gg",
+"g.bbbbbbbbbbbbb.gg",
+"g............ggggg",
+"g.##########.ggggg",
+"g............ggggg",
+"g.gggggggggggggggg",
+"g................g",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/area.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char * area_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"g.gggggggggggggggg",
+"g.ggggggggg.gggggg",
+"g..ggggggg.c.ggggg",
+"g.c.ggggg.cbc.gggg",
+"g.bc.ggg.cbcbc.ggg",
+"g.cbc.g.cbcbcbc.gg",
+"g.bcbc.cbcbc.cbc.g",
+"g.cbcbcbcbc.#.cbcg",
+"g..cbcbcbc.###.cbg",
+"g.#..bcbc.#####.cg",
+"g.###..c.#######.g",
+"g.#####.#########g",
+"g.###############g",
+"g................g",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/chart_scatter_1_1.svg
@@ -0,0 +1,264 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_scatter_1_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6060"
+ width="21.259842"
+ height="21.259842"
+ x="62.007874"
+ y="143.50394"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6146"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="30.118105"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6147"
+ width="21.259842"
+ height="21.259842"
+ x="147.04724"
+ y="93.897629"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6148"
+ width="21.259842"
+ height="21.259842"
+ x="189.56693"
+ y="33.661411"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6149"
+ width="21.259842"
+ height="21.259842"
+ x="19.488190"
+ y="157.67715"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6150"
+ width="21.259842"
+ height="21.259842"
+ x="19.488190"
+ y="93.897629"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6151"
+ width="21.259842"
+ height="21.259842"
+ x="62.007874"
+ y="51.377949"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6152"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="154.13385"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6153"
+ width="21.259842"
+ height="21.259842"
+ x="147.04724"
+ y="117.38901"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6154"
+ width="21.259842"
+ height="21.259842"
+ x="189.56693"
+ y="139.96062"
+ rx="0.0000000" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/doughnut.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char *untitled doughnut_icon = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"ggggggg....ggggggg",
+"ggggg..gf.#..ggggg",
+"gggg.fgfg.###.gggg",
+"gggg.gf....##.gggg",
+"ggg.gf.dggd.##.ggg",
+"ggg.fg.gggg.##.ggg",
+"ggg.gf.gggg....ggg",
+"ggg.f..dggd.bc.ggg",
+"gggg.bc....bc.gggg",
+"gggg..bcbcbcb.gggg",
+"ggggg..bcbc..ggggg",
+"ggggggg....ggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/chart_ring_1_1.svg
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_ring_1_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="57.631955"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#83a67f;stroke-width:6.3750000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M -18.851812,33.928311 C -4.0899124,33.701211 8.4008876,48.463111 2.2689876,64.814711"
+ id="path6293"
+ sodipodi:nodetypes="cc"
+ transform="matrix(2.189392,0.000000,0.000000,2.189392,156.8993,-17.98082)" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#83a67f;stroke-width:6.6250000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M -18.965322,40.514381 C -8.4049224,40.627881 -2.6137224,49.257881 -2.8409224,56.752481"
+ id="path6294"
+ sodipodi:nodetypes="cc"
+ transform="matrix(2.189392,0.000000,0.000000,2.189392,156.8993,-17.98082)" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#d1940c;stroke-width:6.1250010;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M 2.1531476,65.040338 C -4.3549724,82.018432 -26.394892,84.085088 -36.867860,70.929045"
+ id="path6295"
+ sodipodi:nodetypes="cc"
+ transform="matrix(2.189392,0.000000,0.000000,2.189392,156.8993,-17.98082)" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#d1940c;stroke-width:6.7500010;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M -2.8432824,56.864535 C -2.9923624,67.029434 -12.995672,76.136349 -25.399002,71.837511"
+ id="path6296"
+ sodipodi:nodetypes="cc"
+ transform="matrix(2.189392,0.000000,0.000000,2.189392,156.8993,-17.98082)" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#c5d2c8;stroke-width:6.5000010;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M -37.249786,70.718022 C -48.413587,56.128328 -38.204450,33.667577 -19.267192,33.910796"
+ id="path6297"
+ sodipodi:nodetypes="cc"
+ transform="matrix(2.189392,0.000000,0.000000,2.189392,156.8993,-17.98082)" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#c5d2c8;stroke-width:6.5000010;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ d="M -19.194882,40.626460 C -26.423212,40.939680 -32.683892,45.210317 -34.649442,52.883920 C -36.594531,60.591196 -33.050873,68.536437 -25.512522,71.610460"
+ id="path6298"
+ sodipodi:nodetypes="csc"
+ transform="matrix(2.189392,0.000000,0.000000,2.189392,156.8993,-17.98082)" />
+ <path
+ style="font-size:12.000000;fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:0.80919887;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M -0.56981237,63.679111 L 5.1077876,65.950211 M -34.295014,68.902611 L -39.291414,72.877011 M -18.965412,44.148111 L -18.965412,30.975911 M 6.9248876,56.752511 C 6.9248876,71.043911 -4.6739124,82.642711 -18.965312,82.642711 C -33.256614,82.642711 -44.855514,71.043911 -44.855514,56.752511 C -44.855514,42.461111 -33.256614,30.862411 -18.965312,30.862411 C -4.6739124,30.862411 6.9248876,42.461111 6.9248876,56.752511 z M 0.79308763,56.752511 C 0.79308763,67.659111 -8.0586124,76.510911 -18.965212,76.510911 C -29.871814,76.510911 -38.723514,67.659111 -38.723514,56.752511 C -38.723514,45.846011 -29.871814,36.994311 -18.965212,36.994311 C -8.0586124,36.994311 0.79308763,45.846011 0.79308763,56.752511 z "
+ id="path157"
+ transform="matrix(2.189392,0.000000,0.000000,2.189392,156.8993,-17.98082)" />
+ <path
+ style="font-size:12.000000;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.80919887;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M -18.965412,36.994211 L -18.965412,44.034511 M -6.2475124,56.865911 L 0.79278763,56.866011 M -23.848212,68.221211 L -26.914112,75.148011 M 0.79278763,56.752411 C 0.79278763,67.659011 -8.0588124,76.510611 -18.965412,76.510611 C -29.871914,76.510611 -38.723714,67.659011 -38.723714,56.752411 C -38.723714,45.845911 -29.871914,36.994211 -18.965412,36.994211 C -8.0588124,36.994211 0.79278763,45.845911 0.79278763,56.752411 z M -6.2475124,56.752411 C -6.2475124,63.772711 -11.945112,69.470311 -18.965412,69.470311 C -25.985712,69.470311 -31.683414,63.772711 -31.683414,56.752411 C -31.683414,49.732111 -25.985712,44.034511 -18.965412,44.034511 C -11.945112,44.034511 -6.2475124,49.732111 -6.2475124,56.752411 z "
+ id="path6300"
+ transform="matrix(2.189392,0.000000,0.000000,2.189392,156.8993,-17.98082)" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_bar_1_2.svg
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_bar_1_2.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <g
+ id="g6947"
+ transform="matrix(0.000000,1.000000,-1.000000,0.000000,216.9027,-10.63681)">
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6938"
+ width="28.346457"
+ height="28.346460"
+ x="100.98425"
+ y="157.67715" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7854146;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6939"
+ width="28.346457"
+ height="95.669167"
+ x="100.98425"
+ y="62.007870" />
+ </g>
+ <g
+ id="g6950"
+ transform="matrix(-8.492014e-17,1.000000,-1.000000,-8.492014e-17,216.9027,-10.62993)">
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6941"
+ width="28.346457"
+ height="60.236225"
+ x="143.50394"
+ y="125.78740" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6942"
+ width="28.346457"
+ height="38.976379"
+ x="143.50394"
+ y="86.811020" />
+ </g>
+ <g
+ id="g6944"
+ transform="matrix(1.444699e-17,1.000000,-1.000000,1.444699e-17,216.9027,-10.62993)">
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6936"
+ width="28.346457"
+ height="31.889763"
+ x="58.464565"
+ y="104.52755" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6935"
+ width="28.346457"
+ height="49.606300"
+ x="58.464565"
+ y="136.41731" />
+ </g>
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_line_1_3.svg
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/gnome/head/gnumeric/src/cut-n-paste-code/goffice/pixmaps"
+ sodipodi:docname="chart_line_1_3.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="57.856204"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 30.125000,39.875000 L 30.125000,48.718750 L 200.18750,48.718750 L 200.18750,39.875000 L 30.125000,39.875000 z "
+ id="path4680" />
+ <path
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 157.59375 55.937500 L 154.06250 61.656250 L 112.00000 129.59375 L 73.375000 151.93750 L 30.000000 98.250000 L 30.000000 112.34375 L 68.875000 160.46875 L 71.250000 163.40625 L 74.531250 161.50000 L 117.43750 136.71875 L 118.37500 136.15625 L 118.96875 135.21875 L 158.06250 72.031250 L 200.12500 131.87500 L 200.37500 131.68750 L 200.37500 116.81250 L 161.43750 61.437500 L 157.59375 55.937500 z "
+ id="path5302" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_line_2_1.svg
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/gnome/head/gnumeric/src/cut-n-paste-code/goffice/pixmaps"
+ sodipodi:docname="chart_line_2_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 114.09375 31.125000 L 111.06250 39.187500 L 69.187500 150.62500 L 30.000000 164.31250 L 30.000000 172.21875 C 30.000000 172.70324 30.097823 173.15330 30.156250 173.62500 L 74.062500 158.31250 L 76.031250 157.62500 L 76.750000 155.68750 L 116.31250 50.375000 L 154.12500 107.00000 L 157.62500 112.21875 L 161.37500 107.15625 L 200.37500 54.375000 L 200.37500 40.750000 C 200.37500 40.408602 200.27922 40.085212 200.25000 39.750000 L 200.18750 39.687500 L 158.00000 96.812500 L 118.90625 38.281250 L 114.09375 31.125000 z "
+ id="path4680" />
+ <path
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:8.8582678;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ d="M 74.062500 53.937500 L 69.343750 59.000000 L 30.000000 101.37500 L 30.000000 114.43750 L 71.156250 70.093750 L 111.12500 166.46875 L 113.53125 172.31250 L 118.18750 168.03125 L 158.06250 131.46875 L 200.37500 160.71875 L 200.37500 149.96875 L 160.18750 122.15625 L 157.28125 120.12500 L 154.68750 122.53125 L 116.87500 157.21875 L 76.687500 60.312500 L 74.062500 53.937500 z "
+ id="path5302" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6060"
+ width="21.259842"
+ height="21.259842"
+ x="62.007874"
+ y="143.50394"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6146"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="30.118105"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6147"
+ width="21.259842"
+ height="21.259842"
+ x="147.04724"
+ y="93.897629"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6148"
+ width="21.259842"
+ height="21.259842"
+ x="189.56693"
+ y="33.661411"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6149"
+ width="21.259842"
+ height="21.259842"
+ x="19.488190"
+ y="157.67715"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6150"
+ width="21.259842"
+ height="21.259842"
+ x="19.488190"
+ y="93.897629"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6151"
+ width="21.259842"
+ height="21.259842"
+ x="62.007874"
+ y="51.377949"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6152"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="154.13385"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6153"
+ width="21.259842"
+ height="21.259842"
+ x="147.04724"
+ y="117.38901"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6154"
+ width="21.259842"
+ height="21.259842"
+ x="189.56693"
+ y="139.96062"
+ rx="0.0000000" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_column_1_1.svg
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_column_1_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7825561;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6928"
+ width="21.259842"
+ height="71.741028"
+ x="51.377953"
+ y="115.15747" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6929"
+ width="21.259842"
+ height="111.23093"
+ x="79.724411"
+ y="75.678513" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6930"
+ width="21.259842"
+ height="49.606297"
+ x="129.46867"
+ y="136.41731" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6931"
+ width="21.259842"
+ height="141.73228"
+ x="157.81512"
+ y="44.291332" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_column_1_3.svg
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_column_1_3.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6936"
+ width="28.346457"
+ height="31.889763"
+ x="58.464565"
+ y="104.52755" />
+ <rect
+ style="fill:#c5d2c8;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7798914;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6937"
+ width="28.346457"
+ height="67.314575"
+ x="58.464565"
+ y="37.212978"
+ ry="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6938"
+ width="28.346457"
+ height="28.346460"
+ x="100.98425"
+ y="157.67715" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7854146;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6939"
+ width="28.346457"
+ height="95.669167"
+ x="100.98425"
+ y="62.007870" />
+ <rect
+ style="fill:#c5d2c8;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7805604;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6940"
+ width="28.346457"
+ height="25.249683"
+ x="100.98425"
+ y="36.758186" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6941"
+ width="28.346457"
+ height="60.236225"
+ x="143.50394"
+ y="125.78740" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6942"
+ width="28.346457"
+ height="38.976379"
+ x="143.50394"
+ y="86.811020" />
+ <rect
+ style="fill:#c5d2c8;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ id="rect6943"
+ width="28.346457"
+ height="49.664383"
+ x="143.50394"
+ y="37.204720" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ id="rect6935"
+ width="28.346457"
+ height="49.606300"
+ x="58.464565"
+ y="136.41731" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/bubble.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char const * const bubble_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"gggg....gggggggggg",
+"ggg.cbcb.ggggggggg",
+"ggg.bcbc.gg..ggggg",
+"ggg.cbcb.g.##.gggg",
+"ggg.bcbc.g.##.gggg",
+"gggg....ggg..ggggg",
+"gggggggggggggggggg",
+"ggggggggggg..ggggg",
+"ggggg..ggg.cb.gggg",
+"gggg.##.gg.bc.gggg",
+"gggg.##.ggg..ggggg",
+"ggggg..ggggggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/chart_scatter_3_2.svg
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_scatter_3_2.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#83a67f;stroke-width:8.8582677;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 37.204724,86.811018 L 83.267717,47.834640 L 122.24409,97.440939 L 175.39370,74.420392 L 111.61417,171.85039"
+ id="path4680"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:none;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#d1940c;stroke-width:8.8582677;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 125.78740,42.994894 L 51.377953,157.67716 L 108.07087,136.41732 L 157.81512,150.59055 L 193.11024,129.33070"
+ id="path5302"
+ sodipodi:nodetypes="ccccc" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/stock.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char const * const boxplot_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggg.ggggg",
+"g.gggggggggg...ggg",
+"g.gggggggggg.ggggg",
+"g.gg.ggg.ggg.ggggg",
+"g.gg.ggg...ggggggg",
+"g.gg...g.ggg...ggg",
+"g.gg.ggg.ggg.a.ggg",
+"g.gggggggggg.a.ggg",
+"g.gg...ggggg.a.ggg",
+"g.gg.a.ggggg.a.ggg",
+"g.gg.a.g...g.a.ggg",
+"g.gg.a.g.a.g.a.ggg",
+"g.gg.a.g.a.g.a.ggg",
+"g.gg.a.g.a.g.a.ggg",
+"g................g",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/pixmaps/chart_bar_1_1.svg
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/gnome/head/gnumeric/src/cut-n-paste-code/goffice/pixmaps"
+ sodipodi:docname="chart_bar_1_1.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <g
+ id="g6989"
+ transform="matrix(8.538092e-18,1.000000,-1.000000,8.538092e-18,216.1417,-10.76789)">
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7825561;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6928"
+ width="21.259842"
+ height="71.741028"
+ x="51.377953"
+ y="115.15747" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6929"
+ width="21.259842"
+ height="111.23093"
+ x="79.724411"
+ y="75.678513" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6930"
+ width="21.259842"
+ height="49.606297"
+ x="129.46867"
+ y="136.41731" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:#000000;stroke-width:1.7716535;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect6931"
+ width="21.259842"
+ height="141.73228"
+ x="157.81512"
+ y="44.291332" />
+ </g>
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/chart_radar_1_2.svg
@@ -0,0 +1,313 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.39"
+ width="64.000000mm"
+ height="60.000000mm"
+ sodipodi:docbase="/home/manu/Desktop/Chart-icons"
+ sodipodi:docname="chart_radar_1_2.svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ id="linearGradient6655">
+ <stop
+ style="stop-color:#ffda86;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6656" />
+ <stop
+ style="stop-color:#c68c0b;stop-opacity:1.0000000;"
+ offset="0.81471545"
+ id="stop6657" />
+ <stop
+ style="stop-color:#664805;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop6658" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6649">
+ <stop
+ style="stop-color:#b5e6b0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6650" />
+ <stop
+ style="stop-color:#6c8969;stop-opacity:1.0000000;"
+ offset="0.81471545"
+ id="stop6652" />
+ <stop
+ style="stop-color:#3b4b39;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop6651" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient4044">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="0.0000000"
+ id="stop4045" />
+ <stop
+ style="stop-color:#363636;stop-opacity:0.18750000;"
+ offset="1.0000000"
+ id="stop4046" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3418">
+ <stop
+ style="stop-color:#f7f7f7;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3419" />
+ <stop
+ style="stop-color:#dedede;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3420" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient2791">
+ <stop
+ style="stop-color:#fbfbfb;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2792" />
+ <stop
+ style="stop-color:#e9e9e9;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2793" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2791"
+ id="linearGradient2794"
+ x1="0.49693251"
+ y1="0.058441557"
+ x2="0.49079755"
+ y2="0.96103895" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3418"
+ id="linearGradient3417"
+ x1="0.53846157"
+ y1="0.012987013"
+ x2="0.53254437"
+ y2="0.98051947" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient4044"
+ id="linearGradient4043"
+ x1="0.49689442"
+ y1="0.012987013"
+ x2="0.49689442"
+ y2="0.99350649" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0000000"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.4593316"
+ inkscape:cx="85.039367"
+ inkscape:cy="82.694721"
+ inkscape:window-width="1270"
+ inkscape:window-height="971"
+ showborder="true"
+ showgrid="true"
+ gridspacingy="1.0000000mm"
+ gridspacingx="1.0000000mm"
+ gridoriginy="0.50000000mm"
+ gridoriginx="0.50000000mm"
+ inkscape:grid-points="true"
+ inkscape:grid-bbox="true"
+ gridtolerance="1.0000000px"
+ inkscape:guide-bbox="true"
+ inkscape:guide-points="true"
+ guidetolerance="1.0000000mm"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ showguides="true">
+ <sodipodi:guide
+ orientation="vertical"
+ position="23.994627"
+ id="guide5924" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="58.080453"
+ id="guide5925" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="92.166278"
+ id="guide5926" />
+ <sodipodi:guide
+ orientation="vertical"
+ position="126.25210"
+ id="guide5927" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect4047"
+ width="184.25197"
+ height="177.15660"
+ x="23.031496"
+ y="23.040251"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient4043);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3421"
+ width="177.16536"
+ height="173.62204"
+ x="26.574802"
+ y="23.031490"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient2794);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect2169"
+ width="170.38249"
+ height="159.80026"
+ x="30.118111"
+ y="26.554472"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:url(#linearGradient3417);fill-opacity:0.74901998;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect3416"
+ width="163.39728"
+ height="149.19994"
+ x="31.955462"
+ y="31.699234"
+ rx="17.594519"
+ ry="14.164467" />
+ <rect
+ style="fill:none;fill-opacity:0.74901998;fill-rule:evenodd;stroke:#000000;stroke-width:3.5262673;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;"
+ id="rect5928"
+ width="170.38249"
+ height="159.80026"
+ x="29.993284"
+ y="26.574797"
+ rx="17.594519"
+ ry="14.164467" />
+ <g
+ id="g576"
+ style="stroke:#000000;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000;stroke-dasharray:none;"
+ transform="matrix(3.226160,0.000000,0.000000,3.226160,12.70631,11.39544)">
+ <path
+ style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke-linecap:butt;stroke-linejoin:miter;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 32.000000,8.0000000 L 32.000000,30.000000 L 54.825400,24.583600"
+ id="path571" />
+ <path
+ style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke-linecap:butt;stroke-linejoin:miter;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 46.106800,51.416400 L 32.000000,30.000000 L 17.893200,51.416400"
+ id="path572" />
+ <path
+ style="fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke-linecap:butt;stroke-linejoin:miter;stroke-width:1.0983048;stroke-miterlimit:4.0000000;stroke-dasharray:none;"
+ d="M 9.1746400,24.583600 L 32.000000,30.000000"
+ id="path573" />
+ </g>
+ <path
+ style="font-size:12.000000;fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#83a67f;stroke-width:8.8582677;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000;"
+ d="M 114.88784,36.358436 L 147.67821,99.179587 L 153.71628,164.07348 L 82.894321,156.84558 L 57.340880,93.912237 L 114.88784,36.358436 z "
+ id="path575"
+ sodipodi:nodetypes="cccccc" />
+ <path
+ style="font-size:12.000000;fill:none;fill-opacity:0.75000000;fill-rule:evenodd;stroke:#d1940c;stroke-width:8.8582677;stroke-miterlimit:4.0000000;stroke-dasharray:none;stroke-opacity:1.0000000;"
+ d="M 116.07507,65.055195 L 116.73352,65.055195 L 183.08564,90.891263 L 183.08564,90.891263 L 142.65992,147.61587 L 101.57832,129.72681 L 70.906555,96.272817 L 116.07507,65.055195 z "
+ id="path574"
+ sodipodi:nodetypes="cccccc" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6730"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="54.921253"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6060"
+ width="21.259842"
+ height="21.259842"
+ x="104.52756"
+ y="23.031490"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6726"
+ width="21.259842"
+ height="21.259842"
+ x="47.834644"
+ y="86.811020"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6733"
+ width="21.259842"
+ height="21.259842"
+ x="59.944958"
+ y="86.811020"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6727"
+ width="21.259842"
+ height="21.259842"
+ x="134.52084"
+ y="92.125977"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6728"
+ width="21.259842"
+ height="21.259842"
+ x="143.50394"
+ y="154.13385"
+ rx="0.0000000" />
+ <rect
+ style="fill:#83a67f;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6729"
+ width="21.259842"
+ height="21.259842"
+ x="72.600563"
+ y="147.04724"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6731"
+ width="21.259842"
+ height="21.259842"
+ x="170.54958"
+ y="79.724403"
+ rx="0.0000000" />
+ <rect
+ style="fill:#d1940c;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:3.0000000pt;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;"
+ id="rect6732"
+ width="21.259842"
+ height="21.259842"
+ x="130.91183"
+ y="136.41731"
+ rx="0.0000000" />
+</svg>
--- /dev/null
+++ lib/goffice/pixmaps/Makefile.am
@@ -0,0 +1,52 @@
+icondir = $(gnumeric_icondir)
+icon_DATA = \
+ chart_area_1_1.png chart_area_1_2.png chart_area_1_3.png \
+ chart_bar_1_1.png chart_bar_1_2.png chart_bar_1_3.png \
+ chart_bar_2_1.png chart_bar_2_2.png chart_bar_2_3.png \
+ chart_column_1_1.png chart_column_1_2.png chart_column_1_3.png \
+ chart_column_2_1.png chart_column_2_2.png chart_column_2_3.png \
+ chart_column_3_1.png \
+ chart_cone_1_1.png chart_cone_1_2.png chart_cone_1_3.png \
+ chart_cone_2_1.png chart_cone_2_2.png chart_cone_2_3.png \
+ chart_cone_3_1.png \
+ chart_cylinder_1_1.png chart_cylinder_1_2.png chart_cylinder_1_3.png \
+ chart_cylinder_2_1.png chart_cylinder_2_2.png chart_cylinder_2_3.png \
+ chart_cylinder_3_1.png \
+ chart_pyramid_1_1.png chart_pyramid_1_2.png chart_pyramid_1_3.png \
+ chart_pyramid_2_1.png chart_pyramid_2_2.png chart_pyramid_2_3.png \
+ chart_pyramid_3_1.png \
+ chart_radar_1_1.png chart_radar_1_2.png chart_radar_1_3.png \
+ chart_stock_1_1.png chart_stock_1_2.png \
+ chart_stock_2_1.png chart_stock_2_2.png \
+ chart_line_1_1.png chart_line_1_2.png chart_line_1_3.png \
+ chart_line_2_1.png chart_line_2_2.png chart_line_2_3.png \
+ chart_line_3_1.png \
+ chart_pie_1_1.png chart_pie_1_2.png chart_pie_1_3.png \
+ chart_pie_2_1.png chart_pie_2_2.png chart_pie_2_3.png \
+ chart_ring_1_1.png chart_ring_1_2.png \
+ chart_scatter_1_1.png \
+ chart_scatter_2_1.png chart_scatter_2_2.png \
+ chart_scatter_3_1.png chart_scatter_3_2.png \
+ chart_bubble_1_1.png \
+ \
+ area.xpm \
+ bar.xpm \
+ bubble.xpm \
+ column.xpm \
+ doughnut.xpm \
+ linegraph.xpm \
+ pie.xpm \
+ radar.xpm \
+ scatter.xpm \
+ stock.xpm \
+ surface.xpm \
+ \
+ bar-none.png \
+ bar-vplus.png \
+ bar-vminus.png \
+ bar-vboth.png \
+ bar-hplus.png \
+ bar-hminus.png \
+ bar-hboth.png
+
+EXTRA_DIST = $(icon_DATA)
--- /dev/null
+++ lib/goffice/pixmaps/pie.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char const * const pie_icon [] = {
+/* width height num_colors chars_per_pixel */
+" 18 18 9 1",
+/* colors */
+". c #000000",
+"# c #ff0000",
+"a c #00ffff",
+"b c #008400",
+"c c #008484",
+"d c #848484",
+"e c #c6c6c6",
+"f c #ffff00",
+"g c None",
+/* pixels */
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"ggggggg....ggggggg",
+"ggggg..gf.#..ggggg",
+"gggg.fgfg.###.gggg",
+"gggg.gfgf.###.gggg",
+"ggg.gfgfg.####.ggg",
+"ggg.fgfgf.####.ggg",
+"ggg.gfgf.......ggg",
+"ggg.fgf.bcbcbc.ggg",
+"gggg.f.bcbcbc.gggg",
+"gggg..bcbcbcb.gggg",
+"ggggg..bcbc..ggggg",
+"ggggggg....ggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg",
+"gggggggggggggggggg"
+};
--- /dev/null
+++ lib/goffice/graph/gog-view.c
@@ -0,0 +1,636 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-view.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-renderer.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+
+/* this should be per model */
+#define PAD_HACK 4 /* pts */
+
+enum {
+ GOG_VIEW_PROP_0,
+ GOG_VIEW_PROP_PARENT,
+ GOG_VIEW_PROP_MODEL
+};
+
+static GObjectClass *parent_klass;
+
+static void
+cb_child_added (GogObject *parent, GogObject *child,
+ GogView *view)
+{
+ g_return_if_fail (view->model == parent);
+
+ gog_object_new_view (child, view);
+ gog_view_queue_resize (view);
+}
+
+static void
+cb_remove_child (GogObject *parent, GogObject *child,
+ GogView *view)
+{
+ GSList *ptr = view->children;
+ GogObjectClass const *klass;
+ GogView *tmp;
+
+ g_return_if_fail (view->model == parent);
+
+ gog_view_queue_resize (view);
+ for (; ptr != NULL ; ptr = ptr->next) {
+ tmp = GOG_VIEW (ptr->data);
+
+ g_return_if_fail (tmp != NULL);
+
+ if (tmp->model == child) {
+ g_object_unref (tmp);
+ return;
+ }
+ }
+
+ /* The object may not create a view */
+ klass = GOG_OBJECT_GET_CLASS (child);
+ if (klass->view_type != 0)
+ g_warning ("%s (%p) saw %s(%p) being removed from %s(%p) for which I didn't have a child",
+ G_OBJECT_TYPE_NAME (view), view,
+ G_OBJECT_TYPE_NAME (child), child,
+ G_OBJECT_TYPE_NAME (parent), parent);
+}
+
+static void
+cb_model_changed (GogObject *model, gboolean resized, GogView *view)
+{
+ gog_debug (0, g_warning ("model %s(%p) for view %s(%p) changed %d",
+ G_OBJECT_TYPE_NAME (model), model,
+ G_OBJECT_TYPE_NAME (view), view, resized););
+ if (resized)
+ gog_view_queue_resize (view);
+ else
+ gog_view_queue_redraw (view);
+}
+
+/* make the list of view children match the models order */
+static void
+cb_model_reordered (GogView *view)
+{
+ GSList *tmp, *new_order = NULL;
+ GSList *ptr = view->model->children;
+
+ for (; ptr != NULL ; ptr = ptr->next) {
+ tmp = view->children;
+ /* not all the views may be created yet check for NULL */
+ while (tmp != NULL && GOG_VIEW (tmp->data)->model != ptr->data)
+ tmp = tmp->next;
+ if (tmp != NULL)
+ new_order = g_slist_prepend (new_order, tmp->data);
+ }
+ g_slist_free (view->children);
+ view->children = g_slist_reverse (new_order);
+}
+
+static void
+gog_view_set_property (GObject *gobject, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogView *view = GOG_VIEW (gobject);
+ gboolean init_state = (view->renderer == NULL || view->model == NULL);
+
+ switch (param_id) {
+ case GOG_VIEW_PROP_PARENT:
+ g_return_if_fail (view->parent == NULL);
+
+ view->parent = GOG_VIEW (g_value_get_object (value));
+ if (view->parent != NULL) {
+ view->renderer = view->parent->renderer;
+ view->parent->children = g_slist_prepend (view->parent->children, view);
+ cb_model_reordered (view->parent);
+ }
+ break;
+
+ case GOG_VIEW_PROP_MODEL:
+ g_return_if_fail (view->model == NULL);
+
+ view->model = GOG_OBJECT (g_value_get_object (value));
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ /* renderer set via parent or manually */
+ if (init_state && view->renderer != NULL && view->model != NULL) {
+ GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
+ GSList *ptr = view->model->children;
+
+ for ( ;ptr != NULL ; ptr = ptr->next)
+ gog_object_new_view (ptr->data, view);
+
+ g_signal_connect_object (G_OBJECT (view->model),
+ "child_added",
+ G_CALLBACK (cb_child_added), view, 0);
+ g_signal_connect_object (G_OBJECT (view->model),
+ "child_removed",
+ G_CALLBACK (cb_remove_child), view, 0);
+ g_signal_connect_object (G_OBJECT (view->model),
+ "changed",
+ G_CALLBACK (cb_model_changed), view, 0);
+ g_signal_connect_object (G_OBJECT (view->model),
+ "children-reordered",
+ G_CALLBACK (cb_model_reordered), view, G_CONNECT_SWAPPED);
+
+ if (klass->state_init != NULL)
+ (klass->state_init) (view);
+ }
+}
+
+static void
+gog_view_finalize (GObject *obj)
+{
+ GogView *tmp, *view = GOG_VIEW (obj);
+ GSList *ptr;
+
+ if (view->parent != NULL)
+ view->parent->children = g_slist_remove (view->parent->children, view);
+
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ tmp = GOG_VIEW (ptr->data);
+ /* not really necessary, but helpful during initial deployment
+ * when not everything has a view yet */
+ if (tmp != NULL) {
+ tmp->parent = NULL; /* short circuit */
+ g_object_unref (tmp);
+ }
+ }
+ g_slist_free (view->children);
+ view->children = NULL;
+
+ (*parent_klass->finalize) (obj);
+}
+
+static void
+gog_view_size_request_real (GogView *view, GogViewRequisition *req)
+{
+ req->w = req->h = 1.;
+}
+
+static void
+gog_view_size_allocate_real (GogView *view, GogViewAllocation const *allocation)
+{
+ GSList *ptr;
+ GogView *child;
+ GogObjectPosition pos;
+ GogViewRequisition req;
+ GogViewAllocation tmp, available = *allocation, res = *allocation;
+ double const pad_h = gog_renderer_pt2r_y (view->renderer, PAD_HACK);
+ double const pad_w = gog_renderer_pt2r_x (view->renderer, PAD_HACK);
+
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+
+ pos = child->model->position;
+ if (pos & GOG_POSITION_MANUAL) {
+ /* position relative to the entire region */
+ tmp = available;
+ /* add some flags to control interpretation of manual
+ * eg abs/percentage from start/end */
+ g_warning ("manual is not supported yet");
+ } else if (pos & GOG_POSITION_COMPASS) {
+ gboolean vertical = TRUE;
+
+ /* Dead simple */
+ gog_view_size_request (child, &req);
+ if (req.h > res.h)
+ req.h = res.h;
+ if (req.w > res.w)
+ req.w = res.w;
+ tmp = res;
+
+ if (pos & GOG_POSITION_N) {
+ if (req.h > 0) {
+ res.y += req.h + pad_h;
+ res.h -= req.h + pad_h;
+ } else
+ req.h = 0;
+ tmp.h = req.h;
+ vertical = FALSE;
+ } else if (pos & GOG_POSITION_S) {
+ if (req.h > 0) {
+ res.h -= req.h + pad_h;
+ tmp.y = res.y + res.h + pad_h;
+ } else
+ req.h = 0;
+ tmp.h = req.h;
+ vertical = FALSE;
+ }
+
+ if (pos & GOG_POSITION_E) {
+ if (req.w > 0) {
+ res.w -= req.w + pad_w;
+ tmp.x = res.x + res.w + pad_w;
+ } else
+ req.w = 0;
+ tmp.w = req.w;
+ /* For NE & NW only alignment fill makes sense */
+ if (pos & (GOG_POSITION_N|GOG_POSITION_S))
+ pos = GOG_POSITION_ALIGN_FILL;
+ } else if (pos & GOG_POSITION_W) {
+ if (req.w > 0) {
+ res.x += req.w + pad_w;
+ res.w -= req.w + pad_w;
+ } else
+ req.w = 0;
+ tmp.w = req.w;
+ /* For NE & NW only alignment fill makes sense */
+ if (pos & (GOG_POSITION_N|GOG_POSITION_S))
+ pos = GOG_POSITION_ALIGN_FILL;
+ }
+
+ pos &= GOG_POSITION_ALIGNMENT;
+ if (GOG_POSITION_ALIGN_FILL != pos) {
+ if (vertical) {
+ if (GOG_POSITION_ALIGN_END == pos) {
+ if (tmp.h >= req.h)
+ tmp.y += tmp.h - req.h;
+ } else if (GOG_POSITION_ALIGN_CENTER == pos) {
+ if (tmp.h >= req.h)
+ tmp.y += (tmp.h - req.h) / 2.;
+ }
+ tmp.h = req.h;
+ } else {
+ if (GOG_POSITION_ALIGN_END == pos) {
+ if (tmp.w >= req.w)
+ tmp.x += tmp.w - req.w;
+ } else if (GOG_POSITION_ALIGN_CENTER == pos) {
+ if (tmp.w >= req.w)
+ tmp.x += (tmp.w - req.w) / 2.;
+ }
+ tmp.w = req.w;
+ }
+ }
+
+ gog_view_size_allocate (child, &tmp);
+ } else if (pos != GOG_POSITION_SPECIAL)
+ g_warning ("unexpected position %x for child %p of %p",
+ pos, child, view);
+ }
+ view->residual = res;
+}
+
+/* A simple default implementation */
+static void
+gog_view_render_real (GogView *view, GogViewAllocation const *bbox)
+{
+ GSList *ptr;
+ for (ptr = view->children ; ptr != NULL ; ptr = ptr->next)
+ gog_view_render (ptr->data, bbox);
+}
+
+static void
+gog_view_class_init (GogViewClass *view_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) view_klass;
+
+ parent_klass = g_type_class_peek_parent (view_klass);
+ gobject_klass->set_property = gog_view_set_property;
+ gobject_klass->finalize = gog_view_finalize;
+ view_klass->size_request = gog_view_size_request_real;
+ view_klass->size_allocate = gog_view_size_allocate_real;
+ view_klass->render = gog_view_render_real;
+ view_klass->clip = FALSE;
+
+ g_object_class_install_property (gobject_klass, GOG_VIEW_PROP_PARENT,
+ g_param_spec_object ("parent", "parent",
+ "the GogView parent",
+ GOG_VIEW_TYPE, G_PARAM_WRITABLE));
+ g_object_class_install_property (gobject_klass, GOG_VIEW_PROP_MODEL,
+ g_param_spec_object ("model", "model",
+ "the GogObject this view displays",
+ GOG_OBJECT_TYPE, G_PARAM_WRITABLE));
+}
+
+static void
+gog_view_init (GogView *view)
+{
+ view->allocation_valid = FALSE;
+ view->child_allocations_valid = FALSE;
+ view->being_updated = FALSE;
+ view->model = NULL;
+ view->parent = NULL;
+ view->children = NULL;
+}
+
+GSF_CLASS_ABSTRACT (GogView, gog_view,
+ gog_view_class_init, gog_view_init,
+ G_TYPE_OBJECT)
+
+GogObject *
+gog_view_get_model (GogView const *view)
+{
+ return view->model;
+}
+
+/**
+ * gog_view_queue_redraw :
+ * @view : a #GogView
+ *
+ * Requests a redraw for the entire graph.
+ **/
+void
+gog_view_queue_redraw (GogView *view)
+{
+ g_return_if_fail (GOG_VIEW (view) != NULL);
+ g_return_if_fail (view->renderer != NULL);
+
+ gog_renderer_request_update (view->renderer);
+}
+
+/**
+ * gog_view_queue_resize :
+ * @view : a #GogView
+ *
+ * Flags a view to have its size renegotiated; should
+ * be called when a model for some reason has a new size request.
+ * For example, when you change the size of a legend.
+ **/
+void
+gog_view_queue_resize (GogView *view)
+{
+ g_return_if_fail (GOG_VIEW (view) != NULL);
+ g_return_if_fail (view->renderer != NULL);
+
+ gog_renderer_request_update (view->renderer);
+
+#if 0 /* optimization that breaks when child contributes to size of parent */
+ view->allocation_valid = FALSE; /* in case there is no parent */
+ if (NULL == (view = view->parent))
+ return;
+ view->allocation_valid = FALSE;
+ while (NULL != (view = view->parent) && view->child_allocations_valid)
+ view->child_allocations_valid = FALSE;
+#else
+ do
+ view->allocation_valid = FALSE; /* in case there is no parent */
+ while (NULL != (view = view->parent) && view->allocation_valid);
+#endif
+}
+
+/**
+ * gog_view_size_request :
+ * @view : a #GogView
+ * @requisition : a #GogViewRequisition.
+ *
+ * When called @requisition holds the available space and is populated with the
+ * desired size based on that input and other elements of the view or its model's
+ * state (eg the position).
+ *
+ * Remember that the size request is not necessarily the size a view will
+ * actually be allocated.
+ **/
+void
+gog_view_size_request (GogView *view, GogViewRequisition *requisition)
+{
+ GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (requisition != NULL);
+ if (klass->size_request)
+ (klass->size_request) (view, requisition);
+ else
+ requisition->w = requisition->h = 1.;
+}
+
+/**
+ * gog_view_size_allocate :
+ * @view : a #GogView
+ * @allocation: position and size to be allocated to @view
+ *
+ * Assign a size and position to a GogView. Primarilly used by containers.
+ **/
+void
+gog_view_size_allocate (GogView *view, GogViewAllocation const *allocation)
+{
+ GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
+
+ g_return_if_fail (allocation != NULL);
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (klass->size_allocate != NULL);
+ g_return_if_fail (!view->being_updated);
+
+ gog_debug (0, g_warning ("size_allocate %s %p : x = %g, y = %g w = %g, h = %g",
+ G_OBJECT_TYPE_NAME (view), view,
+ allocation->x, allocation->y, allocation->w, allocation->h););
+
+ view->being_updated = TRUE;
+ (klass->size_allocate) (view, allocation);
+ view->being_updated = FALSE;
+
+ if (&view->allocation != allocation)
+ view->allocation = *allocation;
+ view->allocation_valid = view->child_allocations_valid = TRUE;
+}
+
+gboolean
+gog_view_update_sizes (GogView *view)
+{
+ g_return_val_if_fail (GOG_VIEW (view) != NULL, TRUE);
+ g_return_val_if_fail (!view->being_updated, TRUE);
+
+ if (!view->allocation_valid)
+ gog_view_size_allocate (view, &view->allocation);
+ else if (!view->child_allocations_valid) {
+ GSList *ptr;
+
+ view->being_updated = TRUE;
+ for (ptr = view->children ; ptr != NULL ; ptr = ptr->next)
+ gog_view_update_sizes (ptr->data);
+ view->being_updated = FALSE;
+
+ view->child_allocations_valid = TRUE;
+ } else
+ return FALSE;
+ return TRUE;
+}
+
+void
+gog_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
+
+ g_return_if_fail (view->renderer != NULL);
+
+ if (view->residual.w < 0 || view->residual.h < 0)
+ return;
+
+ if (klass->clip) {
+ gog_renderer_clip_push (view->renderer, &view->allocation);
+ klass->render (view, bbox);
+ gog_renderer_clip_pop (view->renderer);
+ }
+ else
+ klass->render (view, bbox);
+}
+
+/**
+ * gog_view_info_at_point :
+ * @view : a #GogView
+ * @x :
+ * @y :
+ * @cur_selection : If @cur_selection is the object @x, at y, and it could create
+ * a child there, create it
+ * @obj : If non-NULL store the object @x, at y
+ * @name : store the name of the most derived object even if it does not yet exist
+ * caller is responsible for freeing the string (ignored in NULL)
+ *
+ * Returns TRUE if an object is found
+ **/
+gboolean
+gog_view_info_at_point (GogView *view, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name)
+{
+ GSList *ptr;
+ GogViewClass *klass = GOG_VIEW_GET_CLASS (view);
+
+ g_return_val_if_fail (klass != NULL, FALSE);
+ g_return_val_if_fail (view->allocation_valid, FALSE);
+ g_return_val_if_fail (view->child_allocations_valid, FALSE);
+
+ if (x < view->allocation.x ||
+ x >= (view->allocation.x + view->allocation.w) ||
+ y < view->allocation.y ||
+ y >= (view->allocation.y + view->allocation.h))
+ return FALSE;
+
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next)
+ if (gog_view_info_at_point (ptr->data, x, y, cur_selection, obj, name))
+ return TRUE;
+
+ if (klass->info_at_point != NULL)
+ return (klass->info_at_point) (view, x, y, cur_selection, obj, name);
+
+ if (obj != NULL)
+ *obj = view->model;
+ if (name != NULL)
+ *name = g_strdup (gog_object_get_name (view->model));
+
+ return TRUE;
+}
+
+/**
+ * gog_view_size_child_request :
+ * @view : #GogView
+ * @avail : the amount of space available in total
+ * @req : holds the amount of space for the parent, and is expanded with the
+ * needs of the children.
+ *
+ * Takes the space requested in @req and expands it to hold all @view->model's
+ * children.
+ * Returns the necessary size in @req.
+ **/
+void
+gog_view_size_child_request (GogView *view,
+ GogViewRequisition const *avail,
+ GogViewRequisition *res)
+{
+ GSList *ptr, *list;
+ GogView *child;
+ GogObjectPosition pos;
+ GogViewRequisition req;
+ double const pad_h = gog_renderer_pt2r_y (view->renderer, PAD_HACK);
+ double const pad_w = gog_renderer_pt2r_x (view->renderer, PAD_HACK);
+
+ /* walk the list in reverse */
+ list = g_slist_reverse (g_slist_copy (view->children));
+ for (ptr = list; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+
+ pos = child->model->position;
+ if (pos & GOG_POSITION_MANUAL) {
+ g_warning ("manual is not supported yet");
+ } else if (pos & GOG_POSITION_COMPASS) {
+ /* Dead simple */
+ gog_view_size_request (child, &req);
+
+ if (pos & (GOG_POSITION_N|GOG_POSITION_S)) {
+ if (req.h > 0)
+ res->h += req.h + pad_h;
+ } else if (res->h < req.h)
+ res->h = req.h;
+
+ if (pos & (GOG_POSITION_E|GOG_POSITION_W)) {
+ if (req.w > 0)
+ res->w += req.w + pad_w;
+ } else if (res->w < req.w)
+ res->w = req.w;
+
+ } else if (pos != GOG_POSITION_SPECIAL)
+ g_warning ("unexpected position %x for child %p of %p",
+ pos, child, view);
+ }
+ g_slist_free (list);
+}
+
+/**
+ * gog_view_find_child_view :
+ * @container : #GogView
+ * @target_model : #GogObject
+ *
+ * Find the GogView contained in @container that icorresponds to @model.
+ * Returns NULL on error
+ **/
+GogView *
+gog_view_find_child_view (GogView const *container, GogObject const *target_model)
+{
+ GogObject const *obj, *old_target;
+ GSList *ptr;
+
+ g_return_val_if_fail (IS_GOG_VIEW (container), NULL);
+ g_return_val_if_fail (IS_GOG_OBJECT (target_model), NULL);
+
+ /* @container is a view for @target_models parent */
+ obj = target_model;
+ while (obj != NULL && container->model != obj)
+ obj = obj->parent;
+
+ g_return_val_if_fail (obj != NULL, NULL);
+
+ for ( ; obj != target_model ; container = ptr->data) {
+ /* find the parent of @target_object that should be a child of this view */
+ old_target = obj;
+ obj = target_model;
+ while (obj != NULL && obj->parent != old_target)
+ obj = obj->parent;
+
+ g_return_val_if_fail (obj != NULL, NULL);
+
+ for (ptr = container->children ; ptr != NULL ; ptr = ptr->next)
+ if (GOG_VIEW (ptr->data)->model == obj)
+ break;
+
+ g_return_val_if_fail (ptr != NULL, NULL);
+ }
+
+ return (GogView *)container;
+}
--- /dev/null
+++ lib/goffice/graph/gog-error-bar-prefs.glade
@@ -0,0 +1,423 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+
+<widget class="GtkWindow" id="window2">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">window2</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkVBox" id="gog_error_bar_prefs">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="category_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Error category</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="category_alignment">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkComboBox" id="category_combo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">None
+Absolute
+Relative
+Percent</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="style_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="style_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Style</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="style_table">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="display_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Dis_play:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">5</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="width_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Width:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_RIGHT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">5</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">width</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="line_width_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Line width:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_RIGHT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">5</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">line_width</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="color_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Colo_r:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_RIGHT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">5</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="width">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">4 0 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="line_width">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 0 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="values_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="values_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Values</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="values_table">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="plus_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">(+)</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="minus_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">(-)</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-chart.c
@@ -0,0 +1,759 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-chart.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-chart-impl.h>
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-graph-impl.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-grid.h>
+#include <goffice/graph/gog-grid-line.h>
+#include <goffice/graph/gog-renderer.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <math.h>
+
+enum {
+ CHART_PROP_0,
+ CHART_PROP_CARDINALITY_VALID
+};
+
+static GType gog_chart_view_get_type (void);
+static GObjectClass *chart_parent_klass;
+
+static void
+gog_chart_update (GogObject *obj)
+{
+ GogChart *chart = GOG_CHART (obj);
+ unsigned full = chart->full_cardinality;
+ unsigned visible = chart->visible_cardinality;
+
+ gog_chart_get_cardinality (chart, NULL, NULL);
+
+ if (full != chart->full_cardinality ||
+ visible != chart->visible_cardinality)
+ g_object_notify (G_OBJECT (chart), "cardinality-valid");
+}
+
+static void
+gog_chart_finalize (GObject *obj)
+{
+ GogChart *chart = GOG_CHART (obj);
+
+ /* on exit the role remove routines are not called */
+ g_slist_free (chart->plots);
+ g_slist_free (chart->axes);
+
+ (chart_parent_klass->finalize) (obj);
+}
+
+static void
+gog_chart_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogChart *chart = GOG_CHART (obj);
+ switch (param_id) {
+ case CHART_PROP_CARDINALITY_VALID:
+ g_value_set_boolean (value, chart->cardinality_valid);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_chart_children_reordered (GogObject *obj)
+{
+ GSList *ptr, *accum = NULL;
+ GogChart *chart = GOG_CHART (obj);
+
+ for (ptr = obj->children; ptr != NULL ; ptr = ptr->next)
+ if (IS_GOG_PLOT (ptr->data))
+ accum = g_slist_prepend (accum, ptr->data);
+ g_slist_free (chart->plots);
+ chart->plots = g_slist_reverse (accum);
+
+ gog_chart_request_cardinality_update (chart);
+}
+
+static void
+role_plot_post_add (GogObject *parent, GogObject *plot)
+{
+ GogChart *chart = GOG_CHART (parent);
+ gboolean ok = TRUE;
+
+ /* APPEND to keep order, there won't be that many */
+ chart->plots = g_slist_append (chart->plots, plot);
+ gog_chart_request_cardinality_update (chart);
+
+ if (chart->plots->next == NULL)
+ ok = gog_chart_axis_set_assign (chart,
+ gog_plot_axis_set_pref (GOG_PLOT (plot)));
+ ok |= gog_plot_axis_set_assign (GOG_PLOT (plot),
+ chart->axis_set);
+
+ /* a quick post condition to keep us on our toes */
+ g_return_if_fail (ok);
+}
+
+static void
+role_plot_pre_remove (GogObject *parent, GogObject *plot)
+{
+ GogChart *chart = GOG_CHART (parent);
+ gog_plot_axis_clear (GOG_PLOT (plot), GOG_AXIS_SET_ALL);
+ chart->plots = g_slist_remove (chart->plots, plot);
+ gog_chart_request_cardinality_update (chart);
+}
+
+static gboolean
+role_grid_can_add (GogObject const *parent)
+{
+ GogChart const *chart = GOG_CHART (parent);
+ return chart->grid == NULL && chart->axis_set == GOG_AXIS_SET_XY;
+}
+static void
+role_grid_post_add (GogObject *parent, GogObject *child)
+{
+ GogChart *chart = GOG_CHART (parent);
+ g_return_if_fail (chart->grid == NULL);
+ chart->grid = child;
+}
+
+static void
+role_grid_pre_remove (GogObject *parent, GogObject *grid)
+{
+ GogChart *chart = GOG_CHART (parent);
+ g_return_if_fail (chart->grid == grid);
+ chart->grid = NULL;
+}
+
+static gboolean
+axis_can_add (GogObject const *parent, GogAxisType t)
+{
+ GogChart *chart = GOG_CHART (parent);
+ if (chart->axis_set == GOG_AXIS_SET_UNKNOWN)
+ return FALSE;
+ return (chart->axis_set & (1 << t)) != 0;
+}
+static gboolean
+axis_can_remove (GogObject const *child)
+{
+ return NULL == gog_axis_contributors (GOG_AXIS (child));
+}
+
+static void
+axis_post_add (GogObject *axis, GogAxisType t)
+{
+ GogChart *chart = GOG_CHART (axis->parent);
+ g_object_set (G_OBJECT (axis), "type", (int)t, NULL);
+ chart->axes = g_slist_prepend (chart->axes, axis);
+}
+
+static void
+axis_pre_remove (GogObject *parent, GogObject *axis)
+{
+ GogChart *chart = GOG_CHART (parent);
+ gog_axis_clear_contributors (GOG_AXIS (axis));
+ chart->axes = g_slist_remove (chart->axes, axis);
+}
+
+static gboolean x_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_X); }
+static void x_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_X); }
+static gboolean y_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_Y); }
+static void y_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_Y); }
+static gboolean z_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_Z); }
+static void z_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_Z); }
+static gboolean circular_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_CIRCULAR); }
+static void circular_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_CIRCULAR); }
+static gboolean radial_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_RADIAL); }
+static void radial_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_RADIAL); }
+
+static GogObjectRole const roles[] = {
+ { N_("Legend"), "GogLegend", 0,
+ GOG_POSITION_COMPASS, GOG_POSITION_E|GOG_POSITION_ALIGN_CENTER, GOG_OBJECT_NAME_BY_ROLE,
+ NULL, NULL, NULL, NULL, NULL, NULL, { -1 } },
+ { N_("Title"), "GogLabel", 1,
+ GOG_POSITION_COMPASS, GOG_POSITION_N|GOG_POSITION_ALIGN_CENTER, GOG_OBJECT_NAME_BY_ROLE,
+ NULL, NULL, NULL, NULL, NULL, NULL, { -1 } },
+ { N_("Grid"), "GogGrid", 0,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_grid_can_add, NULL, NULL, role_grid_post_add, role_grid_pre_remove, NULL, { -1 } },
+ { N_("X-Axis"), "GogAxis", 1,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ x_axis_can_add, axis_can_remove, NULL, x_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_X } },
+ { N_("Y-Axis"), "GogAxis", 2,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ y_axis_can_add, axis_can_remove, NULL, y_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_Y } },
+ { N_("Z-Axis"), "GogAxis", 3,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ z_axis_can_add, axis_can_remove, NULL, z_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_Z } },
+ { N_("Circular-Axis"), "GogAxis", 1,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ circular_axis_can_add, axis_can_remove, NULL, circular_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_CIRCULAR } },
+ { N_("Radial-Axis"), "GogAxis", 2,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ radial_axis_can_add, axis_can_remove, NULL, radial_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_RADIAL } },
+ { N_("Plot"), "GogPlot", 4, /* keep the axis before the plots */
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_TYPE,
+ NULL, NULL, NULL, role_plot_post_add, role_plot_pre_remove, NULL, { -1 } }
+};
+
+static void
+gog_chart_class_init (GogObjectClass *gog_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *)gog_klass;
+
+ chart_parent_klass = g_type_class_peek_parent (gog_klass);
+ gobject_klass->finalize = gog_chart_finalize;
+ gobject_klass->get_property = gog_chart_get_property;
+
+ g_object_class_install_property (gobject_klass, CHART_PROP_CARDINALITY_VALID,
+ g_param_spec_boolean ("cardinality-valid", "cardinality-valid",
+ "Is the charts cardinality currently vaid",
+ FALSE, G_PARAM_READABLE));
+
+ gog_klass->view_type = gog_chart_view_get_type ();
+ gog_klass->update = gog_chart_update;
+ gog_klass->children_reordered = gog_chart_children_reordered;
+ gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+}
+
+static void
+gog_chart_init (GogChart *chart)
+{
+ chart->x = 0;
+ chart->y = 0;
+ chart->cols = 0;
+ chart->rows = 0;
+
+ /* start as true so that we can queue an update when it changes */
+ chart->cardinality_valid = TRUE;
+ chart->axis_set = GOG_AXIS_SET_UNKNOWN;
+}
+
+GSF_CLASS (GogChart, gog_chart,
+ gog_chart_class_init, gog_chart_init,
+ GOG_OUTLINED_OBJECT_TYPE)
+
+/**
+ * gog_chart_get_position :
+ * @chart : const #GogChart
+ * @x :
+ * @y :
+ * @cols :
+ * @rows :
+ *
+ * Returns TRUE if the chart has been positioned.
+ **/
+gboolean
+gog_chart_get_position (GogChart const *chart,
+ unsigned *x, unsigned *y, unsigned *cols, unsigned *rows)
+{
+ g_return_val_if_fail (GOG_CHART (chart), FALSE);
+
+ if (chart->cols <= 0 || chart->rows <= 0)
+ return FALSE;
+
+ if (x != NULL) *x = chart->x;
+ if (y != NULL) *y = chart->y;
+ if (cols != NULL) *cols = chart->cols;
+ if (rows != NULL) *rows = chart->rows;
+
+ return TRUE;
+}
+
+/**
+ * gog_chart_set_position :
+ * @chart : #GogChart
+ * @x :
+ * @y :
+ * @cols :
+ * @rows :
+ *
+ **/
+void
+gog_chart_set_position (GogChart *chart,
+ unsigned x, unsigned y, unsigned cols, unsigned rows)
+{
+ g_return_if_fail (GOG_CHART (chart) != NULL);
+
+ if (chart->x == x && chart->y == y &&
+ chart->cols == cols && chart->rows == rows)
+ return;
+
+ chart->x = x;
+ chart->y = y;
+ chart->cols = cols;
+ chart->rows = rows;
+
+ gog_graph_validate_chart_layout (GOG_GRAPH (GOG_OBJECT (chart)->parent));
+ gog_object_emit_changed (GOG_OBJECT (chart), TRUE);
+}
+
+void
+gog_chart_get_cardinality (GogChart *chart, unsigned *full, unsigned *visible)
+{
+ GSList *ptr;
+ unsigned tmp_full, tmp_visible;
+
+ g_return_if_fail (GOG_CHART (chart) != NULL);
+
+ if (!chart->cardinality_valid) {
+ chart->cardinality_valid = TRUE;
+ chart->full_cardinality = chart->visible_cardinality = 0;
+ for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next) {
+ gog_plot_get_cardinality (ptr->data, &tmp_full, &tmp_visible);
+ chart->full_cardinality += tmp_full;
+ chart->visible_cardinality += tmp_visible;
+ }
+ }
+
+ if (full != NULL)
+ *full = chart->full_cardinality;
+ if (visible != NULL)
+ *visible = chart->visible_cardinality;
+}
+
+void
+gog_chart_request_cardinality_update (GogChart *chart)
+{
+ g_return_if_fail (GOG_CHART (chart) != NULL);
+
+ if (chart->cardinality_valid) {
+ chart->cardinality_valid = FALSE;
+ gog_object_request_update (GOG_OBJECT (chart));
+ }
+}
+
+void
+gog_chart_foreach_elem (GogChart *chart, gboolean only_visible,
+ GogEnumFunc handler, gpointer data)
+{
+ GSList *ptr;
+
+ g_return_if_fail (GOG_CHART (chart) != NULL);
+ g_return_if_fail (chart->cardinality_valid);
+
+ for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next)
+ gog_plot_foreach_elem (ptr->data, only_visible, handler, data);
+}
+
+GSList *
+gog_chart_get_plots (GogChart const *chart)
+{
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, NULL);
+ return chart->plots;
+}
+
+GogAxisSet
+gog_chart_axis_set (GogChart const *chart)
+{
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, GOG_AXIS_SET_UNKNOWN);
+ return chart->axis_set;
+}
+
+gboolean
+gog_chart_axis_set_is_valid (GogChart const *chart, GogAxisSet type)
+{
+ GSList *ptr;
+
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, FALSE);
+
+ for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next)
+ if (!gog_plot_axis_set_is_valid (ptr->data, type))
+ return FALSE;
+ return TRUE;
+}
+
+static void
+gog_chart_add_axis (GogChart *chart, GogAxisType type)
+{
+ unsigned i = G_N_ELEMENTS (roles);
+ while (i-- > 0)
+ if (roles[i].user.i == (int)type) {
+ gog_object_add_by_role (GOG_OBJECT (chart), roles + i, NULL);
+ return;
+ }
+ g_warning ("unknown axis type %d", type);
+}
+
+gboolean
+gog_chart_axis_set_assign (GogChart *chart, GogAxisSet axis_set)
+{
+ GogAxis *axis;
+ GSList *ptr;
+ GogAxisType type;
+
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, FALSE);
+
+ if (chart->axis_set == axis_set)
+ return TRUE;
+ chart->axis_set = axis_set;
+
+ if (chart->grid != NULL && axis_set != GOG_AXIS_SET_XY) {
+ GogObject *grid = chart->grid; /* clear_parent clears ::grid */
+ gog_object_clear_parent (GOG_OBJECT (grid));
+ g_object_unref (grid);
+ } else if (chart->grid == NULL && axis_set == GOG_AXIS_SET_XY)
+ gog_object_add_by_name (GOG_OBJECT (chart), "Grid", NULL);
+
+ /* Add at least 1 instance of any required axis */
+ for (type = 0 ; type < GOG_AXIS_TYPES ; type++)
+ if ((axis_set & (1 << type))) {
+ GSList *tmp = gog_chart_get_axis (chart, type);
+ if (tmp == NULL)
+ gog_chart_add_axis (chart, type);
+ else
+ g_slist_free (tmp);
+ }
+
+ /* link the plots */
+ for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next)
+ if (!gog_plot_axis_set_assign (ptr->data, axis_set))
+ return FALSE;
+
+ /* remove any existing axis that do not fit this scheme */
+ for (ptr = GOG_OBJECT (chart)->children ; ptr != NULL ; ) {
+ axis = ptr->data;
+ ptr = ptr->next; /* list may change under us */
+ if (IS_GOG_AXIS (axis)) {
+ type = -1;
+ g_object_get (G_OBJECT (axis), "type", &type, NULL);
+ if (type < 0 || type >= GOG_AXIS_TYPES) {
+ g_warning ("Invalid axis");
+ continue;
+ }
+
+ if (0 == (axis_set & (1 << type))) {
+ gog_object_clear_parent (GOG_OBJECT (axis));
+ g_object_unref (axis);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * gog_chart_get_axis :
+ * @chart : #GogChart
+ * @target : #GogAxisType
+ *
+ * Return a list which the caller must free of all axis of type @target
+ * associated with @chart.
+ **/
+GSList *
+gog_chart_get_axis (GogChart const *chart, GogAxisType target)
+{
+ GSList *ptr, *res = NULL;
+ GogAxis *axis;
+ int type;
+
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, NULL);
+
+ for (ptr = GOG_OBJECT (chart)->children ; ptr != NULL ; ptr = ptr->next) {
+ axis = ptr->data;
+ if (IS_GOG_AXIS (axis)) {
+ type = -1;
+ g_object_get (G_OBJECT (axis), "type", &type, NULL);
+ if (type < 0 || type >= GOG_AXIS_TYPES) {
+ g_warning ("Invalid axis");
+ continue;
+ }
+ if (type == target)
+ res = g_slist_prepend (res, axis);
+ }
+ }
+
+ return res;
+}
+
+/**
+ * gog_chart_get_grid :
+ * @chart : #GogChart
+ *
+ * Returns the grid associated with @chart if one exists
+ * otherwise NULL.
+ **/
+GogGrid *
+gog_chart_get_grid (GogChart const *chart)
+{
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, NULL);
+ return GOG_GRID (chart->grid);
+}
+
+/*********************************************************************/
+
+typedef struct {
+ GogOutlinedView base;
+
+ GogViewAllocation plot_area;
+} GogChartView;
+typedef GogOutlinedViewClass GogChartViewClass;
+
+#define GOG_CHART_VIEW_TYPE (gog_chart_view_get_type ())
+#define GOG_CHART_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_CHART_VIEW_TYPE, GogChartView))
+#define IS_GOG_CHART_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_CHART_VIEW_TYPE))
+
+static GogViewClass *cview_parent_klass;
+
+GogViewAllocation const *
+gog_chart_view_get_plot_area (GogView const *view)
+{
+ g_return_val_if_fail ((GOG_CHART_VIEW (view) != NULL), NULL);
+
+ return & (GOG_CHART_VIEW(view)->plot_area);
+}
+
+static void
+child_request (GogView *view, GogViewAllocation *res,
+ GogViewAllocation const *plot_area,
+ gboolean allocate)
+{
+ GSList *ptr;
+ GogView *child;
+ GogAxis const *axis;
+ GogViewRequisition req;
+ GogViewAllocation allocation;
+
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (child->model->position != GOG_POSITION_SPECIAL ||
+ !IS_GOG_AXIS (child->model))
+ continue;
+
+ axis = GOG_AXIS (child->model);
+ req.w = req.h = 0.;
+ gog_view_size_request (child, &req);
+ allocation = *plot_area;
+ switch (gog_axis_get_atype (axis)) {
+ case GOG_AXIS_X:
+ if (req.h > 0) {
+ res->h -= req.h;
+ allocation.h = req.h;
+ if (gog_axis_get_pos (axis) == GOG_AXIS_AT_HIGH) {
+ allocation.y = res->y;
+ res->y += req.h;
+ } else
+ allocation.y = res->y + res->h;
+ }
+
+
+ break;
+ case GOG_AXIS_Y:
+ if (req.w > 0) {
+ res->w -= req.w;
+ allocation.w = req.w;
+ if (gog_axis_get_pos (axis) == GOG_AXIS_AT_LOW) {
+ allocation.x = res->x;
+ res->x += req.w;
+ } else
+ allocation.x = res->x + res->w;
+ }
+ break;
+ default:
+ break;
+ }
+ if (allocate)
+ gog_view_size_allocate (child, &allocation);
+ }
+}
+
+static void
+gog_chart_view_size_allocate (GogView *view, GogViewAllocation const *allocation)
+{
+ GSList *ptr;
+ GogView *child;
+ GogChart *chart = GOG_CHART (view->model);
+ GogViewAllocation res = *allocation;
+ GogViewAllocation tmp, axis_alloc;
+
+ (cview_parent_klass->size_allocate) (view, &res);
+
+ res = view->residual;
+ switch (chart->axis_set) {
+
+ case GOG_AXIS_SET_XY:
+ {
+ GogViewPadding axis_padding, padding = {0., 0., 0., 0.};
+
+ tmp = res;
+ child_request (view, &res, &res, FALSE);
+
+ /* FIXME: we need to iterate until convergence */
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (child->model->position != GOG_POSITION_SPECIAL ||
+ !IS_GOG_AXIS (child->model))
+ continue;
+
+ gog_axis_view_padding_request (child, &axis_padding, &res);
+ padding.wr = MAX (padding.wr, axis_padding.wr);
+ padding.wl = MAX (padding.wl, axis_padding.wl);
+ padding.hb = MAX (padding.hb, axis_padding.hb);
+ padding.ht = MAX (padding.ht, axis_padding.ht);
+ }
+ res.x += padding.wl;
+ res.w -= padding.wl + padding.wr;
+ res.y += padding.ht;
+ res.h -= padding.ht + padding.hb;
+
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (child->model->position != GOG_POSITION_SPECIAL ||
+ !IS_GOG_AXIS (child->model))
+ continue;
+
+ switch (gog_axis_get_atype (GOG_AXIS (child->model)))
+ {
+ case GOG_AXIS_X:
+ axis_alloc = tmp;
+ axis_alloc.x = res.x;
+ axis_alloc.w = res.w;
+ gog_view_size_allocate (child, &axis_alloc);
+ break;
+
+ case GOG_AXIS_Y:
+ axis_alloc = tmp;
+ axis_alloc.y = res.y;
+ axis_alloc.h = res.h;
+ gog_view_size_allocate (child, &axis_alloc);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ break;
+
+ case GOG_AXIS_SET_RADAR:
+ /* Give the axes the whole residual area. */
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (IS_GOG_AXIS (child->model))
+ gog_view_size_allocate (child, &res);
+ }
+ break;
+ case GOG_AXIS_SET_NONE:
+ break;
+
+ case GOG_AXIS_SET_UNKNOWN:
+ return;
+ default:
+ g_warning ("only have layout engine for xy, radar, and none currently");
+ return;
+ }
+
+ /* overlay all the plots in the residual */
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (child->model->position == GOG_POSITION_SPECIAL &&
+ (IS_GOG_PLOT (child->model) || child->model == chart->grid))
+ gog_view_size_allocate (child, &res);
+ }
+
+ GOG_CHART_VIEW(view)->plot_area = res;
+}
+
+static void
+gog_chart_view_init (GogChartView *cview)
+{
+}
+
+static void
+grid_line_render (GSList *start_ptr, GogViewAllocation const *bbox)
+{
+ GSList *ptr, *child_ptr;
+ GogView *child_view, *axis_child_view;
+
+ /* Render minor lines first */
+ for (ptr = start_ptr; ptr != NULL; ptr = ptr->next) {
+ child_view = ptr->data;
+ if (IS_GOG_AXIS (child_view->model)) {
+ for (child_ptr = child_view->children; child_ptr != NULL; child_ptr = child_ptr->next) {
+ axis_child_view = child_ptr->data;
+ if (IS_GOG_GRID_LINE (axis_child_view->model) &&
+ gog_grid_line_is_minor (GOG_GRID_LINE (axis_child_view->model)))
+ gog_view_render (axis_child_view, bbox);
+ }
+ }
+ }
+ /* then render major lines */
+ for (ptr = start_ptr; ptr != NULL; ptr = ptr->next) {
+ child_view = ptr->data;
+ if (IS_GOG_AXIS (child_view->model)) {
+ for (child_ptr = child_view->children; child_ptr != NULL; child_ptr = child_ptr->next) {
+ axis_child_view = child_ptr->data;
+ if (IS_GOG_GRID_LINE (axis_child_view->model) &&
+ !gog_grid_line_is_minor (GOG_GRID_LINE (axis_child_view->model)))
+ gog_view_render (axis_child_view, bbox);
+ }
+ }
+ }
+}
+
+static void
+gog_chart_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GSList *ptr;
+ GogView *child_view;
+ gboolean grid_line_rendered = FALSE;
+
+ cview_parent_klass->render (view, bbox);
+
+ /* KLUDGE: render grid lines before axis */
+ for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
+ child_view = ptr->data;
+ if (!grid_line_rendered && IS_GOG_AXIS (child_view->model)) {
+ grid_line_render (ptr, bbox);
+ grid_line_rendered = TRUE;
+ }
+ gog_view_render (ptr->data, bbox);
+ }
+}
+
+static void
+gog_chart_view_class_init (GogChartViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+ GogOutlinedViewClass *oview_klass = (GogOutlinedViewClass *) gview_klass;
+
+ cview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->size_allocate = gog_chart_view_size_allocate;
+ view_klass->clip = TRUE;
+ view_klass->render = gog_chart_view_render;
+ oview_klass->call_parent_render = FALSE;
+}
+
+static GSF_CLASS (GogChartView, gog_chart_view,
+ gog_chart_view_class_init, gog_chart_view_init,
+ GOG_OUTLINED_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/go-data.h
@@ -0,0 +1,84 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-data.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_DATA_H
+#define GO_DATA_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/utils/goffice-utils.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_DATA_TYPE (go_data_get_type ())
+#define GO_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_TYPE, GOData))
+#define IS_GO_DATA(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_TYPE))
+
+GType go_data_get_type (void);
+gboolean go_data_needs_recalc (GOData const *dat);
+GOData *go_data_dup (GOData const *src);
+gboolean go_data_eq (GOData const *a, GOData const *b);
+GOFormat *go_data_preferred_fmt (GOData const *dat);
+char *go_data_as_str (GOData const *dat);
+gboolean go_data_from_str (GOData *dat, char const *str);
+void go_data_emit_changed (GOData *dat);
+
+/*************************************************************************/
+
+#define GO_DATA_SCALAR_TYPE (go_data_scalar_get_type ())
+#define GO_DATA_SCALAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_SCALAR_TYPE, GODataScalar))
+#define IS_GO_DATA_SCALAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_SCALAR_TYPE))
+
+GType go_data_scalar_get_type (void);
+
+double go_data_scalar_get_value (GODataScalar *val);
+char const *go_data_scalar_get_str (GODataScalar *val);
+
+/*************************************************************************/
+
+#define GO_DATA_VECTOR_TYPE (go_data_vector_get_type ())
+#define GO_DATA_VECTOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_VECTOR_TYPE, GODataVector))
+#define IS_GO_DATA_VECTOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_VECTOR_TYPE))
+
+GType go_data_vector_get_type (void);
+
+int go_data_vector_get_len (GODataVector *vec);
+double *go_data_vector_get_values (GODataVector *vec);
+double go_data_vector_get_value (GODataVector *vec, unsigned i);
+char *go_data_vector_get_str (GODataVector *vec, unsigned i);
+void go_data_vector_get_minmax (GODataVector *vec, double *min, double *max);
+
+/*************************************************************************/
+
+#define GO_DATA_MATRIX_TYPE (go_data_matrix_get_type ())
+#define GO_DATA_MATRIX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_MATRIX_TYPE, GODataMatrix))
+#define IS_GO_DATA_MATRIX(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_MATRIX_TYPE))
+
+GType go_data_matrix_get_type (void);
+
+GOMatrixSize go_data_matrix_get_size (GODataMatrix *mat);
+double *go_data_matrix_get_values (GODataMatrix *mat);
+double go_data_matrix_get_value (GODataMatrix *mat, unsigned i, unsigned j);
+char *go_data_matrix_get_str (GODataMatrix *mat, unsigned i, unsigned j);
+void go_data_matrix_get_minmax (GODataMatrix *mat, double *min, double *max);
+
+G_END_DECLS
+
+#endif /* GO_DATA_H */
--- /dev/null
+++ lib/goffice/graph/gog-object.h
@@ -0,0 +1,156 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-object.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_OBJECT_H
+#define GOG_OBJECT_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+#include <command-context.h> /* for GnmCmdContext */
+#include <libart_lgpl/art_rect.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GOG_OBJECT_NAME_BY_ROLE = 1,
+ GOG_OBJECT_NAME_BY_TYPE = 2,
+ GOG_OBJECT_NAME_MANUALLY = 3
+} GogObjectNamingConv;
+
+struct _GogObjectRole {
+ char const *id; /* for persistence */
+ char const *is_a_typename;
+ unsigned priority;
+
+ guint32 allowable_positions;
+ GogObjectPosition default_position;
+ GogObjectNamingConv naming_conv;
+
+ gboolean (*can_add) (GogObject const *parent);
+ gboolean (*can_remove) (GogObject const *child);
+ GogObject *(*allocate) (GogObject *parent);
+ void (*post_add) (GogObject *parent, GogObject *child);
+ void (*pre_remove) (GogObject *parent, GogObject *child);
+ void (*post_remove) (GogObject *parent, GogObject *child);
+
+ union { /* allow people to tack some useful tidbits on the end */
+ int i;
+ float f;
+ gpointer p;
+ } user;
+};
+
+struct _GogObject {
+ GObject base;
+
+ char *user_name; /* user assigned, NULL will fall back to id */
+ char *id; /* system generated */
+ GogObjectRole const *role;
+
+ GogObject *parent;
+ GSList *children;
+
+ GogObjectPosition position;
+ GogViewAllocation *manual_position;
+
+ unsigned needs_update : 1;
+ unsigned being_updated : 1;
+ unsigned explicitly_typed_role : 1; /* did we create it automaticly */
+};
+
+typedef struct {
+ GObjectClass base;
+
+ GHashTable *roles;
+ GType view_type;
+
+ unsigned use_parent_as_proxy : 1; /* when we change, pretend it was our parent */
+
+ /* Virtuals */
+ void (*update) (GogObject *obj);
+ void (*parent_changed) (GogObject *obj, gboolean was_set);
+ char const *(*type_name) (GogObject const *obj);
+ gpointer (*editor) (GogObject *obj,
+ GogDataAllocator *dalloc, GnmCmdContext *cc);
+
+ /* signals */
+ void (*changed) (GogObject *obj, gboolean size);
+ void (*name_changed) (GogObject *obj);
+ void (*possible_additions_changed) (GogObject const *obj);
+ void (*child_added) (GogObject *parent, GogObject *child);
+ void (*child_removed) (GogObject *parent, GogObject *child);
+ void (*child_name_changed) (GogObject const *obj, GogObject const *child);
+ void (*children_reordered) (GogObject *obj);
+} GogObjectClass;
+
+#define GOG_OBJECT_TYPE (gog_object_get_type ())
+#define GOG_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_OBJECT_TYPE, GogObject))
+#define IS_GOG_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_OBJECT_TYPE))
+#define GOG_OBJECT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_OBJECT_TYPE, GogObjectClass))
+#define IS_GOG_OBJECT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_OBJECT_TYPE))
+#define GOG_OBJECT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_OBJECT_TYPE, GogObjectClass))
+
+#define GOG_PARAM_PERSISTENT (1 << (G_PARAM_USER_SHIFT+0))
+#define GOG_PARAM_FORCE_SAVE (1 << (G_PARAM_USER_SHIFT+1)) /* even if the value == default */
+
+GType gog_object_get_type (void);
+
+GogObject *gog_object_dup (GogObject const *obj, GogObject *new_parent);
+GogObject *gog_object_get_parent (GogObject const *obj);
+GogObject *gog_object_get_parent_typed (GogObject const *obj, GType t);
+GogGraph *gog_object_get_graph (GogObject const *obj);
+GogTheme *gog_object_get_theme (GogObject const *obj);
+char const *gog_object_get_id (GogObject const *obj);
+char const *gog_object_get_name (GogObject const *obj);
+void gog_object_set_name (GogObject *obj, char *name, GError **err);
+GSList *gog_object_get_children (GogObject const *obj, GogObjectRole const *filter);
+GogObject *gog_object_get_child_by_role(GogObject const *obj, GogObjectRole const *role);
+gpointer gog_object_get_editor (GogObject *obj,
+ GogDataAllocator *dalloc, GnmCmdContext *cc);
+GogView *gog_object_new_view (GogObject const *obj, GogView *view);
+gboolean gog_object_is_deletable (GogObject const *obj);
+GSList *gog_object_possible_additions (GogObject const *obj);
+GogObject *gog_object_add_by_role (GogObject *parent,
+ GogObjectRole const *role, GogObject *child);
+GogObject *gog_object_add_by_name (GogObject *parent,
+ char const *role, GogObject *child);
+void gog_object_can_reorder (GogObject const *obj,
+ gboolean *inc_ok, gboolean *dec_ok);
+GogObject *gog_object_reorder (GogObject const *obj,
+ gboolean inc, gboolean goto_max);
+GogObjectPosition gog_object_get_pos (GogObject const *obj);
+gboolean gog_object_set_pos (GogObject *obj, GogObjectPosition p);
+
+GogObjectRole const *gog_object_find_role_by_name (GogObject const *obj,
+ char const *role);
+
+/* protected */
+void gog_object_update (GogObject *obj);
+gboolean gog_object_request_update (GogObject *obj);
+void gog_object_emit_changed (GogObject *obj, gboolean size);
+gboolean gog_object_clear_parent (GogObject *obj);
+gboolean gog_object_set_parent (GogObject *child, GogObject *parent,
+ GogObjectRole const *role, char *name);
+void gog_object_register_roles (GogObjectClass *klass,
+ GogObjectRole const *roles, unsigned n_roles);
+
+G_END_DECLS
+
+#endif /* GOG_OBJECT_H */
--- /dev/null
+++ lib/goffice/graph/gog-object.c
@@ -0,0 +1,873 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-object.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-graph-impl.h> /* for gog_graph_request_update */
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/go-data.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <stdlib.h>
+
+enum {
+ CHILD_ADDED,
+ CHILD_REMOVED,
+ CHILD_NAME_CHANGED,
+ CHILDREN_REORDERED,
+ NAME_CHANGED,
+ CHANGED,
+ LAST_SIGNAL
+};
+static gulong gog_object_signals [LAST_SIGNAL] = { 0, };
+
+static GObjectClass *parent_klass;
+static void
+gog_object_finalize (GObject *gobj)
+{
+ GogObject *obj = GOG_OBJECT (gobj);
+
+ g_free (obj->user_name); obj->user_name = NULL;
+ g_free (obj->id); obj->id = NULL;
+
+ g_slist_foreach (obj->children, (GFunc) g_object_unref, NULL);
+ g_slist_free (obj->children);
+ obj->children = NULL;
+
+ (parent_klass->finalize) (gobj);
+}
+
+static void
+gog_object_parent_changed (GogObject *child, gboolean was_set)
+{
+ GSList *ptr = child->children;
+ for (; ptr != NULL ; ptr = ptr->next) {
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (ptr->data);
+ (*klass->parent_changed) (ptr->data, was_set);
+ }
+
+ if (IS_GOG_DATASET (child))
+ gog_dataset_parent_changed (GOG_DATASET (child), was_set);
+}
+
+static void
+gog_object_class_init (GObjectClass *klass)
+{
+ GogObjectClass *gog_klass = (GogObjectClass *)klass;
+ parent_klass = g_type_class_peek_parent (klass);
+ klass->finalize = gog_object_finalize;
+ gog_klass->parent_changed = gog_object_parent_changed;
+
+ gog_object_signals [CHILD_ADDED] = g_signal_new ("child-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, child_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ gog_object_signals [CHILD_REMOVED] = g_signal_new ("child-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, child_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ gog_object_signals [CHILD_NAME_CHANGED] = g_signal_new ("child-name-changed",
+ G_TYPE_FROM_CLASS (gog_klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, child_name_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ gog_object_signals [CHILDREN_REORDERED] = g_signal_new ("children-reordered",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, children_reordered),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ gog_object_signals [NAME_CHANGED] = g_signal_new ("name-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, name_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ gog_object_signals [CHANGED] = g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+static void
+gog_object_init (GogObject *obj)
+{
+ obj->children = NULL;
+ obj->user_name = NULL;
+ obj->id = NULL;
+ obj->needs_update = FALSE;
+ obj->being_updated = FALSE;
+}
+
+GSF_CLASS (GogObject, gog_object,
+ gog_object_class_init, gog_object_init,
+ G_TYPE_OBJECT)
+
+static char *
+gog_object_generate_name (GogObject *obj)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+ GogObject *tmp;
+ char const *type_name;
+ unsigned name_len, i, max_index = 0;
+ GSList *ptr;
+
+ g_return_val_if_fail (klass != NULL, NULL);
+ g_return_val_if_fail (obj->role != NULL, NULL);
+
+ switch (obj->role->naming_conv) {
+ default :
+ case GOG_OBJECT_NAME_MANUALLY :
+ g_warning ("Role %s should not be autogenerating names",
+ obj->role->id);
+
+ case GOG_OBJECT_NAME_BY_ROLE :
+ g_return_val_if_fail (obj->role != NULL, NULL);
+ type_name = _(obj->role->id);
+ break;
+
+ case GOG_OBJECT_NAME_BY_TYPE :
+ g_return_val_if_fail (klass->type_name != NULL, NULL);
+ type_name = _((*klass->type_name) (obj));
+ break;
+ }
+
+ g_return_val_if_fail (type_name != NULL, NULL);
+ name_len = strlen (type_name);
+
+ for (ptr = obj->parent->children; ptr != NULL ; ptr = ptr->next) {
+ tmp = GOG_OBJECT (ptr->data);
+ if (tmp->id != NULL &&
+ 0 == strncmp (type_name, tmp->id, name_len)) {
+ i = strtol (tmp->id + name_len, NULL, 10);
+ if (max_index < i)
+ max_index = i;
+ }
+ }
+ return g_strdup_printf ("%s%d", type_name, max_index + 1);
+}
+
+/**
+ * gog_object_dup :
+ * @src : #GogObject
+ * @new_parent : #GogObject the parent tree for the object (can be NULL)
+ *
+ * Create a deep copy of @obj using @new_parent as its parent.
+ **/
+GogObject *
+gog_object_dup (GogObject const *src, GogObject *new_parent)
+{
+ gint n, last;
+ GParamSpec **props;
+ GogObject *dst = NULL;
+ GSList *ptr;
+ GValue val = { 0 };
+
+ if (src == NULL)
+ return NULL;
+
+ g_return_val_if_fail (GOG_OBJECT (src) != NULL, NULL);
+
+ if (src->role == NULL || src->explicitly_typed_role)
+ dst = g_object_new (G_OBJECT_TYPE (src), NULL);
+ if (new_parent)
+ dst = gog_object_add_by_role (new_parent, src->role, dst);
+
+ dst->position = src->position;
+ /* properties */
+ props = g_object_class_list_properties (G_OBJECT_GET_CLASS (src), &n);
+ while (n-- > 0)
+ if (props[n]->flags & GOG_PARAM_PERSISTENT) {
+ g_value_init (&val, props[n]->value_type);
+ g_object_get_property (G_OBJECT (src), props[n]->name, &val);
+ g_object_set_property (G_OBJECT (dst), props[n]->name, &val);
+ g_value_unset (&val);
+ }
+ g_free (props);
+
+ if (IS_GOG_DATASET (src)) { /* convenience to save data */
+ GogDataset const *src_set = GOG_DATASET (src);
+ GogDataset *dst_set = GOG_DATASET (dst);
+
+ gog_dataset_dims (src_set, &n, &last);
+ for ( ; n <= last ; n++)
+ gog_dataset_set_dim (dst_set, n,
+ go_data_dup (gog_dataset_get_dim (src_set, n)),
+ NULL);
+ }
+
+ for (ptr = src->children; ptr != NULL ; ptr = ptr->next)
+ /* children added directly to new parent, no need to use the
+ * function result */
+ gog_object_dup (ptr->data, dst);
+
+ return dst;
+}
+
+/**
+ * gog_object_get_parent :
+ * @obj : a #GogObject
+ *
+ * Returns @obj's parent, potentially NULL if it has not been added to a
+ * heirarchy yet. does not change ref-count in any way.
+ **/
+GogObject *
+gog_object_get_parent (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+ return obj->parent;
+}
+
+/**
+ * gog_object_get_parent_typed :
+ * @obj : a #GogObject
+ * @type : a #GType
+ *
+ * Returns @obj's parent of type @type, potentially NULL if it has not been
+ * added to a heirarchy yet or none of the parents are of type @type.
+ **/
+GogObject *
+gog_object_get_parent_typed (GogObject const *obj, GType t)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+
+ for (; obj != NULL ; obj = obj->parent)
+ if (G_TYPE_CHECK_INSTANCE_TYPE (obj, t))
+ return GOG_OBJECT (obj); /* const cast */
+ return NULL;
+}
+
+/**
+ * gog_object_get_graph :
+ * @obj : const * #GogObject
+ *
+ * Returns the parent graph.
+ **/
+GogGraph *
+gog_object_get_graph (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+
+ for (; obj != NULL ; obj = obj->parent)
+ if (IS_GOG_GRAPH (obj))
+ return GOG_GRAPH (obj);
+ return NULL;
+}
+
+GogTheme *
+gog_object_get_theme (GogObject const *obj)
+{
+ GogGraph *graph = gog_object_get_graph (obj);
+
+ return (graph != NULL) ? gog_graph_get_theme (graph) : NULL;
+}
+
+/**
+ * gog_object_get_name :
+ * @obj : a #GogObject
+ *
+ * No need to free the result
+ **/
+char const *
+gog_object_get_name (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+ return (obj->user_name != NULL && *obj->user_name != '\0') ? obj->user_name : obj->id;
+}
+
+/**
+ * gog_object_set_name :
+ * @obj : #GogObject
+ * @name :
+ * @err : #GError
+ *
+ * Assign the new name and signals that it has changed.
+ * NOTE : it _absorbs_ @name rather than copying it, and generates a new name
+ * if @name == NULL
+ **/
+void
+gog_object_set_name (GogObject *obj, char *name, GError **err)
+{
+ GogObject *tmp;
+
+ g_return_if_fail (GOG_OBJECT (obj) != NULL);
+
+ if (obj->user_name == name)
+ return;
+ g_free (obj->user_name);
+ obj->user_name = name;
+
+ g_signal_emit (G_OBJECT (obj),
+ gog_object_signals [NAME_CHANGED], 0);
+
+ for (tmp = obj; tmp != NULL ; tmp = tmp->parent)
+ g_signal_emit (G_OBJECT (tmp),
+ gog_object_signals [CHILD_NAME_CHANGED], 0, obj);
+}
+
+/**
+ * gog_object_get_children :
+ * @obj : a #GogObject
+ * @filter : an optional #GogObjectRole to use as a filter
+ *
+ * The list needs to be Freed
+ **/
+GSList *
+gog_object_get_children (GogObject const *obj, GogObjectRole const *filter)
+{
+ GSList *ptr, *res = NULL;
+
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+
+ if (filter == NULL)
+ return g_slist_copy (obj->children);
+
+ for (ptr = obj->children ; ptr != NULL ; ptr = ptr->next)
+ if (GOG_OBJECT (ptr->data)->role == filter)
+ res = g_slist_prepend (res, ptr->data);
+ return g_slist_reverse (res);
+}
+
+/**
+ * gog_object_get_child_by_role :
+ * @obj : a #GogObject
+ * @role : a #GogObjectRole to use as a filter
+ *
+ * A convenience routine to handle a unique child
+ * Returns NULL and spews an error if there is more than one.
+ **/
+GogObject *
+gog_object_get_child_by_role (GogObject const *obj, GogObjectRole const *role)
+{
+ GogObject *res = NULL;
+ GSList *children = gog_object_get_children (obj, role);
+
+ if (children != NULL && children->next == NULL)
+ res = children->data;
+ g_slist_free (children);
+ return res;
+}
+
+/**
+ * gog_object_is_deletable :
+ * @obj : a #GogObject
+ *
+ * Can the specified @obj be deleted ?
+ **/
+gboolean
+gog_object_is_deletable (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, FALSE);
+
+ if (IS_GOG_GRAPH (obj))
+ return FALSE;
+
+ return obj->role == NULL || obj->role->can_remove == NULL ||
+ (obj->role->can_remove) (obj);
+}
+
+struct possible_add_closure {
+ GSList *res;
+ GogObject const *parent;
+};
+
+static void
+cb_collect_possible_additions (char const *name, GogObjectRole const *role,
+ struct possible_add_closure *data)
+{
+ if (role->can_add == NULL || (role->can_add) (data->parent))
+ data->res = g_slist_prepend (data->res, (gpointer)role);
+}
+
+static int
+gog_object_position_cmp (GogObjectPosition pos)
+{
+ if (pos & GOG_POSITION_COMPASS)
+ return 0;
+ if (pos == GOG_POSITION_SPECIAL)
+ return 2;
+ return 1; /* GOG_POSITION_MANUAL */
+}
+
+static int
+gog_role_cmp (GogObjectRole const *a, GogObjectRole const *b)
+{
+ int index_a = gog_object_position_cmp (a->allowable_positions);
+ int index_b = gog_object_position_cmp (b->allowable_positions);
+
+ /* intentionally reverse to put SPECIAL at the top */
+ if (index_a < index_b)
+ return 1;
+ else if (index_a > index_b)
+ return -1;
+ return b->priority - a->priority;
+}
+
+static int
+gog_role_cmp_full (GogObjectRole const *a, GogObjectRole const *b)
+{
+ int res = gog_role_cmp (a, b);
+ if (res != 0)
+ return res;
+ return g_utf8_collate (a->id, b->id);
+}
+
+/**
+ * gog_object_possible_additions :
+ * @parent : a #GogObject
+ *
+ * returns a list of GogObjectRoles that could be added
+ *
+ * The resulting list needs to be freed
+ **/
+GSList *
+gog_object_possible_additions (GogObject const *parent)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (parent);
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ if (klass->roles != NULL) {
+ struct possible_add_closure data;
+ data.res = NULL;
+ data.parent = parent;
+
+ g_hash_table_foreach (klass->roles,
+ (GHFunc) cb_collect_possible_additions, &data);
+
+ return g_slist_sort (data.res, (GCompareFunc) gog_role_cmp_full);
+ }
+
+ return NULL;
+}
+
+/**
+ * gog_object_can_reorder :
+ * @obj : #GogObject
+ * @inc_ok : possibly NULL pointer.
+ * @dec_ok : possibly NULL pointer.
+ *
+ * If @obj can move forward or backward in its parents child list
+ **/
+void
+gog_object_can_reorder (GogObject const *obj, gboolean *inc_ok, gboolean *dec_ok)
+{
+ GogObject const *parent;
+ GSList *ptr;
+
+ g_return_if_fail (GOG_OBJECT (obj) != NULL);
+
+ if (inc_ok != NULL)
+ *inc_ok = FALSE;
+ if (dec_ok != NULL)
+ *dec_ok = FALSE;
+
+ if (obj->parent == NULL || gog_object_get_graph (obj) == NULL)
+ return;
+ parent = obj->parent;
+ ptr = parent->children;
+
+ g_return_if_fail (ptr != NULL);
+
+ /* find a pointer to the previous sibling */
+ if (ptr->data != obj) {
+ while (ptr->next != NULL && ptr->next->data != obj)
+ ptr = ptr->next;
+
+ g_return_if_fail (ptr->next != NULL);
+
+ if (inc_ok != NULL &&
+ !gog_role_cmp (((GogObject *)ptr->data)->role, obj->role))
+ *inc_ok = TRUE;
+
+ ptr = ptr->next;
+ }
+
+ /* ptr now points at @obj */
+ if (dec_ok != NULL && ptr->next != NULL &&
+ !gog_role_cmp (obj->role, ((GogObject *)ptr->next->data)->role))
+ *dec_ok = TRUE;
+}
+
+/**
+ * gog_object_reorder :
+ * @obj : #GogObject
+ * @inc :
+ * @goto_max :
+ *
+ * Returns the object just before @obj in the new ordering.
+ **/
+GogObject *
+gog_object_reorder (GogObject const *obj, gboolean inc, gboolean goto_max)
+{
+ GogObject *parent, *obj_follows;
+ GSList **ptr, *tmp;
+
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+
+ if (obj->parent == NULL || gog_object_get_graph (obj) == NULL)
+ return NULL;
+ parent = obj->parent;
+
+ if (inc)
+ parent->children = g_slist_reverse (parent->children);
+
+ for (ptr = &parent->children; *ptr != NULL && (*ptr)->data != obj ;)
+ ptr = &(*ptr)->next;
+
+ g_return_val_if_fail (*ptr != NULL, NULL);
+ g_return_val_if_fail ((*ptr)->next != NULL, NULL);
+
+ tmp = *ptr;
+ *ptr = tmp->next;
+ ptr = &(*ptr)->next;
+
+ while (goto_max && *ptr != NULL &&
+ !gog_role_cmp (obj->role, ((GogObject *)((*ptr)->data))->role))
+ ptr = &(*ptr)->next;
+
+ tmp->next = *ptr;
+ *ptr = tmp;
+
+ if (inc)
+ parent->children = g_slist_reverse (parent->children);
+
+ if (parent->children->data != obj) {
+ for (tmp = parent->children ; tmp->next->data != obj ; )
+ tmp = tmp->next;
+ obj_follows = tmp->data;
+ } else
+ obj_follows = NULL;
+
+ /* Pass the sibling that precedes obj, or NULL if is the head */
+ g_signal_emit (G_OBJECT (parent),
+ gog_object_signals [CHILDREN_REORDERED], 0);
+ gog_object_emit_changed (parent, TRUE);
+
+ return obj_follows;
+}
+
+/**
+ * gog_object_get_editor :
+ * @obj : #GogObject
+ * @dalloc : #GogDataAllocator
+ * @cc : #GnmCmdContext
+ *
+ **/
+gpointer
+gog_object_get_editor (GogObject *obj, GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+ g_return_val_if_fail (klass != NULL, NULL);
+ if (klass->editor) {
+ /* If there are pending updates do them before creating the editor
+ * to avoid expensive widget changes later */
+ gog_graph_force_update (gog_object_get_graph (obj));
+ return (*klass->editor) (obj, dalloc, cc);
+ }
+ return NULL;
+}
+
+/**
+ * gog_object_new_view :
+ * @obj : a #GogObject
+ * @data :
+ **/
+GogView *
+gog_object_new_view (GogObject const *obj, GogView *parent)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ if (klass->view_type != 0)
+ /* set model before parent */
+ return g_object_new (klass->view_type,
+ "model", obj,
+ "parent", parent,
+ NULL);
+
+ return NULL;
+}
+
+void
+gog_object_update (GogObject *obj)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+ GSList *ptr;
+
+ g_return_if_fail (klass != NULL);
+
+ ptr = obj->children; /* depth first */
+ for (; ptr != NULL ; ptr = ptr->next)
+ gog_object_update (ptr->data);
+
+ if (obj->needs_update) {
+ obj->needs_update = FALSE;
+ obj->being_updated = TRUE;
+ gog_debug (0, g_warning ("updating %s (%p)", G_OBJECT_TYPE_NAME (obj), obj););
+ if (klass->update != NULL)
+ (*klass->update) (obj);
+ obj->being_updated = FALSE;
+ }
+}
+
+gboolean
+gog_object_request_update (GogObject *obj)
+{
+ GogGraph *graph;
+ g_return_val_if_fail (GOG_OBJECT (obj), FALSE);
+ g_return_val_if_fail (!obj->being_updated, FALSE);
+
+ if (obj->needs_update)
+ return FALSE;
+
+ graph = gog_object_get_graph (obj);
+ if (graph == NULL) /* we are not linked into a graph yet */
+ return FALSE;
+
+ gog_graph_request_update (graph);
+ obj->needs_update = TRUE;
+
+ return TRUE;
+}
+
+void
+gog_object_emit_changed (GogObject *obj, gboolean resize)
+{
+ GogObjectClass *gog_klass;
+
+ g_return_if_fail (GOG_OBJECT (obj));
+
+ gog_klass = GOG_OBJECT_GET_CLASS (obj);
+
+ if (gog_klass->use_parent_as_proxy) {
+ obj = obj->parent;
+ if (obj != NULL) {
+ g_return_if_fail (IS_GOG_OBJECT (obj));
+ gog_object_emit_changed (obj, resize);
+ }
+ return;
+ }
+ g_signal_emit (G_OBJECT (obj),
+ gog_object_signals [CHANGED], 0, resize);
+}
+
+/******************************************************************************/
+
+/**
+ * gog_object_clear_parent :
+ * @obj : #GogObject
+ *
+ * Does _not_ unref the child, which in effect adds a ref by freeing up the ref
+ * previously associated with the parent.
+ **/
+gboolean
+gog_object_clear_parent (GogObject *obj)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+ GogObject *parent;
+
+ g_return_val_if_fail (GOG_OBJECT (obj), FALSE);
+ g_return_val_if_fail (obj->parent != NULL, FALSE);
+ g_return_val_if_fail (gog_object_is_deletable (obj), FALSE);
+
+ parent = obj->parent;
+ g_signal_emit (G_OBJECT (parent),
+ gog_object_signals [CHILD_REMOVED], 0, obj);
+ (*klass->parent_changed) (obj, FALSE);
+
+ if (obj->role != NULL && obj->role->pre_remove != NULL)
+ (obj->role->pre_remove) (parent, obj);
+
+ parent->children = g_slist_remove (parent->children, obj);
+ obj->parent = NULL;
+
+ if (obj->role != NULL && obj->role->post_remove != NULL)
+ (obj->role->post_remove) (parent, obj);
+
+ obj->role = NULL;
+
+ return TRUE;
+}
+
+/**
+ * gog_object_set_parent :
+ * @child : #GogObject.
+ * @parent : #GogObject.
+ * @id : optionally %NULL.
+ * @role : a static string that can be sent to @parent::add
+ *
+ * Absorbs a ref to @child
+ **/
+gboolean
+gog_object_set_parent (GogObject *child, GogObject *parent,
+ GogObjectRole const *role, char *id)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (child);
+ GSList **step;
+
+ g_return_val_if_fail (GOG_OBJECT (child), FALSE);
+ g_return_val_if_fail (child->parent == NULL, FALSE);
+ g_return_val_if_fail (role != NULL, FALSE);
+
+ child->parent = parent;
+ child->role = role;
+ child->position = role->default_position;
+
+ /* Insert sorted based on hokey little ordering */
+ step = &parent->children;
+ while (*step != NULL &&
+ gog_role_cmp_full (GOG_OBJECT ((*step)->data)->role, role) >= 0)
+ step = &((*step)->next);
+ *step = g_slist_prepend (*step, child);
+
+ g_free (child->id);
+ g_free (child->user_name);
+ child->id = (id != NULL) ? id : gog_object_generate_name (child);
+ if (child->id == NULL) child->id = g_strdup ("BROKEN");
+
+ if (role->post_add != NULL)
+ (role->post_add) (parent, child);
+ (*klass->parent_changed) (child, TRUE);
+
+ g_signal_emit (G_OBJECT (parent),
+ gog_object_signals [CHILD_ADDED], 0, child);
+
+ return TRUE;
+}
+
+GogObject *
+gog_object_add_by_role (GogObject *parent, GogObjectRole const *role, GogObject *child)
+{
+ GType is_a;
+ gboolean const explicitly_typed_role = (child != NULL);
+
+ g_return_val_if_fail (role != NULL, NULL);
+ g_return_val_if_fail (GOG_OBJECT (parent) != NULL, NULL);
+
+ is_a = g_type_from_name (role->is_a_typename);
+
+ g_return_val_if_fail (is_a != 0, NULL);
+
+ if (child == NULL)
+ child = (role->allocate)
+ ? (role->allocate) (parent)
+ : g_object_new (is_a, NULL);
+
+ g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (child, is_a), NULL);
+ child->explicitly_typed_role = explicitly_typed_role;
+ if (gog_object_set_parent (child, parent, role, NULL))
+ return child;
+ g_object_unref (child);
+ return NULL;
+}
+
+/**
+ * gog_object_add_by_name :
+ * @parent : #GogObject
+ * @role :
+ * @child : optionally null #GogObject
+ *
+ * Returns a newly created child of @parent in @role. If @child is provided,
+ * it is assumed to be an unaffiliated object that will be assigned in @role.
+ * On failure return NULL.
+ **/
+GogObject *
+gog_object_add_by_name (GogObject *parent,
+ char const *role, GogObject *child)
+{
+ return gog_object_add_by_role (parent,
+ gog_object_find_role_by_name (parent, role), child);
+}
+
+GogObjectPosition
+gog_object_get_pos (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, GOG_POSITION_SPECIAL);
+ return obj->position;
+}
+
+/**
+ * gog_object_set_pos :
+ * @obj : #GogObject
+ * @pos : #GogObjectPosition
+ *
+ * Attempts to set the position of @obj to @pos.
+ * Returns TRUE the new position is permitted.
+ **/
+gboolean
+gog_object_set_pos (GogObject *obj, GogObjectPosition pos)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, FALSE);
+ g_return_val_if_fail (obj->role != NULL, FALSE);
+
+ if (obj->position == pos)
+ return TRUE;
+
+ if ((obj->role->allowable_positions & pos) !=
+ (pos & (GOG_POSITION_COMPASS|GOG_POSITION_ANY_MANUAL)))
+ return FALSE;
+ obj->position = pos;
+ gog_object_emit_changed (obj, TRUE);
+ return TRUE;
+}
+
+GogObjectRole const *
+gog_object_find_role_by_name (GogObject const *obj, char const *role)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ return g_hash_table_lookup (klass->roles, role);
+}
+
+void
+gog_object_register_roles (GogObjectClass *klass,
+ GogObjectRole const *roles, unsigned n_roles)
+{
+ unsigned i;
+ if (klass->roles == NULL)
+ klass->roles = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (i = 0 ; i < n_roles ; i++) {
+ g_return_if_fail (g_hash_table_lookup (klass->roles,
+ (gpointer )roles[i].id) == NULL);
+ g_hash_table_replace (klass->roles,
+ (gpointer )roles[i].id, (gpointer) (roles + i));
+ }
+}
--- /dev/null
+++ lib/goffice/graph/gog-outlined-object.c
@@ -0,0 +1,171 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-outlined-object.c : some utility classes for objects with outlines.
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-outlined-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/utils/go-units.h>
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+enum {
+ OUTLINED_OBJECT_PROP_0,
+ OUTLINED_OBJECT_PROP_PADDING_PTS
+};
+
+static void
+gog_outlined_object_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (obj);
+
+ switch (param_id) {
+ case OUTLINED_OBJECT_PROP_PADDING_PTS :
+ goo->padding_pts = g_value_get_double (value);
+ gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+}
+
+static void
+gog_outlined_object_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (obj);
+
+ switch (param_id) {
+ case OUTLINED_OBJECT_PROP_PADDING_PTS:
+ g_value_set_double (value, goo->padding_pts);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_outlined_object_class_init (GObjectClass *gobject_klass)
+{
+ gobject_klass->set_property = gog_outlined_object_set_property;
+ gobject_klass->get_property = gog_outlined_object_get_property;
+
+ g_object_class_install_property (gobject_klass, OUTLINED_OBJECT_PROP_PADDING_PTS,
+ g_param_spec_double ("padding_pts", "Padding Pts",
+ "# of pts separating charts in the grid.",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+}
+
+static void
+gog_outlined_object_init (GogOutlinedObject *goo)
+{
+ goo->padding_pts = GO_CM_TO_PT ((double).25);
+}
+
+GSF_CLASS (GogOutlinedObject, gog_outlined_object,
+ gog_outlined_object_class_init, gog_outlined_object_init,
+ GOG_STYLED_OBJECT_TYPE)
+
+double
+gog_outlined_object_get_pad (GogOutlinedObject const *goo)
+{
+ g_return_val_if_fail (GOG_OUTLINED_OBJECT (goo) != NULL, 0.);
+ return goo->padding_pts;
+}
+
+/*****************************************************************************/
+
+static GogViewClass *oview_parent_klass;
+
+static void
+gog_outlined_view_size_request (GogView *v, GogViewRequisition *req)
+{
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (v->model);
+ double outline = gog_renderer_line_size (v->renderer,
+ goo->base.style->outline.width);
+ double is_outline_visible = gog_style_is_outline_visible (goo->base.style);
+
+ if (goo->base.style->fill.type != GOG_FILL_STYLE_NONE || is_outline_visible) {
+ req->w += outline * 2 +
+ gog_renderer_pt2r_y (v->renderer, goo->padding_pts);
+ req->h += outline * 2 +
+ gog_renderer_pt2r_y (v->renderer, goo->padding_pts);
+ }
+}
+
+static void
+gog_outlined_view_size_allocate (GogView *v, GogViewAllocation const *a)
+{
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (v->model);
+ GogViewAllocation res = *a;
+ double outline = gog_renderer_line_size (v->renderer,
+ goo->base.style->outline.width);
+ double is_outline_visible = gog_style_is_outline_visible (goo->base.style);
+
+ /* We only need internal padding if there is an outline or a pattern */
+ if (goo->base.style->fill.type != GOG_FILL_STYLE_NONE || is_outline_visible) {
+ double pad_x = gog_renderer_pt2r_x (v->renderer, goo->padding_pts);
+ double pad_y = gog_renderer_pt2r_y (v->renderer, goo->padding_pts);
+ res.x += outline + pad_x/2;
+ res.y += outline + pad_y/2;
+ res.w -= outline * 2. + pad_x;
+ res.h -= outline * 2. + pad_y;
+ }
+ (oview_parent_klass->size_allocate) (v, &res);
+}
+
+static void
+gog_outlined_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogOutlinedViewClass *klass = GOG_OUTLINED_VIEW_GET_CLASS (view);
+
+ GogStyledObject *sobj = GOG_STYLED_OBJECT (view->model);
+ gog_renderer_push_style (view->renderer, sobj->style);
+ gog_renderer_draw_sharp_rectangle (view->renderer, &view->allocation, NULL);
+ gog_renderer_pop_style (view->renderer);
+
+ if (klass->call_parent_render)
+ (oview_parent_klass->render) (view, bbox);
+}
+
+static void
+gog_outlined_view_class_init (GogOutlinedViewClass *oview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) oview_klass;
+
+ oview_parent_klass = g_type_class_peek_parent (view_klass);
+ view_klass->size_request = gog_outlined_view_size_request;
+ view_klass->size_allocate = gog_outlined_view_size_allocate;
+ view_klass->render = gog_outlined_view_render;
+
+ oview_klass->call_parent_render = TRUE;
+}
+
+GSF_CLASS (GogOutlinedView, gog_outlined_view,
+ gog_outlined_view_class_init, NULL,
+ GOG_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-styled-object.h
@@ -0,0 +1,59 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-styled-object.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_STYLED_OBJECT_H
+#define GOG_STYLED_OBJECT_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogStyledObject {
+ GogObject base;
+
+ GogStyle *style;
+};
+
+typedef struct {
+ GogObjectClass base;
+
+ /* virtual */
+ void (*init_style) (GogStyledObject *obj, GogStyle *style);
+
+ /* signal */
+ void (*style_changed) (GogStyledObject *obj, GogStyle const *new_style);
+} GogStyledObjectClass;
+
+#define GOG_STYLED_OBJECT_TYPE (gog_styled_object_get_type ())
+#define GOG_STYLED_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_STYLED_OBJECT_TYPE, GogStyledObject))
+#define IS_GOG_STYLED_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_STYLED_OBJECT_TYPE))
+#define GOG_STYLED_OBJECT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_STYLED_OBJECT_TYPE, GogStyledObjectClass))
+
+GType gog_styled_object_get_type (void);
+gboolean gog_styled_object_set_style (GogStyledObject *gso, GogStyle *style);
+GogStyle *gog_styled_object_get_style (GogStyledObject *gso);
+GogStyle *gog_styled_object_get_auto_style (GogStyledObject *gso);
+void gog_styled_object_style_changed (GogStyledObject *gso);
+void gog_styled_object_apply_theme (GogStyledObject *gso, GogStyle *style);
+
+G_END_DECLS
+
+#endif /* GOG_STYLED_OBJECT_H */
--- /dev/null
+++ lib/goffice/graph/gog-renderer-gnome-print.c
@@ -0,0 +1,646 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-gnome-print.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+/* <style.h> is only needed for gnm_font_find_closest_from_weight_slant */
+#include <style.h>
+#include <goffice/graph/gog-renderer-gnome-print.h>
+#include <goffice/graph/gog-renderer-impl.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-units.h>
+#include <goffice/utils/go-font.h>
+#include <goffice/utils/go-marker.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+#include <libart_lgpl/art_render_gradient.h>
+#include <libart_lgpl/art_render_svp.h>
+
+#include <math.h>
+#include <string.h>
+
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+#include <libgnomeprint/gnome-print-pango.h>
+#endif
+
+#define GOG_RENDERER_GNOME_PRINT_TYPE (gog_renderer_gnome_print_get_type ())
+#define GOG_RENDERER_GNOME_PRINT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RENDERER_GNOME_PRINT_TYPE, GogRendererGnomePrint))
+#define IS_GOG_RENDERER_GNOME_PRINT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RENDERER_GNOME_PRINT_TYPE))
+
+typedef struct _GogRendererGnomePrint GogRendererGnomePrint;
+
+struct _GogRendererGnomePrint {
+ GogRenderer base;
+
+ GPtrArray *fonts;
+ GnomePrintContext *gp_context;
+ PangoLayout *layout;
+};
+
+typedef GogRendererClass GogRendererGnomePrintClass;
+
+static GObjectClass *parent_klass;
+
+static GType gog_renderer_gnome_print_get_type (void);
+
+static void
+gog_renderer_gnome_print_finalize (GObject *obj)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (obj);
+
+ if (prend->gp_context != NULL) {
+ g_object_unref (prend->gp_context);
+ prend->gp_context = NULL;
+ }
+
+ if (prend->layout) {
+ g_object_unref (prend->layout);
+ prend->layout = NULL;
+ }
+
+ if (prend->fonts != NULL) {
+ int i;
+ GnomeFont *font;
+ for (i = prend->fonts->len; i-- > 0 ; ) {
+ font = g_ptr_array_index (prend->fonts, i);
+ if (font != NULL)
+ gnome_font_unref (font);
+ }
+
+ g_ptr_array_free (prend->fonts, TRUE);
+ prend->fonts = NULL;
+ }
+
+ (*parent_klass->finalize) (obj);
+}
+
+/*
+ * print_make_rectangle_path
+ * @pc print context
+ * @left left side x coordinate
+ * @bottom bottom side y coordinate
+ * @right right side x coordinate
+ * @top top side y coordinate
+ *
+ * Make a rectangular path.
+ * */
+
+static void
+print_make_rectangle_path (GnomePrintContext *pc,
+ double left, double bottom,
+ double right, double top)
+{
+ g_return_if_fail (pc != NULL);
+
+ gnome_print_newpath (pc);
+ gnome_print_moveto (pc, left, bottom);
+ gnome_print_lineto (pc, left, top);
+ gnome_print_lineto (pc, right, top);
+ gnome_print_lineto (pc, right, bottom);
+ gnome_print_closepath (pc);
+}
+
+static GnomeFont *
+get_font (GogRendererGnomePrint *prend, GOFont const *gf)
+{
+ GnomeFont *res = NULL;
+
+ if (gf->font_index < (int)prend->fonts->len)
+ res = g_ptr_array_index (prend->fonts, gf->font_index);
+ else
+ g_ptr_array_set_size (prend->fonts, gf->font_index+1);
+
+ if (res == NULL) {
+ PangoFontDescription *desc = gf->desc;
+ res = gnm_font_find_closest_from_weight_slant (
+ pango_font_description_get_family (desc),
+ pango_font_description_get_weight (desc) >= PANGO_WEIGHT_BOLD ? GNOME_FONT_BOLD : GNOME_FONT_REGULAR,
+ pango_font_description_get_style (desc) != PANGO_STYLE_NORMAL,
+ prend->base.zoom * pango_font_description_get_size (desc) / PANGO_SCALE);
+ g_ptr_array_index (prend->fonts, gf->font_index) = res;
+ }
+
+ return res;
+}
+
+static void
+set_color (GogRendererGnomePrint *prend, GOColor color)
+{
+ double r = ((double) UINT_RGBA_R (color)) / 255.;
+ double g = ((double) UINT_RGBA_G (color)) / 255.;
+ double b = ((double) UINT_RGBA_B (color)) / 255.;
+ double a = ((double) UINT_RGBA_A (color)) / 255.;
+ gnome_print_setrgbcolor (prend->gp_context, r, g, b);
+ gnome_print_setopacity (prend->gp_context, a);
+}
+
+static void
+gog_renderer_gnome_print_clip_push (GogRenderer *rend, GogRendererClip *clip)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (rend);
+
+ gnome_print_gsave (prend->gp_context);
+ print_make_rectangle_path (prend->gp_context,
+ clip->area.x, -clip->area.y,
+ clip->area.w + clip->area.x,
+ -clip->area.h - clip->area.y);
+ gnome_print_clip (prend->gp_context);
+}
+
+static void
+gog_renderer_gnome_print_clip_pop (GogRenderer *rend, GogRendererClip *clip)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (rend);
+
+ gnome_print_grestore (prend->gp_context);
+}
+
+static void
+draw_path (GogRendererGnomePrint *prend, ArtVpath const *path)
+{
+ gnome_print_newpath (prend->gp_context);
+ for ( ; path->code != ART_END ; path++)
+ switch (path->code) {
+ case ART_MOVETO_OPEN :
+ case ART_MOVETO :
+ gnome_print_moveto (prend->gp_context,
+ path->x, -path->y);
+ break;
+ case ART_LINETO :
+ gnome_print_lineto (prend->gp_context,
+ path->x, -path->y);
+ break;
+ default :
+ break;
+ }
+}
+
+static void
+setup_clip (GogRendererGnomePrint *prend, GogViewAllocation const *bound)
+{
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_moveto (prend->gp_context, bound->x, -bound->y);
+ gnome_print_lineto (prend->gp_context, bound->x + bound->w, -bound->y);
+ gnome_print_lineto (prend->gp_context, bound->x + bound->w, -bound->y - bound->h);
+ gnome_print_lineto (prend->gp_context, bound->x, -bound->y - bound->h);
+ gnome_print_clip (prend->gp_context);
+}
+
+static void
+set_dash (GogRendererGnomePrint *prend, ArtVpathDash *dash)
+{
+ if (dash == NULL ||
+ dash->n_dash == 0)
+ gnome_print_setdash (prend->gp_context, 0, NULL, 0.);
+ else
+ gnome_print_setdash (prend->gp_context, dash->n_dash, dash->dash, dash->offset);
+}
+
+static void
+gog_renderer_gnome_print_draw_path (GogRenderer *renderer, ArtVpath const *path,
+ GogViewAllocation const *bound)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (renderer);
+ GogStyle const *style = renderer->cur_style;
+
+ if (style->line.dash_type == GO_LINE_NONE)
+ return;
+
+ set_color (prend, style->line.color);
+ set_dash (prend, renderer->line_dash);
+ gnome_print_setlinewidth (prend->gp_context,
+ gog_renderer_line_size (renderer, style->line.width));
+
+ if (bound != NULL)
+ setup_clip (prend, bound);
+
+ if (style->line.dash_type != GO_LINE_SOLID && renderer->cur_clip != NULL) {
+ ArtVpath *clipped = go_line_clip_vpath (path, &renderer->cur_clip->area);
+ draw_path (prend, clipped);
+ g_free (clipped);
+ } else
+ draw_path (prend, path);
+
+ gnome_print_stroke (prend->gp_context);
+
+ if (bound != NULL)
+ gnome_print_grestore (prend->gp_context);
+}
+
+static void
+print_image (GogRendererGnomePrint *prend, GdkPixbuf *image, int w, int h)
+{
+ if (gdk_pixbuf_get_has_alpha (image))
+ gnome_print_rgbaimage (prend->gp_context,
+ gdk_pixbuf_get_pixels (image), w, h,
+ gdk_pixbuf_get_rowstride (image));
+ else
+ gnome_print_rgbimage (prend->gp_context,
+ gdk_pixbuf_get_pixels (image), w, h,
+ gdk_pixbuf_get_rowstride (image));
+}
+
+#define PIXBUF_SIZE 1024
+static void
+gog_renderer_gnome_print_draw_polygon (GogRenderer *renderer, ArtVpath const *path,
+ gboolean narrow, GogViewAllocation const *bound)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (renderer);
+ GogStyle const *style = renderer->cur_style;
+ gboolean with_outline = (!narrow && style->outline.dash_type != GO_LINE_NONE);
+ GdkPixbuf *image;
+ ArtDRect bbox;
+ ArtRender *render;
+ gint i, j, imax, jmax, w, h, x, y;
+ GOColor color;
+ ArtGradientLinear gradient;
+ ArtGradientStop stops[2];
+
+ if (bound != NULL)
+ setup_clip (prend, bound);
+
+ if (style->fill.type != GOG_FILL_STYLE_NONE || with_outline) {
+ if (style->outline.dash_type != GO_LINE_SOLID && renderer->cur_clip != NULL) {
+ ArtVpath *clipped = go_line_clip_vpath (path, &renderer->cur_clip->area);
+ draw_path (prend, clipped);
+ g_free (clipped);
+ } else
+ draw_path (prend, path);
+ gnome_print_closepath (prend->gp_context);
+ }
+
+ if (style->fill.type != GOG_FILL_STYLE_NONE) {
+
+ art_vpath_bbox_drect (path, &bbox);
+
+ switch (style->fill.type) {
+ case GOG_FILL_STYLE_PATTERN:
+ gnome_print_gsave (prend->gp_context);
+ if (go_pattern_is_solid (&style->fill.pattern, &color)) {
+ set_color (prend, color);
+ gnome_print_fill (prend->gp_context);
+ } else {
+ ArtSVP *fill = art_svp_from_vpath ((ArtVpath *)path);
+ image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, bbox.x1, bbox.y1);
+ gdk_pixbuf_fill (image, 0);
+ go_pattern_render_svp (&style->fill.pattern,
+ fill, 0, 0, bbox.x1, bbox.y1,
+ gdk_pixbuf_get_pixels (image),
+ gdk_pixbuf_get_rowstride (image));
+
+ gnome_print_translate (prend->gp_context, 0, - bbox.y1);
+ gnome_print_scale (prend->gp_context, bbox.x1, bbox.y1);
+ gnome_print_rgbaimage (prend->gp_context,
+ gdk_pixbuf_get_pixels (image),
+ gdk_pixbuf_get_width (image),
+ gdk_pixbuf_get_height (image),
+ gdk_pixbuf_get_rowstride (image));
+
+ art_free (fill);
+ g_object_unref (image);
+ }
+ gnome_print_grestore (prend->gp_context);
+ break;
+
+ case GOG_FILL_STYLE_GRADIENT:
+ image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, PIXBUF_SIZE, PIXBUF_SIZE);
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_clip (prend->gp_context);
+ render = art_render_new (0, 0, PIXBUF_SIZE, PIXBUF_SIZE,
+ gdk_pixbuf_get_pixels (image),
+ gdk_pixbuf_get_rowstride (image),
+ gdk_pixbuf_get_n_channels (image) - 1,
+ 8, ART_ALPHA_SEPARATE, NULL);
+
+ go_gradient_setup (&gradient,
+ style->fill.gradient.dir,
+ style->fill.pattern.back, style->fill.pattern.fore,
+ 0, 0, PIXBUF_SIZE, PIXBUF_SIZE,
+ stops);
+ art_render_gradient_linear (render,
+ &gradient, ART_FILTER_NEAREST);
+ art_render_invoke (render);
+ gnome_print_translate (prend->gp_context, bbox.x0, - bbox.y1);
+ gnome_print_scale (prend->gp_context, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0);
+ gnome_print_rgbaimage (prend->gp_context,
+ gdk_pixbuf_get_pixels (image),
+ gdk_pixbuf_get_width (image),
+ gdk_pixbuf_get_height (image),
+ gdk_pixbuf_get_rowstride (image));
+ gnome_print_grestore (prend->gp_context);
+ g_object_unref (image);
+ break;
+
+ case GOG_FILL_STYLE_IMAGE:
+ image = style->fill.image.image;
+ if (image == NULL)
+ break;
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_clip (prend->gp_context);
+ switch (style->fill.image.type) {
+ case GOG_IMAGE_CENTERED:
+ w = (bbox.x1 - bbox.x0) - gdk_pixbuf_get_width (image);
+ if (w > 0) w /= 2.; else w = 0.;
+ h = (bbox.y1 - bbox.y0) - gdk_pixbuf_get_height (image);
+ if (h > 0) h /= 2.; else h = 0.;
+
+ gnome_print_translate (prend->gp_context,
+ bbox.x0 + w, - bbox.y1 - h);
+ print_image (prend, image,
+ gdk_pixbuf_get_width (image),
+ gdk_pixbuf_get_height (image));
+ break;
+ case GOG_IMAGE_STRETCHED:
+ gnome_print_translate (prend->gp_context, bbox.x0, - bbox.y1);
+ gnome_print_scale (prend->gp_context, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0);
+ print_image (prend, image,
+ gdk_pixbuf_get_width (image),
+ gdk_pixbuf_get_height (image));
+ break;
+
+ case GOG_IMAGE_WALLPAPER:
+ imax = (bbox.x1 - bbox.x0) / (w = gdk_pixbuf_get_width (image));
+ jmax = (bbox.y1 - bbox.y0) / (h = gdk_pixbuf_get_height (image));
+ x = 0;
+ for (i = 0; i < imax; i++) {
+ y = 0;
+ for (j = 0; j < jmax; j++) {
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_translate (prend->gp_context,
+ bbox.x0 + x,
+ - y - h - bbox.y0);
+ gnome_print_scale (prend->gp_context, w, h);
+ print_image (prend, image, w, h);
+ gnome_print_grestore (prend->gp_context);
+ y += h;
+ }
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_translate (prend->gp_context,
+ bbox.x0 + x,
+ - y - (int)(bbox.y1 - bbox.y0) % h - bbox.y0);
+ gnome_print_scale (prend->gp_context, w, (int)(bbox.y1 - bbox.y0) % h);
+ print_image (prend, image, w, (int)(bbox.y1 - bbox.y0) % h);
+ gnome_print_grestore (prend->gp_context);
+ x += w;
+ }
+ y = 0;
+ for (j = 0; j < jmax; j++) {
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_translate (prend->gp_context, bbox.x0 + x, - y - h - bbox.y0);
+ gnome_print_scale (prend->gp_context, (int)(bbox.x1 - bbox.x0) % w, h);
+ print_image (prend, image, (int)(bbox.x1 - bbox.x0) % w, h);
+ gnome_print_grestore (prend->gp_context);
+ y += h;
+ }
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_translate (prend->gp_context, bbox.x0 + x, - y - (int)(bbox.y1 - bbox.y0) % h - bbox.y0);
+ gnome_print_scale (prend->gp_context, (int)(bbox.x1 - bbox.x0) % w, (int)(bbox.y1 - bbox.y0) % h);
+ print_image (prend, image, (int)(bbox.x1 - bbox.x0) % w, (int)(bbox.y1 - bbox.y0) % h);
+ gnome_print_grestore (prend->gp_context);
+ break;
+ }
+ gnome_print_grestore (prend->gp_context);
+ break;
+
+ case GOG_FILL_STYLE_NONE:
+ break; /* impossible */
+ }
+ }
+
+ if (with_outline) {
+ set_color (prend, style->outline.color);
+ set_dash (prend, renderer->outline_dash);
+ gnome_print_setlinewidth (prend->gp_context,
+ gog_renderer_line_size (renderer, style->outline.width));
+ gnome_print_stroke (prend->gp_context);
+ }
+ if (bound != NULL)
+ gnome_print_grestore (prend->gp_context);
+}
+
+static void
+gog_renderer_gnome_print_draw_text (GogRenderer *rend, char const *text,
+ GogViewAllocation const *pos, GtkAnchorType anchor,
+ GogViewAllocation *result)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (rend);
+ GnomeFont *gfont = get_font (prend, rend->cur_style->font.font);
+
+ if (text[0]) {
+ double x, y, w, h;
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+ int iw, ih;
+ const double dummy_dpi = 300; /* FIXME: What exactly is this? */
+ PangoFontDescription *pango_font = /* FIXME: can i get the pango font directly ? */
+ gnome_font_get_pango_description (gfont, dummy_dpi);
+
+ pango_layout_set_font_description (prend->layout, pango_font);
+ pango_layout_set_text (prend->layout, text, -1);
+ pango_layout_get_size (prend->layout, &iw, &ih);
+ w = iw / (double)PANGO_SCALE;
+ h = ih / (double)PANGO_SCALE;
+#else
+ /* This code will die when we require libgnomeprint 2.8 */
+ double font_ascent = gnome_font_get_ascender (gfont);
+ w = gnome_font_get_width_utf8 (gfont, text);
+ h = font_ascent + gnome_font_get_descender (gfont);
+#endif
+ x = pos->x;
+ switch (anchor) {
+ case GTK_ANCHOR_CENTER : case GTK_ANCHOR_N : case GTK_ANCHOR_S :
+ x -= w / 2.0;
+ break;
+ case GTK_ANCHOR_NE : case GTK_ANCHOR_SE : case GTK_ANCHOR_E :
+ x -= w;
+ break;
+ default : break;
+ }
+ if (x <= 0)
+ x = 0;
+
+ y = pos->y;
+ switch (anchor) {
+ case GTK_ANCHOR_CENTER : case GTK_ANCHOR_E : case GTK_ANCHOR_W :
+ y -= h / 2.0;
+ break;
+ case GTK_ANCHOR_SE : case GTK_ANCHOR_S : case GTK_ANCHOR_SW :
+ y -= h;
+ break;
+ default : break;
+ }
+ if (y <= 0)
+ y = 0;
+
+#warning "add clipping"
+
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+ gnome_print_moveto (prend->gp_context,x, -y);
+ gnome_print_pango_layout (prend->gp_context, prend->layout);
+ pango_font_description_free (pango_font);
+#else
+ /* This code will die when we require libgnomeprint 2.8 */
+ gnome_print_setfont (prend->gp_context, gfont);
+ gnome_print_moveto (prend->gp_context, x, -y - font_ascent);
+ gnome_print_show (prend->gp_context, text);
+#endif
+ if (result != NULL) {
+ result->x = x;
+ result->y = y;
+ result->w = w;
+ result->h = h;
+ }
+ }
+}
+
+static void
+gog_renderer_gnome_print_measure_text (GogRenderer *rend,
+ char const *text, GogViewRequisition *size)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (rend);
+ GnomeFont *gfont = get_font (prend, rend->cur_style->font.font);
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+ int iw, ih;
+ const double dummy_dpi = 300; /* FIXME: What exactly is this? */
+ PangoFontDescription *pango_font = /* FIXME: can i get the pango font directly ? */
+ gnome_font_get_pango_description (gfont, dummy_dpi);
+
+ pango_layout_set_font_description (prend->layout, pango_font);
+ pango_layout_set_text (prend->layout, text, -1);
+ pango_layout_get_size (prend->layout, &iw, &ih);
+ size->w = iw / (double)PANGO_SCALE;
+ size->h = ih / (double)PANGO_SCALE;
+#else
+ size->w = gnome_font_get_width_utf8 (gfont, text);
+ size->h = gnome_font_get_ascender (gfont) - gnome_font_get_descender (gfont);
+#endif
+}
+
+static void
+gog_renderer_gnome_print_draw_marker (GogRenderer *renderer, double x, double y)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (renderer);
+ GOMarker *marker = renderer->cur_style->marker.mark;
+ ArtVpath const *outline_path_raw, *fill_path_raw;
+ ArtVpath *outline_path, *fill_path;
+ double scaling[6], translation[6], affine[6];
+ double half_size;
+
+ g_return_if_fail (marker != NULL);
+
+ go_marker_get_paths (marker, &outline_path_raw, &fill_path_raw);
+
+ if ((outline_path_raw == NULL) ||
+ (fill_path_raw == NULL))
+ return;
+
+ gnome_print_gsave (prend->gp_context);
+
+ half_size = gog_renderer_line_size (renderer, marker->size) / 2.0;
+ art_affine_scale (scaling, half_size, half_size);
+ art_affine_translate (translation, x, y);
+ art_affine_multiply (affine, scaling, translation);
+
+ outline_path = art_vpath_affine_transform (outline_path_raw, affine);
+ fill_path = art_vpath_affine_transform (fill_path_raw, affine);
+
+ gnome_print_setlinecap (prend->gp_context, ART_PATH_STROKE_CAP_ROUND);
+ set_color (prend, marker->fill_color);
+ draw_path (prend, fill_path);
+ gnome_print_closepath (prend->gp_context);
+ gnome_print_fill (prend->gp_context);
+
+ set_color (prend, marker->outline_color);
+ gnome_print_setlinewidth (prend->gp_context,
+ gog_renderer_line_size (renderer,
+ go_marker_get_outline_width (marker)));
+ draw_path (prend, outline_path);
+ gnome_print_stroke (prend->gp_context);
+ gnome_print_newpath (prend->gp_context);
+
+ gnome_print_grestore (prend->gp_context);
+
+ g_free (outline_path);
+ g_free (fill_path);
+}
+
+static void
+gog_renderer_gnome_print_class_init (GogRendererClass *rend_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) rend_klass;
+
+ parent_klass = g_type_class_peek_parent (rend_klass);
+ gobject_klass->finalize = gog_renderer_gnome_print_finalize;
+ rend_klass->clip_push = gog_renderer_gnome_print_clip_push;
+ rend_klass->clip_pop = gog_renderer_gnome_print_clip_pop;
+ rend_klass->draw_path = gog_renderer_gnome_print_draw_path;
+ rend_klass->draw_polygon = gog_renderer_gnome_print_draw_polygon;
+ rend_klass->draw_text = gog_renderer_gnome_print_draw_text;
+ rend_klass->draw_marker = gog_renderer_gnome_print_draw_marker;
+ rend_klass->measure_text = gog_renderer_gnome_print_measure_text;
+}
+
+static void
+gog_renderer_gnome_print_init (GogRendererGnomePrint *prend)
+{
+ prend->gp_context = NULL;
+ prend->fonts = g_ptr_array_new ();
+}
+
+static GSF_CLASS (GogRendererGnomePrint, gog_renderer_gnome_print,
+ gog_renderer_gnome_print_class_init, gog_renderer_gnome_print_init,
+ GOG_RENDERER_TYPE)
+
+void
+gog_graph_print_to_gnome_print (GogGraph *graph,
+ GnomePrintContext *gp_context,
+ double width, double height)
+{
+ GogViewAllocation allocation;
+ GogRendererGnomePrint *prend =
+ g_object_new (GOG_RENDERER_GNOME_PRINT_TYPE,
+ "model", graph,
+ "zoom", 1.,
+ NULL);
+ prend->gp_context = g_object_ref (gp_context);
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+ prend->layout = gnome_print_pango_create_layout (prend->gp_context);
+#else
+ prend->layout = 0;
+#endif
+ allocation.x = 0.;
+ allocation.y = 0.;
+ allocation.w = width;
+ allocation.h = height;
+ gog_view_size_allocate (prend->base.view, &allocation);
+
+ /* FIXME FIXME FIXME this is a workaround for a bug in libgnomeprint
+ * where line with width == 1.0 don't scale properly before an other
+ * line width is set.
+ *
+ * http://bugzilla.gnome.org/show_bug.cgi?id=149452
+ */
+ gnome_print_setlinewidth (prend->gp_context, 0.1);
+
+ gog_view_render (prend->base.view, NULL);
+ g_object_unref (prend);
+}
--- /dev/null
+++ lib/goffice/graph/gog-plot-engine.h
@@ -0,0 +1,59 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-plot-engine.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_PLOT_ENGINE_H
+#define GOG_PLOT_ENGINE_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogPlotType {
+ GogPlotFamily *family;
+ char *engine;
+
+ char *name, *sample_image_file;
+ char *description; /* untranslated */
+ int col, row;
+
+ GHashTable *properties;
+};
+
+struct _GogPlotFamily {
+ char *name, *sample_image_file;
+
+ GHashTable *types;
+};
+
+/* GogPlotFamily hashed by name */
+GHashTable const *gog_plot_families (void);
+GogPlotFamily *gog_plot_family_by_name (char const *name);
+GogPlotFamily *gog_plot_family_register (char const *name, char const *sample_image_file);
+GogPlotType *gog_plot_type_register (GogPlotFamily *famlily, int col, int row,
+ char const *name, char const *sample_image_file,
+ char const *description, char const *engine);
+
+void gog_plugin_services_init (void);
+void gog_plugin_services_shutdown (void);
+
+G_END_DECLS
+
+#endif /* GOG_PLOT_ENGINE_H */
--- /dev/null
+++ lib/goffice/graph/gog-grid.h
@@ -0,0 +1,37 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-grid.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_GRID_H
+#define GOG_GRID_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_GRID_TYPE (gog_grid_get_type ())
+#define GOG_GRID(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRID_TYPE, GogGrid))
+#define IS_GOG_GRID(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRID_TYPE))
+
+GType gog_grid_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_GRID_H */
--- /dev/null
+++ lib/goffice/graph/gog-renderer-pixbuf.c
@@ -0,0 +1,875 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-pixbuf.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-renderer-pixbuf.h>
+#include <goffice/graph/gog-renderer-impl.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-font.h>
+#include <goffice/utils/go-marker.h>
+#include <goffice/utils/go-units.h>
+#include <goffice/utils/go-math.h>
+
+#include <libart_lgpl/art_render_gradient.h>
+#include <libart_lgpl/art_render_svp.h>
+#include <libart_lgpl/art_render_mask.h>
+#include <pango/pangoft2.h>
+#include <gsf/gsf-impl-utils.h>
+//#include <src/application.h>
+#include <application.h>
+
+#include <math.h>
+
+struct _GogRendererPixbuf {
+ GogRenderer base;
+
+ int w, h;
+ int x_offset, y_offset;
+ GdkPixbuf *buffer;
+ guchar *pixels; /* from pixbuf */
+ int rowstride;
+
+ PangoContext *pango_context;
+ PangoLayout *pango_layout;
+};
+
+typedef GogRendererClass GogRendererPixbufClass;
+
+static GObjectClass *parent_klass;
+
+static void
+gog_renderer_pixbuf_finalize (GObject *obj)
+{
+ GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (obj);
+
+ if (prend->buffer != NULL) {
+ g_object_unref (prend->buffer);
+ prend->buffer = NULL;
+ }
+
+ if (prend->pango_layout != NULL) {
+ g_object_unref (prend->pango_layout);
+ prend->pango_layout = NULL;
+ }
+
+ if (prend->pango_context != NULL) {
+#ifdef HAVE_PANGO_CONTEXT_GET_FONT_MAP
+ /* See http://bugzilla.gnome.org/show_bug.cgi?id=143542 */
+ go_pango_fc_font_map_cache_clear (PANGO_FC_FONT_MAP (pango_context_get_font_map (prend->pango_context)));
+#endif
+ g_object_unref (prend->pango_context);
+ prend->pango_context = NULL;
+ }
+
+ (*parent_klass->finalize) (obj);
+}
+
+typedef struct
+{
+ GdkPixbuf *buffer;
+ double x_offset;
+ double y_offset;
+} ClipData;
+
+static void
+gog_renderer_pixbuf_clip_push (GogRenderer *rend, GogRendererClip *clip)
+{
+ ClipData *clip_data;
+ GdkRectangle graph_rect, clip_rect, res_rect;
+ GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
+
+ clip->data = g_new (ClipData, 1);
+ clip_data = (ClipData *) clip->data;
+
+ clip_data->x_offset = prend->x_offset;
+ clip_data->y_offset = prend->y_offset;
+ clip_data->buffer = NULL;
+
+ graph_rect.x = graph_rect.y = 0;
+ graph_rect.width = gdk_pixbuf_get_width (prend->buffer);
+ graph_rect.height = gdk_pixbuf_get_height (prend->buffer);
+
+ clip_rect.x = floor (clip->area.x - prend->x_offset + 0.5);
+ clip_rect.y = floor (clip->area.y - prend->y_offset + 0.5);
+ clip_rect.width = floor (clip->area.x - prend->x_offset + clip->area.w + 0.5) - clip_rect.x;
+ clip_rect.height = floor (clip->area.y -prend->y_offset + clip->area.h + 0.5) - clip_rect.y;
+
+ if (gdk_rectangle_intersect (&graph_rect, &clip_rect, &res_rect)) {
+ clip_data->buffer = prend->buffer;
+ prend->buffer = gdk_pixbuf_new_subpixbuf (clip_data->buffer,
+ res_rect.x, res_rect.y,
+ res_rect.width, res_rect.height);
+ prend->x_offset += res_rect.x;
+ prend->y_offset += res_rect.y;
+ }
+
+ if (prend->buffer == NULL)
+ g_warning ("Pixbuf renderer: invalid clipping region");
+
+ prend->pixels = gdk_pixbuf_get_pixels (prend->buffer);
+ prend->w = gdk_pixbuf_get_width (prend->buffer);
+ prend->h = gdk_pixbuf_get_height (prend->buffer);
+ prend->rowstride = gdk_pixbuf_get_rowstride (prend->buffer);
+}
+
+static void
+gog_renderer_pixbuf_clip_pop (GogRenderer *rend, GogRendererClip *clip)
+{
+ GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
+ ClipData *clip_data = clip->data;
+
+ if (clip_data->buffer != NULL) {
+ if (prend->buffer != NULL)
+ g_object_unref (prend->buffer);
+ prend->buffer = clip_data->buffer;
+ }
+ prend->pixels = gdk_pixbuf_get_pixels (prend->buffer);
+ prend->w = gdk_pixbuf_get_width (prend->buffer);
+ prend->h = gdk_pixbuf_get_height (prend->buffer);
+ prend->rowstride = gdk_pixbuf_get_rowstride (prend->buffer);
+ prend->x_offset = clip_data->x_offset;
+ prend->y_offset = clip_data->y_offset;
+
+ g_free (clip->data);
+ clip->data = NULL;
+}
+
+static ArtSVP *
+clip_path (GogViewAllocation const *bound)
+{
+ ArtVpath path[6];
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ path[0].x = path[1].x = path[4].x = bound->x;
+ path[2].x = path[3].x = path[0].x + bound->w;
+ path[0].y = path[3].y = path[4].y = bound->y;
+ path[1].y = path[2].y = path[0].y + bound->h;
+ return art_svp_from_vpath ((ArtVpath *)path);
+}
+
+static double
+line_size (GogRenderer const *rend, double width)
+{
+ if (go_sub_epsilon (width) <= 0.) /* cheesy version of hairline */
+ return 1.;
+
+ width *= rend->scale;
+ if (width <= 1.)
+ return width;
+
+ return floor (width);
+}
+
+static double
+gog_renderer_pixbuf_line_size (GogRenderer const *rend, double width)
+{
+ double size = line_size (rend, width);
+
+ if (size < 1.0)
+ return ceil (size);
+
+ return size;
+}
+
+static void
+gog_renderer_pixbuf_sharp_path (GogRenderer *rend, ArtVpath *path, double line_width)
+{
+ ArtVpath *iter = path;
+
+ if (((int) (rint (line_width)) % 2 == 0) && line_width > 1.0)
+ while (iter->code != ART_END) {
+ iter->x = floor (iter->x + .5);
+ iter->y = floor (iter->y + .5);
+ iter++;
+ }
+ else
+ while (iter->code != ART_END) {
+ iter->x = floor (iter->x) + .5;
+ iter->y = floor (iter->y) + .5;
+ iter++;
+ }
+}
+
+static void
+gog_renderer_pixbuf_draw_path (GogRenderer *rend, ArtVpath const *path,
+ GogViewAllocation const *bound)
+{
+ GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
+ GogStyle const *style = rend->cur_style;
+ double width = line_size (rend, style->line.width);
+ ArtSVP *svp;
+ ArtVpath *dashed_path;
+
+ switch (style->line.dash_type) {
+ case GO_LINE_NONE:
+ return;
+ case GO_LINE_SOLID:
+ svp = art_svp_vpath_stroke ((ArtVpath *) path,
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_CAP_BUTT,
+ width, 4, 0.5);
+ break;
+ default:
+ dashed_path = go_line_dash_vpath (path, rend->line_dash,
+ rend->cur_clip != NULL ? &rend->cur_clip->area : NULL);
+ if (dashed_path == NULL)
+ return;
+ svp = art_svp_vpath_stroke (dashed_path,
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_CAP_BUTT,
+ width, 4, 0.5);
+ g_free (dashed_path);
+ }
+
+ if (bound != NULL) {
+ ArtSVP *orig = svp;
+ ArtSVP *clip = clip_path (bound);
+ svp = art_svp_intersect (clip, orig);
+ art_svp_free (clip);
+ art_svp_free (orig);
+ }
+
+ go_color_render_svp (style->line.color, svp,
+ prend->x_offset,
+ prend->y_offset,
+ prend->w + prend->x_offset,
+ prend->h + prend->y_offset,
+ prend->pixels, prend->rowstride);
+ art_svp_free (svp);
+}
+
+static ArtRender *
+gog_art_renderer_new (GogRendererPixbuf *prend)
+{
+ return art_render_new (prend->x_offset,
+ prend->y_offset,
+ prend->w + prend->x_offset,
+ prend->h + prend->y_offset,
+ prend->pixels, prend->rowstride,
+ gdk_pixbuf_get_n_channels (prend->buffer) - 1,
+ 8, ART_ALPHA_SEPARATE, NULL);
+}
+
+static void
+gog_renderer_pixbuf_draw_polygon (GogRenderer *rend, ArtVpath const *path,
+ gboolean narrow, GogViewAllocation const *bound)
+{
+ GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
+ GogStyle const *style = rend->cur_style;
+ ArtVpath *dashed_path;
+ ArtRender *render;
+ ArtSVP *fill, *outline = NULL;
+ ArtDRect bbox;
+ ArtGradientLinear gradient;
+ ArtGradientStop stops[2];
+ GdkPixbuf *image;
+ gint i, j, imax, jmax, h, w;
+ double width = line_size (rend, style->outline.width);
+
+ if (!narrow) {
+ switch (style->outline.dash_type) {
+ case GO_LINE_NONE:
+ break;
+ case GO_LINE_SOLID:
+ outline = art_svp_vpath_stroke ((ArtVpath *) path,
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_CAP_BUTT,
+ width, 4, 0.5);
+ break;
+ default:
+ dashed_path = go_line_dash_vpath (path, rend->outline_dash,
+ rend->cur_clip != NULL ? &rend->cur_clip->area : NULL);
+ if (dashed_path != NULL) {
+ outline = art_svp_vpath_stroke (dashed_path,
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_CAP_BUTT,
+ width, 4, 0.5);
+ g_free (dashed_path);
+ }
+ }
+ if (bound != NULL && outline != NULL) {
+ ArtSVP *orig = outline;
+ ArtSVP *clip = clip_path (bound);
+ outline = art_svp_intersect (clip, orig);
+ art_svp_free (clip);
+ art_svp_free (orig);
+ }
+ }
+
+ if (style->fill.type != GOG_FILL_STYLE_NONE) {
+ fill = art_svp_from_vpath ((ArtVpath *)path);
+ if (bound != NULL) {
+ ArtSVP *orig = fill;
+ ArtSVP *clip = clip_path (bound);
+ fill = art_svp_intersect (clip, orig);
+ art_svp_free (clip);
+ art_svp_free (orig);
+ }
+#if 0 /* art_svp_minus is not implemented */
+ if (outline != NULL) {
+ ArtSVP *tmp = art_svp_minus (fill, outline);
+ art_svp_free (fill);
+ fill = tmp;
+ }
+#endif
+
+ switch (style->fill.type) {
+ case GOG_FILL_STYLE_PATTERN:
+ go_pattern_render_svp (&style->fill.pattern,
+ fill,
+ prend->x_offset,
+ prend->y_offset,
+ prend->w + prend->x_offset,
+ prend->h + prend->y_offset,
+ prend->pixels, prend->rowstride);
+ break;
+
+ case GOG_FILL_STYLE_GRADIENT: {
+
+ art_vpath_bbox_drect ((ArtVpath *)path, &bbox);
+ render = gog_art_renderer_new (prend);
+ art_render_svp (render, fill);
+
+ go_gradient_setup (&gradient,
+ style->fill.gradient.dir,
+ style->fill.pattern.back, style->fill.pattern.fore,
+ bbox.x0, bbox.y0, bbox.x1, bbox.y1,
+ stops);
+
+ art_render_gradient_linear (render,
+ &gradient, ART_FILTER_NEAREST);
+ art_render_invoke (render);
+ break;
+ }
+
+ case GOG_FILL_STYLE_IMAGE: {
+ GdkRectangle path_rect, clip_rect, dest_rect;
+
+ image = style->fill.image.image;
+ if (image == NULL)
+ break;
+
+ art_vpath_bbox_drect (path, &bbox);
+
+ path_rect.x = bbox.x0 - prend->x_offset;
+ path_rect.y = bbox.y0 - prend->y_offset;
+ path_rect.width = bbox.x1 - bbox.x0;
+ path_rect.height = bbox.y1 - bbox.y0;
+
+ clip_rect.x = clip_rect.y = 0;
+ clip_rect.width = prend->w;
+ clip_rect.height = prend->h;
+
+ if (gdk_rectangle_intersect (&path_rect, &clip_rect, &dest_rect)) {
+ switch (style->fill.image.type) {
+ case GOG_IMAGE_CENTERED:
+ w = ((bbox.x1 - bbox.x0) - gdk_pixbuf_get_width (image)) / 2.;
+ if (w < 0.) w = 0.;
+ h = ((bbox.y1 - bbox.y0) - gdk_pixbuf_get_height (image)) / 2.;
+ if (h < 0.) h = 0.;
+ gdk_pixbuf_composite (image, prend->buffer,
+ dest_rect.x + w, dest_rect.y + h,
+ gdk_pixbuf_get_width (image),
+ gdk_pixbuf_get_height (image),
+ path_rect.x + w, path_rect.y + h,
+ 1., 1.,
+ GDK_INTERP_BILINEAR, 255);
+ break;
+ case GOG_IMAGE_STRETCHED:
+ gdk_pixbuf_composite (image, prend->buffer,
+ dest_rect.x, dest_rect.y,
+ dest_rect.width, dest_rect.height,
+ path_rect.x, path_rect.y,
+ path_rect.width /
+ (double)gdk_pixbuf_get_width (image),
+ path_rect.height /
+ (double)gdk_pixbuf_get_height (image),
+ GDK_INTERP_BILINEAR, 255);
+ break;
+
+ case GOG_IMAGE_WALLPAPER: {
+ GdkRectangle image_rect, copy_rect;
+
+ imax = path_rect.width /
+ (image_rect.width = gdk_pixbuf_get_width (image));
+ jmax = path_rect.height /
+ (image_rect.height = gdk_pixbuf_get_height (image));
+
+ image_rect.x = path_rect.x;
+ for (i = 0; i <= imax; i++)
+ {
+ image_rect.y = path_rect.y;
+ for (j = 0; j <= jmax; j++) {
+
+ if (gdk_rectangle_intersect (&image_rect,
+ &dest_rect,
+ ©_rect))
+ gdk_pixbuf_copy_area (image,
+ copy_rect.x - image_rect.x,
+ copy_rect.y - image_rect.y,
+ copy_rect.width,
+ copy_rect.height,
+ prend->buffer,
+ copy_rect.x,
+ copy_rect.y);
+ image_rect.y += image_rect.height;
+ }
+ image_rect.x +=image_rect.width;
+
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ case GOG_FILL_STYLE_NONE:
+ break; /* impossible */
+ }
+ if (fill != NULL)
+ art_svp_free (fill);
+ }
+
+ if (outline != NULL) {
+ go_color_render_svp (style->outline.color, outline,
+ prend->x_offset,
+ prend->y_offset,
+ prend->w + prend->x_offset,
+ prend->h + prend->y_offset,
+ prend->pixels, prend->rowstride);
+ art_svp_free (outline);
+ }
+}
+
+static PangoContext *
+gog_renderer_pixbuf_get_pango_context (GogRendererPixbuf *prend)
+{
+ PangoFT2FontMap *font_map;
+
+ if (prend->pango_context != NULL)
+ return prend->pango_context;
+
+ font_map = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ());
+ pango_ft2_font_map_set_resolution (font_map,
+ gnm_app_display_dpi_get (TRUE),
+ gnm_app_display_dpi_get (FALSE));
+ prend->pango_context = pango_ft2_font_map_create_context (font_map);
+ g_object_unref (font_map);
+
+ return prend->pango_context;
+}
+
+static PangoLayout *
+gog_renderer_pixbuf_get_pango_layout (GogRendererPixbuf *prend)
+{
+ PangoContext *context;
+ PangoAttribute *attr;
+ PangoAttrList *attrs = NULL;
+ PangoFontDescription const *fd = prend->base.cur_style->font.font->desc;
+
+ {
+ char *pfdStr;
+ pfdStr = pango_font_description_to_string( fd );
+ printf( "pfd=[%s]\n", pfdStr );
+ g_free( pfdStr );
+ }
+
+ if (prend->pango_layout != NULL)
+ return prend->pango_layout;
+
+ context = gog_renderer_pixbuf_get_pango_context (prend);
+
+ prend->pango_layout = pango_layout_new (context);
+
+ pango_layout_set_font_description (prend->pango_layout, fd);
+
+ /*
+ * Manually scale the font size to compensate for
+ * Before the fix to http://bugzilla.gnome.org/show_bug.cgi?id=121543
+ * the scale would otherwise be ignored.
+ */
+ attr = pango_attr_size_new (prend->base.zoom *
+ pango_font_description_get_size (fd));
+ attr->start_index = 0;
+ attr->end_index = -1;
+
+ attrs = pango_attr_list_new ();
+ pango_attr_list_insert (attrs, attr);
+ pango_layout_set_attributes (prend->pango_layout, attrs);
+ pango_attr_list_unref (attrs);
+
+ return prend->pango_layout;
+}
+
+static void
+gog_renderer_pixbuf_draw_text (GogRenderer *rend, char const *text,
+ GogViewAllocation const *pos, GtkAnchorType anchor,
+ GogViewAllocation *result)
+{
+ FT_Bitmap ft_bitmap;
+ GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
+ PangoRectangle rect;
+ PangoLayout *layout;
+ guint8 r, g, b, a, alpha, *dst, *src;
+ int h, w, i, x, y;
+ GogStyle const *style = rend->cur_style;
+
+ layout = gog_renderer_pixbuf_get_pango_layout ((GogRendererPixbuf *) rend);
+ pango_layout_set_text (layout, text, -1);
+ pango_layout_get_extents (layout, NULL, &rect);
+ rect.x = PANGO_PIXELS (rect.x);
+ rect.y = PANGO_PIXELS (rect.y);
+ x = (int)((pos->x - prend->x_offset) * PANGO_SCALE);
+ y = (int)((pos->y - prend->y_offset) * PANGO_SCALE);
+ switch (anchor) {
+ case GTK_ANCHOR_CENTER : case GTK_ANCHOR_N : case GTK_ANCHOR_S :
+ x -= rect.width / 2;
+ break;
+ case GTK_ANCHOR_NE : case GTK_ANCHOR_SE : case GTK_ANCHOR_E :
+ x -= rect.width;
+ break;
+ default : break;
+ }
+ x = (x > 0) ? (x + PANGO_SCALE / 2) / PANGO_SCALE : 0;
+ w = (rect.width + PANGO_SCALE / 2) / PANGO_SCALE;
+/* Makes rendering inconsitent with gnome-print and svg renderer */
+/* if (w > pos->w && pos->w >= 0)*/
+/* w = pos->w;*/
+ if ((x + w) > prend->w)
+ w = prend->w - x;
+
+ switch (anchor) {
+ case GTK_ANCHOR_CENTER : case GTK_ANCHOR_E : case GTK_ANCHOR_W :
+ y -= rect.height / 2;
+ break;
+ case GTK_ANCHOR_SE : case GTK_ANCHOR_S : case GTK_ANCHOR_SW :
+ y -= rect.height;
+ break;
+ default : break;
+ }
+ y = (y > 0) ? (y + PANGO_SCALE / 2) / PANGO_SCALE : 0;
+ h = (rect.height + PANGO_SCALE / 2) / PANGO_SCALE;
+/* Makes rendering inconsitent with gnome-print and svg renderer */
+/* if (h > pos->h && pos->h >= 0)*/
+/* h = pos->h;*/
+ if ((y + h) > prend->h)
+ h = prend->h - y;
+
+ if (result != NULL) {
+ result->x = x;
+ result->y = y;
+ result->w = w;
+ result->h = h;
+ }
+
+ if (w <= 0 || h <= 0)
+ return;
+
+ ft_bitmap.rows = h;
+ ft_bitmap.width = w;
+ ft_bitmap.pitch = (w+3) & ~3;
+ ft_bitmap.buffer = g_malloc0 (ft_bitmap.rows * ft_bitmap.pitch);
+ ft_bitmap.num_grays = 256;
+ ft_bitmap.pixel_mode = ft_pixel_mode_grays;
+ ft_bitmap.palette_mode = 0;
+ ft_bitmap.palette = NULL;
+ pango_ft2_render_layout (&ft_bitmap, layout, -rect.x, -rect.y);
+
+ r = UINT_RGBA_R (style->font.color);
+ g = UINT_RGBA_G (style->font.color);
+ b = UINT_RGBA_B (style->font.color);
+ a = UINT_RGBA_A (style->font.color);
+
+ /* do the compositing manually, ArtRender as used in librsvg is dog
+ * slow, and I do not feel like leaping through 20 different data
+ * structures to composite 1 byte images, onto rgba */
+ dst = prend->pixels;
+ dst += (y * prend->rowstride);
+ dst += (x + rect.x)* 4;
+ src = ft_bitmap.buffer;
+
+ while (h--) {
+ for (i = w; i-- > 0 ; dst += 4, src++) {
+ /* FIXME: Do the libart thing instead of divide by 255 */
+ alpha = (a * (*src)) / 255;
+ dst[0] = (dst[0] * (255 - alpha) + r * alpha) / 255;
+ dst[1] = (dst[1] * (255 - alpha) + g * alpha) / 255;
+ dst[2] = (dst[2] * (255 - alpha) + b * alpha) / 255;
+ dst[3] = (dst[3] * (255 - alpha) + a * alpha) / 255;
+ }
+ dst += prend->rowstride - w*4;
+ src += ft_bitmap.pitch - w;
+ }
+
+ g_free (ft_bitmap.buffer);
+}
+
+static void
+gog_renderer_pixbuf_draw_marker (GogRenderer *rend, double x, double y)
+{
+ GdkRectangle r1, r2, dest;
+ GogStyle const *style = rend->cur_style;
+ GogRendererPixbuf *prend = GOG_RENDERER_PIXBUF (rend);
+ GdkPixbuf const *marker_pixbuf = go_marker_get_pixbuf (style->marker.mark, rend->scale);
+
+ if (marker_pixbuf == NULL)
+ return;
+
+ r2.x = r2.y = 0;
+ r2.width = prend->w;
+ r2.height = prend->h;
+
+ r1.width = gdk_pixbuf_get_width (marker_pixbuf);
+ r1.height = gdk_pixbuf_get_height (marker_pixbuf);
+ r1.x = floor (floor (x + .5) - r1.width / 2.0 - prend->x_offset);
+ r1.y = floor (floor (y + .5) - r1.height / 2.0 - prend->y_offset);
+
+ if (gdk_rectangle_intersect (&r1, &r2, &dest))
+ gdk_pixbuf_composite (marker_pixbuf, prend->buffer,
+ dest.x, dest.y,
+ dest.width, dest.height,
+ r1.x, r1.y,
+ 1.0, 1.0,
+ GDK_INTERP_NEAREST,
+ 255);
+}
+
+static void
+gog_renderer_pixbuf_measure_text (GogRenderer *rend,
+ char const *text, GogViewRequisition *size)
+{
+ PangoRectangle logical;
+ PangoLayout *layout;
+
+ layout = gog_renderer_pixbuf_get_pango_layout ((GogRendererPixbuf *) rend);
+ pango_layout_set_text (layout, text, -1);
+ pango_layout_get_pixel_extents (layout, NULL, &logical);
+
+ size->w = logical.width;
+ size->h = logical.height;
+}
+
+static void
+gog_renderer_pixbuf_pop_style (GogRenderer *rend)
+{
+ GogRendererPixbuf *prend = (GogRendererPixbuf *) rend;
+
+ if (prend->pango_layout != NULL)
+ {
+ g_object_unref (prend->pango_layout);
+ prend->pango_layout = NULL;
+ }
+}
+
+static void
+gog_renderer_pixbuf_push_style (GogRenderer *rend, GogStyle const *style)
+{
+ GogRendererPixbuf *prend = (GogRendererPixbuf *) rend;
+
+ if (prend->pango_layout != NULL)
+ {
+ g_object_unref (prend->pango_layout);
+ prend->pango_layout = NULL;
+ }
+}
+
+static void
+gog_renderer_pixbuf_class_init (GogRendererClass *rend_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) rend_klass;
+
+ parent_klass = g_type_class_peek_parent (rend_klass);
+ gobject_klass->finalize = gog_renderer_pixbuf_finalize;
+ rend_klass->push_style = gog_renderer_pixbuf_push_style;
+ rend_klass->pop_style = gog_renderer_pixbuf_pop_style;
+ rend_klass->clip_push = gog_renderer_pixbuf_clip_push;
+ rend_klass->clip_pop = gog_renderer_pixbuf_clip_pop;
+ rend_klass->sharp_path = gog_renderer_pixbuf_sharp_path;
+ rend_klass->draw_path = gog_renderer_pixbuf_draw_path;
+ rend_klass->draw_polygon = gog_renderer_pixbuf_draw_polygon;
+ rend_klass->draw_text = gog_renderer_pixbuf_draw_text;
+ rend_klass->draw_marker = gog_renderer_pixbuf_draw_marker;
+ rend_klass->measure_text = gog_renderer_pixbuf_measure_text;
+ rend_klass->line_size = gog_renderer_pixbuf_line_size;
+}
+
+static void
+gog_renderer_pixbuf_init (GogRendererPixbuf *prend)
+{
+ prend->buffer = NULL;
+ prend->w = prend->h = 1; /* just in case */
+ prend->x_offset = prend->y_offset = 0;
+ prend->pango_layout = NULL;
+ prend->pango_context = NULL;
+}
+
+GSF_CLASS (GogRendererPixbuf, gog_renderer_pixbuf,
+ gog_renderer_pixbuf_class_init, gog_renderer_pixbuf_init,
+ GOG_RENDERER_TYPE)
+
+GdkPixbuf *
+gog_renderer_pixbuf_get (GogRendererPixbuf *prend)
+{
+ g_return_val_if_fail (prend != NULL, NULL);
+
+ return prend->buffer;
+}
+
+#if 0 /* An initial non-working attempt to use different dpi to render
+ different zooms */
+
+/* fontmaps are reasonably expensive use a cache to share them */
+static GHashTable *fontmap_cache = NULL; /* PangoFT2FontMap hashed by y_dpi */
+static gboolean
+cb_remove_entry (gpointer key, PangoFT2FontMap *value, PangoFT2FontMap *target)
+{
+ return value == target;
+}
+static void
+cb_map_is_gone (gpointer data, GObject *where_the_object_was)
+{
+ g_warning ("fontmap %p is gone",where_the_object_was);
+ g_hash_table_foreach_steal (fontmap_cache,
+ (GHRFunc) cb_remove_entry, where_the_object_was);
+}
+static void
+cb_weak_unref (GObject *fontmap)
+{
+ g_object_weak_unref (fontmap, cb_map_is_gone, NULL);
+}
+static PangoFT2FontMap *
+fontmap_from_cache (double x_dpi, double y_dpi)
+{
+ PangoFT2FontMap *fontmap = NULL;
+ int key_dpi = floor (y_dpi + .5);
+ gpointer key = GUINT_TO_POINTER (key_dpi);
+
+ if (fontmap_cache != NULL)
+ fontmap = g_hash_table_lookup (fontmap_cache, key);
+ else
+ fontmap_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) cb_weak_unref);
+
+ if (fontmap == NULL) {
+ fontmap = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ());
+ pango_ft2_font_map_set_resolution (fontmap, x_dpi, y_dpi);
+ g_object_weak_ref (G_OBJECT (fontmap), cb_map_is_gone, NULL);
+ g_hash_table_insert (fontmap_cache, key, fontmap);
+ } else
+ g_object_ref (fontmap);
+
+ g_warning ("fontmap %d = %p", key_dpi, fontmap);
+ return fontmap;
+}
+#endif
+
+/**
+ * gog_renderer_update :
+ * @prend :
+ * @w :
+ * @h :
+ *
+ * Returns TRUE if the size actually changed.
+ **/
+gboolean
+gog_renderer_pixbuf_update (GogRendererPixbuf *prend, int w, int h, double zoom)
+{
+ gboolean redraw = TRUE;
+ GogView *view;
+ GogViewAllocation allocation;
+
+ g_return_val_if_fail (prend != NULL, FALSE);
+ g_return_val_if_fail (prend->base.view != NULL, FALSE);
+
+ view = prend->base.view;
+ allocation.x = allocation.y = 0.;
+ allocation.w = w;
+ allocation.h = h;
+ if (prend->w != w || prend->h != h) {
+ double dpi_x, dpi_y;
+
+ prend->w = w;
+ prend->h = h;
+ prend->base.scale_x = w / prend->base.logical_width_pts;
+ prend->base.scale_y = h / prend->base.logical_height_pts;
+ prend->base.scale = MIN (prend->base.scale_x, prend->base.scale_y);
+ prend->base.zoom = zoom;
+ dpi_x = gog_renderer_pt2r_x (&prend->base, GO_IN_TO_PT ((double)1.))
+ / zoom;
+ dpi_y = gog_renderer_pt2r_y (&prend->base, GO_IN_TO_PT ((double)1.))
+ / zoom;
+
+ if (prend->buffer != NULL) {
+ g_object_unref (prend->buffer);
+ prend->buffer = NULL;
+ }
+
+ if (prend->pango_layout != NULL) {
+ g_object_unref (prend->pango_layout);
+ prend->pango_layout = NULL;
+ }
+
+ if (prend->pango_context != NULL) {
+ g_object_unref (prend->pango_context);
+ prend->pango_context = NULL;
+ }
+
+ /* make sure we dont try to queue an update while updating */
+ prend->base.needs_update = TRUE;
+
+ /* scale just changed need to recalculate sizes */
+ gog_renderer_invalidate_size_requests (&prend->base);
+ gog_view_size_allocate (view, &allocation);
+ } else if (w != view->allocation.w || h != view->allocation.h)
+ gog_view_size_allocate (view, &allocation);
+ else
+ redraw = gog_view_update_sizes (view);
+
+ redraw |= prend->base.needs_update;
+ prend->base.needs_update = FALSE;
+
+ gog_debug (0, g_warning ("rend_pixbuf:update = %d", redraw););
+
+ if (redraw) {
+ if (prend->buffer == NULL) {
+ prend->buffer = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ prend->w, prend->h);
+ if (prend->buffer == NULL) {
+ g_warning ("Chart is too large");
+ return FALSE;
+ }
+ prend->pixels = gdk_pixbuf_get_pixels (prend->buffer);
+ prend->rowstride = gdk_pixbuf_get_rowstride (prend->buffer);
+ }
+ gdk_pixbuf_fill (prend->buffer, 0);
+
+ gog_view_render (view, NULL);
+ }
+
+ return redraw;
+}
--- /dev/null
+++ lib/goffice/graph/gog-guru.h
@@ -0,0 +1,40 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-guru.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_GURU_H
+#define GOG_GURU_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <command-context.h>
+#include <gtk/gtkwindow.h>
+
+G_BEGIN_DECLS
+
+/* This interface _will_ change */
+/* The callback in the closure should match the following prototype: */
+/* typedef void (*GogGuruRegister) (GogGraph *graph, gpointer user); */
+
+GtkWidget *gog_guru (GogGraph *graph, GogDataAllocator *dalloc,
+ GnmCmdContext *cc, GtkWindow *toplevel,
+ GClosure *closure);
+
+G_END_DECLS
+
+#endif /* GOG_GURU_H */
--- /dev/null
+++ lib/goffice/graph/go-data-impl.h
@@ -0,0 +1,103 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-data-impl.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_DATA_IMPL_H
+#define GO_DATA_IMPL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/go-data.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GO_DATA_CACHE_IS_VALID = 1 << 0,
+ GO_DATA_IS_EDITABLE = 1 << 1,
+ GO_DATA_VECTOR_LEN_CACHED = 1 << 2,
+ GO_DATA_MATRIX_SIZE_CACHED = GO_DATA_VECTOR_LEN_CACHED
+} GODataFlags;
+
+struct _GOData {
+ GObject base;
+ gint32 flags; /* dunno what to do with these yet */
+};
+typedef struct {
+ GObjectClass base;
+
+ GOData *(*dup) (GOData const *src);
+ gboolean (*eq) (GOData const *a, GOData const *b);
+ GOFormat *(*preferred_fmt) (GOData const *dat);
+ char *(*as_str) (GOData const *dat);
+ gboolean (*from_str) (GOData *dat, char const *str);
+ void (*emit_changed) (GOData *dat);
+
+ /* signals */
+ void (*changed) (GOData *dat);
+} GODataClass;
+
+struct _GODataScalar {
+ GOData base;
+};
+
+typedef struct {
+ GODataClass base;
+ double (*get_value) (GODataScalar *scalar);
+ char const *(*get_str) (GODataScalar *scalar);
+/* PangoLayout *(get_fmt_str) (GODataScalar *scalar); */
+} GODataScalarClass;
+
+struct _GODataVector {
+ GOData base;
+
+ int len; /* negative if dirty, includes missing values */
+ double *values; /* NULL = inititialized/unsupported, nan = missing */
+ double minimum, maximum;
+};
+typedef struct {
+ GODataClass base;
+
+ void (*load_len) (GODataVector *vec);
+ void (*load_values) (GODataVector *vec);
+ double (*get_value) (GODataVector *vec, unsigned i);
+ char *(*get_str) (GODataVector *vec, unsigned i);
+/* PangoLayout *(get_fmt_str) (GODataVector *vec, unsigned i); */
+} GODataVectorClass;
+
+struct _GODataMatrix {
+ GOData base;
+
+ GOMatrixSize size; /* negative if dirty, includes missing values */
+ double *values; /* NULL = uninitialized/unsupported, nan = missing */
+ double minimum, maximum;
+};
+
+typedef struct {
+ GODataClass base;
+
+ void (*load_size) (GODataMatrix *vec);
+ void (*load_values) (GODataMatrix *vec);
+ double (*get_value) (GODataMatrix *mat, unsigned i, unsigned j);
+ char *(*get_str) (GODataMatrix *mat, unsigned i, unsigned j);
+/* PangoLayout *(get_fmt_str) (GODataMatrix *mat, unsigned i, unsigned j); */
+} GODataMatrixClass;
+
+G_END_DECLS
+
+#endif /* GO_DATA_IMPL_H */
--- /dev/null
+++ lib/goffice/graph/gog-style.c
@@ -0,0 +1,1845 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-style.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-font.h>
+#include <goffice/utils/go-file.h>
+#include <goffice/utils/go-line.h>
+#include <goffice/utils/go-marker.h>
+
+#include <goffice/gui-utils/go-color-palette.h>
+#include <goffice/gui-utils/go-combo-color.h>
+#include <goffice/gui-utils/go-combo-pixmaps.h>
+
+// #include <src/gui-util.h>
+#include <gui-util.h>
+#include <glade/glade-xml.h>
+#include <gtk/gtkcheckbutton.h>
+#include <gtk/gtkspinbutton.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkrange.h>
+#include <gtk/gtkcombobox.h>
+#include <gtk/gtknotebook.h>
+#include <widgets/widget-font-selector.h>
+#include <gui-file.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include <math.h>
+
+#define HSCALE 100
+#define VSCALE 120
+
+typedef GObjectClass GogStyleClass;
+
+static GObjectClass *parent_klass;
+
+/**
+ * I would have liked to do this differently and have a tighter binding between theme element and style
+ * eg gog_style_new (theme_element)
+ * However that will not work easily in the context of xls import where we do
+ * not know what the type is destined for until later. This structure melds
+ * smoothly with both approaches at the expense of a bit of power.
+ **/
+/*************************************************************************/
+
+typedef struct {
+ GladeXML *gui;
+ GogStyle *style;
+ GogStyle *default_style;
+ GObject *object_with_style;
+ gboolean enable_edit;
+ gulong style_changed_handler;
+ struct {
+ struct {
+ GtkWidget *fore, *back, *combo;
+ } pattern;
+ struct {
+ GtkWidget *start, *end, *end_label, *combo;
+ GtkWidget *brightness, *brightness_box;
+ guint timer;
+ } gradient;
+ struct {
+ GdkPixbuf *image;
+ } image;
+ } fill;
+ struct {
+ GtkWidget *combo;
+ } marker;
+} StylePrefState;
+
+static void
+cb_style_changed (GogStyledObject *obj, GogStyle *style, StylePrefState *state)
+{
+}
+
+static void
+set_style (StylePrefState const *state)
+{
+ if (state->object_with_style != NULL) {
+ if (state->style_changed_handler)
+ g_signal_handler_block (state->object_with_style, state->style_changed_handler);
+ g_object_set (G_OBJECT (state->object_with_style), "style", state->style, NULL);
+ if (state->style_changed_handler)
+ g_signal_handler_unblock (state->object_with_style, state->style_changed_handler);
+ }
+}
+
+static GtkWidget *
+create_go_combo_color (StylePrefState *state,
+ GOColor initial_val, GOColor default_val,
+ char const *group, char const *label_name,
+ GCallback func)
+{
+ GtkWidget *w;
+
+ w = go_combo_color_new (NULL, _("Automatic"), default_val,
+ go_color_group_fetch (group, NULL));
+ go_combo_color_set_instant_apply (GO_COMBO_COLOR (w), FALSE);
+ go_combo_color_set_allow_alpha (GO_COMBO_COLOR (w), TRUE);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (glade_xml_get_widget (state->gui, label_name)), w);
+ go_combo_color_set_color (GO_COMBO_COLOR (w), initial_val);
+ g_signal_connect (G_OBJECT (w),
+ "color_changed",
+ G_CALLBACK (func), state);
+ return w;
+}
+
+static void
+gog_style_set_image_preview (GdkPixbuf *pix, StylePrefState *state)
+{
+ GdkPixbuf *scaled;
+ int width, height;
+ char *size;
+ GtkWidget *w;
+
+ if (state->fill.image.image != pix) {
+ if (state->fill.image.image != NULL)
+ g_object_unref (state->fill.image.image);
+ state->fill.image.image = pix;
+ if (state->fill.image.image != NULL)
+ g_object_ref (state->fill.image.image);
+ }
+
+ w = glade_xml_get_widget (state->gui, "fill_image_sample");
+
+ scaled = gnm_pixbuf_intelligent_scale (pix, HSCALE, VSCALE);
+ gtk_image_set_from_pixbuf (GTK_IMAGE (w), scaled);
+ g_object_unref (scaled);
+
+ w = glade_xml_get_widget (state->gui, "image-size-label");
+ width = gdk_pixbuf_get_width (pix);
+ height = gdk_pixbuf_get_height (pix);
+
+ size = g_strdup_printf (_("%d x %d"), width, height);
+ gtk_label_set_text (GTK_LABEL (w), size);
+ g_free (size);
+}
+
+/************************************************************************/
+static void
+cb_outline_dash_type_changed (GtkWidget *cc, int dash_type, StylePrefState const *state)
+{
+ GogStyle *style = state->style;
+ gboolean is_auto = dash_type < 0;
+
+ if (is_auto)
+ dash_type = -dash_type;
+ style->outline.auto_dash = is_auto;
+ style->outline.dash_type = dash_type;
+ set_style (state);
+}
+
+static void
+cb_outline_size_changed (GtkAdjustment *adj, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+
+ g_return_if_fail (style != NULL);
+
+ style->outline.width = rint (adj->value * 100.) / 100.;
+ set_style (state);
+}
+
+static void
+cb_outline_color_changed (G_GNUC_UNUSED GOComboColor *cc, GOColor color,
+ G_GNUC_UNUSED gboolean is_custom,
+ G_GNUC_UNUSED gboolean by_user,
+ gboolean is_default, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+
+ g_return_if_fail (style != NULL);
+
+ style->outline.color = color;
+ style->outline.auto_color = is_default;
+ set_style (state);
+}
+
+static void
+outline_init (StylePrefState *state, gboolean enable)
+{
+ GogStyle *style = state->style;
+ GogStyle *default_style = state->default_style;
+ GtkWidget *w, *table;
+
+ if (!enable) {
+ gtk_widget_hide (glade_xml_get_widget (state->gui, "outline_box"));
+ return;
+ }
+
+ table = glade_xml_get_widget (state->gui, "outline_table");
+
+ /* DashType */
+ w = go_line_dash_selector (default_style->outline.dash_type);
+ gtk_table_attach (GTK_TABLE (table), w, 1, 3, 0, 1, 0, 0, 0, 0);
+ go_combo_pixmaps_select_id (GO_COMBO_PIXMAPS (w), style->outline.dash_type);
+ g_signal_connect (G_OBJECT (w),
+ "changed",
+ G_CALLBACK (cb_outline_dash_type_changed), state);
+ /* Size */
+ w = glade_xml_get_widget (state->gui, "outline_size_spin");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), style->outline.width);
+ g_signal_connect (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)),
+ "value_changed",
+ G_CALLBACK (cb_outline_size_changed), state);
+ /* Color */
+ w = create_go_combo_color (state,
+ style->outline.color, default_style->outline.color,
+ "outline_color", "outline_color_label",
+ G_CALLBACK (cb_outline_color_changed));
+ gtk_table_attach (GTK_TABLE (table), w, 1, 2, 1, 2, 0, 0, 0, 0);
+ gtk_widget_show_all (table);
+}
+
+
+/************************************************************************/
+
+static void
+cb_line_dash_type_changed (GtkWidget *cc, int dash_type, StylePrefState const *state)
+{
+ GogStyle *style = state->style;
+ gboolean is_auto = dash_type < 0;
+
+ if (is_auto)
+ dash_type = -dash_type;
+ style->line.auto_dash = is_auto;
+ style->line.dash_type = dash_type;
+ set_style (state);
+}
+
+static void
+cb_line_size_changed (GtkAdjustment *adj, StylePrefState const *state)
+{
+ GogStyle *style = state->style;
+
+ g_return_if_fail (style != NULL);
+
+ style->line.width = rint (adj->value * 100.) / 100.;
+ set_style (state);
+}
+
+static void
+cb_line_color_changed (G_GNUC_UNUSED GOComboColor *cc, GOColor color,
+ G_GNUC_UNUSED gboolean is_custom,
+ G_GNUC_UNUSED gboolean by_user,
+ gboolean is_default, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+
+ g_return_if_fail (style != NULL);
+
+ style->line.color = color;
+ style->line.auto_color = is_default;
+ set_style (state);
+}
+
+static void
+line_init (StylePrefState *state, gboolean enable)
+{
+ GogStyle *style = state->style;
+ GogStyle *default_style = state->default_style;
+ GtkWidget *w, *table;
+
+ if (!enable) {
+ gtk_widget_hide (glade_xml_get_widget (state->gui, "line_box"));
+ return;
+ }
+
+ table = glade_xml_get_widget (state->gui, "line_table");
+
+ /* DashType */
+ w = go_line_dash_selector (default_style->line.dash_type);
+ gtk_table_attach (GTK_TABLE (table), w, 1, 3, 0, 1, 0, 0, 0, 0);
+ go_combo_pixmaps_select_id (GO_COMBO_PIXMAPS (w), style->line.dash_type);
+ g_signal_connect (G_OBJECT (w),
+ "changed",
+ G_CALLBACK (cb_line_dash_type_changed), state);
+
+ /* Size */
+ w = glade_xml_get_widget (state->gui, "line_size_spin");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), style->line.width);
+ g_signal_connect (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)),
+ "value_changed",
+ G_CALLBACK (cb_line_size_changed), state);
+
+ /* Colour */
+ w = create_go_combo_color (state,
+ style->line.color, default_style->line.color,
+ "line_color", "line_color_label",
+ G_CALLBACK (cb_line_color_changed));
+ gtk_table_attach (GTK_TABLE (table), w, 1, 2, 1, 2, 0, 0, 0, 0);
+ gtk_widget_show_all (table);
+}
+
+/************************************************************************/
+
+static void
+cb_pattern_type_changed (GtkWidget *cc, int pattern, StylePrefState const *state)
+{
+ GogStyle *style = state->style;
+ gboolean is_auto = pattern < 0;
+
+ if (is_auto)
+ pattern = -pattern;
+ style->fill.pattern.pattern = pattern;
+ set_style (state);
+}
+
+static void
+populate_pattern_combo (StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ GogStyle *default_style = state->default_style;
+ GtkWidget *table, *combo;
+ GOPatternType type = GO_PATTERN_SOLID;
+
+ if (state->fill.pattern.combo != NULL)
+ gtk_widget_destroy (state->fill.pattern.combo);
+
+ state->fill.pattern.combo = combo = go_pattern_selector (
+ style->fill.pattern.fore,
+ style->fill.pattern.back,
+ default_style->fill.pattern.pattern);
+
+ table = glade_xml_get_widget (state->gui, "fill_pattern_table");
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 2, 0, 1, 0, 0, 0, 0);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (glade_xml_get_widget (state->gui, "fill_pattern_type_label")), combo);
+
+ if (style->fill.type == GOG_FILL_STYLE_PATTERN)
+ type = style->fill.pattern.pattern;
+ go_combo_pixmaps_select_id (GO_COMBO_PIXMAPS(combo), type);
+ g_signal_connect (G_OBJECT (combo),
+ "changed",
+ G_CALLBACK (cb_pattern_type_changed), state);
+ gtk_widget_show (combo);
+}
+
+static void
+cb_fg_color_changed (G_GNUC_UNUSED GOComboColor *cc, GOColor color,
+ G_GNUC_UNUSED gboolean is_custom,
+ G_GNUC_UNUSED gboolean by_user,
+ gboolean is_default, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+
+ g_return_if_fail (style != NULL);
+ g_return_if_fail (GOG_FILL_STYLE_PATTERN == style->fill.type);
+
+ style->fill.pattern.fore = color;
+ style->fill.auto_fore = is_default;
+ set_style (state);
+ populate_pattern_combo (state);
+}
+
+static void
+cb_bg_color_changed (G_GNUC_UNUSED GOComboColor *cc, GOColor color,
+ G_GNUC_UNUSED gboolean is_custom,
+ G_GNUC_UNUSED gboolean by_user,
+ gboolean is_default, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+
+ g_return_if_fail (style != NULL);
+ g_return_if_fail (GOG_FILL_STYLE_PATTERN == style->fill.type);
+
+ style->fill.pattern.back = color;
+ style->fill.auto_back = is_default;
+ set_style (state);
+ populate_pattern_combo (state);
+}
+
+static void
+fill_pattern_init (StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ GogStyle *default_style = state->default_style;
+
+ GtkWidget *w, *table =
+ glade_xml_get_widget (state->gui, "fill_pattern_table");
+
+ state->fill.pattern.fore = w = create_go_combo_color (state,
+ style->fill.pattern.fore,
+ default_style->fill.pattern.fore,
+ "pattern_foreground", "fill_pattern_foreground_label",
+ G_CALLBACK (cb_fg_color_changed));
+ gtk_table_attach (GTK_TABLE (table), w, 1, 2, 1, 2, 0, 0, 0, 0);
+
+ state->fill.pattern.back = w = create_go_combo_color (state,
+ style->fill.pattern.back,
+ default_style->fill.pattern.back,
+ "pattern_background", "fill_pattern_background_label",
+ G_CALLBACK (cb_bg_color_changed));
+ gtk_table_attach (GTK_TABLE (table), w, 1, 2, 2, 3, 0, 0, 0, 0);
+
+ populate_pattern_combo (state);
+ gtk_widget_show_all (table);
+}
+
+/************************************************************************/
+
+static GOGradientDirection default_to_last_selected_type = GO_GRADIENT_N_TO_S;
+static void
+cb_gradient_type_changed (GtkWidget *cc, int id, StylePrefState const *state)
+{
+ GogStyle *style = state->style;
+ style->fill.gradient.dir = default_to_last_selected_type = id;
+ set_style (state);
+}
+
+static void
+populate_gradient_combo (StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ GtkWidget *combo, *table;
+
+ if (state->fill.gradient.combo != NULL)
+ gtk_widget_destroy (state->fill.gradient.combo);
+
+ state->fill.gradient.combo = combo = go_gradient_selector (
+ style->fill.pattern.back,
+ style->fill.pattern.fore);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (glade_xml_get_widget (state->gui, "fill_gradient_direction_label")), combo);
+
+ table = glade_xml_get_widget (state->gui, "fill_gradient_table");
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 2, 0, 1, 0, 0, 0, 0);
+ go_combo_pixmaps_select_id (GO_COMBO_PIXMAPS (combo),
+ (style->fill.type == GOG_FILL_STYLE_GRADIENT)
+ ? style->fill.gradient.dir : default_to_last_selected_type);
+
+ g_signal_connect (G_OBJECT (combo),
+ "changed",
+ G_CALLBACK (cb_gradient_type_changed), state);
+ gtk_widget_show (combo);
+}
+
+static void
+cb_fill_gradient_start_color (G_GNUC_UNUSED GOComboColor *cc, GOColor color,
+ G_GNUC_UNUSED gboolean is_custom,
+ G_GNUC_UNUSED gboolean by_user,
+ gboolean is_default, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ style->fill.pattern.back = color;
+ style->fill.auto_back = is_default;
+ set_style (state);
+ populate_gradient_combo (state);
+}
+
+static gboolean
+cb_delayed_gradient_combo_update (StylePrefState *state)
+{
+ state->fill.gradient.timer = 0;
+ populate_gradient_combo (state);
+ return FALSE;
+}
+
+static void
+cb_fill_gradient_end_color (G_GNUC_UNUSED GOComboColor *cc, GOColor color,
+ G_GNUC_UNUSED gboolean is_custom,
+ gboolean by_user,
+ gboolean is_default, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+
+ style->fill.pattern.fore = color;
+ style->fill.auto_fore = is_default;
+ set_style (state);
+
+ if (by_user)
+ populate_gradient_combo (state);
+ else {
+ if (state->fill.gradient.timer != 0)
+ g_source_remove (state->fill.gradient.timer);
+ state->fill.gradient.timer = g_timeout_add (100,
+ (GSourceFunc) cb_delayed_gradient_combo_update, state);
+ }
+}
+
+static void
+cb_gradient_brightness_value_changed (GtkWidget *w, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+
+ gog_style_set_fill_brightness (style,
+ gtk_range_get_value (GTK_RANGE (w)));
+ go_combo_color_set_color (GO_COMBO_COLOR (state->fill.gradient.end),
+ style->fill.pattern.fore);
+ set_style (state);
+}
+
+static void
+cb_gradient_style_changed (GtkWidget *w, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+
+ GtkWidget *val = glade_xml_get_widget (state->gui,
+ "fill_gradient_brightness");
+ GtkWidget *box = glade_xml_get_widget (state->gui,
+ "fill_gradient_brightness_box");
+
+ gboolean two_color = gtk_combo_box_get_active (GTK_COMBO_BOX (w)) == 0;
+
+ if (two_color) {
+ style->fill.gradient.brightness = -1;
+ gtk_widget_hide (box);
+ } else {
+ gtk_widget_show (box);
+ gog_style_set_fill_brightness (style,
+ gtk_range_get_value (GTK_RANGE (val)));
+ go_combo_color_set_color (GO_COMBO_COLOR (state->fill.gradient.end),
+ style->fill.pattern.fore);
+ }
+ g_object_set (G_OBJECT (state->fill.gradient.end), "visible", two_color, NULL);
+ g_object_set (G_OBJECT (state->fill.gradient.end_label), "visible", two_color, NULL);
+ set_style (state);
+}
+
+static void
+fill_gradient_init (StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ GogStyle *default_style = state->default_style;
+ GtkWidget *w, *table = glade_xml_get_widget (state->gui, "fill_gradient_table");
+ GtkWidget *type = glade_xml_get_widget (state->gui, "fill_gradient_type");
+
+ state->fill.gradient.start = w = create_go_combo_color (state,
+ style->fill.pattern.back,
+ default_style->fill.pattern.back,
+ "gradient_start", "fill_gradient_start_label",
+ G_CALLBACK (cb_fill_gradient_start_color));
+ gtk_table_attach (GTK_TABLE (table), w, 1, 2, 2, 3, 0, 0, 0, 0);
+ gtk_widget_show (w);
+
+ state->fill.gradient.end = w = create_go_combo_color (state,
+ style->fill.pattern.fore,
+ default_style->fill.pattern.fore,
+ "gradient_end", "fill_gradient_end_label",
+ G_CALLBACK (cb_fill_gradient_end_color));
+ gtk_table_attach (GTK_TABLE (table), w, 3, 4, 2, 3, 0, 0, 0, 0);
+ gtk_widget_show (w);
+
+ state->fill.gradient.end_label = glade_xml_get_widget (state->gui,
+ "fill_gradient_end_label");
+ state->fill.gradient.brightness = glade_xml_get_widget (state->gui,
+ "fill_gradient_brightness");
+ state->fill.gradient.brightness_box = glade_xml_get_widget (state->gui,
+ "fill_gradient_brightness_box");
+
+ if ((style->fill.type != GOG_FILL_STYLE_GRADIENT) ||
+ (style->fill.gradient.brightness < 0)) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (type), 0);
+ gtk_widget_hide (state->fill.gradient.brightness_box);
+ } else {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (type), 1);
+ gtk_widget_show (state->fill.gradient.brightness_box);
+ gtk_range_set_value (GTK_RANGE (state->fill.gradient.brightness),
+ style->fill.gradient.brightness);
+ gtk_widget_hide (state->fill.gradient.end);
+ gtk_widget_hide (state->fill.gradient.end_label);
+ }
+
+ g_signal_connect (G_OBJECT (type),
+ "changed",
+ G_CALLBACK (cb_gradient_style_changed), state);
+ g_signal_connect (G_OBJECT (state->fill.gradient.brightness),
+ "value_changed",
+ G_CALLBACK (cb_gradient_brightness_value_changed), state);
+
+ populate_gradient_combo (state);
+ gtk_widget_show (table);
+}
+
+/************************************************************************/
+
+static void
+cb_image_file_select (GtkWidget *cc, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ char *filename, *uri, *old_uri;
+ GtkWidget *w;
+
+ g_return_if_fail (style != NULL);
+ g_return_if_fail (GOG_FILL_STYLE_IMAGE == style->fill.type);
+
+ filename = style->fill.image.filename;
+ old_uri = filename ? go_filename_to_uri (filename) : NULL;
+ printf( "cb_image_file_select .. gui_image_file_select\n" );
+ //uri = gui_image_file_select (NULL, old_uri);
+ uri = "http://localhost/";
+ g_free (old_uri);
+ if (uri == NULL)
+ return;
+ filename = go_filename_from_uri (uri);
+ g_free (uri);
+ if (filename == NULL) {
+ g_warning ("Sorry -- cannot handle URIs here right now.");
+ return;
+ }
+#warning "Handle URIs here."
+
+ gog_style_set_fill_image_filename (style, filename);
+
+ w = glade_xml_get_widget (state->gui, "fill_image_sample");
+ g_object_set_data (G_OBJECT (w), "filename",
+ style->fill.image.filename);
+
+ gog_style_set_image_preview (style->fill.image.image, state);
+ set_style (state);
+}
+
+static void
+cb_image_style_changed (GtkWidget *w, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ g_return_if_fail (style != NULL);
+ g_return_if_fail (GOG_FILL_STYLE_IMAGE == style->fill.type);
+ style->fill.image.type = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
+ set_style (state);
+}
+
+static void
+fill_image_init (StylePrefState *state)
+{
+ GtkWidget *w, *sample, *type;
+ GogStyle *style = state->style;
+
+ w = glade_xml_get_widget (state->gui, "fill_image_select_picture");
+ g_signal_connect (G_OBJECT (w),
+ "clicked",
+ G_CALLBACK (cb_image_file_select), state);
+
+ sample = glade_xml_get_widget (state->gui, "fill_image_sample");
+ gtk_widget_set_size_request (sample, HSCALE + 10, VSCALE + 10);
+ type = glade_xml_get_widget (state->gui, "fill_image_fit");
+
+ state->fill.image.image = NULL;
+
+ if (GOG_FILL_STYLE_IMAGE == style->fill.type) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (type),
+ style->fill.image.type);
+ gog_style_set_image_preview (style->fill.image.image, state);
+ state->fill.image.image = style->fill.image.image;
+ if (state->fill.image.image)
+ g_object_ref (state->fill.image.image);
+ g_object_set_data (G_OBJECT (sample), "filename",
+ style->fill.image.filename);
+ } else
+ gtk_combo_box_set_active (GTK_COMBO_BOX (type), 0);
+ g_signal_connect (G_OBJECT (type),
+ "changed",
+ G_CALLBACK (cb_image_style_changed), state);
+}
+
+/************************************************************************/
+
+static void
+cb_fill_type_changed (GtkWidget *menu, StylePrefState *state)
+{
+ GtkWidget *w;
+
+ state->style->fill.type = gtk_combo_box_get_active (GTK_COMBO_BOX (menu));
+ set_style (state);
+
+ w = glade_xml_get_widget (state->gui, "fill_notebook");
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (w), state->style->fill.type);
+}
+
+static void
+fill_init (StylePrefState *state, gboolean enable)
+{
+ GtkWidget *w;
+
+ if (!enable) {
+ gtk_widget_hide (glade_xml_get_widget (state->gui, "fill_box"));
+ return;
+ }
+
+ fill_pattern_init (state);
+ fill_gradient_init (state);
+ fill_image_init (state);
+
+ w = glade_xml_get_widget (state->gui, "fill_notebook");
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (w), state->style->fill.type);
+ w = glade_xml_get_widget (state->gui, "fill_type_menu");
+ gtk_combo_box_set_active (GTK_COMBO_BOX (w), state->style->fill.type);
+ g_signal_connect (G_OBJECT (w),
+ "changed",
+ G_CALLBACK (cb_fill_type_changed), state);
+
+ w = glade_xml_get_widget (state->gui, "fill_box");
+ gtk_widget_show (GTK_WIDGET (w));
+}
+
+/************************************************************************/
+
+
+static void
+cb_marker_shape_changed (GtkWidget *cc, int shape, StylePrefState const *state)
+{
+ GogStyle *style = state->style;
+ gboolean is_auto = shape < 0;
+
+ if (is_auto)
+ shape = -shape;
+ go_marker_set_shape (style->marker.mark, shape);
+ style->marker.auto_shape = is_auto;
+ set_style (state);
+}
+
+static void
+populate_marker_combo (StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ GtkWidget *combo, *table;
+
+ if (state->marker.combo != NULL)
+ gtk_widget_destroy (state->marker.combo);
+
+ state->marker.combo = combo = go_marker_selector (
+ go_marker_get_outline_color (style->marker.mark),
+ go_marker_get_fill_color (style->marker.mark),
+ go_marker_get_shape (state->default_style->marker.mark));
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (glade_xml_get_widget (state->gui, "marker_shape_label")), combo);
+
+ table = glade_xml_get_widget (state->gui, "marker_table");
+ gtk_table_attach (GTK_TABLE (table), combo, 1, 2, 0, 1, 0, 0, 0, 0);
+ go_combo_pixmaps_select_id (GO_COMBO_PIXMAPS (combo),
+ go_marker_get_shape (style->marker.mark));
+ g_signal_connect (G_OBJECT (combo),
+ "changed",
+ G_CALLBACK (cb_marker_shape_changed), state);
+ gtk_widget_show (combo);
+}
+
+static void
+cb_marker_outline_color_changed (G_GNUC_UNUSED GOComboColor *cc, GOColor color,
+ G_GNUC_UNUSED gboolean is_custom,
+ G_GNUC_UNUSED gboolean by_user,
+ gboolean is_auto, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ if (is_auto)
+ color = go_marker_get_outline_color (state->default_style->marker.mark);
+ go_marker_set_outline_color (style->marker.mark, color);
+ style->marker.auto_outline_color = is_auto;
+ set_style (state);
+ populate_marker_combo (state);
+}
+
+static void
+cb_marker_fill_color_changed (G_GNUC_UNUSED GOComboColor *cc, GOColor color,
+ G_GNUC_UNUSED gboolean is_custom,
+ G_GNUC_UNUSED gboolean by_user,
+ gboolean is_auto, StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ if (is_auto)
+ color = go_marker_get_fill_color (state->default_style->marker.mark);
+ go_marker_set_fill_color (style->marker.mark, color);
+ style->marker.auto_fill_color = is_auto;
+ set_style (state);
+ populate_marker_combo (state);
+}
+
+static void
+cb_marker_size_changed (GtkAdjustment *adj, StylePrefState *state)
+{
+ go_marker_set_size (state->style->marker.mark, adj->value);
+ set_style (state);
+}
+
+static void
+marker_init (StylePrefState *state, gboolean enable)
+{
+ GogStyle *style = state->style;
+ GogStyle *default_style = state->default_style;
+ GtkWidget *table, *w;
+
+ if (!enable) {
+ gtk_widget_hide (glade_xml_get_widget (state->gui, "marker_box"));
+ return;
+ }
+
+ populate_marker_combo (state);
+ table = glade_xml_get_widget (state->gui, "marker_table");
+
+ w = create_go_combo_color (state,
+ go_marker_get_fill_color (style->marker.mark),
+ go_marker_get_fill_color (default_style->marker.mark),
+ "pattern_foreground", "marker_fill_label",
+ G_CALLBACK (cb_marker_fill_color_changed));
+ gtk_table_attach (GTK_TABLE (table), w, 1, 2, 1, 2, 0, 0, 0, 0);
+
+ w = create_go_combo_color (state,
+ go_marker_get_outline_color (style->marker.mark),
+ go_marker_get_outline_color (default_style->marker.mark),
+ "pattern_foreground", "marker_outline_label",
+ G_CALLBACK (cb_marker_outline_color_changed));
+ gtk_table_attach (GTK_TABLE (table), w, 1, 2, 2, 3, 0, 0, 0, 0);
+
+ w = glade_xml_get_widget (state->gui, "marker_size_spin");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w),
+ go_marker_get_size (style->marker.mark));
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_marker_size_changed), state);
+
+ gtk_widget_show_all (table);
+}
+
+/************************************************************************/
+
+static void
+cb_font_changed (FontSelector *fs, G_GNUC_UNUSED gpointer mstyle,
+ StylePrefState *state)
+{
+ GogStyle *style = state->style;
+ PangoFontDescription *new_font = pango_font_description_copy (style->font.font->desc);
+
+ // --jsled
+ //font_selector_get_pango (fs, new_font);
+ gog_style_set_font (style, new_font);
+ set_style (state);
+}
+
+static void
+font_init (StylePrefState *state, guint32 enable, gpointer optional_notebook)
+{
+ GogStyle *style = state->style;
+ GtkWidget *w, *box;
+
+ if (!enable)
+ return;
+
+ g_return_if_fail (style->font.font != NULL);
+ g_return_if_fail (GTK_NOTEBOOK (optional_notebook) != NULL);
+
+ box = gtk_vbox_new (FALSE, 5);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 12);
+
+#if 0
+ w = gtk_check_button_new_with_label (_("Automatic"));
+ gtk_box_pack_start (GTK_BOX (box), w, FALSE, TRUE, 0);
+#endif
+ gtk_widget_show_all (box);
+
+ /* --jsled
+ w = font_selector_new ();
+ font_selector_set_from_pango (FONT_SELECTOR (w), style->font.font->desc);
+ g_signal_connect (G_OBJECT (w),
+ "font_changed",
+ G_CALLBACK (cb_font_changed), state);
+ gtk_box_pack_end (GTK_BOX (box), w, TRUE, TRUE, 0);
+ gtk_widget_show (w);
+ */
+
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (optional_notebook), box,
+ gtk_label_new (_("Font")));
+ gtk_widget_show (GTK_WIDGET (optional_notebook));
+}
+
+/************************************************************************/
+
+static void
+cb_parent_is_gone (StylePrefState *state, GObject *where_the_object_was)
+{
+ state->style_changed_handler = 0;
+ state->object_with_style = NULL;
+}
+
+static void
+gog_style_pref_state_free (StylePrefState *state)
+{
+ if (state->style_changed_handler) {
+ g_signal_handler_disconnect (state->object_with_style,
+ state->style_changed_handler);
+ g_object_weak_unref (G_OBJECT (state->object_with_style),
+ (GWeakNotify) cb_parent_is_gone, state);
+ }
+ g_object_unref (state->style);
+ g_object_unref (state->default_style);
+ g_object_unref (state->gui);
+ if (state->fill.gradient.timer != 0) {
+ g_source_remove (state->fill.gradient.timer);
+ state->fill.gradient.timer = 0;
+ }
+ if (state->fill.image.image != NULL)
+ g_object_unref (state->fill.image.image);
+ g_free (state);
+}
+
+static gpointer
+style_editor (GogStyle *style,
+ GogStyle *default_style,
+ GnmCmdContext *cc,
+ gpointer optional_notebook,
+ GObject *object_with_style,
+ gboolean watch_for_external_change)
+{
+ GogStyleFlag enable;
+ GtkWidget *w;
+ GladeXML *gui;
+ StylePrefState *state;
+
+ g_return_val_if_fail (style != NULL, NULL);
+ g_return_val_if_fail (default_style != NULL, NULL);
+
+ enable = style->interesting_fields;
+
+ gui = gnm_glade_xml_new (cc, "gog-style-prefs.glade", "gog_style_prefs", NULL);
+ if (gui == NULL)
+ return NULL;
+
+ g_object_ref (style);
+ g_object_ref (default_style);
+
+ state = g_new0 (StylePrefState, 1);
+ state->gui = gui;
+ state->style = style;
+ state->default_style = default_style;
+ state->object_with_style = object_with_style;
+ state->enable_edit = FALSE;
+
+ outline_init (state, enable & GOG_STYLE_OUTLINE);
+ line_init (state, enable & GOG_STYLE_LINE);
+ fill_init (state, enable & GOG_STYLE_FILL);
+ marker_init (state, enable & GOG_STYLE_MARKER);
+ font_init (state, enable & GOG_STYLE_FONT, optional_notebook);
+
+ state->enable_edit = TRUE;
+
+ if (object_with_style != NULL && watch_for_external_change) {
+ state->style_changed_handler = g_signal_connect (G_OBJECT (object_with_style),
+ "style-changed",
+ G_CALLBACK (cb_style_changed), state);
+ g_object_weak_ref (G_OBJECT (object_with_style),
+ (GWeakNotify) cb_parent_is_gone, state);
+ }
+
+ w = glade_xml_get_widget (gui, "gog_style_prefs");
+ g_object_set_data_full (G_OBJECT (w),
+ "state", state, (GDestroyNotify) gog_style_pref_state_free);
+
+ if (optional_notebook != NULL) {
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (optional_notebook), w,
+ gtk_label_new (_("Style")));
+ return GTK_WIDGET (optional_notebook);
+ }
+ return w;
+}
+
+gpointer
+gog_style_editor (GogStyle *style,
+ GogStyle *default_style,
+ GnmCmdContext *cc,
+ gpointer optional_notebook,
+ GObject *object_with_style)
+{
+ return style_editor (style, default_style, cc, optional_notebook,
+ object_with_style, FALSE);
+}
+
+gpointer
+gog_styled_object_editor (GogStyledObject *gso, GnmCmdContext *cc, gpointer optional_notebook)
+{
+ GogStyle *style = gog_style_dup (gog_styled_object_get_style (gso));
+ GogStyle *default_style = gog_styled_object_get_auto_style (gso);
+ gpointer editor = style_editor (style, default_style, cc,
+ optional_notebook, G_OBJECT (gso), TRUE);
+ g_object_unref (style);
+ g_object_unref (default_style);
+
+ return editor;
+}
+
+/*****************************************************************************/
+
+GogStyle *
+gog_style_new (void)
+{
+ return g_object_new (GOG_STYLE_TYPE, NULL);
+}
+
+/**
+ * gog_style_dup :
+ * @src : #GogStyle
+ *
+ **/
+GogStyle *
+gog_style_dup (GogStyle const *src)
+{
+ GogStyle *dst;
+
+ g_return_val_if_fail (GOG_STYLE (src) != NULL, NULL);
+
+ dst = gog_style_new ();
+ gog_style_assign (dst, src);
+ return dst;
+}
+
+void
+gog_style_assign (GogStyle *dst, GogStyle const *src)
+{
+ if (src == dst)
+ return;
+
+ g_return_if_fail (GOG_STYLE (src) != NULL);
+ g_return_if_fail (GOG_STYLE (dst) != NULL);
+
+ if (GOG_FILL_STYLE_IMAGE == src->fill.type &&
+ src->fill.image.image != NULL)
+ g_object_ref (src->fill.image.image);
+ if (GOG_FILL_STYLE_IMAGE == dst->fill.type) {
+ if (dst->fill.image.image != NULL)
+ g_object_unref (dst->fill.image.image);
+ g_free (dst->fill.image.filename);
+ }
+
+ if (src->font.font != NULL)
+ go_font_ref (src->font.font);
+ if (dst->font.font != NULL)
+ go_font_unref (dst->font.font);
+
+ dst->outline = src->outline;
+ dst->fill = src->fill;
+ dst->line = src->line;
+ if (dst->marker.mark)
+ g_object_unref (dst->marker.mark);
+ dst->marker = src->marker;
+ dst->marker.mark = go_marker_dup (src->marker.mark);
+ dst->font = src->font;
+ dst->line = src->line;
+
+ if (GOG_FILL_STYLE_IMAGE == dst->fill.type)
+ dst->fill.image.filename = g_strdup (dst->fill.image.filename);
+
+ dst->interesting_fields = src->interesting_fields;
+ dst->disable_theming = src->disable_theming;
+}
+
+/**
+ * gog_style_apply_theme :
+ * @dst : #GogStyle
+ * @src : #GogStyle
+ *
+ * Merge the attributes from @src onto the elements of @dst that were not user
+ * assigned (is_auto)
+ **/
+void
+gog_style_apply_theme (GogStyle *dst, GogStyle const *src)
+{
+ if (src == dst)
+ return;
+
+ g_return_if_fail (GOG_STYLE (src) != NULL);
+ g_return_if_fail (GOG_STYLE (dst) != NULL);
+
+ if (dst->outline.auto_dash)
+ dst->outline.dash_type = src->outline.dash_type;
+ if (dst->outline.auto_color)
+ dst->outline.color = src->outline.color;
+ if (dst->fill.auto_fore)
+ dst->fill.pattern.fore = src->fill.pattern.fore;
+ if (dst->fill.auto_back)
+ dst->fill.pattern.back = src->fill.pattern.back;
+ if (dst->line.auto_dash)
+ dst->line.dash_type = src->line.dash_type;
+ if (dst->line.auto_color)
+ dst->line.color = src->line.color;
+ if (dst->marker.auto_shape)
+ go_marker_set_shape (dst->marker.mark,
+ go_marker_get_shape (src->marker.mark));
+ if (dst->marker.auto_outline_color)
+ go_marker_set_outline_color (dst->marker.mark,
+ go_marker_get_outline_color (src->marker.mark));
+ if (dst->marker.auto_fill_color)
+ go_marker_set_fill_color (dst->marker.mark,
+ go_marker_get_fill_color (src->marker.mark));
+
+#if 0
+ /* Fonts are not themed until we have some sort of auto mechanism
+ * stronger than 'auto_size' */
+ if (src->font.font != NULL)
+ go_font_ref (src->font.font);
+ if (dst->font.font != NULL)
+ go_font_unref (dst->font.font);
+ dst->font = src->font;
+#endif
+}
+
+static void
+gog_style_finalize (GObject *obj)
+{
+ GogStyle *style = GOG_STYLE (obj);
+
+ if (GOG_FILL_STYLE_IMAGE == style->fill.type &&
+ style->fill.image.image != NULL)
+ g_object_unref (style->fill.image.image);
+
+ if (style->font.font != NULL) {
+ go_font_unref (style->font.font);
+ style->font.font = NULL;
+ }
+
+ if (style->marker.mark != NULL) {
+ g_object_unref (style->marker.mark);
+ style->marker.mark = NULL;
+ }
+
+ (parent_klass->finalize) (obj);
+}
+
+static void
+gog_style_class_init (GogStyleClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *)klass;
+ parent_klass = g_type_class_peek_parent (klass);
+ gobject_klass->finalize = gog_style_finalize;
+}
+
+static void
+gog_style_init (GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_ALL;
+ style->disable_theming = 0;
+ gog_style_force_auto (style);
+ style->line.dash_type = GO_LINE_SOLID;
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->outline.width = 0;
+ style->fill.type = GOG_FILL_STYLE_PATTERN;
+ style->fill.gradient.brightness = -1.;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_BLACK);
+ style->font.font = go_font_new_by_index (0);
+ style->font.color = RGBA_BLACK;
+}
+
+static struct {
+ GogFillStyle fstyle;
+ char const *name;
+} fill_names[] = {
+ { GOG_FILL_STYLE_NONE, "none" },
+ { GOG_FILL_STYLE_PATTERN, "pattern" },
+ { GOG_FILL_STYLE_GRADIENT, "gradient" },
+ { GOG_FILL_STYLE_IMAGE, "image" }
+};
+
+static gboolean
+bool_prop (xmlNode *node, char const *name, gboolean *res)
+{
+ char *str = xmlGetProp (node, name);
+ if (str != NULL) {
+ *res = g_ascii_tolower (*str) == 't' ||
+ g_ascii_tolower (*str) == 'y' ||
+ strtol (str, NULL, 0);
+ xmlFree (str);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static GogFillStyle
+str_as_fill_style (char const *name)
+{
+ unsigned i;
+ for (i = 0; i < G_N_ELEMENTS (fill_names); i++)
+ if (strcmp (fill_names[i].name, name) == 0)
+ return fill_names[i].fstyle;
+ return GOG_FILL_STYLE_PATTERN;
+}
+
+static char const *
+fill_style_as_str (GogFillStyle fstyle)
+{
+ unsigned i;
+ for (i = 0; i < G_N_ELEMENTS (fill_names); i++)
+ if (fill_names[i].fstyle == fstyle)
+ return fill_names[i].name;
+ return "pattern";
+}
+
+static void
+gog_style_line_load (xmlNode *node, GogStyleLine *line)
+{
+ char *str;
+ gboolean tmp;
+
+ str = xmlGetProp (node, "dash");
+ if (str != NULL) {
+ line->dash_type = go_line_dash_from_str (str);
+ xmlFree (str);
+ }
+ if (bool_prop (node, "auto-dash", &tmp))
+ line->auto_dash = tmp;
+ str = xmlGetProp (node, "width");
+ if (str != NULL) {
+ line->width = g_strtod (str, NULL);
+ /* For compatibility with older graphs, when dash_type
+ * didn't exist */
+ if (line->width < 0.) {
+ line->width = 0.;
+ line->dash_type = GO_LINE_NONE;
+ }
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, "color");
+ if (str != NULL) {
+ line->color = go_color_from_str (str);
+ xmlFree (str);
+ }
+ if (bool_prop (node, "auto-color", &tmp))
+ line->auto_color = tmp;
+}
+
+static void
+gog_style_line_dom_save (xmlNode *parent, xmlChar const *name,
+ GogStyleLine const *line)
+{
+ gchar *str;
+ xmlNode *node = xmlNewDocNode (parent->doc, NULL, name, NULL);
+
+ xmlSetProp (node, (xmlChar const *) "dash",
+ go_line_dash_as_str (line->dash_type));
+ xmlSetProp (node, (xmlChar const *) "auto-dash",
+ line->auto_dash ? "true" : "false");
+ str = g_strdup_printf ("%f", line->width);
+ xmlSetProp (node, (xmlChar const *) "width", str);
+ g_free (str);
+ str = go_color_as_str (line->color);
+ xmlSetProp (node, (xmlChar const *) "color", str);
+ g_free (str);
+ xmlSetProp (node, (xmlChar const *) "auto-color",
+ line->auto_color ? "true" : "false");
+ xmlAddChild (parent, node);
+}
+static void
+gog_style_line_sax_save (GsfXMLOut *output, char const *name,
+ GogStyleLine const *line)
+{
+ gsf_xml_out_start_element (output, name);
+ gsf_xml_out_add_cstr_unchecked (output, "dash",
+ go_line_dash_as_str (line->dash_type));
+ gsf_xml_out_add_bool (output, "auto-dash", line->auto_dash);
+ gsf_xml_out_add_float (output, "width", line->width, 1);
+ go_xml_out_add_color (output, "color", line->color);
+ gsf_xml_out_add_bool (output, "auto-color", line->auto_color);
+ gsf_xml_out_end_element (output);
+}
+
+static void
+gog_style_gradient_sax_save (GsfXMLOut *output, GogStyle const *style)
+{
+ gsf_xml_out_start_element (output, "gradient");
+ gsf_xml_out_add_cstr_unchecked (output, "direction",
+ go_gradient_dir_as_str (style->fill.gradient.dir));
+ go_xml_out_add_color (output, "start-color",
+ style->fill.pattern.back);
+ if (style->fill.gradient.brightness >= 0.)
+ gsf_xml_out_add_float (output, "brightness",
+ style->fill.gradient.brightness, 2);
+ else
+ go_xml_out_add_color (output, "end-color",
+ style->fill.pattern.fore);
+ gsf_xml_out_end_element (output);
+}
+
+static void
+gog_style_fill_sax_save (GsfXMLOut *output, GogStyle const *style)
+{
+ gsf_xml_out_start_element (output, "fill");
+ gsf_xml_out_add_cstr_unchecked (output, "type",
+ fill_style_as_str (style->fill.type));
+ gsf_xml_out_add_bool (output, "is-auto",
+ style->fill.auto_back);
+ gsf_xml_out_add_bool (output, "auto-fore",
+ style->fill.auto_fore);
+
+ switch (style->fill.type) {
+ case GOG_FILL_STYLE_NONE: break;
+ case GOG_FILL_STYLE_PATTERN:
+ gsf_xml_out_start_element (output, "pattern");
+ gsf_xml_out_add_cstr_unchecked (output, "type",
+ go_pattern_as_str (style->fill.pattern.pattern));
+ go_xml_out_add_color (output, "fore",
+ style->fill.pattern.fore);
+ go_xml_out_add_color (output, "back",
+ style->fill.pattern.back);
+ gsf_xml_out_end_element (output);
+ break;
+
+ case GOG_FILL_STYLE_GRADIENT:
+ gog_style_gradient_sax_save (output, style);
+ break;
+ case GOG_FILL_STYLE_IMAGE:
+ /* FIXME: TODO */
+ break;
+ default:
+ break;
+ }
+ gsf_xml_out_end_element (output);
+}
+
+static void
+gog_style_gradient_load (xmlNode *node, GogStyle *style)
+{
+ char *str = xmlGetProp (node, "direction");
+ if (str != NULL) {
+ style->fill.gradient.dir
+ = go_gradient_dir_from_str (str);
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, "start-color");
+ if (str != NULL) {
+ style->fill.pattern.back
+ = go_color_from_str (str);
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, "brightness");
+ if (str != NULL) {
+ gog_style_set_fill_brightness (style, g_strtod (str, NULL));
+ xmlFree (str);
+ } else {
+ str = xmlGetProp (node, "end-color");
+ if (str != NULL) {
+ style->fill.pattern.fore
+ = go_color_from_str (str);
+ xmlFree (str);
+ }
+ }
+}
+
+static void
+gog_style_gradient_dom_save (xmlNode *parent, GogStyle const *style)
+{
+ gchar *str;
+ xmlNode *node = xmlNewDocNode (parent->doc, NULL, "gradient", NULL);
+
+ xmlSetProp (node, (xmlChar const *) "direction",
+ go_gradient_dir_as_str (style->fill.gradient.dir));
+ str = go_color_as_str (style->fill.pattern.back);
+ xmlSetProp (node, (xmlChar const *) "start-color", str);
+ g_free (str);
+ if (style->fill.gradient.brightness >= 0.) {
+ str = g_strdup_printf ("%f",
+ style->fill.gradient.brightness);
+ xmlSetProp (node, (xmlChar const *) "brightness", str);
+ g_free (str);
+ } else {
+ str = go_color_as_str (style->fill.pattern.fore);
+ xmlSetProp (node, (xmlChar const *) "end-color", str);
+ g_free (str);
+ }
+ xmlAddChild (parent, node);
+}
+
+static void
+gog_style_fill_load (xmlNode *node, GogStyle *style)
+{
+ xmlNode *ptr;
+ gboolean tmp;
+ char *str = xmlGetProp (node, "type");
+
+ if (str == NULL)
+ return;
+ style->fill.type = str_as_fill_style (str);
+ xmlFree (str);
+
+ if (bool_prop (node, "is-auto", &tmp))
+ style->fill.auto_back = tmp;
+ if (bool_prop (node, "auto-fore", &tmp))
+ style->fill.auto_fore = tmp;
+
+ switch (style->fill.type) {
+ case GOG_FILL_STYLE_PATTERN:
+ for (ptr = node->xmlChildrenNode ;
+ ptr != NULL ; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL)
+ continue;
+ if (strcmp (ptr->name, "pattern") == 0) {
+ str = xmlGetProp (ptr, "type");
+ if (str != NULL) {
+ style->fill.pattern.pattern
+ = go_pattern_from_str (str);
+ xmlFree (str);
+ }
+ str = xmlGetProp (ptr, "fore");
+ if (str != NULL) {
+ style->fill.pattern.fore
+ = go_color_from_str (str);
+ xmlFree (str);
+ }
+ str = xmlGetProp (ptr, "back");
+ if (str != NULL) {
+ style->fill.pattern.back
+ = go_color_from_str (str);
+ xmlFree (str);
+ }
+ }
+ }
+ break;
+ case GOG_FILL_STYLE_GRADIENT:
+ for (ptr = node->xmlChildrenNode ;
+ ptr != NULL ; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL)
+ continue;
+ if (strcmp (ptr->name, "gradient") == 0)
+ gog_style_gradient_load (ptr, style);
+ }
+ break;
+ case GOG_FILL_STYLE_IMAGE:
+ /* FIXME: TODO */
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+gog_style_fill_dom_save (xmlNode *parent, GogStyle const *style)
+{
+ gchar *str;
+ xmlNode *node = xmlNewDocNode (parent->doc, NULL, "fill", NULL);
+ xmlNode *child;
+ xmlSetProp (node, (xmlChar const *) "type",
+ fill_style_as_str (style->fill.type));
+ xmlSetProp (node, (xmlChar const *) "is-auto",
+ style->fill.auto_back ? "true" : "false");
+ xmlSetProp (node, (xmlChar const *) "auto-fore",
+ style->fill.auto_fore ? "true" : "false");
+ switch (style->fill.type) {
+ case GOG_FILL_STYLE_NONE:
+ break;
+ case GOG_FILL_STYLE_PATTERN:
+ child = xmlNewDocNode (parent->doc, NULL, "pattern", NULL);
+ xmlSetProp (child, (xmlChar const *) "type",
+ go_pattern_as_str (style->fill.pattern.pattern));
+ str = go_color_as_str (style->fill.pattern.fore);
+ xmlSetProp (child, (xmlChar const *) "fore", str);
+ g_free (str);
+ str = go_color_as_str (style->fill.pattern.back);
+ xmlSetProp (child, (xmlChar const *) "back", str);
+ g_free (str);
+ xmlAddChild (node, child);
+ break;
+ case GOG_FILL_STYLE_GRADIENT:
+ gog_style_gradient_dom_save (node, style);
+ break;
+ case GOG_FILL_STYLE_IMAGE:
+ /* FIXME: TODO */
+ break;
+ default:
+ break;
+ }
+ xmlAddChild (parent, node);
+}
+
+static void
+gog_style_marker_load (xmlNode *node, GogStyle *style)
+{
+ char *str;
+ GOMarker *marker = go_marker_dup (style->marker.mark);
+
+ str = xmlGetProp (node, "shape");
+ if (str != NULL) {
+ style->marker.auto_shape = TRUE;
+ bool_prop (node, "auto-shape", &style->marker.auto_shape);
+ go_marker_set_shape (marker, go_marker_shape_from_str (str));
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, "outline-color");
+ if (str != NULL) {
+ style->marker.auto_outline_color = TRUE;
+ bool_prop (node, "auto-outline", &style->marker.auto_outline_color);
+ go_marker_set_outline_color (marker, go_color_from_str (str));
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, "fill-color");
+ if (str != NULL) {
+ style->marker.auto_fill_color = TRUE;
+ bool_prop (node, "auto-fill", &style->marker.auto_fill_color);
+ go_marker_set_fill_color (marker, go_color_from_str (str));
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, "size");
+ if (str != NULL) {
+ go_marker_set_size (marker, g_strtod (str, NULL));
+ xmlFree (str);
+ }
+ gog_style_set_marker (style, marker);
+}
+
+static void
+gog_style_marker_dom_save (xmlNode *parent, GogStyle const *style)
+{
+ gchar *str;
+ xmlNode *node = xmlNewDocNode (parent->doc, NULL, "marker", NULL);
+
+ xmlSetProp (node, (xmlChar const *) "auto-shape",
+ style->marker.auto_shape ? "true" : "false");
+ xmlSetProp (node, (xmlChar const *) "shape",
+ go_marker_shape_as_str (go_marker_get_shape (style->marker.mark)));
+
+ xmlSetProp (node, (xmlChar const *) "auto-outline",
+ style->marker.auto_outline_color ? "true" : "false");
+ str = go_color_as_str (go_marker_get_outline_color (style->marker.mark));
+ xmlSetProp (node, (xmlChar const *) "outline-color", str);
+ g_free (str);
+
+ xmlSetProp (node, (xmlChar const *) "auto-fill",
+ style->marker.auto_fill_color ? "true" : "false");
+ str = go_color_as_str (go_marker_get_fill_color (style->marker.mark));
+ xmlSetProp (node, (xmlChar const *) "fill-color", str);
+ g_free (str);
+
+ str = g_strdup_printf ("%d", go_marker_get_size (style->marker.mark));
+ xmlSetProp (node, (xmlChar const *) "size", str);
+ g_free (str);
+
+ xmlAddChild (parent, node);
+}
+
+static void
+gog_style_marker_sax_save (GsfXMLOut *output, GogStyle const *style)
+{
+ gsf_xml_out_start_element (output, "marker");
+ gsf_xml_out_add_bool (output, "auto-shape", style->marker.auto_shape);
+ gsf_xml_out_add_cstr (output, "shape",
+ go_marker_shape_as_str (go_marker_get_shape (style->marker.mark)));
+ gsf_xml_out_add_bool (output, "auto-outline",
+ style->marker.auto_outline_color);
+ go_xml_out_add_color (output, "outline-color",
+ go_marker_get_outline_color (style->marker.mark));
+ gsf_xml_out_add_bool (output, "auto-fill", style->marker.auto_fill_color);
+ go_xml_out_add_color (output, "fill-color",
+ go_marker_get_fill_color (style->marker.mark));
+ gsf_xml_out_add_int (output, "size",
+ go_marker_get_size (style->marker.mark));
+ gsf_xml_out_end_element (output);
+}
+
+static void
+gog_style_font_load (xmlNode *node, GogStyle *style)
+{
+ char *str;
+ gboolean tmp;
+
+ str = xmlGetProp (node, "color");
+ if (str != NULL) {
+ style->font.color = go_color_from_str (str);
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, "font");
+ if (str != NULL) {
+ PangoFontDescription *desc;
+
+ desc = pango_font_description_from_string (str);
+ if (desc != NULL)
+ gog_style_set_font (style, desc);
+ xmlFree (str);
+ }
+ if (bool_prop (node, "auto-scale", &tmp))
+ style->font.auto_scale = tmp;
+}
+
+static void
+gog_style_font_dom_save (xmlNode *parent, GogStyle const *style)
+{
+ gchar *str;
+ xmlNode *node = xmlNewDocNode (parent->doc, NULL, "font", NULL);
+
+ str = go_color_as_str (style->font.color);
+ xmlSetProp (node, (xmlChar const *) "color", str);
+ g_free (str);
+ str = go_font_as_str (style->font.font);
+ xmlSetProp (node, (xmlChar const *) "font", str);
+ g_free (str);
+ xmlSetProp (node, (xmlChar const *) "auto-scale",
+ style->font.auto_scale ? "true" : "false");
+
+ xmlAddChild (parent, node);
+}
+static void
+gog_style_font_sax_save (GsfXMLOut *output, GogStyle const *style)
+{
+ char *str;
+ gsf_xml_out_start_element (output, "font");
+ go_xml_out_add_color (output, "color", style->font.color);
+ str = go_font_as_str (style->font.font);
+ gsf_xml_out_add_cstr_unchecked (output, "font", str);
+ g_free (str);
+ gsf_xml_out_add_bool (output, "auto-scale", style->font.auto_scale);
+ gsf_xml_out_end_element (output);
+}
+
+static gboolean
+gog_style_persist_dom_load (GogPersist *gp, xmlNode *node)
+{
+ GogStyle *style = GOG_STYLE (gp);
+ xmlNode *ptr;
+
+ /* while reloading no need to reapply settings */
+ for (ptr = node->xmlChildrenNode ; ptr != NULL ; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL)
+ continue;
+ if (strcmp (ptr->name, "outline") == 0)
+ gog_style_line_load (ptr, &style->outline);
+ else if (strcmp (ptr->name, "line") == 0)
+ gog_style_line_load (ptr, &style->line);
+ else if (strcmp (ptr->name, "fill") == 0)
+ gog_style_fill_load (ptr, style);
+ else if (strcmp (ptr->name, "marker") == 0)
+ gog_style_marker_load (ptr, style);
+ else if (strcmp (ptr->name, "font") == 0)
+ gog_style_font_load (ptr, style);
+ }
+ return TRUE;
+}
+
+static void
+gog_style_persist_dom_save (GogPersist const *gp, xmlNode *parent)
+{
+ GogStyle const *style = GOG_STYLE (gp);
+
+ xmlSetProp (parent, (xmlChar const *) "type",
+ G_OBJECT_TYPE_NAME (style));
+
+ if (style->interesting_fields & GOG_STYLE_OUTLINE)
+ gog_style_line_dom_save (parent, "outline", &style->outline);
+ if (style->interesting_fields & GOG_STYLE_LINE)
+ gog_style_line_dom_save (parent, "line", &style->line);
+ if (style->interesting_fields & GOG_STYLE_FILL)
+ gog_style_fill_dom_save (parent, style);
+ if (style->interesting_fields & GOG_STYLE_MARKER)
+ gog_style_marker_dom_save (parent, style);
+ if (style->interesting_fields & GOG_STYLE_FONT)
+ gog_style_font_dom_save (parent, style);
+}
+
+static void
+gog_style_persist_sax_save (GogPersist const *gp, GsfXMLOut *output)
+{
+ GogStyle const *style = GOG_STYLE (gp);
+
+ gsf_xml_out_add_cstr_unchecked (output, "type",
+ G_OBJECT_TYPE_NAME (style));
+
+ if (style->interesting_fields & GOG_STYLE_OUTLINE)
+ gog_style_line_sax_save (output, "outline", &style->outline);
+ if (style->interesting_fields & GOG_STYLE_LINE)
+ gog_style_line_sax_save (output, "line", &style->line);
+ if (style->interesting_fields & GOG_STYLE_FILL)
+ gog_style_fill_sax_save (output, style);
+ if (style->interesting_fields & GOG_STYLE_MARKER)
+ gog_style_marker_sax_save (output, style);
+ if (style->interesting_fields & GOG_STYLE_FONT)
+ gog_style_font_sax_save (output, style);
+}
+
+static void
+gog_style_persist_init (GogPersistClass *iface)
+{
+ iface->dom_load = gog_style_persist_dom_load;
+ iface->dom_save = gog_style_persist_dom_save;
+ iface->sax_save = gog_style_persist_sax_save;
+}
+
+GSF_CLASS_FULL (GogStyle, gog_style,
+ gog_style_class_init, gog_style_init,
+ G_TYPE_OBJECT, 0,
+ GSF_INTERFACE (gog_style_persist_init, GOG_PERSIST_TYPE))
+
+gboolean
+gog_style_is_different_size (GogStyle const *a, GogStyle const *b)
+{
+ if (a == NULL || b == NULL)
+ return TRUE;
+ return a->outline.dash_type != b->outline.dash_type ||
+ a->outline.width != b->outline.width ||
+ a->line.width != b->line.width ||
+ a->fill.type != b->fill.type ||
+ !go_font_eq (a->font.font, b->font.font);
+}
+
+gboolean
+gog_style_is_marker_visible (GogStyle const *style)
+{
+#warning TODO : make this smarter
+ return (style->interesting_fields & GOG_STYLE_MARKER) &&
+ go_marker_get_shape (style->marker.mark) != GO_MARKER_NONE;
+}
+
+gboolean
+gog_style_is_outline_visible (GogStyle const *style)
+{
+#warning TODO : make this smarter
+ return UINT_RGBA_A (style->outline.color) > 0 &&
+ style->outline.dash_type != GO_LINE_NONE;
+}
+
+gboolean
+gog_style_is_line_visible (GogStyle const *style)
+{
+#warning TODO : make this smarter
+ return UINT_RGBA_A (style->line.color) > 0 &&
+ style->line.dash_type != GO_LINE_NONE;
+}
+
+void
+gog_style_force_auto (GogStyle *style)
+{
+
+ if (style->marker.mark != NULL)
+ g_object_unref (G_OBJECT (style->marker.mark));
+ style->marker.mark = go_marker_new ();
+ style->marker.auto_shape =
+ style->marker.auto_outline_color =
+ style->marker.auto_fill_color =
+ style->outline.auto_dash =
+ style->outline.auto_color =
+ style->line.auto_dash =
+ style->line.auto_color =
+ style->fill.auto_fore =
+ style->fill.auto_back =
+ style->font.auto_scale = TRUE;
+}
+
+/**
+ * gog_style_set_marker :
+ * @style : #GogStyle
+ * @marker : #GOMarker
+ *
+ * Absorb a reference to @marker and assign it to @style.
+ **/
+void
+gog_style_set_marker (GogStyle *style, GOMarker *marker)
+{
+ g_return_if_fail (GOG_STYLE (style) != NULL);
+ g_return_if_fail (GO_MARKER (marker) != NULL);
+
+ if (style->marker.mark != marker) {
+ if (style->marker.mark != NULL)
+ g_object_unref (style->marker.mark);
+ style->marker.mark = marker;
+ }
+}
+
+void
+gog_style_set_font (GogStyle *style, PangoFontDescription *desc)
+{
+ GOFont const *font;
+
+ g_return_if_fail (GOG_STYLE (style) != NULL);
+
+ font = go_font_new_by_desc (desc);
+ if (font != NULL) {
+ go_font_unref (style->font.font);
+ style->font.font = font;
+ }
+}
+
+void
+gog_style_set_fill_brightness (GogStyle *style, float brightness)
+{
+ g_return_if_fail (GOG_STYLE (style) != NULL);
+ g_return_if_fail (style->fill.type == GOG_FILL_STYLE_GRADIENT);
+
+ style->fill.gradient.brightness = brightness;
+ style->fill.pattern.fore = (brightness < 50.)
+ ? UINT_INTERPOLATE(style->fill.pattern.back, RGBA_WHITE, 1. - brightness / 50.)
+ : UINT_INTERPOLATE(style->fill.pattern.back, RGBA_BLACK, brightness / 50. - 1.);
+}
+
+/**
+ * gog_style_set_fill_image_filename :
+ * @style : #GogStyle
+ * @filename :
+ *
+ * absorb the string and eventually free it.
+ **/
+void
+gog_style_set_fill_image_filename (GogStyle *style, char *filename)
+{
+ g_return_if_fail (GOG_STYLE (style) != NULL);
+
+ if (style->fill.type == GOG_FILL_STYLE_IMAGE) {
+ if (style->fill.image.image != NULL)
+ g_object_unref (style->fill.image.image);
+ g_free (style->fill.image.filename);
+ } else {
+ style->fill.type = GOG_FILL_STYLE_IMAGE;
+ style->fill.image.type = GOG_IMAGE_CENTERED;
+ }
+
+ style->fill.image.filename = filename;
+ style->fill.image.image = gdk_pixbuf_new_from_file (filename, NULL);
+}
+
+static void
+cb_switch_page (G_GNUC_UNUSED GtkNotebook *n, G_GNUC_UNUSED GtkNotebookPage *p,
+ guint page_num, guint *store_page)
+{
+ *store_page = page_num;
+}
+
+void
+gog_style_handle_notebook (gpointer notebook, guint *page)
+{
+ g_return_if_fail (GTK_NOTEBOOK (notebook) != NULL);
+ g_return_if_fail (page != NULL);
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), *page);
+ g_signal_connect (G_OBJECT (notebook),
+ "switch_page",
+ G_CALLBACK (cb_switch_page), page);
+}
--- /dev/null
+++ lib/goffice/graph/gog-renderer.c
@@ -0,0 +1,629 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-renderer-impl.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/utils/go-units.h>
+#include <goffice/utils/go-font.h>
+#include <goffice/utils/go-math.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <math.h>
+
+/* We need to define an hair line width for the svg and gnome_print renderer.
+ * 0.24 pt is the dot size of a 300 dpi printer, if the plot is printed at scale 1:1 */
+#define GOG_RENDERER_HAIR_LINE_WIDTH 0.24
+
+enum {
+ RENDERER_PROP_0,
+ RENDERER_PROP_MODEL,
+ RENDERER_PROP_VIEW,
+ RENDERER_PROP_LOGICAL_WIDTH_PTS,
+ RENDERER_PROP_LOGICAL_HEIGHT_PTS,
+ RENDERER_PROP_ZOOM
+};
+enum {
+ RENDERER_SIGNAL_REQUEST_UPDATE,
+ RENDERER_SIGNAL_LAST
+};
+static gulong renderer_signals [RENDERER_SIGNAL_LAST] = { 0, };
+
+static GObjectClass *parent_klass;
+
+static void
+gog_renderer_finalize (GObject *obj)
+{
+ GogRenderer *rend = GOG_RENDERER (obj);
+
+ go_line_vpath_dash_free (rend->line_dash);
+ rend->line_dash = NULL;
+ go_line_vpath_dash_free (rend->outline_dash);
+ rend->outline_dash = NULL;
+
+ if (rend->clip_stack != NULL)
+ g_warning ("Missing calls to gog_renderer_pop_clip");
+
+ if (rend->cur_style != NULL) {
+ g_warning ("Missing calls to gog_renderer_style_pop left dangling style references");
+ g_slist_foreach (rend->style_stack,
+ (GFunc)g_object_unref, NULL);
+ g_slist_free (rend->style_stack);
+ rend->style_stack = NULL;
+ g_object_unref ((gpointer)rend->cur_style);
+ rend->cur_style = NULL;
+ }
+
+ if (rend->view != NULL) {
+ g_object_unref (rend->view);
+ rend->view = NULL;
+ }
+
+ if (rend->font_watcher != NULL) {
+ go_font_cache_unregister (rend->font_watcher);
+ g_closure_unref (rend->font_watcher);
+ rend->font_watcher = NULL;
+ }
+
+ (*parent_klass->finalize) (obj);
+}
+
+static void
+gog_renderer_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogRenderer *rend = GOG_RENDERER (obj);
+
+ switch (param_id) {
+ case RENDERER_PROP_MODEL:
+ rend->model = GOG_GRAPH (g_value_get_object (value));
+ if (rend->view != NULL)
+ g_object_unref (rend->view);
+ rend->view = g_object_new (gog_graph_view_get_type (),
+ "renderer", rend,
+ "model", rend->model,
+ NULL);
+ gog_renderer_request_update (rend);
+ break;
+ case RENDERER_PROP_LOGICAL_WIDTH_PTS:
+ rend->logical_width_pts = g_value_get_double (value);
+ break;
+
+ case RENDERER_PROP_LOGICAL_HEIGHT_PTS:
+ rend->logical_height_pts = g_value_get_double (value);
+ break;
+
+ case RENDERER_PROP_ZOOM:
+ rend->zoom = g_value_get_double (value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+}
+
+static void
+gog_renderer_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogRenderer const *rend = GOG_RENDERER (obj);
+
+ switch (param_id) {
+ case RENDERER_PROP_MODEL:
+ g_value_set_object (value, rend->model);
+ break;
+ case RENDERER_PROP_VIEW:
+ g_value_set_object (value, rend->view);
+ break;
+ case RENDERER_PROP_LOGICAL_WIDTH_PTS:
+ g_value_set_double (value, rend->logical_width_pts);
+ break;
+ case RENDERER_PROP_LOGICAL_HEIGHT_PTS:
+ g_value_set_double (value, rend->logical_height_pts);
+ break;
+ case RENDERER_PROP_ZOOM:
+ g_value_set_double (value, rend->zoom);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+void
+gog_renderer_request_update (GogRenderer *renderer)
+{
+ g_return_if_fail (GOG_RENDERER (renderer) != NULL);
+
+ if (renderer->needs_update)
+ return;
+ renderer->needs_update = TRUE;
+ g_signal_emit (G_OBJECT (renderer),
+ renderer_signals [RENDERER_SIGNAL_REQUEST_UPDATE], 0);
+}
+
+void
+gog_renderer_invalidate_size_requests (GogRenderer *rend)
+{
+ g_return_if_fail (GOG_RENDERER (rend) != NULL);
+
+ if (rend->view)
+ gog_renderer_request_update (rend);
+}
+
+static void
+update_dash (GogRenderer *rend)
+{
+ double size;
+
+ go_line_vpath_dash_free (rend->line_dash);
+ rend->line_dash = NULL;
+ go_line_vpath_dash_free (rend->outline_dash);
+ rend->outline_dash = NULL;
+
+ if (rend->cur_style == NULL)
+ return;
+
+ size = gog_renderer_line_size (rend, rend->cur_style->line.width);
+ rend->line_dash = go_line_get_vpath_dash (rend->cur_style->line.dash_type, size);
+ size = gog_renderer_line_size (rend, rend->cur_style->outline.width);
+ rend->outline_dash = go_line_get_vpath_dash (rend->cur_style->outline.dash_type, size);
+}
+
+void
+gog_renderer_push_style (GogRenderer *rend, GogStyle const *style)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (GOG_STYLE (style) != NULL);
+
+ if (rend->cur_style != NULL)
+ rend->style_stack = g_slist_prepend (
+ rend->style_stack, (gpointer)rend->cur_style);
+ g_object_ref ((gpointer)style);
+ rend->cur_style = style;
+
+ if (klass->push_style)
+ klass->push_style (rend, style);
+
+ update_dash (rend);
+}
+
+void
+gog_renderer_pop_style (GogRenderer *rend)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (rend->cur_style != NULL);
+
+ g_object_unref ((gpointer)rend->cur_style);
+ if (rend->style_stack != NULL) {
+ rend->cur_style = rend->style_stack->data;
+ rend->style_stack = g_slist_remove (rend->style_stack,
+ rend->cur_style);
+ } else
+ rend->cur_style = NULL;
+
+ if (klass->pop_style)
+ klass->pop_style (rend);
+
+ update_dash (rend);
+}
+
+/**
+ * gog_renderer_clip_push :
+ * @rend : #GogRenderer
+ * @region: #GogViewAllocation
+ *
+ * region defines the current clipping region.
+ **/
+void
+gog_renderer_clip_push (GogRenderer *rend, GogViewAllocation const *region)
+{
+ GogRendererClip *clip;
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+
+ clip = g_new (GogRendererClip, 1);
+ clip->area = *region;
+
+ rend->clip_stack = g_slist_prepend (rend->clip_stack, clip);
+ rend->cur_clip = clip;
+
+ (klass->clip_push) (rend, clip);
+}
+
+/**
+ * gog_renderer_clip_pop :
+ * @rend : #GogRenderer
+ *
+ * End the current clipping.
+ **/
+void
+gog_renderer_clip_pop (GogRenderer *rend)
+{
+ GogRendererClip *clip;
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (rend->clip_stack != NULL);
+
+ clip = (GogRendererClip *) rend->clip_stack->data;
+
+ (klass->clip_pop) (rend, clip);
+
+ g_free (clip);
+ rend->clip_stack = g_slist_delete_link (rend->clip_stack, rend->clip_stack);
+
+ if (rend->clip_stack != NULL)
+ rend->cur_clip = (GogRendererClip *) rend->clip_stack->data;
+ else
+ rend->cur_clip = NULL;
+}
+
+/**
+ * gog_renderer_draw_sharp_polygon :
+ * @rend : #GogRenderer
+ * @path : #ArtVpath
+ * @bound : #GogViewAllocation optional clip
+ *
+ * Draws @path using the outline elements of the current style,
+ * trying to make line with sharp edge.
+ **/
+void
+gog_renderer_draw_sharp_path (GogRenderer *rend, ArtVpath *path,
+ GogViewAllocation const *bound)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (rend->cur_style != NULL);
+
+ if (klass->sharp_path) {
+ (klass->sharp_path) (rend, path,
+ gog_renderer_line_size (rend, rend->cur_style->line.width));
+ }
+
+ (klass->draw_path) (rend, path, bound);
+}
+
+/**
+ * gog_renderer_draw_polygon :
+ * @rend : #GogRenderer
+ * @path : #ArtVpath
+ * @bound : #GogViewAllocation optional clip
+ *
+ * Draws @path using the outline elements of the current style.
+ **/
+void
+gog_renderer_draw_path (GogRenderer *rend, ArtVpath const *path,
+ GogViewAllocation const *bound)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (rend->cur_style != NULL);
+
+ (klass->draw_path) (rend, path, bound);
+}
+
+/**
+ * gog_renderer_draw_sharp_polygon :
+ * @rend : #GogRenderer
+ * @path : #ArtVpath
+ * @narrow : if TRUE skip any outline the current style specifies.
+ * @bound : #GogViewAllocation optional clip
+ *
+ * Draws @path and fills it with the fill elements of the current style,
+ * trying to draw line with sharp edge.
+ * If @narrow is false it alos outlines it using the outline elements.
+ **/
+void
+gog_renderer_draw_sharp_polygon (GogRenderer *rend, ArtVpath *path, gboolean narrow,
+ GogViewAllocation const *bound)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (rend->cur_style != NULL);
+
+ if (klass->sharp_path) {
+ (klass->sharp_path) (rend, path,
+ gog_renderer_line_size (rend, rend->cur_style->outline.width));
+ }
+
+ (klass->draw_polygon) (rend, path, narrow, bound);
+}
+
+/**
+ * gog_renderer_draw_polygon :
+ * @rend : #GogRenderer
+ * @path : #ArtVpath
+ * @narrow : if TRUE skip any outline the current style specifies.
+ * @bound : #GogViewAllocation optional clip
+ *
+ * Draws @path and fills it with the fill elements of the current style.
+ * If @narrow is false it alos outlines it using the outline elements.
+ **/
+void
+gog_renderer_draw_polygon (GogRenderer *rend, ArtVpath const *path, gboolean narrow,
+ GogViewAllocation const *bound)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (rend->cur_style != NULL);
+
+ (klass->draw_polygon) (rend, path, narrow, bound);
+}
+
+/**
+ * gog_renderer_draw_text :
+ * @rend : #GogRenderer
+ * @text : the string to draw
+ * @pos : #GogViewAllocation
+ * @anchor : #GtkAnchorType how to draw relative to @pos
+ * @result : an optionally NULL #GogViewAllocation
+ *
+ * Have @rend draw @text in the at @pos.{x,y} anchored by the @anchor corner.
+ * If @pos.w or @pos.h are >= 0 then clip the results to less than that size.
+ * If @result is supplied it will recieve the actual position of the result.
+ **/
+void
+gog_renderer_draw_text (GogRenderer *rend, char const *text,
+ GogViewAllocation const *pos, GtkAnchorType anchor,
+ GogViewAllocation *result)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (rend->cur_style != NULL);
+ g_return_if_fail (text != NULL);
+
+ if (*text == '\0') {
+ if (result != NULL) {
+ result->x = pos->x;
+ result->y = pos->y;
+ result->w = 0.;
+ result->h = 0.;
+ }
+ return;
+ }
+
+ (klass->draw_text) (rend, text, pos, anchor, result);
+}
+
+/**
+ * gog_renderer_draw_marker :
+ * @rend : #GogRenderer
+ * @pos : #ArtPoint
+ **/
+void
+gog_renderer_draw_marker (GogRenderer *rend, double x, double y)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (rend->cur_style != NULL);
+
+ (klass->draw_marker) (rend, x, y);
+}
+
+/**
+ * gog_renderer_measure_text :
+ * @rend : #GogRenderer
+ * @text : the string to draw
+ * @size : #GogViewRequisition to store the size of @text.
+ **/
+void
+gog_renderer_measure_text (GogRenderer *rend,
+ char const *text, GogViewRequisition *size)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (rend->cur_style != NULL);
+ g_return_if_fail (text != NULL);
+
+ if (*text == '\0') {
+ /* Make sure invisible things don't skew size */
+ size->w = size->h = 0;
+ return;
+ }
+
+ (klass->measure_text) (rend, text, size);
+
+ /* Make sure invisible things don't skew size */
+ if (size->w == 0)
+ size->h = 0;
+ else if (size->h == 0)
+ size->w = 0;
+}
+
+static void
+cb_font_removed (GogRenderer *rend, GOFont const *font)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ g_return_if_fail (klass != NULL);
+
+ gog_debug (0, g_warning ("notify a '%s' that %p is invalid",
+ G_OBJECT_TYPE_NAME (rend), font););
+
+ if (klass->font_removed)
+ (klass->font_removed) (rend, font);
+}
+
+static void
+gog_renderer_class_init (GogRendererClass *renderer_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *)renderer_klass;
+
+ parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = gog_renderer_finalize;
+ gobject_klass->set_property = gog_renderer_set_property;
+ gobject_klass->get_property = gog_renderer_get_property;
+
+ renderer_klass->sharp_path = NULL;
+ renderer_klass->line_size = NULL;
+
+ g_object_class_install_property (gobject_klass, RENDERER_PROP_MODEL,
+ g_param_spec_object ("model", "model",
+ "the GogGraph this renderer displays",
+ GOG_GRAPH_TYPE, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, RENDERER_PROP_VIEW,
+ g_param_spec_object ("view", "view",
+ "the GogView this renderer is displaying",
+ GOG_VIEW_TYPE, G_PARAM_READABLE));
+ g_object_class_install_property (gobject_klass, RENDERER_PROP_LOGICAL_WIDTH_PTS,
+ g_param_spec_double ("logical_width_pts", "Logical Width Pts",
+ "Logical width of the drawing area in pts",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, RENDERER_PROP_LOGICAL_HEIGHT_PTS,
+ g_param_spec_double ("logical_height_pts", "Logical Height Pts",
+ "Logical height of the drawing area in pts",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, RENDERER_PROP_ZOOM,
+ g_param_spec_double ("zoom", "zoom Height Pts",
+ "global scale factor",
+ 1., G_MAXDOUBLE, 1., G_PARAM_READWRITE));
+
+ renderer_signals [RENDERER_SIGNAL_REQUEST_UPDATE] = g_signal_new ("request-update",
+ G_TYPE_FROM_CLASS (renderer_klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogRendererClass, request_update),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+gog_renderer_init (GogRenderer *rend)
+{
+ rend->cur_clip = NULL;
+ rend->clip_stack = NULL;
+
+ rend->line_dash = NULL;
+ rend->outline_dash = NULL;
+
+ rend->needs_update = FALSE;
+ rend->cur_style = NULL;
+ rend->style_stack = NULL;
+ rend->zoom = rend->scale = rend->scale_x = rend->scale_y = 1.;
+ rend->logical_width_pts = GO_CM_TO_PT ((double)12);
+ rend->logical_height_pts = GO_CM_TO_PT ((double)8);
+ rend->font_watcher = g_cclosure_new_swap (G_CALLBACK (cb_font_removed),
+ rend, NULL);
+ go_font_cache_register (rend->font_watcher);
+}
+
+GSF_CLASS (GogRenderer, gog_renderer,
+ gog_renderer_class_init, gog_renderer_init,
+ G_TYPE_OBJECT)
+
+/**
+ * gog_renderer_draw_rectangle :
+ * @renderer : #GogRenderer
+ * @rect : #GogViewAllocation
+ * @bound : #GogViewAllocation optional clip
+ *
+ * A utility routine to build a vpath in @rect.
+ **/
+static void
+draw_rectangle (GogRenderer *rend, GogViewAllocation const *rect,
+ GogViewAllocation const *bound, gboolean sharp)
+{
+ gboolean const narrow = (rect->w < 3.) || (rect->h < 3.);
+ double o, o_2;
+ ArtVpath path[6];
+
+ if (!narrow) {
+ o = gog_renderer_line_size (rend, rend->cur_style->outline.width);
+ o_2 = o / 2.;
+ } else
+ o = o_2 = 0.;
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ path[0].x = path[1].x = path[4].x = rect->x + o_2;
+ path[2].x = path[3].x = path[0].x + rect->w - o;
+ path[0].y = path[3].y = path[4].y = rect->y + o_2;
+ path[1].y = path[2].y = path[0].y + rect->h - o;
+
+ if (sharp)
+ gog_renderer_draw_sharp_polygon (rend, path, narrow, bound);
+ else
+ gog_renderer_draw_polygon (rend, path, narrow, bound);
+}
+
+void
+gog_renderer_draw_sharp_rectangle (GogRenderer *rend, GogViewAllocation const *rect,
+ GogViewAllocation const *bound)
+{
+ draw_rectangle (rend, rect, bound, TRUE);
+}
+
+void
+gog_renderer_draw_rectangle (GogRenderer *rend, GogViewAllocation const *rect,
+ GogViewAllocation const *bound)
+{
+ draw_rectangle (rend, rect, bound, FALSE);
+}
+
+double
+gog_renderer_line_size (GogRenderer const *rend, double width)
+{
+ GogRendererClass *klass = GOG_RENDERER_GET_CLASS (rend);
+
+ if (klass->line_size)
+ return (klass->line_size) (rend, width);
+
+ if (go_sub_epsilon (width) <= 0.)
+ width = GOG_RENDERER_HAIR_LINE_WIDTH;
+ return width * rend->scale;
+}
+
+double
+gog_renderer_pt2r_x (GogRenderer const *rend, double d)
+{
+ return d * rend->scale_x;
+}
+
+double
+gog_renderer_pt2r_y (GogRenderer const *rend, double d)
+{
+ return d * rend->scale_y;
+}
+
+double
+gog_renderer_pt2r (GogRenderer const *rend, double d)
+{
+ return d * rend->scale;
+}
+
--- /dev/null
+++ lib/goffice/graph/gog-theme.h
@@ -0,0 +1,49 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-theme.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_THEME_H
+#define GOG_THEME_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_THEME_TYPE (gog_theme_get_type ())
+#define GOG_THEME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_THEME_TYPE, GogTheme))
+#define IS_GOG_THEME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_THEME_TYPE))
+
+GType gog_theme_get_type (void);
+
+void gog_theme_fillin_style (GogTheme *theme, GogStyle *style,
+ GogObject *obj, int i, gboolean complete_overwrite);
+void gog_theme_register (GogTheme *theme, gboolean is_default);
+void gog_theme_register_file (char const *name, char const *file);
+GogTheme *gog_theme_lookup (char const *name);
+char const *gog_theme_get_name (GogTheme const *theme);
+
+
+/* private */
+void gog_themes_init (void);
+void gog_themes_shutdown (void);
+
+G_END_DECLS
+
+#endif /* GOG_THEME_H */
--- /dev/null
+++ lib/goffice/graph/gog-legend.c
@@ -0,0 +1,385 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-legend.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-legend.h>
+#include <goffice/graph/gog-outlined-object.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-units.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <gtk/gtknotebook.h>
+#include <glib/gi18n.h>
+
+struct _GogLegend {
+ GogOutlinedObject base;
+
+ double swatch_size_pts;
+ double swatch_padding_pts;
+ gulong chart_cardinality_handle;
+ gulong chart_child_name_changed_handle;
+ unsigned cached_count;
+ gboolean names_changed;
+};
+
+typedef GogStyledObjectClass GogLegendClass;
+
+enum {
+ LEGEND_PROP_0,
+ LEGEND_SWATCH_SIZE_PTS,
+ LEGEND_SWATCH_PADDING_PTS
+};
+
+static GType gog_legend_view_get_type (void);
+
+static GObjectClass *parent_klass;
+
+static void
+gog_legend_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogLegend *legend = GOG_LEGEND (obj);
+
+ switch (param_id) {
+ case LEGEND_SWATCH_SIZE_PTS :
+ legend->swatch_size_pts = g_value_get_double (value);
+ break;
+ case LEGEND_SWATCH_PADDING_PTS :
+ legend->swatch_padding_pts = g_value_get_double (value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+}
+
+static void
+gog_legend_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogLegend *legend = GOG_LEGEND (obj);
+
+ switch (param_id) {
+ case LEGEND_SWATCH_SIZE_PTS :
+ g_value_set_double (value, legend->swatch_size_pts);
+ break;
+ case LEGEND_SWATCH_PADDING_PTS :
+ g_value_set_double (value, legend->swatch_padding_pts);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+cb_chart_names_changed (GogLegend *legend)
+{
+ if (legend->names_changed)
+ return;
+ legend->names_changed = TRUE;
+ gog_object_request_update (GOG_OBJECT (legend));
+}
+
+static void
+gog_legend_parent_changed (GogObject *obj, gboolean was_set)
+{
+ GogObjectClass *gog_object_klass = GOG_OBJECT_CLASS (parent_klass);
+ GogLegend *legend = GOG_LEGEND (obj);
+
+ if (was_set) {
+ if (legend->chart_cardinality_handle == 0)
+ legend->chart_cardinality_handle =
+ g_signal_connect_object (G_OBJECT (obj->parent),
+ "notify::cardinality-valid",
+ G_CALLBACK (gog_object_request_update),
+ legend, G_CONNECT_SWAPPED);
+ if (legend->chart_child_name_changed_handle == 0)
+ legend->chart_child_name_changed_handle =
+ g_signal_connect_object (G_OBJECT (obj->parent),
+ "child-name-changed",
+ G_CALLBACK (cb_chart_names_changed),
+ legend, G_CONNECT_SWAPPED);
+ } else {
+ if (legend->chart_cardinality_handle != 0) {
+ g_signal_handler_disconnect (G_OBJECT (obj->parent),
+ legend->chart_cardinality_handle);
+ legend->chart_cardinality_handle = 0;
+ }
+ if (legend->chart_child_name_changed_handle != 0) {
+ g_signal_handler_disconnect (G_OBJECT (obj->parent),
+ legend->chart_child_name_changed_handle);
+ legend->chart_child_name_changed_handle = 0;
+ }
+ }
+
+ gog_object_klass->parent_changed (obj, was_set);
+}
+
+static void
+gog_legend_update (GogObject *obj)
+{
+ GogLegend *legend = GOG_LEGEND (obj);
+ unsigned visible;
+ gog_chart_get_cardinality (GOG_CHART (obj->parent), NULL, &visible);
+ if (legend->cached_count != visible)
+ legend->cached_count = visible;
+ else if (!legend->names_changed)
+ return;
+ legend->names_changed = FALSE;
+ gog_object_emit_changed (obj, TRUE);
+}
+
+static gpointer
+gog_legend_editor (GogObject *gobj, GogDataAllocator *dalloc, GnmCmdContext *cc)
+{
+ static guint legend_pref_page = 0;
+ GtkWidget *notebook = gtk_notebook_new ();
+
+ gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, notebook);
+ gog_style_handle_notebook (notebook, &legend_pref_page);
+ return notebook;
+}
+
+static void
+gog_legend_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL | GOG_STYLE_FONT;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_legend_class_init (GogLegendClass *klass)
+{
+ static GogObjectRole const roles[] = {
+ { N_("Title"), "GogLabel", 0,
+ GOG_POSITION_COMPASS, GOG_POSITION_N|GOG_POSITION_ALIGN_CENTER, GOG_OBJECT_NAME_BY_ROLE,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+ };
+
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+
+ parent_klass = g_type_class_peek_parent (klass);
+ gobject_klass->set_property = gog_legend_set_property;
+ gobject_klass->get_property = gog_legend_get_property;
+
+ gog_klass->parent_changed = gog_legend_parent_changed;
+ gog_klass->update = gog_legend_update;
+ gog_klass->editor = gog_legend_editor;
+ gog_klass->view_type = gog_legend_view_get_type ();
+ style_klass->init_style = gog_legend_init_style;
+ gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+
+ g_object_class_install_property (gobject_klass, LEGEND_SWATCH_SIZE_PTS,
+ g_param_spec_double ("swatch_size_pts", "Swatch Size pts",
+ "size of the swatches in pts.",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, LEGEND_SWATCH_PADDING_PTS,
+ g_param_spec_double ("swatch_padding_pts", "Swatch Padding pts",
+ "padding between the swatches in pts.",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+}
+
+static void
+gog_legend_init (GogLegend *legend)
+{
+ legend->swatch_size_pts = GO_CM_TO_PT ((double).25);
+ legend->swatch_padding_pts = GO_CM_TO_PT ((double).2);
+ legend->cached_count = 0;
+}
+
+GSF_CLASS (GogLegend, gog_legend,
+ gog_legend_class_init, gog_legend_init,
+ GOG_OUTLINED_OBJECT_TYPE)
+
+typedef struct {
+ GogOutlinedView base;
+ double line_height;
+ gboolean uses_lines;
+} GogLegendView;
+typedef GogOutlinedViewClass GogLegendViewClass;
+
+#define GOG_LEGEND_VIEW_TYPE (gog_legend_view_get_type ())
+#define GOG_LEGEND_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_LEGEND_VIEW_TYPE, GogLegendView))
+#define IS_GOG_LEGEND_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_LEGEND_VIEW_TYPE))
+
+static GogViewClass *lview_parent_klass;
+
+typedef struct {
+ GogView const *view;
+ GogViewRequisition maximum;
+ gboolean uses_lines;
+} size_closure;
+
+static void
+cb_size_elements (unsigned i, GogStyle const *style, char const *name,
+ size_closure *dat)
+{
+ GogViewRequisition req;
+ gog_renderer_push_style (dat->view->renderer, style);
+ gog_renderer_measure_text (dat->view->renderer, name, &req);
+ gog_renderer_pop_style (dat->view->renderer);
+
+ if (dat->maximum.w < req.w)
+ dat->maximum.w = req.w;
+ if (dat->maximum.h < req.h)
+ dat->maximum.h = req.h;
+ if (!dat->uses_lines && (style->interesting_fields & GOG_STYLE_LINE))
+ dat->uses_lines = TRUE;
+}
+
+static void
+gog_legend_view_size_request (GogView *v, GogViewRequisition *avail)
+{
+ size_closure dat;
+ GogViewRequisition res;
+ GogChart *chart = GOG_CHART (v->model->parent);
+ GogLegend *l = GOG_LEGEND (v->model);
+ unsigned n, mult = 1;
+
+#warning TODO : make this smarter (multiple columns and shrinking text)
+ dat.view = v;
+ dat.maximum.w = 0.;
+ dat.maximum.h = gog_renderer_pt2r_y (v->renderer, l->swatch_size_pts);
+ dat.uses_lines = FALSE;
+ gog_chart_foreach_elem (chart, TRUE,
+ (GogEnumFunc) cb_size_elements, &dat);
+ ((GogLegendView *)v)->line_height = dat.maximum.h;
+ ((GogLegendView *)v)->uses_lines = dat.uses_lines;
+
+ if (dat.uses_lines)
+ mult = 3;
+
+ /* 1/2 between swatch and label */
+ res.w = dat.maximum.w + gog_renderer_pt2r_x (v->renderer,
+ mult * l->swatch_size_pts + .5 * l->swatch_padding_pts);
+ gog_chart_get_cardinality (chart, NULL, &n);
+ res.h = n * dat.maximum.h;
+
+ gog_view_size_child_request (v, avail, &res);
+ avail->w = res.w;
+ avail->h = res.h;
+ lview_parent_klass->size_request (v, avail);
+}
+
+typedef struct {
+ GogView const *view;
+ GogViewAllocation swatch;
+ double step;
+ double label_offset;
+ double bottom;
+ ArtVpath line_path[3];
+} render_closure;
+
+static void
+cb_render_elements (unsigned i, GogStyle const *base_style, char const *name,
+ render_closure *dat)
+{
+ GogViewAllocation swatch = dat->swatch;
+ GogView const *v = dat->view;
+ GogStyle *style = NULL;
+ GogViewAllocation pos;
+
+ swatch.y += i * dat->step;
+ /* Allow for floating point inaccuracy */
+ if (swatch.y > dat->bottom + 0.0001)
+ return;
+
+ if (base_style->interesting_fields & GOG_STYLE_LINE) { /* line and marker */
+ style = (GogStyle *)base_style;
+ gog_renderer_push_style (v->renderer, style);
+ dat->line_path[0].y = dat->line_path[1].y = swatch.y + swatch.h / 2.;
+ gog_renderer_draw_sharp_path (v->renderer, dat->line_path, NULL);
+ gog_renderer_draw_marker (v->renderer,
+ (dat->line_path[0].x + dat->line_path[1].x) / 2.,
+ dat->line_path[0].y);
+ } else { /* area swatch */
+ style = gog_style_dup (base_style);
+ style->outline.width = 0; /* hairline */
+ style->outline.color = RGBA_BLACK;
+
+ gog_renderer_push_style (v->renderer, style);
+ gog_renderer_draw_sharp_rectangle (v->renderer, &swatch, NULL);
+ }
+ pos.x = swatch.x + dat->label_offset;
+ pos.y = swatch.y;
+ pos.h = pos.w = -1;
+ gog_renderer_draw_text (v->renderer, name, &pos, GTK_ANCHOR_NW, NULL);
+
+ gog_renderer_pop_style (v->renderer);
+
+ if (style != base_style)
+ g_object_unref (style);
+}
+
+static void
+gog_legend_view_render (GogView *v, GogViewAllocation const *bbox)
+{
+ render_closure dat;
+ GogLegend *l = GOG_LEGEND (v->model);
+ double pad_x = gog_renderer_pt2r_x (v->renderer, l->swatch_padding_pts);
+
+ (lview_parent_klass->render) (v, bbox);
+
+ dat.view = v;
+ dat.swatch.x = v->residual.x;
+ dat.swatch.y = v->residual.y;
+ dat.swatch.w = gog_renderer_pt2r_x (v->renderer, l->swatch_size_pts);
+ dat.swatch.h = gog_renderer_pt2r_y (v->renderer, l->swatch_size_pts);
+ dat.label_offset = dat.swatch.w + pad_x / 2.;
+ if (((GogLegendView *)v)->uses_lines) {
+ dat.line_path[0].code = ART_MOVETO;
+ dat.line_path[1].code = ART_LINETO;
+ dat.line_path[2].code = ART_END;
+ dat.line_path[0].x = dat.swatch.x;
+ dat.line_path[1].x = dat.swatch.x + 3. * dat.swatch.w;
+ dat.swatch.x += dat.swatch.w;
+ dat.label_offset += dat.swatch.w;
+ }
+ dat.step = ((GogLegendView *)v)->line_height;
+ dat.bottom = v->residual.y + v->residual.h -
+ ((GogLegendView *)v)->line_height;
+ gog_chart_foreach_elem (GOG_CHART (v->model->parent), TRUE,
+ (GogEnumFunc) cb_render_elements, &dat);
+}
+
+static void
+gog_legend_view_class_init (GogLegendViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+
+ lview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->size_request = gog_legend_view_size_request;
+ view_klass->render = gog_legend_view_render;
+ view_klass->clip = TRUE;
+}
+
+static GSF_CLASS (GogLegendView, gog_legend_view,
+ gog_legend_view_class_init, NULL,
+ GOG_OUTLINED_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-style.h
@@ -0,0 +1,149 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-style.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_GRAPH_STYLE_H
+#define GO_GRAPH_STYLE_H
+
+#include <goffice/graph/gog-object-xml.h>
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/utils/goffice-utils.h>
+#include <goffice/utils/go-gradient.h>
+#include <goffice/utils/go-line.h>
+#include <goffice/utils/go-pattern.h>
+#include <glib-object.h>
+#include <command-context.h> /* for GnmCmdContext */
+
+// +jsled
+#include <gdk-pixbuf/gdk-pixdata.h>
+#include <pango/pango.h>
+// -jsled
+
+G_BEGIN_DECLS
+
+#define GOG_STYLE_TYPE (gog_style_get_type ())
+#define GOG_STYLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_STYLE_TYPE, GogStyle))
+#define IS_GOG_STYLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_STYLE_TYPE))
+
+GType gog_style_get_type (void);
+
+typedef enum {
+ GOG_STYLE_OUTLINE = 1 << 0,
+ GOG_STYLE_FILL = 1 << 1,
+ GOG_STYLE_LINE = 1 << 2,
+ GOG_STYLE_MARKER = 1 << 3,
+ GOG_STYLE_FONT = 1 << 4,
+ GOG_STYLE_ALL = 0x1F
+} GogStyleFlag;
+
+typedef enum {
+ GOG_FILL_STYLE_NONE = 0,
+ GOG_FILL_STYLE_PATTERN = 1,
+ GOG_FILL_STYLE_GRADIENT = 2,
+ GOG_FILL_STYLE_IMAGE = 3
+} GogFillStyle;
+
+typedef enum {
+ GOG_IMAGE_STRETCHED,
+ GOG_IMAGE_WALLPAPER,
+ GOG_IMAGE_CENTERED
+} GogImageType;
+
+typedef struct {
+ /* <0 == no outline,
+ * =0 == hairline : unscaled, minimum useful (can be bigger than visible) size.
+ * >0 in pts */
+ float width;
+ GOLineDashType dash_type;
+ gboolean auto_dash;
+ GOColor color;
+ gboolean auto_color;
+ unsigned pattern; /* TODO border type from gnumeric */
+} GogStyleLine;
+
+typedef struct {
+ GOMarker *mark;
+ gboolean auto_shape;
+ gboolean auto_outline_color;
+ gboolean auto_fill_color;
+} GogStyleMark;
+
+struct _GogStyle {
+ GObject base;
+
+ GogStyleFlag interesting_fields;
+ GogStyleFlag disable_theming;
+
+ GogStyleLine outline, line;
+ struct {
+ GogFillStyle type;
+ gboolean auto_fore, auto_back; /* share between pattern and gradient */
+ gboolean invert_if_negative; /* placeholder for XL */
+
+ /* This could all be a union but why bother ? */
+ GOPattern pattern;
+ struct {
+ GOGradientDirection dir;
+ float brightness; /* < 0 => 2 color */
+ } gradient;
+ struct {
+ GogImageType type;
+ GdkPixbuf *image;
+ char *filename;
+ } image;
+ } fill;
+ GogStyleMark marker;
+ struct {
+ GOColor color;
+ GOFont const *font;
+ gboolean auto_scale;
+ } font;
+};
+
+GogStyle *gog_style_new (void);
+GogStyle *gog_style_dup (GogStyle const *style);
+void gog_style_assign (GogStyle *dst, GogStyle const *src);
+void gog_style_apply_theme (GogStyle *dst, GogStyle const *src);
+void gog_style_set_marker (GogStyle *style, GOMarker *marker);
+void gog_style_set_font (GogStyle *style,
+ PangoFontDescription *desc);
+void gog_style_set_fill_brightness (GogStyle *style, float brightness);
+void gog_style_set_fill_image_filename (GogStyle *style, char *filename);
+
+gboolean gog_style_is_different_size (GogStyle const *a, GogStyle const *b);
+gboolean gog_style_is_marker_visible (GogStyle const *style);
+gboolean gog_style_is_line_visible (GogStyle const *style);
+gboolean gog_style_is_outline_visible (GogStyle const *style);
+void gog_style_force_auto (GogStyle *style);
+
+gpointer gog_style_editor (GogStyle *style,
+ GogStyle *default_style,
+ GnmCmdContext *cc,
+ gpointer optional_notebook,
+ GObject *object_with_style);
+gpointer gog_styled_object_editor (GogStyledObject *gso,
+ GnmCmdContext *cc,
+ gpointer optional_notebook);
+
+/* move this to the widget utils dir when we get one */
+void gog_style_handle_notebook (gpointer notebook, guint *page);
+
+G_END_DECLS
+
+#endif /* GO_GRAPH_STYLE_H */
--- /dev/null
+++ lib/goffice/graph/gog-series.h
@@ -0,0 +1,56 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-series.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_SERIES_H
+#define GOG_SERIES_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-styled-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_SERIES_ELEMENT_TYPE (gog_series_element_get_type ())
+#define GOG_SERIES_ELEMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_SERIES_ELEMENT_TYPE, GogSeriesElement))
+#define IS_GOG_SERIES_ELEMENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_SERIES_ELEMENT_TYPE))
+GType gog_series_element_get_type (void);
+
+#define GOG_SERIES_TYPE (gog_series_get_type ())
+#define GOG_SERIES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_SERIES_TYPE, GogSeries))
+#define IS_GOG_SERIES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_SERIES_TYPE))
+
+GType gog_series_get_type (void);
+gboolean gog_series_is_valid (GogSeries const *series);
+gboolean gog_series_has_legend (GogSeries const *series);
+GODataScalar *gog_series_get_name (GogSeries const *series);
+GogPlot *gog_series_get_plot (GogSeries const *series);
+void gog_series_set_name (GogSeries *series,
+ GODataScalar *val, GError **err);
+void gog_series_set_dim (GogSeries *series, int dim_i,
+ GOData *val, GError **err);
+void gog_series_set_index (GogSeries *series,
+ int ind, gboolean is_manual);
+
+unsigned gog_series_num_elements (GogSeries const *series);
+GList const *gog_series_get_overrides (GogSeries const *series);
+
+G_END_DECLS
+
+#endif /* GOG_SERIES_H */
--- /dev/null
+++ lib/goffice/graph/go-data-simple.c
@@ -0,0 +1,537 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-data-simple.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <goffice/goffice-config.h>
+#include <goffice/graph/go-data-simple.h>
+#include <goffice/graph/go-data-impl.h>
+#include <goffice/utils/go-math.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+struct _GODataScalarVal {
+ GODataScalar base;
+ double val;
+ char *str;
+};
+typedef GODataScalarClass GODataScalarValClass;
+
+static GObjectClass *scalar_val_parent_klass;
+
+static void
+go_data_scalar_val_finalize (GObject *obj)
+{
+ GODataScalarVal *val = (GODataScalarVal *)obj;
+
+ if (val->str != NULL) {
+ g_free (val->str);
+ val->str = NULL;
+ }
+
+ (*scalar_val_parent_klass->finalize) (obj);
+}
+
+static GOData *
+go_data_scalar_val_dup (GOData const *src)
+{
+ GODataScalarVal *dst = g_object_new (G_OBJECT_TYPE (src), NULL);
+ GODataScalarVal const *src_val = (GODataScalarVal const *)src;
+ dst->val = src_val->val;
+ return GO_DATA (dst);
+}
+
+static gboolean
+go_data_scalar_val_eq (GOData const *a, GOData const *b)
+{
+ GODataScalarVal const *sval_a = (GODataScalarVal const *)a;
+ GODataScalarVal const *sval_b = (GODataScalarVal const *)b;
+
+ /* GOData::eq is used for identity, not arithmetic */
+ return sval_a->val == sval_b->val;
+}
+
+static char *
+go_data_scalar_val_as_str (GOData const *dat)
+{
+ return g_strdup (go_data_scalar_get_str (GO_DATA_SCALAR (dat)));
+}
+
+static gboolean
+go_data_scalar_val_from_str (GOData *dat, char const *str)
+{
+ GODataScalarVal *sval = (GODataScalarVal *)dat;
+ double tmp;
+ char *end;
+ errno = 0; /* strto(ld) sets errno, but does not clear it. */
+ tmp = strtod (str, &end);
+
+ if (end == str || *end != '\0' || errno == ERANGE)
+ return FALSE;
+
+ g_free (sval->str);
+ sval->str = NULL;
+ sval->val = tmp;
+ return TRUE;
+}
+
+static double
+go_data_scalar_val_get_value (GODataScalar *dat)
+{
+ GODataScalarVal const *sval = (GODataScalarVal const *)dat;
+ return sval->val;
+}
+
+static char const *
+go_data_scalar_val_get_str (GODataScalar *dat)
+{
+ GODataScalarVal *sval = (GODataScalarVal *)dat;
+
+ if (sval->str == NULL)
+ sval->str = g_strdup_printf ("%g", sval->val);
+ return sval->str;
+}
+
+static void
+go_data_scalar_val_class_init (GObjectClass *gobject_klass)
+{
+ GODataClass *godata_klass = (GODataClass *) gobject_klass;
+ GODataScalarClass *scalar_klass = (GODataScalarClass *) gobject_klass;
+
+ scalar_val_parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = go_data_scalar_val_finalize;
+ godata_klass->dup = go_data_scalar_val_dup;
+ godata_klass->eq = go_data_scalar_val_eq;
+ godata_klass->as_str = go_data_scalar_val_as_str;
+ godata_klass->from_str = go_data_scalar_val_from_str;
+ scalar_klass->get_value = go_data_scalar_val_get_value;
+ scalar_klass->get_str = go_data_scalar_val_get_str;
+}
+
+GSF_CLASS (GODataScalarVal, go_data_scalar_val,
+ go_data_scalar_val_class_init, NULL,
+ GO_DATA_SCALAR_TYPE)
+
+GOData *
+go_data_scalar_val_new (double val)
+{
+ GODataScalarVal *res = g_object_new (GO_DATA_SCALAR_VAL_TYPE, NULL);
+ res->val = val;
+ return GO_DATA (res);
+}
+
+/*****************************************************************************/
+
+struct _GODataScalarStr {
+ GODataScalar base;
+ char const *str;
+ gboolean needs_free;
+};
+typedef GODataScalarClass GODataScalarStrClass;
+
+static GObjectClass *scalar_str_parent_klass;
+
+static void
+go_data_scalar_str_finalize (GObject *obj)
+{
+ GODataScalarStr *str = (GODataScalarStr *)obj;
+
+ if (str->needs_free && str->str != NULL) {
+ g_free ((char *)str->str);
+ str->str = NULL;
+ }
+ (*scalar_str_parent_klass->finalize) (obj);
+}
+
+static GOData *
+go_data_scalar_str_dup (GOData const *src)
+{
+ GODataScalarStr *dst = g_object_new (G_OBJECT_TYPE (src), NULL);
+ GODataScalarStr const *src_val = (GODataScalarStr const *)src;
+ dst->needs_free = TRUE;
+ dst->str = g_strdup (src_val->str);
+ return GO_DATA (dst);
+}
+
+static gboolean
+go_data_scalar_str_eq (GOData const *a, GOData const *b)
+{
+ GODataScalarStr const *str_a = (GODataScalarStr const *)a;
+ GODataScalarStr const *str_b = (GODataScalarStr const *)b;
+ return 0 == strcmp (str_a->str, str_b->str);
+}
+
+static char *
+go_data_scalar_str_as_str (GOData const *dat)
+{
+ GODataScalarStr const *str = (GODataScalarStr const *)dat;
+ return g_strdup (str->str);
+}
+
+static gboolean
+go_data_scalar_str_from_str (GOData *dat, char const *string)
+{
+ GODataScalarStr *str = (GODataScalarStr *)dat;
+
+ if (str->str == string)
+ return TRUE;
+ if (str->needs_free)
+ g_free ((char *)str->str);
+ str->str = g_strdup (string);
+ str->needs_free = TRUE;
+ return TRUE;
+}
+
+static double
+go_data_scalar_str_get_value (GODataScalar *dat)
+{
+ return go_nan;
+}
+
+static char const *
+go_data_scalar_str_get_str (GODataScalar *dat)
+{
+ GODataScalarStr const *str = (GODataScalarStr const *)dat;
+ return str->str;
+}
+
+static void
+go_data_scalar_str_class_init (GObjectClass *gobject_klass)
+{
+ GODataClass *godata_klass = (GODataClass *) gobject_klass;
+ GODataScalarClass *scalar_klass = (GODataScalarClass *) gobject_klass;
+
+ scalar_str_parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = go_data_scalar_str_finalize;
+ godata_klass->dup = go_data_scalar_str_dup;
+ godata_klass->eq = go_data_scalar_str_eq;
+ godata_klass->as_str = go_data_scalar_str_as_str;
+ godata_klass->from_str = go_data_scalar_str_from_str;
+ scalar_klass->get_value = go_data_scalar_str_get_value;
+ scalar_klass->get_str = go_data_scalar_str_get_str;
+}
+
+static void
+go_data_scalar_str_init (GObject *obj)
+{
+ GODataScalarStr *str = (GODataScalarStr *)obj;
+ str->str = "";
+ str->needs_free = FALSE;
+}
+
+GSF_CLASS (GODataScalarStr, go_data_scalar_str,
+ go_data_scalar_str_class_init, go_data_scalar_str_init,
+ GO_DATA_SCALAR_TYPE)
+
+GOData *
+go_data_scalar_str_new (char const *str, gboolean needs_free)
+{
+ GODataScalarStr *res = g_object_new (GO_DATA_SCALAR_STR_TYPE, NULL);
+ res->str = str;
+ res->needs_free = needs_free;
+ return GO_DATA (res);
+}
+void
+go_data_scalar_str_set_str (GODataScalarStr *str,
+ char const *text, gboolean needs_free)
+{
+ if (str->str == text)
+ return;
+ if (str->needs_free)
+ g_free ((char *)str->str);
+ str->str = text;
+ str->needs_free = needs_free;
+ go_data_emit_changed (GO_DATA (str));
+}
+
+/*****************************************************************************/
+
+struct _GODataVectorVal {
+ GODataVector base;
+ unsigned n;
+ double const *val;
+};
+typedef GODataVectorClass GODataVectorValClass;
+
+static GObjectClass *vector_val_parent_klass;
+
+static void
+go_data_vector_val_finalize (GObject *obj)
+{
+ /* GODataVectorVal *val = (GODataVectorVal *)obj; */
+
+ (*vector_val_parent_klass->finalize) (obj);
+}
+
+static GOData *
+go_data_vector_val_dup (GOData const *src)
+{
+ GODataVectorVal *dst = g_object_new (G_OBJECT_TYPE (src), NULL);
+ GODataVectorVal const *src_val = (GODataVectorVal const *)src;
+ dst->val = src_val->val;
+ dst->n = src_val->n;
+ return GO_DATA (dst);
+}
+
+static gboolean
+go_data_vector_val_eq (GOData const *a, GOData const *b)
+{
+ GODataVectorVal const *val_a = (GODataVectorVal const *)a;
+ GODataVectorVal const *val_b = (GODataVectorVal const *)b;
+
+ /* GOData::eq is used for identity, not arithmetic */
+ return val_a->val == val_b->val && val_a->n == val_b->n;
+}
+
+static void
+go_data_vector_val_load_len (GODataVector *vec)
+{
+ vec->base.flags |= GO_DATA_VECTOR_LEN_CACHED;
+ vec->len = ((GODataVectorVal *)vec)->n;
+}
+
+static void
+go_data_vector_val_load_values (GODataVector *vec)
+{
+ GODataVectorVal const *val = (GODataVectorVal const *)vec;
+ double minimum = DBL_MAX, maximum = -DBL_MAX;
+ int i = val->n;
+
+ vec->values = (double *)val->val;
+
+ while (i-- > 0) {
+ if (minimum > val->val[i])
+ minimum = val->val[i];
+ if (maximum < val->val[i])
+ maximum = val->val[i];
+ }
+ vec->minimum = minimum;
+ vec->maximum = maximum;
+ vec->base.flags |= GO_DATA_CACHE_IS_VALID;
+}
+static double
+go_data_vector_val_get_value (GODataVector *vec, unsigned i)
+{
+ return vec->values[i];
+}
+static char *
+go_data_vector_val_get_str (GODataVector *vec, unsigned i)
+{
+ return g_strdup_printf ("%g", vec->values[i]);
+}
+
+static void
+go_data_vector_val_class_init (GObjectClass *gobject_klass)
+{
+ GODataClass *godata_klass = (GODataClass *) gobject_klass;
+ GODataVectorClass *vector_klass = (GODataVectorClass *) gobject_klass;
+
+ vector_val_parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = go_data_vector_val_finalize;
+ godata_klass->dup = go_data_vector_val_dup;
+ godata_klass->eq = go_data_vector_val_eq;
+ godata_klass->as_str = NULL;
+ godata_klass->from_str = NULL;
+ vector_klass->load_len = go_data_vector_val_load_len;
+ vector_klass->load_values = go_data_vector_val_load_values;
+ vector_klass->get_value = go_data_vector_val_get_value;
+ vector_klass->get_str = go_data_vector_val_get_str;
+}
+
+GSF_CLASS (GODataVectorVal, go_data_vector_val,
+ go_data_vector_val_class_init, NULL,
+ GO_DATA_VECTOR_TYPE)
+
+GOData *
+go_data_vector_val_new (double const *val, unsigned n)
+{
+ GODataVectorVal *res = g_object_new (GO_DATA_VECTOR_VAL_TYPE, NULL);
+ res->val = val;
+ res->n = n;
+ return GO_DATA (res);
+}
+
+/*****************************************************************************/
+
+struct _GODataVectorStr {
+ GODataVector base;
+ char const * const *str;
+ unsigned n;
+
+ GOTranslateFunc translate_func;
+ gpointer translate_data;
+ GDestroyNotify translate_notify;
+};
+typedef GODataVectorClass GODataVectorStrClass;
+
+static GObjectClass *vector_str_parent_klass;
+
+static void
+go_data_vector_str_finalize (GObject *obj)
+{
+ /* GODataVectorStr *str = (GODataVectorStr *)obj; */
+
+ (*vector_str_parent_klass->finalize) (obj);
+}
+
+static GOData *
+go_data_vector_str_dup (GOData const *src)
+{
+ GODataVectorStr *dst = g_object_new (G_OBJECT_TYPE (src), NULL);
+ GODataVectorStr const *src_val = (GODataVectorStr const *)src;
+ dst->n = src_val->n;
+ dst->str = src_val->str;
+ return GO_DATA (dst);
+}
+
+static gboolean
+go_data_vector_str_eq (GOData const *a, GOData const *b)
+{
+ GODataVectorStr const *str_a = (GODataVectorStr const *)a;
+ GODataVectorStr const *str_b = (GODataVectorStr const *)b;
+ return str_a->str == str_b->str && str_a->n == str_b->n;
+}
+
+static void
+go_data_vector_str_load_len (GODataVector *vec)
+{
+ vec->base.flags |= GO_DATA_VECTOR_LEN_CACHED;
+ vec->len = ((GODataVectorStr *)vec)->n;
+}
+static void
+go_data_vector_str_load_values (GODataVector *vec)
+{
+}
+static double
+go_data_vector_str_get_value (GODataVector *vec, unsigned i)
+{
+ return go_nan;
+}
+static char *
+go_data_vector_str_get_str (GODataVector *vec, unsigned i)
+{
+ GODataVectorStr *strs = (GODataVectorStr *)vec;
+ if (strs->translate_func == NULL)
+ return g_strdup (strs->str[i]);
+ return g_strdup ((strs->translate_func) (strs->str[i],
+ strs->translate_data));
+}
+
+static void
+go_data_vector_str_class_init (GObjectClass *gobject_klass)
+{
+ GODataClass *godata_klass = (GODataClass *) gobject_klass;
+ GODataVectorClass *vector_klass = (GODataVectorClass *) gobject_klass;
+
+ vector_str_parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = go_data_vector_str_finalize;
+ godata_klass->dup = go_data_vector_str_dup;
+ godata_klass->eq = go_data_vector_str_eq;
+ godata_klass->as_str = NULL;
+ godata_klass->from_str = NULL;
+ vector_klass->load_len = go_data_vector_str_load_len;
+ vector_klass->load_values = go_data_vector_str_load_values;
+ vector_klass->get_value = go_data_vector_str_get_value;
+ vector_klass->get_str = go_data_vector_str_get_str;
+}
+
+static void
+go_data_vector_str_init (GObject *obj)
+{
+ GODataVectorStr *str = (GODataVectorStr *)obj;
+ str->str = NULL;
+ str->n = 0;
+ str->translate_func = NULL;
+ str->translate_data = NULL;
+ str->translate_notify = NULL;
+}
+
+GSF_CLASS (GODataVectorStr, go_data_vector_str,
+ go_data_vector_str_class_init, go_data_vector_str_init,
+ GO_DATA_VECTOR_TYPE)
+
+GOData *
+go_data_vector_str_new (char const * const *str, unsigned n)
+{
+ GODataVectorStr *res = g_object_new (GO_DATA_VECTOR_STR_TYPE, NULL);
+ res->str = str;
+ res->n = n;
+ return GO_DATA (res);
+}
+
+/**
+ * go_data_vector_str_set_translate_func:
+ * @vec: a #GODataVectorStr
+ * @func: a #GOTranslateFunc
+ * @data: data to be passed to @func and @notify
+ * @notify: a #GODestroyNotify function to be called when @vec is
+ * destroyed or when the translation function is changed
+ *
+ * Sets a function to be used for translating elements of @vec
+ **/
+void
+go_data_vector_str_set_translate_func (GODataVectorStr *vec,
+ GOTranslateFunc func,
+ gpointer data,
+ GDestroyNotify notify)
+{
+ g_return_if_fail (GO_DATA_VECTOR_STR (vec) != NULL);
+
+ if (vec->translate_notify != NULL)
+ (*vec->translate_notify) (vec->translate_data);
+
+ vec->translate_func = func;
+ vec->translate_data = data;
+ vec->translate_notify = notify;
+}
+
+static char const *
+dgettext_swapped (char const *msgid,
+ char const *domainname)
+{
+ return dgettext (domainname, msgid);
+}
+
+/**
+ * go_data_vector_str_set_translation_domain:
+ * @action_group: a #GtkActionGroup
+ * @domain: the translation domain to use for dgettext() calls
+ *
+ * Sets the translation domain and uses dgettext() for translating the
+ * elements of @vec.
+ * Note that libgoffice expects all strings to be encoded in UTF-8, therefore
+ * the translation domain must have its codeset set to UTF-8, see
+ * bind_textdomain_codeset() in the gettext() documentation.
+ *
+ * If you're not using gettext() for localization, see
+ * go_data_vector_str_set_translate_func().
+ **/
+void
+go_data_vector_str_set_translation_domain (GODataVectorStr *vec,
+ char const *domain)
+{
+ g_return_if_fail (GO_DATA_VECTOR_STR (vec) != NULL);
+
+ go_data_vector_str_set_translate_func (vec,
+ (GOTranslateFunc)dgettext_swapped, g_strdup (domain), g_free);
+}
--- /dev/null
+++ lib/goffice/graph/gog-control-foocanvas.h
@@ -0,0 +1,48 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-control-foocanvas.h : A foocanvas item to display GogGraph
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_CONTROL_FOOCANVAS_H
+#define GOG_CONTROL_FOOCANVAS_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <libfoocanvas/foo-canvas.h>
+#include <goffice/graph/gog-renderer-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GOG_CONTROL_FOOCANVAS_TYPE (gog_control_foocanvas_get_type ())
+#define GOG_CONTROL_FOOCANVAS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_CONTROL_FOOCANVAS_TYPE, GogControlFooCanvas))
+#define IS_GOG_CONTROL_FOOCANVAS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_CONTROL_FOOCANVAS_TYPE))
+
+typedef struct {
+ FooCanvasGroup base;
+
+ double new_h, new_w;
+
+ GogGraph *model;
+ GogRendererPixbuf *renderer;
+} GogControlFooCanvas;
+typedef FooCanvasGroupClass GogControlFooCanvasClass;
+
+GType gog_control_foocanvas_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_CONTROL_FOOCANVAS_H */
--- /dev/null
+++ lib/goffice/graph/gog-renderer-svg.c
@@ -0,0 +1,683 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-svg.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-renderer-svg.h>
+#include <goffice/graph/gog-renderer-impl.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-font.h>
+#include <goffice/utils/go-marker.h>
+#include <goffice/utils/go-units.h>
+
+#include <gsf/gsf-libxml.h>
+#include <gsf/gsf-impl-utils.h>
+#include <pango/pangoft2.h>
+
+#include <libxml/tree.h>
+
+#include <locale.h>
+#include <math.h>
+
+#define CC2XML(s) ((const xmlChar *)(s))
+
+#define GOG_RENDERER_SVG_TYPE (gog_renderer_svg_get_type ())
+#define GOG_RENDERER_SVG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RENDERER_SVG_TYPE, GogRendererSvg))
+#define IS_GOG_RENDERER_SVG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RENDERER_SVG_TYPE))
+
+typedef struct _GogRendererSvg GogRendererSvg;
+
+struct _GogRendererSvg {
+ GogRenderer base;
+
+ xmlDocPtr doc;
+ xmlNodePtr defs;
+ xmlNodePtr current_node;
+ GHashTable *table;
+ gint grad, pat, img;
+ unsigned clip_counter;
+
+ PangoContext *pango_context;
+};
+
+typedef GogRendererClass GogRendererSvgClass;
+
+static GObjectClass *parent_klass;
+
+static GType gog_renderer_svg_get_type (void);
+
+static void
+gog_renderer_svg_finalize (GObject *obj)
+{
+ GogRendererSvg *prend = GOG_RENDERER_SVG (obj);
+
+ if (prend->pango_context != NULL) {
+ g_object_unref (prend->pango_context);
+ prend->pango_context = NULL;
+ }
+
+ (*parent_klass->finalize) (obj);
+}
+
+static void
+gog_renderer_svg_clip_push (GogRenderer *rend, GogRendererClip *clip)
+{
+ GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
+ char *buf;
+ xmlNodePtr child;
+ xmlNodePtr node;
+ char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
+
+ prend->clip_counter++;
+
+ setlocale (LC_NUMERIC, "C");
+ node = xmlNewDocNode (prend->doc, NULL, CC2XML("clipPath"), NULL);
+ xmlAddChild (prend->defs, node);
+ buf = g_strdup_printf ("clip%i", prend->clip_counter);
+ xmlNewProp (node, CC2XML("id"), CC2XML(buf));
+ g_free (buf);
+ child = xmlNewDocNode (prend->doc, NULL, CC2XML("rect"), NULL);
+ xmlAddChild (node, child);
+ buf = g_strdup_printf ("%g", clip->area.x);
+ xmlNewProp (child, CC2XML("x"), CC2XML(buf));
+ g_free (buf);
+ buf = g_strdup_printf ("%g", clip->area.y);
+ xmlNewProp (child, CC2XML("y"), CC2XML(buf));
+ g_free (buf);
+ buf = g_strdup_printf ("%g", clip->area.w);
+ xmlNewProp (child, CC2XML("width"), CC2XML(buf));
+ g_free (buf);
+ buf = g_strdup_printf ("%g", clip->area.h);
+ xmlNewProp (child, CC2XML("height"), CC2XML(buf));
+ g_free (buf);
+
+ node = xmlNewDocNode (prend->doc, NULL, CC2XML("g"), NULL);
+ xmlAddChild (prend->current_node, node);
+ buf = g_strdup_printf ("url(#clip%i)", prend->clip_counter);
+ xmlNewProp (node, CC2XML ("clip-path"), CC2XML (buf));
+ g_free (buf);
+ setlocale (LC_NUMERIC, old_num_locale);
+ g_free (old_num_locale);
+
+ prend->current_node = node;
+}
+
+static void
+gog_renderer_svg_clip_pop (GogRenderer *rend, GogRendererClip *clip)
+{
+ GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
+
+ prend->current_node = prend->current_node->parent;
+}
+
+static void
+draw_path (GogRendererSvg *prend, ArtVpath const *path, GString *string)
+{
+ for ( ; path->code != ART_END ; path++)
+ switch (path->code) {
+ case ART_MOVETO_OPEN :
+ case ART_MOVETO :
+ g_string_append_printf (string, "M%g %g", path->x, path->y);
+ break;
+ case ART_LINETO :
+ g_string_append_printf (string, "L%g %g", path->x, path->y);
+ break;
+ default :
+ break;
+ }
+}
+
+static void
+stroke_dasharray (xmlNodePtr node, ArtVpathDash *dash)
+{
+ GString *string;
+ int i;
+
+ if (dash == NULL || dash->n_dash < 1)
+ return;
+
+ string = g_string_new ("");
+ for (i = 0; i < dash->n_dash; i++)
+ g_string_append_printf (string, i == 0 ? "%g" : " %g", dash->dash[i]);
+ xmlNewProp (node, CC2XML ("stroke-dasharray"), CC2XML (string->str));
+ g_string_free (string, TRUE);
+}
+
+static void
+gog_renderer_svg_draw_path (GogRenderer *renderer, ArtVpath const *path,
+ GogViewAllocation const *bound)
+{
+ GogRendererSvg *prend = GOG_RENDERER_SVG (renderer);
+ GogStyle const *style = renderer->cur_style;
+ xmlNodePtr node;
+ GString *string;
+ char *buf;
+ int opacity;
+ char *old_num_locale;
+
+ if (style->line.dash_type == GO_LINE_NONE)
+ return;
+
+ node = xmlNewDocNode (prend->doc, NULL, "path", NULL);
+ old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
+
+ setlocale (LC_NUMERIC, "C");
+ xmlAddChild (prend->current_node, node);
+ string = g_string_new ("");
+ draw_path (prend, path, string);
+ xmlNewProp (node, CC2XML ("d"), CC2XML (string->str));
+ g_string_free (string, TRUE);
+ xmlNewProp (node, CC2XML ("fill"), CC2XML ("none"));
+ buf = g_strdup_printf ("%g", gog_renderer_line_size (renderer, style->line.width));
+ xmlNewProp (node, CC2XML ("stroke-width"), CC2XML (buf));
+ g_free (buf);
+ /* TODO: clip dashed lines to prevent possible rsvg crash */
+ stroke_dasharray (node, renderer->line_dash);
+ buf = g_strdup_printf ("#%06x", style->line.color >> 8);
+ xmlNewProp (node, CC2XML ("stroke"), CC2XML (buf));
+ g_free (buf);
+ opacity = style->line.color & 0xff;
+ if (opacity != 255) {
+ buf = g_strdup_printf ("%g", (double) opacity / 255.);
+ xmlNewProp (node, CC2XML ("stroke-opacity"), CC2XML (buf));
+ g_free (buf);
+ }
+ setlocale (LC_NUMERIC, old_num_locale);
+ g_free (old_num_locale);
+}
+
+static void
+gog_renderer_svg_draw_polygon (GogRenderer *renderer, ArtVpath const *path,
+ gboolean narrow, GogViewAllocation const *bound)
+{
+ GogRendererSvg *prend = GOG_RENDERER_SVG (renderer);
+ GogStyle const *style = renderer->cur_style;
+ gboolean with_outline = (!narrow && style->outline.dash_type != GO_LINE_NONE);
+ xmlNodePtr node;
+ char *buf, *name, *id;
+ int opacity;
+ char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
+
+ setlocale (LC_NUMERIC, "C");
+ if (style->fill.type != GOG_FILL_STYLE_NONE || with_outline) {
+ GString *string = g_string_new ("");
+ node = xmlNewDocNode (prend->doc, NULL, "path", NULL);
+ xmlAddChild (prend->current_node, node);
+ draw_path (prend, path, string);
+ g_string_append (string, "z");
+ xmlNewProp (node, CC2XML ("d"), CC2XML (string->str));
+ g_string_free (string, TRUE);
+ } else
+ return;
+
+ if (style->fill.type != GOG_FILL_STYLE_NONE) {
+
+ switch (style->fill.type) {
+ case GOG_FILL_STYLE_PATTERN: {
+ GOColor color;
+ if (go_pattern_is_solid (&style->fill.pattern, &color)) {
+ buf = g_strdup_printf ("#%06x", color >> 8);
+ xmlNewProp (node, CC2XML ("fill"), CC2XML (buf));
+ g_free (buf);
+ opacity = color & 0xff;
+ if (opacity != 255) {
+ buf = g_strdup_printf ("%g", (double) opacity / 255.);
+ xmlNewProp (node, CC2XML ("fill-opacity"), CC2XML (buf));
+ g_free (buf);
+ }
+ }
+ break;
+ }
+
+ case GOG_FILL_STYLE_GRADIENT:
+ id = g_strdup_printf ("g_%x_%x_%x", style->fill.gradient.dir,
+ style->fill.pattern.back, style->fill.pattern.fore);
+ name = (char*) g_hash_table_lookup (prend->table, id);
+ if (!name) {
+ double x1, y1, x2, y2;
+ GOColor start, end;
+ xmlNodePtr child, stop;
+ name = g_strdup_printf ("grad%d", prend->grad++);
+ g_hash_table_insert (prend->table, id, name);
+ if (style->fill.gradient.dir < 4) {
+ x1 = y1 = x2 = 0;
+ y2 = 1;
+ } else if (style->fill.gradient.dir < 8) {
+ x1 = y1 = y2 = 0;
+ x2 = 1;
+ } else if (style->fill.gradient.dir < 12) {
+ x1 = y1 = 0;
+ x2 = y2 = 1;
+ } else {
+ x1 = y2 = 1;
+ x2 = y1 = 0;
+ }
+ child = xmlNewDocNode (prend->doc, NULL, CC2XML ("linearGradient"), NULL);
+ xmlAddChild (prend->defs, child);
+ xmlNewProp (child, CC2XML ("id"), CC2XML (name));
+ xmlNewProp (child, CC2XML ("gradientUnits"), CC2XML ("objectBoundingBox"));
+ switch (style->fill.gradient.dir % 4) {
+ case 0:
+ buf = (char*) "pad";
+ start = style->fill.pattern.fore;
+ end = style->fill.pattern.back;
+ break;
+ case 1:
+ buf = (char*) "pad";
+ start = style->fill.pattern.back;
+ end = style->fill.pattern.fore;
+ break;
+ case 2:
+ buf = (char*) "reflect";
+ start = style->fill.pattern.fore;
+ end = style->fill.pattern.back;
+ x2 = x1 + (x2 - x1) / 2;
+ y2 = y1 + (y2 - y1) / 2;
+ break;
+ default:
+ buf = (char*) "reflect";
+ start = style->fill.pattern.back;
+ end = style->fill.pattern.fore;
+ x2 = x1 + (x2 - x1) / 2;
+ y2 = y1 + (y2 - y1) / 2;
+ break;
+ }
+ xmlNewProp (child, CC2XML ("spreadMethod"), CC2XML (buf));
+ buf = g_strdup_printf ("%g", x1);
+ xmlNewProp (child, CC2XML ("x1"), CC2XML (buf));
+ g_free (buf);
+ buf = g_strdup_printf ("%g", y1);
+ xmlNewProp (child, CC2XML ("y1"), CC2XML (buf));
+ g_free (buf);
+ buf = g_strdup_printf ("%g", x2);
+ xmlNewProp (child, CC2XML ("x2"), CC2XML (buf));
+ g_free (buf);
+ buf = g_strdup_printf ("%g", y2);
+ xmlNewProp (child, CC2XML ("y2"), CC2XML (buf));
+ g_free (buf);
+ stop = xmlNewDocNode (prend->doc, NULL, CC2XML ("stop"), NULL);
+ xmlAddChild (child, stop);
+ xmlNewProp (stop, CC2XML ("offset"), CC2XML ("0"));
+ buf = g_strdup_printf ("#%06x", start >> 8);
+ xmlNewProp (stop, CC2XML ("stop-color"), CC2XML (buf));
+ g_free (buf);
+ opacity = start & 0xff;
+ if (opacity != 255) {
+ buf = g_strdup_printf ("%g", (double) opacity / 255.);
+ xmlNewProp (stop, CC2XML ("stop-opacity"), CC2XML (buf));
+ g_free (buf);
+ }
+ stop = xmlNewDocNode (prend->doc, NULL, CC2XML ("stop"), NULL);
+ xmlAddChild (child, stop);
+ xmlNewProp (stop, CC2XML ("offset"), CC2XML ("1"));
+ buf = g_strdup_printf ("#%06x", end >> 8);
+ xmlNewProp (stop, CC2XML ("stop-color"), CC2XML (buf));
+ g_free (buf);
+ opacity = end & 0xff;
+ if (opacity != 255) {
+ buf = g_strdup_printf ("%g", (double) opacity / 255.);
+ xmlNewProp (stop, CC2XML ("stop-opacity"), CC2XML (buf));
+ g_free (buf);
+ }
+ buf = g_strdup_printf ("url(#%s)", name);
+ } else {
+ buf = g_strdup_printf ("url(#%s)", name);
+ g_free (id);
+ }
+ xmlNewProp (node, CC2XML ("fill"), CC2XML (buf));
+ g_free (buf);
+ break;
+
+ case GOG_FILL_STYLE_IMAGE:
+ break;
+
+ case GOG_FILL_STYLE_NONE:
+ break; /* impossible */
+ }
+ }
+ else
+ xmlNewProp (node, CC2XML ("fill"), CC2XML ("none"));
+
+ if (with_outline) {
+ /* TODO: clip dashed lines to prevent possible rsvg crash */
+ stroke_dasharray (node, renderer->outline_dash);
+ buf = g_strdup_printf ("%g", gog_renderer_line_size (renderer, style->outline.width));
+ xmlNewProp (node, CC2XML ("stroke-width"), CC2XML (buf));
+ g_free (buf);
+ buf = g_strdup_printf ("#%06x", style->outline.color >> 8);
+ xmlNewProp (node, CC2XML ("stroke"), CC2XML (buf));
+ g_free (buf);
+ opacity = style->outline.color & 0xff;
+ if (opacity != 255) {
+ buf = g_strdup_printf ("%g", (double) opacity / 255.);
+ xmlNewProp (node, CC2XML ("stroke-opacity"), CC2XML (buf));
+ g_free (buf);
+ }
+ } else
+ xmlNewProp (node, CC2XML ("stroke"), CC2XML ("none"));
+ setlocale (LC_NUMERIC, old_num_locale);
+ g_free (old_num_locale);
+}
+
+static void
+gog_renderer_svg_draw_marker (GogRenderer *rend, double x, double y)
+{
+ GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
+ GOMarker *marker = rend->cur_style->marker.mark;
+ ArtVpath const *outline_path_raw, *fill_path_raw;
+ ArtVpath *outline_path, *fill_path;
+ double scaling[6], translation[6], affine[6];
+ double half_size;
+ xmlNodePtr node;
+ GString *string;
+ char *buf;
+ int opacity;
+ char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
+
+ setlocale (LC_NUMERIC, "C");
+ g_return_if_fail (marker != NULL);
+
+ go_marker_get_paths (marker, &outline_path_raw, &fill_path_raw);
+
+ if ((outline_path_raw == NULL) ||
+ (fill_path_raw == NULL))
+ return;
+
+ half_size = gog_renderer_line_size (rend, marker->size) / 2.0;
+ art_affine_scale (scaling, half_size, half_size);
+ art_affine_translate (translation, x, y);
+ art_affine_multiply (affine, scaling, translation);
+
+ outline_path = art_vpath_affine_transform (outline_path_raw, affine);
+ fill_path = art_vpath_affine_transform (fill_path_raw, affine);
+
+ node = xmlNewDocNode (prend->doc, NULL, "path", NULL);
+ xmlAddChild (prend->current_node, node);
+ string = g_string_new ("");
+ draw_path (prend, fill_path, string);
+ g_string_append (string, "z");
+ xmlNewProp (node, CC2XML ("d"), CC2XML (string->str));
+ g_string_free (string, TRUE);
+ buf = g_strdup_printf ("#%06x", marker->fill_color >> 8);
+ xmlNewProp (node, CC2XML ("fill"), CC2XML (buf));
+ g_free (buf);
+ xmlNewProp (node, CC2XML ("stroke"), CC2XML ("none"));
+ opacity = marker->fill_color & 0xff;
+ if (opacity != 255) {
+ buf = g_strdup_printf ("%g", (double) opacity / 255.);
+ xmlNewProp (node, CC2XML ("fill-opacity"), CC2XML (buf));
+ g_free (buf);
+ }
+
+ node = xmlNewDocNode (prend->doc, NULL, "path", NULL);
+ xmlAddChild (prend->current_node, node);
+ string = g_string_new ("");
+ draw_path (prend, outline_path, string);
+ g_string_append (string, "z");
+ xmlNewProp (node, CC2XML ("d"), CC2XML (string->str));
+ g_string_free (string, TRUE);
+ xmlNewProp (node, CC2XML ("fill"), CC2XML ("none"));
+ xmlNewProp (node, CC2XML ("stroke-linecap"), CC2XML ("round"));
+ buf = g_strdup_printf ("%g", gog_renderer_line_size (rend, go_marker_get_outline_width (marker)));
+ xmlNewProp (node, CC2XML ("stroke-width"), CC2XML (buf));
+ g_free (buf);
+ buf = g_strdup_printf ("#%06x", marker->outline_color >> 8);
+ xmlNewProp (node, CC2XML ("stroke"), CC2XML (buf));
+ g_free (buf);
+ opacity = marker->outline_color & 0xff;
+ if (opacity != 255) {
+ buf = g_strdup_printf ("%g", (double) opacity / 255.);
+ xmlNewProp (node, CC2XML ("stroke-opacity"), CC2XML (buf));
+ g_free (buf);
+ }
+
+ g_free (outline_path);
+ g_free (fill_path);
+ setlocale (LC_NUMERIC, old_num_locale);
+ g_free (old_num_locale);
+}
+
+static PangoLayout *
+make_layout (GogRenderer *rend, char const *text)
+{
+ GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
+ PangoLayout *layout;
+ PangoFontDescription const *fd = rend->cur_style->font.font->desc;
+
+ if (prend->pango_context == NULL) {
+ PangoFT2FontMap *font_map = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ());
+ /*assume horizontal and vertical resolutions are the same
+ * Why ? */
+ pango_ft2_font_map_set_resolution (font_map,
+ GO_IN_TO_PT((double)1. / gog_renderer_pt2r (rend, 1.0)),
+ GO_IN_TO_PT((double)1. / gog_renderer_pt2r (rend, 1.0)));
+ prend->pango_context = pango_ft2_font_map_create_context (font_map);
+ g_object_unref (font_map);
+ }
+
+ gog_debug (0, {
+ char *msg = pango_font_description_to_string (fd);
+ g_warning (msg);
+ g_free (msg);
+ });
+
+ layout = pango_layout_new (prend->pango_context);
+ pango_layout_set_font_description (layout, fd);
+
+ pango_layout_set_text (layout, text, -1);
+
+ return layout;
+}
+
+static void
+gog_renderer_svg_measure_text (GogRenderer *rend,
+ char const *text, GogViewRequisition *size)
+{
+ PangoRectangle rect;
+ PangoLayout *layout = make_layout (rend, text);
+ pango_layout_get_pixel_extents (layout, NULL, &rect);
+ g_object_unref (layout);
+ size->w = gog_renderer_pt2r (rend, rect.width);
+ size->h = gog_renderer_pt2r (rend, rect.height);
+}
+
+static void
+gog_renderer_svg_draw_text (GogRenderer *rend, char const *text,
+ GogViewAllocation const *pos, GtkAnchorType anchor,
+ GogViewAllocation *result)
+{
+ GogRendererSvg *prend = GOG_RENDERER_SVG (rend);
+ xmlNodePtr node;
+ char *buf;
+ double x, y;
+ int baseline;
+ char *old_num_locale;
+ PangoRectangle rect;
+ PangoLayout* layout = make_layout (rend, "lp");
+ PangoFontDescription const *fd = rend->cur_style->font.font->desc;
+ PangoLayoutIter* iter =pango_layout_get_iter(layout);
+ pango_layout_get_pixel_extents (layout, NULL, &rect);
+ x = pos->x;
+ /* adjust to the base line */
+ y = pos->y;
+ baseline = pango_layout_iter_get_baseline(iter);
+ pango_layout_iter_get_run_extents(iter, NULL, &rect);
+ y += gog_renderer_pt2r(rend, (baseline - rect.y) / PANGO_SCALE);
+ pango_layout_iter_free(iter);
+ g_object_unref (layout);
+
+ switch (anchor) {
+ case GTK_ANCHOR_CENTER : case GTK_ANCHOR_E : case GTK_ANCHOR_W :
+ y -= gog_renderer_pt2r(rend, (double) (rect.height / 2) / PANGO_SCALE);
+ break;
+ case GTK_ANCHOR_SE : case GTK_ANCHOR_S : case GTK_ANCHOR_SW :
+ y -= gog_renderer_pt2r(rend, (double)rect.height / PANGO_SCALE);
+ break;
+ default :
+ break;
+ }
+
+ node = xmlNewDocNode (prend->doc, NULL, "text", NULL);
+ xmlNodeSetContent (node, CC2XML (text));
+ old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
+ setlocale (LC_NUMERIC, "C");
+ xmlAddChild (prend->current_node, node);
+ buf = g_strdup_printf ("%g", x);
+ xmlNewProp (node, CC2XML ("x"), CC2XML (buf));
+ g_free (buf);
+ buf = g_strdup_printf ("%g", y);
+ xmlNewProp (node, CC2XML ("y"), CC2XML (buf));
+ g_free (buf);
+ switch (anchor) {
+ case GTK_ANCHOR_CENTER : case GTK_ANCHOR_N : case GTK_ANCHOR_S :
+ xmlNewProp (node, CC2XML ("text-anchor"), CC2XML ("middle"));
+ break;
+ case GTK_ANCHOR_NE : case GTK_ANCHOR_SE : case GTK_ANCHOR_E :
+ xmlNewProp (node, CC2XML ("text-anchor"), CC2XML ("end"));
+ break;
+ default : break;
+ }
+ xmlNewProp (node, CC2XML ("font-family"), CC2XML (pango_font_description_get_family (fd)));
+ buf = g_strdup_printf ("%d", (int)(rint (gog_renderer_pt2r(rend, pango_font_description_get_size (fd) / PANGO_SCALE))));
+ xmlNewProp (node, CC2XML ("font-size"), CC2XML (buf));
+ g_free (buf);
+ switch (pango_font_description_get_weight (fd)) {
+ case PANGO_WEIGHT_BOLD:
+ xmlNewProp (node, CC2XML ("font-weight"), CC2XML ("bold"));
+ break;
+ case PANGO_WEIGHT_NORMAL: break;
+ default:
+ buf = g_strdup_printf ("%d", pango_font_description_get_weight (fd));
+ xmlNewProp (node, CC2XML ("font-weight"), CC2XML (buf));
+ g_free (buf);
+ break;
+ }
+ switch (pango_font_description_get_style (fd)) {
+ case PANGO_STYLE_ITALIC:
+ xmlNewProp (node, CC2XML ("font-syle"), CC2XML ("italic"));
+ break;
+ case PANGO_STYLE_OBLIQUE:
+ xmlNewProp (node, CC2XML ("font-syle"), CC2XML ("oblique"));
+ break;
+ default: break;
+ }
+ setlocale (LC_NUMERIC, old_num_locale);
+ g_free (old_num_locale);
+}
+
+static void
+gog_renderer_svg_class_init (GogRendererClass *rend_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) rend_klass;
+
+ parent_klass = g_type_class_peek_parent (rend_klass);
+ gobject_klass->finalize = gog_renderer_svg_finalize;
+ rend_klass->clip_push = gog_renderer_svg_clip_push;
+ rend_klass->clip_pop = gog_renderer_svg_clip_pop;
+ rend_klass->draw_path = gog_renderer_svg_draw_path;
+ rend_klass->draw_polygon = gog_renderer_svg_draw_polygon;
+ rend_klass->draw_text = gog_renderer_svg_draw_text;
+ rend_klass->draw_marker = gog_renderer_svg_draw_marker;
+ rend_klass->measure_text = gog_renderer_svg_measure_text;
+}
+
+static GSF_CLASS (GogRendererSvg, gog_renderer_svg,
+ gog_renderer_svg_class_init, NULL,
+ GOG_RENDERER_TYPE)
+
+/**
+ * gog_graph_export_to_svg :
+ * @graph : #GogGraph
+ * @output : #GsfOutput
+ * @width :
+ * @height :
+ *
+ * Renders @graph as SVG and stores it in @output.
+ *
+ * Returns TRUE on success.
+ **/
+gboolean
+gog_graph_export_to_svg (GogGraph *graph, GsfOutput *output,
+ double width, double height, double scale)
+{
+ GogViewAllocation allocation;
+ GogRendererSvg *prend;
+ xmlNsPtr namespace;
+ gboolean success = TRUE;
+ char *buf;
+ char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
+ setlocale (LC_NUMERIC, "C");
+
+ prend = g_object_new (GOG_RENDERER_SVG_TYPE,
+ "model", graph,
+ NULL);
+ prend->base.scale = scale;
+ prend->doc = xmlNewDoc (CC2XML ("1.0"));
+
+ xmlNewDtd (prend->doc,
+ CC2XML ("svg"), CC2XML ("-//W3C//DTD SVG 1.1//EN"),
+ CC2XML ("http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"));
+ prend->doc->children = xmlNewDocNode (prend->doc, NULL, CC2XML ("svg"), NULL);
+ prend->current_node = prend->doc->children;
+ prend->defs = xmlNewDocNode (prend->doc, NULL, CC2XML ("defs"), NULL);
+ xmlAddChild (prend->doc->children, prend->defs);
+ prend->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+ prend->grad = prend->pat = prend->img = 0;
+
+ namespace = xmlNewNs (prend->doc->children, CC2XML ("http://www.w3.org/2000/svg"), NULL);
+ xmlSetNs (prend->doc->children, namespace);
+ xmlNewProp (prend->doc->children, CC2XML ("version"), CC2XML ("1.1"));
+
+ namespace = xmlNewNs (prend->doc->children, CC2XML ("http://www.w3.org/1999/xlink"), CC2XML ("xlink"));
+
+ buf = g_strdup_printf ("%g", width);
+ xmlNewProp (prend->doc->children, CC2XML ("width"), CC2XML (buf));
+ g_free (buf);
+ buf = g_strdup_printf ("%g", height);
+ xmlNewProp (prend->doc->children, CC2XML ("height"), CC2XML (buf));
+ g_free (buf);
+ setlocale (LC_NUMERIC, old_num_locale);
+ g_free (old_num_locale);
+
+ prend->clip_counter = 0;
+ allocation.x = 0.;
+ allocation.y = 0.;
+ allocation.w = width;
+ allocation.h = height;
+ gog_view_size_allocate (prend->base.view, &allocation);
+ gog_view_render (prend->base.view, NULL);
+
+ if ((!g_hash_table_size (prend->table)) &&
+ (prend->clip_counter == 0)) {
+ xmlUnlinkNode (prend->defs);
+ xmlFreeNode (prend->defs);
+ }
+ xmlIndentTreeOutput = TRUE;
+ if (gsf_xmlDocFormatDump (output, prend->doc, "UTF-8", TRUE) < 0)
+ success = FALSE;
+
+ xmlFreeDoc (prend->doc);
+ g_hash_table_destroy (prend->table);
+ g_object_unref (prend);
+
+ return success;
+}
--- /dev/null
+++ lib/goffice/graph/gog-renderer-gnome-print.h
@@ -0,0 +1,36 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-gnome-print.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_RENDERER_GNOME_PRINT_H
+#define GOG_RENDERER_GNOME_PRINT_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+#include <libgnomeprint/gnome-print.h>
+
+G_BEGIN_DECLS
+
+void gog_graph_print_to_gnome_print (GogGraph *graph,
+ GnomePrintContext *gp_context,
+ double width, double height);
+
+G_END_DECLS
+
+#endif /* GOG_RENDERER_GNOME_PRINT_H */
--- /dev/null
+++ lib/goffice/graph/gog-series-impl.h
@@ -0,0 +1,106 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-series-impl.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GO_SERIES_IMPL_H
+#define GO_SERIES_IMPL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-series.h>
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/gog-style.h>
+#include <glib-object.h>
+#include <gtk/gtknotebook.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GogStyledObject base;
+
+ unsigned index;
+} GogSeriesElement;
+
+typedef struct {
+ GogStyledObjectClass base;
+
+ /* Virtuals */
+ gpointer (*gse_editor) (GogObject *gobj, GnmCmdContext *cc);
+} GogSeriesElementClass;
+
+#define GOG_SERIES_ELEMENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_SERIES_ELEMENT_TYPE, GogSeriesElementClass))
+
+typedef enum {
+ GOG_SERIES_REQUIRED, /* it must be there */
+ GOG_SERIES_SUGGESTED, /* allocator will fill it in, but use need not */
+ GOG_SERIES_OPTIONAL,
+ GOG_SERIES_ERRORS
+} GogSeriesPriority;
+
+struct _GogSeriesDimDesc {
+ char const *name;
+ GogSeriesPriority priority;
+ gboolean is_shared;
+ GogDimType val_type;
+ GogMSDimType ms_type;
+};
+
+struct _GogSeriesDesc {
+ unsigned style_fields;
+ unsigned num_dim;
+ GogSeriesDimDesc const *dim;
+};
+
+struct _GogSeries {
+ GogStyledObject base;
+
+ int index;
+ unsigned manual_index : 1;
+ unsigned is_valid : 1;
+ unsigned needs_recalc : 1;
+
+ GogPlot *plot;
+ GogDatasetElement *values;
+ gboolean has_legend;
+ unsigned num_elements;
+ GList *overrides; /* GogSeriesElement (individual points) */
+};
+
+typedef struct {
+ GogStyledObjectClass base;
+
+ GType series_element_type;
+
+ /* Virtuals */
+ void (*dim_changed) (GogSeries *series, int dim_i);
+ void (*populate_editor) (GogSeries *series, GtkNotebook* book, GogDataAllocator *dalloc, GnmCmdContext *cc);
+} GogSeriesClass;
+
+#define GOG_SERIES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_SERIES_TYPE, GogSeriesClass))
+#define IS_GOG_SERIES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_SERIES_TYPE))
+#define GOG_SERIES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_SERIES_TYPE, GogSeriesClass))
+
+/* protected */
+void gog_series_check_validity (GogSeries *series);
+GogSeriesElement *gog_series_get_element (GogSeries const *series, int index);
+
+G_END_DECLS
+
+#endif /* GO_SERIES_IMPL_H */
--- /dev/null
+++ lib/goffice/graph/go-data-simple.h
@@ -0,0 +1,67 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-data-simple.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_DATA_SIMPLE_H
+#define GO_DATA_SIMPLE_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/go-data.h>
+
+#define GO_DATA_SCALAR_VAL_TYPE (go_data_scalar_val_get_type ())
+#define GO_DATA_SCALAR_VAL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_SCALAR_VAL_TYPE, GODataScalarVal))
+#define IS_GO_DATA_SCALAR_VAL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_SCALAR_VAL_TYPE))
+
+typedef struct _GODataScalarVal GODataScalarVal;
+GType go_data_scalar_val_get_type (void);
+GOData *go_data_scalar_val_new (double val);
+
+#define GO_DATA_SCALAR_STR_TYPE (go_data_scalar_str_get_type ())
+#define GO_DATA_SCALAR_STR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_SCALAR_STR_TYPE, GODataScalarStr))
+#define IS_GO_DATA_SCALAR_STR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_SCALAR_STR_TYPE))
+
+typedef struct _GODataScalarStr GODataScalarStr;
+GType go_data_scalar_str_get_type (void);
+GOData *go_data_scalar_str_new (char const *text, gboolean needs_free);
+void go_data_scalar_str_set_str (GODataScalarStr *str,
+ char const *text, gboolean needs_free);
+
+#define GO_DATA_VECTOR_VAL_TYPE (go_data_vector_val_get_type ())
+#define GO_DATA_VECTOR_VAL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_VECTOR_VAL_TYPE, GODataVectorVal))
+#define IS_GO_DATA_VECTOR_VAL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_VECTOR_VAL_TYPE))
+
+typedef struct _GODataVectorVal GODataVectorVal;
+GType go_data_vector_val_get_type (void);
+GOData *go_data_vector_val_new (double const *val, unsigned n);
+
+#define GO_DATA_VECTOR_STR_TYPE (go_data_vector_str_get_type ())
+#define GO_DATA_VECTOR_STR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_VECTOR_STR_TYPE, GODataVectorStr))
+#define IS_GO_DATA_VECTOR_STR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_VECTOR_STR_TYPE))
+
+typedef struct _GODataVectorStr GODataVectorStr;
+GType go_data_vector_str_get_type (void);
+GOData *go_data_vector_str_new (char const * const *str, unsigned n);
+void go_data_vector_str_set_translate_func (GODataVectorStr *vector,
+ GOTranslateFunc func,
+ gpointer data,
+ GDestroyNotify notify);
+void go_data_vector_str_set_translation_domain (GODataVectorStr *vec,
+ char const *domain);
+
+#endif /* GO_DATA_SIMPLE_H */
--- /dev/null
+++ lib/goffice/graph/gog-renderer.h
@@ -0,0 +1,71 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer.h : An abstract interface for rendering engines
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_RENDERER_H
+#define GOG_RENDERER_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <gtk/gtkenums.h>
+#include <libart_lgpl/libart.h>
+#include <gdk/gdk.h>
+
+#define GOG_RENDERER_TYPE (gog_renderer_get_type ())
+#define GOG_RENDERER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GOG_RENDERER_TYPE, GogRenderer))
+#define IS_GOG_RENDERER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GOG_RENDERER_TYPE))
+#define GOG_RENDERER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOG_RENDERER_TYPE, GogRendererClass))
+
+GType gog_renderer_get_type (void);
+
+void gog_renderer_request_update (GogRenderer *r);
+
+void gog_renderer_push_style (GogRenderer *r, GogStyle const *style);
+void gog_renderer_pop_style (GogRenderer *r);
+
+void gog_renderer_clip_push (GogRenderer *r, GogViewAllocation const *region);
+void gog_renderer_clip_pop (GogRenderer *r);
+
+void gog_renderer_draw_sharp_path (GogRenderer *r, ArtVpath *path,
+ GogViewAllocation const *bound);
+void gog_renderer_draw_sharp_polygon (GogRenderer *r, ArtVpath *path,
+ gboolean narrow, GogViewAllocation const *bound);
+void gog_renderer_draw_sharp_rectangle (GogRenderer *r, GogViewAllocation const *rect,
+ GogViewAllocation const *bound);
+
+void gog_renderer_draw_path (GogRenderer *r, ArtVpath const *path,
+ GogViewAllocation const *bound);
+void gog_renderer_draw_polygon (GogRenderer *r, ArtVpath const *path,
+ gboolean narrow, GogViewAllocation const *bound);
+void gog_renderer_draw_rectangle (GogRenderer *r, GogViewAllocation const *rect,
+ GogViewAllocation const *bound);
+
+void gog_renderer_draw_text (GogRenderer *rend, char const *text,
+ GogViewAllocation const *pos, GtkAnchorType anchor,
+ GogViewAllocation *result);
+void gog_renderer_draw_marker (GogRenderer *rend, double x, double y);
+void gog_renderer_measure_text (GogRenderer *rend,
+ char const *text, GogViewRequisition *size);
+
+/* measurement */
+double gog_renderer_line_size (GogRenderer const *r, double width);
+double gog_renderer_pt2r_x (GogRenderer const *r, double d);
+double gog_renderer_pt2r_y (GogRenderer const *r, double d);
+double gog_renderer_pt2r (GogRenderer const *r, double d);
+
+#endif /* GOG_RENDERER_H */
--- /dev/null
+++ lib/goffice/graph/gog-chart-impl.h
@@ -0,0 +1,55 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-chart-impl.h : implementation details for charts
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_CHART_IMPL_H
+#define GOG_CHART_IMPL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-outlined-object.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogChart {
+ GogOutlinedObject base;
+
+ GSList *plots;
+ unsigned full_cardinality, visible_cardinality;
+ gboolean cardinality_valid;
+
+ /* use a simple grid layout to position charts within graph */
+ unsigned x, y, cols, rows;
+
+ GogObject *grid;
+ GSList *axes;
+ GogAxisSet axis_set;
+};
+typedef GogOutlinedObjectClass GogChartClass;
+
+#define GOG_CHART_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_CHART_TYPE, GogChartClass))
+#define IS_GOG_CHART_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_CHART_TYPE))
+
+/* protected */
+
+G_END_DECLS
+
+#endif /* GOG_CHART_IMPL_H */
--- /dev/null
+++ lib/goffice/graph/gog-error-bar.c
@@ -0,0 +1,737 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-error-bar.c :
+ *
+ * Copyright (C) 2004 Jean Brefort (jean.brefort at ac-dijon.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+//#include <src/gui-util.h>
+#include <gui-util.h>
+#include "gog-error-bar.h"
+#include "gog-series-impl.h"
+#include "gog-plot-impl.h"
+#include "gog-object-xml.h"
+#include "gog-data-allocator.h"
+#include "gog-style.h"
+#include "gog-renderer.h"
+#include "go-data-impl.h"
+#include "go-data.h"
+#include <goffice/gui-utils/go-color-palette.h>
+#include <goffice/gui-utils/go-combo-color.h>
+#include <goffice/gui-utils/go-combo-pixmaps.h>
+#include <goffice/utils/go-math.h>
+#include <gsf/gsf-impl-utils.h>
+#include <gtk/gtkspinbutton.h>
+#include <gtk/gtkcombobox.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtklabel.h>
+#include <glade/glade-xml.h>
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+#define CC2XML(s) ((const xmlChar *)(s))
+
+typedef GObjectClass GogErrorBarClass;
+static GObjectClass *error_bar_parent_klass;
+
+#ifdef WITH_GTK
+typedef struct {
+ GogSeries *series;
+ GogErrorBar *bar;
+ char const* property;
+ GogErrorBarDisplay display;
+ GOColor color;
+ double width, line_width;
+} GogErrorBarEditor;
+
+static void
+cb_destroy (G_GNUC_UNUSED GtkWidget *w, GogErrorBarEditor *editor)
+{
+ g_free (editor);
+}
+
+static void
+cb_width_changed (GtkAdjustment *adj, GogErrorBarEditor *editor)
+{
+ editor->width = adj->value;
+ if (editor->bar) {
+ editor->bar->width = adj->value;
+ gog_object_request_update (GOG_OBJECT (editor->series));
+ }
+}
+
+static void
+cb_line_width_changed (GtkAdjustment *adj, GogErrorBarEditor *editor)
+{
+ editor->line_width = adj->value;
+ if (editor->bar) {
+ editor->bar->style->line.width = adj->value;
+ gog_object_request_update (GOG_OBJECT (editor->series));
+ }
+}
+
+static void
+cb_color_changed (G_GNUC_UNUSED GOComboColor *cc, GOColor color,
+ G_GNUC_UNUSED gboolean is_custom,
+ G_GNUC_UNUSED gboolean by_user,
+ G_GNUC_UNUSED gboolean is_default, GogErrorBarEditor *editor)
+{
+ editor->color = color;
+ if (editor->bar) {
+ editor->bar->style->line.color = color;
+ gog_object_request_update (GOG_OBJECT (editor->series));
+ }
+}
+
+static void
+cb_display_changed (G_GNUC_UNUSED GOComboPixmaps *combo, GogErrorBarDisplay display, GogErrorBarEditor *editor)
+{
+ editor->display = display;
+ if (editor->bar) {
+ editor->bar->display = display;
+ gog_object_request_update (GOG_OBJECT (editor->series));
+ }
+}
+
+static void
+cb_type_changed (GtkWidget *w, GogErrorBarEditor *editor)
+{
+ GladeXML *gui = GLADE_XML (g_object_get_data (G_OBJECT (w), "gui"));
+ gpointer data;
+ GogDataset *set;
+ GogDataAllocator *dalloc;
+ int type = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
+ dalloc = GOG_DATA_ALLOCATOR (g_object_get_data (G_OBJECT (w), "allocator"));
+ if (type == GOG_ERROR_BAR_TYPE_NONE) {
+ set = GOG_DATASET (editor->bar->series);
+ gog_dataset_set_dim (set, editor->bar->error_i, NULL, NULL);
+ gog_dataset_set_dim (set, editor->bar->error_i + 1, NULL, NULL);
+ g_object_set (editor->series, editor->property, NULL, NULL);
+ editor->bar = NULL;
+ data = g_object_get_data (G_OBJECT (w), "plus");
+ if (GTK_IS_WIDGET (data))
+ gtk_widget_destroy (GTK_WIDGET(data));
+ data = g_object_get_data (G_OBJECT (w), "minus");
+ if (GTK_IS_WIDGET (data))
+ gtk_widget_destroy (GTK_WIDGET(data));
+ g_object_set_data (G_OBJECT (w), "plus", NULL);
+ g_object_set_data (G_OBJECT (w), "minus", NULL);
+ gtk_widget_hide (glade_xml_get_widget (gui, "values_box"));
+ gtk_widget_hide (glade_xml_get_widget (gui, "style_box"));
+ } else {
+ GtkWidget *table = glade_xml_get_widget (gui, "values_table");
+ if (!editor->bar) {
+ editor->bar = g_object_new (GOG_ERROR_BAR_TYPE, NULL);
+ editor->bar->style->line.color = editor->color;
+ editor->bar->style->line.width = editor->line_width;
+ editor->bar->width = editor->width;
+ editor->bar->display = editor->display;
+ editor->bar->type = type;
+ g_object_set (editor->series, editor->property, editor->bar, NULL);
+ g_object_unref (editor->bar);
+ g_object_get (editor->series, editor->property, &editor->bar, NULL);
+ }
+ editor->bar->type = type;
+ set = GOG_DATASET (editor->bar->series);
+ data = g_object_get_data (G_OBJECT (w), "plus");
+ if (!data) {
+ GtkWidget* al = GTK_WIDGET (gog_data_allocator_editor (dalloc, set, editor->bar->error_i, GOG_DATA_VECTOR));
+ gtk_table_attach (GTK_TABLE (table), al, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ g_object_set_data (G_OBJECT (w), "plus", al);
+ }
+ data = g_object_get_data (G_OBJECT (w), "minus");
+ if (!data) {
+ GtkWidget* al = GTK_WIDGET (gog_data_allocator_editor (dalloc, set, editor->bar->error_i + 1, GOG_DATA_VECTOR));
+ gtk_table_attach (GTK_TABLE (table), al, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ g_object_set_data (G_OBJECT (w), "minus", al);
+ }
+ gtk_widget_show_all (glade_xml_get_widget (gui, "values_box"));
+ gtk_widget_show (glade_xml_get_widget (gui, "style_box"));
+ }
+ gog_object_request_update (GOG_OBJECT (editor->series));
+}
+
+gpointer
+gog_error_bar_prefs (GogSeries *series,
+ char const* property,
+ gboolean horizontal,
+ GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ GladeXML *gui;
+ GtkWidget *w, *bar_prefs;
+ GOComboPixmaps *cpx;
+ GtkTable *style_table, *values_table;
+ GogDataset *set;
+ GdkPixbuf *pixbuf;
+ GogErrorBarEditor *editor;
+
+ g_return_val_if_fail (IS_GOG_SERIES (series), NULL);
+
+ editor = g_new0 (GogErrorBarEditor, 1);
+ editor->series = series;
+ editor->property = property;
+ g_object_get (series, property, &editor->bar, NULL);
+ if (editor->bar) {
+ editor->color = editor->bar->style->line.color;
+ editor->line_width = editor->bar->style->line.width;
+ editor->width = editor->bar->width;
+ editor->display = editor->bar->display;
+ } else {
+ editor->color = RGBA_BLACK;
+ editor->line_width = 1.;
+ editor->width = 5.;
+ editor->display = GOG_ERROR_BAR_DISPLAY_BOTH;
+ }
+ set = GOG_DATASET (series);
+
+ gui = gnm_glade_xml_new (cc, "gog-error-bar-prefs.glade", "gog_error_bar_prefs", NULL);
+
+ /* Style properties */
+
+ /* Width */
+ w = glade_xml_get_widget (gui, "width");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), editor->width);
+ g_signal_connect (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)),
+ "value_changed",
+ G_CALLBACK (cb_width_changed), editor);
+
+ /* Line width */
+ w = glade_xml_get_widget (gui, "line_width");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), editor->line_width);
+ g_signal_connect (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)),
+ "value_changed",
+ G_CALLBACK (cb_line_width_changed), editor);
+
+ style_table = GTK_TABLE (glade_xml_get_widget (gui, "style_table"));
+
+ /* Color */
+ w = go_combo_color_new (NULL, _("Automatic"), RGBA_BLACK,
+ go_color_group_fetch ("color", NULL));
+ go_combo_color_set_instant_apply (GO_COMBO_COLOR (w), FALSE);
+ go_combo_color_set_allow_alpha (GO_COMBO_COLOR (w), TRUE);
+ gtk_label_set_mnemonic_widget (
+ GTK_LABEL (glade_xml_get_widget (gui, "color_label")), w);
+ go_combo_color_set_color (GO_COMBO_COLOR (w), editor->color);
+ g_signal_connect (G_OBJECT (w),
+ "color_changed",
+ G_CALLBACK (cb_color_changed), editor);
+ gtk_table_attach (GTK_TABLE (style_table), w, 1, 2, 3, 4, 0, 0, 0, 0);
+
+ /* Display style */
+ cpx = go_combo_pixmaps_new (4);
+ pixbuf = gnumeric_load_pixbuf ("bar-none.png");
+ go_combo_pixmaps_add_element (cpx,
+ pixbuf,
+ GOG_ERROR_BAR_DISPLAY_NONE,
+ _("No error bar displayed"));
+ if (horizontal) {
+ pixbuf = gnumeric_load_pixbuf ("bar-hplus.png");
+ go_combo_pixmaps_add_element (cpx,
+ pixbuf,
+ GOG_ERROR_BAR_DISPLAY_POSITIVE,
+ _("Positive error bar displayed"));
+ pixbuf = gnumeric_load_pixbuf ("bar-hminus.png");
+ go_combo_pixmaps_add_element (cpx,
+ pixbuf,
+ GOG_ERROR_BAR_DISPLAY_NEGATIVE,
+ _("Negative error bar displayed"));
+ pixbuf = gnumeric_load_pixbuf ("bar-hboth.png");
+ go_combo_pixmaps_add_element (cpx,
+ pixbuf,
+ GOG_ERROR_BAR_DISPLAY_BOTH,
+ _("Full error bar displayed"));
+ } else {
+ pixbuf = gnumeric_load_pixbuf ("bar-vplus.png");
+ go_combo_pixmaps_add_element (cpx,
+ pixbuf,
+ GOG_ERROR_BAR_DISPLAY_POSITIVE,
+ _("Positive error bar displayed"));
+ pixbuf = gnumeric_load_pixbuf ("bar-vminus.png");
+ go_combo_pixmaps_add_element (cpx,
+ pixbuf,
+ GOG_ERROR_BAR_DISPLAY_NEGATIVE,
+ _("Negative error bar displayed"));
+ pixbuf = gnumeric_load_pixbuf ("bar-vboth.png");
+ go_combo_pixmaps_add_element (cpx,
+ pixbuf,
+ GOG_ERROR_BAR_DISPLAY_BOTH,
+ _("Full error bar displayed"));
+ }
+ gtk_table_attach (GTK_TABLE (style_table), GTK_WIDGET(cpx), 1, 2, 0, 1, 0, 0, 0, 0);
+ go_combo_pixmaps_select_id (cpx, editor->display);
+ g_signal_connect (G_OBJECT (cpx), "changed", G_CALLBACK (cb_display_changed), editor);
+
+ /* Category property*/
+ w = glade_xml_get_widget (gui, "category_combo");
+ gtk_combo_box_set_active (GTK_COMBO_BOX (w), (editor->bar)? (int) editor->bar->type: 0);
+ g_object_set_data_full (G_OBJECT (w), "gui", gui, (GDestroyNotify)g_object_unref);
+ g_object_set_data (G_OBJECT (w), "allocator", dalloc);
+ g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (cb_type_changed), editor);
+
+ /* Value properties */
+ bar_prefs = glade_xml_get_widget (gui, "gog_error_bar_prefs");
+ g_signal_connect (bar_prefs, "destroy", G_CALLBACK (cb_destroy), editor);
+ gtk_widget_show_all (bar_prefs);
+
+ values_table = GTK_TABLE (glade_xml_get_widget (gui, "values_table"));
+ if (editor->bar) {
+ GtkWidget* al = GTK_WIDGET (gog_data_allocator_editor (dalloc, set, editor->bar->error_i, GOG_DATA_VECTOR));
+ gtk_widget_show (al);
+ gtk_table_attach (values_table, al, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ g_object_set_data (G_OBJECT (w), "plus", al);
+ al = GTK_WIDGET (gog_data_allocator_editor (dalloc, set, editor->bar->error_i + 1, GOG_DATA_VECTOR));
+ gtk_widget_show (al);
+ gtk_table_attach (values_table, al, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ g_object_set_data (G_OBJECT (w), "minus", al);
+ } else {
+ gtk_widget_hide (glade_xml_get_widget (gui, "values_box"));
+ gtk_widget_hide (glade_xml_get_widget (gui, "style_box"));
+ }
+
+ return GTK_WIDGET(bar_prefs);
+}
+#endif
+
+static void
+gog_error_bar_init (GogErrorBar* bar)
+{
+ bar->type = GOG_ERROR_BAR_TYPE_NONE;
+ bar->display = GOG_ERROR_BAR_DISPLAY_BOTH;
+ bar->width = 5.;
+ bar->style = gog_style_new ();
+ bar->style->line.color = RGBA_BLACK;
+ bar->style->line.width = 1.;
+}
+
+static void
+gog_error_bar_finalize (GObject *obj)
+{
+ GogErrorBar *bar = GOG_ERROR_BAR (obj);
+ if (bar->style) {
+ g_object_unref (bar->style);
+ bar->style = NULL;
+ }
+ (error_bar_parent_klass->finalize) (obj);
+}
+
+static void
+gog_error_bar_class_init (GogErrorBarClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ error_bar_parent_klass = g_type_class_peek_parent (klass);
+
+ gobject_klass->finalize = gog_error_bar_finalize;
+}
+
+static gboolean
+gog_error_bar_persist_dom_load (GogPersist *gp, xmlNode *node)
+{
+ GogErrorBar *bar = GOG_ERROR_BAR (gp);
+
+ gchar* str;
+ str = xmlGetProp (node, CC2XML ("error_type"));
+ if (str) {
+ if (!strcmp (str, "absolute"))
+ bar->type = GOG_ERROR_BAR_TYPE_ABSOLUTE;
+ else if (!strcmp (str, "relative"))
+ bar->type = GOG_ERROR_BAR_TYPE_RELATIVE;
+ else if (!strcmp (str, "percent"))
+ bar->type = GOG_ERROR_BAR_TYPE_PERCENT;
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, CC2XML ("display"));
+ if (str) {
+ if (!strcmp (str, "none"))
+ bar->display = GOG_ERROR_BAR_DISPLAY_NONE;
+ else if (!strcmp (str, "positive"))
+ bar->display = GOG_ERROR_BAR_DISPLAY_POSITIVE;
+ else if (!strcmp (str, "negative"))
+ bar->display = GOG_ERROR_BAR_DISPLAY_NEGATIVE;
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, CC2XML ("width"));
+ if (str) {
+ bar->width = g_strtod (str, NULL);
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, CC2XML ("line_width"));
+ if (str) {
+ bar->style->line.width = g_strtod (str, NULL);
+ xmlFree (str);
+ }
+ str = xmlGetProp (node, CC2XML ("color"));
+ if (str != NULL) {
+ bar->style->line.color = go_color_from_str (str);
+ xmlFree (str);
+ }
+
+ return TRUE;
+}
+
+static void
+gog_error_bar_persist_dom_save (GogPersist const *gp, xmlNode *parent)
+{
+ GogErrorBar const *bar = GOG_ERROR_BAR (gp);
+
+ {
+ const char *str = NULL;
+ xmlSetProp (parent, CC2XML ("type"), CC2XML ("GogErrorBar"));
+ switch (bar->type) {
+ case GOG_ERROR_BAR_TYPE_ABSOLUTE:
+ str = "absolute";
+ break;
+ case GOG_ERROR_BAR_TYPE_RELATIVE:
+ str = "relative";
+ break;
+ case GOG_ERROR_BAR_TYPE_PERCENT:
+ str = "percent";
+ break;
+ default:
+ break;
+ }
+ if (str)
+ xmlSetProp (parent, CC2XML ("error_type"), CC2XML (str));
+ }
+
+ {
+ const char *str = NULL;
+ switch (bar->display) {
+ case GOG_ERROR_BAR_DISPLAY_NONE:
+ str = "none";
+ break;
+ case GOG_ERROR_BAR_DISPLAY_POSITIVE:
+ str = "positive";
+ break;
+ case GOG_ERROR_BAR_DISPLAY_NEGATIVE:
+ str = "negative";
+ break;
+ default:
+ break;
+ }
+ if (str)
+ xmlSetProp (parent, CC2XML ("display"), CC2XML (str));
+ }
+
+ if (bar->width != 5.) {
+ char *str = g_strdup_printf ("%f", bar->width);
+ xmlSetProp (parent, CC2XML ("width"), CC2XML (str));
+ g_free (str);
+ }
+
+ if (bar->style->line.width != 1.) {
+ char *str = g_strdup_printf ("%f", bar->style->line.width);
+ xmlSetProp (parent, CC2XML ("line_width"), CC2XML (str));
+ g_free (str);
+ }
+ if (bar->style->line.color != RGBA_BLACK) {
+ char *str = go_color_as_str (bar->style->line.color);
+ xmlSetProp (parent, CC2XML ("color"), CC2XML (str));
+ g_free (str);
+ }
+}
+
+static void
+gog_error_bar_persist_sax_save (GogPersist const *gp, GsfXMLOut *output)
+{
+ GogErrorBar *bar = GOG_ERROR_BAR (gp);
+ char const *str;
+
+ gsf_xml_out_add_cstr_unchecked (output, "type", "GogErrorBar");
+ switch (bar->type) {
+ case GOG_ERROR_BAR_TYPE_ABSOLUTE: str = "absolute"; break;
+ case GOG_ERROR_BAR_TYPE_RELATIVE: str = "relative"; break;
+ case GOG_ERROR_BAR_TYPE_PERCENT: str = "percent"; break;
+ default: str = NULL; break;
+ }
+ if (str != NULL)
+ gsf_xml_out_add_cstr_unchecked (output, "error_type", str);
+
+ switch (bar->display) {
+ case GOG_ERROR_BAR_DISPLAY_NONE: str = "none"; break;
+ case GOG_ERROR_BAR_DISPLAY_POSITIVE: str = "positive"; break;
+ case GOG_ERROR_BAR_DISPLAY_NEGATIVE: str = "negative"; break;
+ default: str = NULL; break;
+ }
+ if (str != NULL)
+ gsf_xml_out_add_cstr_unchecked (output, "display", str);
+#warning Why 5.0 and why 1.0 ?
+ if (bar->width != 5.)
+ gsf_xml_out_add_float (output, "width", bar->width, 2);
+ if (bar->style->line.width != 1.)
+ gsf_xml_out_add_float (output, "line_width", bar->style->line.width, 2);
+ if (bar->style->line.color != RGBA_BLACK)
+ go_xml_out_add_color (output, "color", bar->style->line.color);
+}
+
+static void
+gog_error_bar_persist_init (GogPersistClass *iface)
+{
+ iface->dom_load = gog_error_bar_persist_dom_load;
+ iface->dom_save = gog_error_bar_persist_dom_save;
+ iface->sax_save = gog_error_bar_persist_sax_save;
+}
+
+GSF_CLASS_FULL (GogErrorBar, gog_error_bar,
+ gog_error_bar_class_init, gog_error_bar_init,
+ G_TYPE_OBJECT, 0,
+ GSF_INTERFACE (gog_error_bar_persist_init, GOG_PERSIST_TYPE))
+
+
+/**
+ * gog_error_bar_get_bounds :
+ * @bar : A GogErrorBar
+ * @index : the index corresponding to the value which error limits are
+ * @min : where the minimum value will be stored
+ * @max : where the maximum value will be stored
+ *
+ * If the value correponding to @index is valid, fills min and max with the error values:
+ * -> positive_error in @max.
+ * -> negative_error in @min.
+ * If one of the errors is not valid or not defined, its value is set to -1.0.
+ *
+ * Return value : FALSE if the @bar->type is GOG_ERROR_BAR_TYPE_NONE or if the value is not valid,
+ * TRUE otherwise.
+ **/
+gboolean
+gog_error_bar_get_bounds (GogErrorBar const *bar, int index, double *min, double *max)
+{
+ double value;
+ GOData *data;
+ int length;
+
+ /* -1 ensures that the bar will not be displayed if the error is not a correct one.
+ With a 0 value, it might be, because of rounding errors */
+ *min = *max = -1.;
+
+ g_return_val_if_fail (GOG_ERROR_BAR (bar) != NULL, FALSE);
+ if (!gog_series_is_valid (bar->series))
+ return FALSE;
+ value = go_data_vector_get_value (GO_DATA_VECTOR (bar->series->values[bar->dim_i].data), index);
+ data = bar->series->values[bar->error_i].data;
+ length = (IS_GO_DATA (data)) ? go_data_vector_get_len (GO_DATA_VECTOR (data)) : 0;
+
+ if ((bar->type == GOG_ERROR_BAR_TYPE_NONE) || isnan (value) || !go_finite (value))
+ return FALSE;
+
+ if (length == 1)
+ *max = go_data_vector_get_value (GO_DATA_VECTOR (data), 0);
+ else if (length > index)
+ *max = go_data_vector_get_value (GO_DATA_VECTOR (data), index);
+
+ data = bar->series->values[bar->error_i + 1].data;
+ length = (IS_GO_DATA (data))? go_data_vector_get_len (GO_DATA_VECTOR (data)): 0;
+ if (length == 0)
+ *min = *max; /* use same values for + and - */
+ else if (length == 1)
+ *min = go_data_vector_get_value (GO_DATA_VECTOR (data), 0);
+ else if (length > index)
+ *min = go_data_vector_get_value (GO_DATA_VECTOR (data), index);
+
+ if (isnan (*min) || !go_finite (*min) || (*min <= 0)) {
+ *min = -1.;
+ }
+ if (isnan (*max) || !go_finite (*max) || (*max <= 0)) {
+ *max = -1.;
+ }
+
+ switch (bar->type)
+ {
+ case GOG_ERROR_BAR_TYPE_RELATIVE:
+ *min *= fabs (value);
+ *max *= fabs (value);
+ break;
+ case GOG_ERROR_BAR_TYPE_PERCENT:
+ *min *= fabs (value) / 100;
+ *max *= fabs (value) / 100;
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+void
+gog_error_bar_get_minmax (const GogErrorBar *bar, double *min, double *max)
+{
+ double *values;
+ int i, imax;
+ double tmp_min, tmp_max, plus, minus;
+
+ g_return_if_fail (GOG_ERROR_BAR (bar) != NULL);
+
+ if (!gog_series_is_valid (bar->series)) {
+ *min = DBL_MAX;
+ *max = -DBL_MAX;
+ return;
+ }
+
+ imax = go_data_vector_get_len (GO_DATA_VECTOR (bar->series->values[bar->dim_i].data));
+ go_data_vector_get_minmax (GO_DATA_VECTOR (bar->series->values[bar->dim_i].data), min, max);
+ values = go_data_vector_get_values (GO_DATA_VECTOR (bar->series->values[bar->dim_i].data));
+
+ for (i = 0; i < imax; i++) {
+ if (gog_error_bar_get_bounds (bar, i, &minus, &plus)) {
+ tmp_min = values[i] - minus;
+ tmp_max = values[i] + plus;
+ if (tmp_min < *min)
+ *min = tmp_min;
+ if (tmp_max > *max)
+ *max = tmp_max;
+ }
+ }
+}
+
+GogErrorBar *
+gog_error_bar_dup (GogErrorBar const *bar)
+{
+ GogErrorBar* dbar;
+
+ g_return_val_if_fail (IS_GOG_ERROR_BAR (bar), NULL);
+
+ dbar = g_object_new (GOG_ERROR_BAR_TYPE, NULL);
+ dbar->type = bar->type;
+ dbar->series = bar->series;
+ dbar->dim_i = bar->dim_i;
+ dbar->error_i = bar->error_i;
+ dbar->display = bar->display;
+ dbar->width = bar->width;
+ if (dbar->style) g_object_unref (dbar->style);
+ dbar->style = gog_style_dup (bar->style);
+ return dbar;
+}
+
+/**
+ * gog_error_bar_render :
+ * @bar : A GogErrorBar
+ * @rend : A GogRenderer
+ * @x_map : A GogAxisMap for the x axis
+ * @y_map : A GogAxisMap for the y axis
+ * @x : x coordinate of the origin of the bar
+ * @y : y coordinate of the origin of the bar
+ * @plus : distance from the origin to the positive end of the bar
+ * @minus : distance from the origin to the negative end of the bar
+ * @horizontal : whether the bar is horizontal or not.
+ *
+ * Displays the error bar. If @plus is negative, the positive side of the bar is not displayed,
+ * and if @minus is negative, the negative side of the bar is not displayed.
+ * x_map and y_map are used to convert coordinates from data space to canvas coordinates.
+ * This function must not be called if #gog_error_bar_get_bounds returned FALSE.
+ **/
+void gog_error_bar_render (const GogErrorBar *bar,
+ GogRenderer *rend,
+ GogAxisMap *x_map, GogAxisMap *y_map,
+ double x, double y,
+ double minus,
+ double plus,
+ gboolean horizontal)
+{
+ ArtVpath path [7];
+ int n;
+ double x_start, y_start, x_end, y_end;
+ double line_width, width;
+ gboolean start = plus > .0 && bar ->display & GOG_ERROR_BAR_DISPLAY_POSITIVE,
+ end = minus > 0. && bar ->display & GOG_ERROR_BAR_DISPLAY_NEGATIVE;
+
+ if (!start && !end) return;
+
+ if (horizontal) {
+ x_start = start ?
+ gog_axis_map_to_canvas (x_map, x + plus) :
+ gog_axis_map_to_canvas (x_map, x);
+ x_end = end ?
+ gog_axis_map_to_canvas (x_map , x - minus) :
+ gog_axis_map_to_canvas (x_map , x);
+ y_start = y_end = gog_axis_map_to_canvas (y_map, y);
+ } else {
+ x_start = x_end = gog_axis_map_to_canvas (x_map ,x);
+ y_start = start ?
+ gog_axis_map_to_canvas (y_map, y + plus) :
+ gog_axis_map_to_canvas (y_map, y);
+ y_end = end ?
+ gog_axis_map_to_canvas (y_map, y - minus) :
+ gog_axis_map_to_canvas (y_map, y);
+ }
+ x = gog_axis_map_to_canvas (x_map, x);
+ y = gog_axis_map_to_canvas (y_map, y);
+
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[0].x = x_start;
+ path[1].x = x_end;
+ path[0].y = path[1].y = y_start;
+ path[0].y = y_start;
+ path[1].y = y_end;
+
+ if (horizontal) {
+ width = gog_renderer_pt2r_y (rend, bar->width) / 2.;
+ line_width = gog_renderer_pt2r_x (rend, bar->style->line.width);
+ } else {
+ width = gog_renderer_pt2r_x (rend, bar->width) / 2.;
+ line_width = gog_renderer_pt2r_y (rend, bar->style->line.width);
+ }
+
+ if ((2. * width) > line_width) {
+ if (start && end) {
+ path[2].code = ART_MOVETO;
+ path[3].code = ART_LINETO;
+ n = 4;
+ } else
+ n = 2;
+ path[n].code = ART_MOVETO;
+ path[n + 1].code = ART_LINETO;
+ path[n + 2].code = ART_END;
+ if (horizontal) {
+ if (start) {
+ path[2].x =path[3].x = x_start;
+ path[2].y = y - width;
+ path[3].y = y + width;
+ }
+ if (end) {
+ path[n].x =path[n+1].x = x_end;
+ path[n].y = y - width;
+ path[n+1].y = y + width;
+ }
+ } else {
+ if (start) {
+ path[2].x = x - width;
+ path[3].x = x + width;
+ path[2].y =path[3].y = y_start;
+ }
+ if (end) {
+ path[n].x = x - width;
+ path[n+1].x = x + width;
+ path[n].y =path[n+1].y = y_end;
+ }
+ }
+ } else
+ path[2].code = ART_END;
+
+ gog_renderer_push_style (rend, bar->style);
+ gog_renderer_draw_sharp_path (rend, path, NULL);
+ gog_renderer_pop_style (rend);
+}
+
+gboolean
+gog_error_bar_is_visible (GogErrorBar *bar)
+{
+ return (bar != NULL) &&
+ (bar->type != GOG_ERROR_BAR_TYPE_NONE) &&
+ (bar->display != GOG_ERROR_BAR_DISPLAY_NONE);
+}
--- /dev/null
+++ lib/goffice/graph/gog-graph.c
@@ -0,0 +1,582 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-graph.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-graph-impl.h>
+#include <goffice/graph/gog-chart-impl.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/go-data.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <stdlib.h>
+
+enum {
+ GRAPH_PROP_0,
+ GRAPH_PROP_THEME,
+ GRAPH_PROP_THEME_NAME
+};
+
+enum {
+ GRAPH_ADD_DATA,
+ GRAPH_REMOVE_DATA,
+ GRAPH_LAST_SIGNAL
+};
+static gulong gog_graph_signals [GRAPH_LAST_SIGNAL] = { 0, };
+static GObjectClass *graph_parent_klass;
+static GogViewClass *gview_parent_klass;
+
+static void
+gog_graph_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogGraph *graph = GOG_GRAPH (obj);
+
+ switch (param_id) {
+ case GRAPH_PROP_THEME :
+ gog_graph_set_theme (graph, g_value_get_object (value));
+ break;
+ case GRAPH_PROP_THEME_NAME :
+ gog_graph_set_theme (graph,
+ gog_theme_lookup (g_value_get_string (value)));
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+}
+
+static void
+gog_graph_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogGraph *graph = GOG_GRAPH (obj);
+
+ switch (param_id) {
+ case GRAPH_PROP_THEME :
+ g_value_set_object (value, graph->theme);
+ break;
+ case GRAPH_PROP_THEME_NAME :
+ g_value_set_string (value, gog_theme_get_name (graph->theme));
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_graph_finalize (GObject *obj)
+{
+ GogGraph *graph = GOG_GRAPH (obj);
+ GSList *tmp;
+
+ tmp = graph->data;
+ graph->data = NULL;
+ g_slist_foreach (tmp, (GFunc) g_object_unref, NULL);
+ g_slist_free (tmp);
+
+ /* on exit the role remove routines are not called */
+ g_slist_free (graph->charts);
+
+ if (graph->idle_handler != 0) {
+ g_source_remove (graph->idle_handler);
+ graph->idle_handler = 0;
+ }
+
+ (graph_parent_klass->finalize) (obj);
+}
+
+static char const *
+gog_graph_type_name (GogObject const *gobj)
+{
+ return N_("Graph");
+}
+
+static void
+role_chart_post_add (GogObject *parent, GogObject *chart)
+{
+ GogGraph *graph = GOG_GRAPH (parent);
+ graph->charts = g_slist_prepend (graph->charts, chart);
+ gog_chart_set_position (GOG_CHART (chart),
+ 0, GOG_GRAPH (graph)->num_rows, 1, 1);
+}
+
+static void
+role_chart_pre_remove (GogObject *parent, GogObject *child)
+{
+ GogGraph *graph = GOG_GRAPH (parent);
+ GogChart *chart = GOG_CHART (child);
+
+ graph->charts = g_slist_remove (graph->charts, chart);
+ gog_graph_validate_chart_layout (graph);
+}
+
+static void
+gog_graph_update (GogObject *obj)
+{
+ GogGraph *graph = GOG_GRAPH (obj);
+ if (graph->idle_handler != 0) {
+ g_source_remove (graph->idle_handler);
+ graph->idle_handler = 0;
+ }
+}
+
+static void
+gog_graph_class_init (GogGraphClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+
+ static GogObjectRole const roles[] = {
+ { N_("Chart"), "GogChart", 0,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ NULL, NULL, NULL, role_chart_post_add, role_chart_pre_remove, NULL },
+ { N_("Title"), "GogLabel", 0,
+ GOG_POSITION_COMPASS, GOG_POSITION_N|GOG_POSITION_ALIGN_CENTER, GOG_OBJECT_NAME_BY_ROLE,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+ };
+
+ graph_parent_klass = g_type_class_peek_parent (klass);
+ gobject_klass->set_property = gog_graph_set_property;
+ gobject_klass->get_property = gog_graph_get_property;
+ gobject_klass->finalize = gog_graph_finalize;
+
+ gog_klass->update = gog_graph_update;
+ gog_klass->type_name = gog_graph_type_name;
+ gog_klass->view_type = gog_graph_view_get_type ();
+ gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+
+ gog_graph_signals [GRAPH_ADD_DATA] = g_signal_new ("add-data",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogGraphClass, add_data),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, G_TYPE_OBJECT);
+
+ gog_graph_signals [GRAPH_REMOVE_DATA] = g_signal_new ("remove-data",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogGraphClass, remove_data),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, G_TYPE_OBJECT);
+
+ g_object_class_install_property (gobject_klass, GRAPH_PROP_THEME,
+ g_param_spec_object ("theme", "Theme",
+ "the theme for elements of the graph",
+ GOG_THEME_TYPE, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, GRAPH_PROP_THEME_NAME,
+ g_param_spec_string ("theme-name", "ThemeName",
+ "the name of the theme for elements of the graph",
+ "default", G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+}
+
+static void
+gog_graph_init (GogGraph *graph)
+{
+ GogStyledObject *gso = GOG_STYLED_OBJECT (graph);
+
+ graph->data = NULL;
+ graph->num_cols = graph->num_rows = 0;
+ graph->idle_handler = 0;
+ graph->theme = gog_theme_lookup (NULL); /* default */
+
+ /* Cheat and assign a name here, graphs will not have parents until we
+ * support graphs in graphs */
+ GOG_OBJECT (graph)->user_name = g_strdup (_("Graph"));
+ gog_theme_fillin_style (graph->theme,
+ gso->style, GOG_OBJECT (graph), 0, TRUE);
+ gog_styled_object_apply_theme (gso, gso->style);
+}
+
+GSF_CLASS (GogGraph, gog_graph,
+ gog_graph_class_init, gog_graph_init,
+ GOG_OUTLINED_OBJECT_TYPE)
+
+/**
+ * gog_graph_validate_chart_layout :
+ * @graph : #GogGraph
+ *
+ * Check the layout of the chart grid and ensure that there are no empty
+ * cols or rows, and resize as necessary
+ */
+gboolean
+gog_graph_validate_chart_layout (GogGraph *graph)
+{
+ GSList *ptr;
+ GogChart *chart = NULL;
+ unsigned i, max_col, max_row;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (GOG_GRAPH (graph) != NULL, FALSE);
+
+ /* There won't be many of charts so we do the right thing */
+
+ /* 1) find the max */
+ max_col = max_row = 0;
+ for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
+ chart = ptr->data;
+ if (max_col < (chart->x + chart->cols))
+ max_col = (chart->x + chart->cols);
+ if (max_row < (chart->y + chart->rows))
+ max_row = (chart->y + chart->rows);
+ }
+
+ /* 2) see if we need to contract any cols */
+ for (i = 0 ; i < max_col ; ) {
+ for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
+ chart = ptr->data;
+ if (chart->x <= i && i < (chart->x + chart->cols))
+ break;
+ }
+ if (ptr == NULL) {
+ changed = TRUE;
+ max_col--;
+ for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
+ chart = ptr->data;
+ if (chart->x > i)
+ (chart->x)--;
+ }
+ } else
+ i = chart->x + chart->cols;
+ }
+
+ /* 3) see if we need to contract any rows */
+ for (i = 0 ; i < max_row ; ) {
+ for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
+ chart = ptr->data;
+ if (chart->y <= i && i < (chart->y + chart->rows))
+ break;
+ }
+ if (ptr == NULL) {
+ changed = TRUE;
+ max_row--;
+ for (ptr = graph->charts ; ptr != NULL ; ptr = ptr->next) {
+ chart = ptr->data;
+ if (chart->y > i)
+ (chart->y)--;
+ }
+ } else
+ i = chart->y + chart->rows;
+ }
+ changed |= (graph->num_cols != max_col || graph->num_rows != max_row);
+
+ graph->num_cols = max_col;
+ graph->num_rows = max_row;
+
+ if (changed)
+ gog_object_emit_changed (GOG_OBJECT (graph), TRUE);
+ return changed;
+}
+
+unsigned
+gog_graph_num_cols (GogGraph const *graph)
+{
+ g_return_val_if_fail (GOG_GRAPH (graph) != NULL, 1);
+ return graph->num_cols;
+}
+
+unsigned
+gog_graph_num_rows (GogGraph const *graph)
+{
+ g_return_val_if_fail (GOG_GRAPH (graph) != NULL, 1);
+ return graph->num_rows;
+}
+
+/**
+ * gog_graph_dup :
+ * @graph : #GogGraph
+ *
+ * A convenience wrapper to make a deep copy of @graph.
+ **/
+GogGraph *
+gog_graph_dup (GogGraph const *graph)
+{
+ GogObject *res = gog_object_dup (GOG_OBJECT (graph), NULL);
+ return GOG_GRAPH (res);
+}
+
+GogTheme *
+gog_graph_get_theme (GogGraph const *graph)
+{
+ g_return_val_if_fail (GOG_GRAPH (graph) != NULL, NULL);
+ return graph->theme;
+}
+
+void
+gog_graph_set_theme (GogGraph *graph, GogTheme *theme)
+{
+ g_return_if_fail (GOG_GRAPH (graph) != NULL);
+ g_return_if_fail (GOG_THEME (theme) != NULL);
+#warning TODO
+}
+
+/**
+ * gog_graph_get_data :
+ * @graph : #GogGraph
+ *
+ * Returns a list of the GOData objects that are data to the graph.
+ * The caller should _not_ modify or free the list.
+ **/
+GSList *
+gog_graph_get_data (GogGraph const *graph)
+{
+ g_return_val_if_fail (GOG_GRAPH (graph) != NULL, NULL);
+ return graph->data;
+}
+
+/**
+ * gog_graph_ref_data :
+ * @graph : #GogGraph
+ * @dat : #GOData
+ *
+ * If @dat or something equivalent to it already exists in the graph use that.
+ * Otherwaise use @dat. Adds a gobject ref to the target and increments a
+ * count of the number of refs made from this #GogGraph.
+ **/
+GOData *
+gog_graph_ref_data (GogGraph *graph, GOData *dat)
+{
+ GObject *g_obj;
+ gpointer res;
+ unsigned count;
+
+ if (dat == NULL)
+ return NULL;
+
+ g_return_val_if_fail (GOG_GRAPH (graph) != NULL, dat);
+ g_return_val_if_fail (GO_DATA (dat) != NULL, dat);
+
+ /* Does it already exist in the graph ? */
+ g_obj = G_OBJECT (graph);
+ res = g_object_get_qdata (g_obj, (GQuark)dat);
+ if (res == NULL) {
+
+ /* is there something like it already */
+ GSList *existing = graph->data;
+ for (; existing != NULL ; existing = existing->next)
+ if (go_data_eq (dat, existing->data))
+ break;
+
+ if (existing == NULL) {
+ g_signal_emit (g_obj, gog_graph_signals [GRAPH_ADD_DATA], 0, dat);
+ graph->data = g_slist_prepend (graph->data, dat);
+ g_object_ref (dat);
+ } else {
+ dat = existing->data;
+ res = g_object_get_qdata (g_obj, (GQuark)dat);
+ }
+ }
+
+ count = GPOINTER_TO_UINT (res) + 1;
+ g_object_set_qdata (g_obj, (GQuark)dat, GUINT_TO_POINTER (count));
+ g_object_ref (dat);
+ return dat;
+}
+
+/**
+ * gog_graph_unref_data :
+ * @graph : #GogGraph
+ * @dat : #GOData
+ *
+ **/
+void
+gog_graph_unref_data (GogGraph *graph, GOData *dat)
+{
+ GObject *g_obj;
+ gpointer res;
+ unsigned count;
+
+ if (dat == NULL)
+ return;
+
+ g_return_if_fail (GO_DATA (dat) != NULL);
+
+ g_object_unref (dat);
+
+ if (graph == NULL)
+ return;
+
+ g_return_if_fail (GOG_GRAPH (graph) != NULL);
+
+ /* once we've been destroyed the list is gone */
+ if (graph->data == NULL)
+ return;
+
+ g_obj = G_OBJECT (graph);
+ res = g_object_get_qdata (g_obj, (GQuark)dat);
+
+ g_return_if_fail (res != NULL);
+
+ count = GPOINTER_TO_UINT (res);
+ if (count-- <= 1) {
+ /* signal before removing in case that unrefs */
+ g_signal_emit (G_OBJECT (graph),
+ gog_graph_signals [GRAPH_REMOVE_DATA], 0, dat);
+ graph->data = g_slist_remove (graph->data, dat);
+ g_object_unref (dat);
+ g_object_set_qdata (g_obj, (GQuark)dat, NULL);
+ } else
+ /* store the decremented count */
+ g_object_set_qdata (g_obj, (GQuark)dat, GUINT_TO_POINTER (count));
+}
+
+static gboolean
+cb_graph_idle (GogGraph *graph)
+{
+ /* an update may queue an update in a different object,
+ * clear the handler early */
+ graph->idle_handler = 0;
+ gog_object_update (GOG_OBJECT (graph));
+ return FALSE;
+}
+
+/**
+ * gog_graph_request_update :
+ * @graph : #GogGraph
+ *
+ * queue an update if one had not already be queued.
+ **/
+gboolean
+gog_graph_request_update (GogGraph *graph)
+{
+ /* people may try to queue an update during destruction */
+ if (G_OBJECT (graph)->ref_count <= 0)
+ return FALSE;
+
+ g_return_val_if_fail (GOG_GRAPH (graph) != NULL, FALSE);
+
+ if (graph->idle_handler == 0) { /* higher priority than canvas */
+ graph->idle_handler = g_idle_add_full (G_PRIORITY_HIGH_IDLE,
+ (GSourceFunc) cb_graph_idle, graph, NULL);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * gog_graph_force_update :
+ * @graph : #GogGraph
+ *
+ * Do an update now if one has been queued.
+ **/
+void
+gog_graph_force_update (GogGraph *graph)
+{
+ /* people may try to queue an update during destruction */
+ if (G_OBJECT (graph)->ref_count > 0 && graph->idle_handler != 0) {
+ g_source_remove (graph->idle_handler);
+ graph->idle_handler = 0;
+ gog_object_update (GOG_OBJECT (graph));
+ }
+}
+
+/************************************************************************/
+
+typedef GogOutlinedView GogGraphView;
+typedef GogOutlinedViewClass GogGraphViewClass;
+
+#define GOG_GRAPH_VIEW_TYPE (gog_graph_view_get_type ())
+#define GOG_GRAPH_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRAPH_VIEW_TYPE, GogGraphView))
+#define IS_GOG_GRAPH_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRAPH_VIEW_TYPE))
+
+enum {
+ GRAPH_VIEW_PROP_0,
+ GRAPH_VIEW_PROP_RENDERER
+};
+
+static void
+gog_graph_view_set_property (GObject *gobject, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogView *view = GOG_VIEW (gobject);
+
+ switch (param_id) {
+ case GRAPH_VIEW_PROP_RENDERER:
+ g_return_if_fail (view->renderer == NULL);
+ view->renderer = GOG_RENDERER (g_value_get_object (value));
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+}
+
+static void
+gog_graph_view_size_allocate (GogView *view, GogViewAllocation const *a)
+{
+ GSList *ptr;
+ double h, w;
+ unsigned x, y, rows, cols;
+ GogView *child;
+ GogGraph *graph = GOG_GRAPH (view->model);
+ GogViewAllocation tmp, res = *a;
+
+ (gview_parent_klass->size_allocate) (view, &res);
+
+ if (gog_graph_num_cols (graph) <= 0 ||
+ gog_graph_num_rows (graph) <= 0)
+ return;
+
+ res = view->residual;
+ w = res.w / gog_graph_num_cols (graph);
+ h = res.h / gog_graph_num_rows (graph);
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (child->model->position == GOG_POSITION_SPECIAL) {
+ gog_chart_get_position (GOG_CHART (child->model),
+ &x, &y, &cols, &rows);
+ tmp.x = x * w + res.x;
+ tmp.y = y * h + res.y;
+ tmp.w = cols * w;
+ tmp.h = rows * h;
+ gog_view_size_allocate (child, &tmp);
+ }
+ }
+}
+
+static void
+gog_graph_view_class_init (GogGraphViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+ GObjectClass *gobject_klass = (GObjectClass *) view_klass;
+
+ gview_parent_klass = g_type_class_peek_parent (gview_klass);
+ gobject_klass->set_property = gog_graph_view_set_property;
+ view_klass->size_allocate = gog_graph_view_size_allocate;
+
+ g_object_class_install_property (gobject_klass, GRAPH_VIEW_PROP_RENDERER,
+ g_param_spec_object ("renderer", "renderer",
+ "the renderer for this view",
+ GOG_RENDERER_TYPE, G_PARAM_WRITABLE));
+}
+
+GSF_CLASS (GogGraphView, gog_graph_view,
+ gog_graph_view_class_init, NULL,
+ GOG_OUTLINED_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-data-set.c
@@ -0,0 +1,208 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-data-set.c : A Utility interface for managing GOData as attributes
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/go-data.h>
+
+GType
+gog_dataset_get_type (void)
+{
+ static GType gog_dataset_type = 0;
+
+ if (!gog_dataset_type) {
+ static GTypeInfo const gog_dataset_info = {
+ sizeof (GogDatasetClass), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ gog_dataset_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GogDataset", &gog_dataset_info, 0);
+ }
+
+ return gog_dataset_type;
+}
+
+/**
+ * gog_dataset_dims :
+ * @set : #GogDataset
+ * @first : inclusive
+ * @last : _inclusive_
+ *
+ * Returns the first and last valid indicises to get/set dim.
+ **/
+void
+gog_dataset_dims (GogDataset const *set, int *first, int *last)
+{
+ GogDatasetClass *klass = GOG_DATASET_GET_CLASS (set);
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (first != NULL);
+ g_return_if_fail (last != NULL);
+ return (klass->dims) (set, first, last);
+}
+
+/**
+ * gog_dataset_get_dim :
+ * @set : #GogDataset
+ * @dim_i :
+ *
+ * Returns the GOData associated with dimension @dim_i. Does NOT add a
+ * reference.
+ **/
+GOData *
+gog_dataset_get_dim (GogDataset const *set, int dim_i)
+{
+ GogDatasetElement *elem = gog_dataset_get_elem (set, dim_i);
+ g_return_val_if_fail (elem != NULL, NULL);
+ return elem->data;
+}
+
+/**
+ * gog_dataset_set_dim :
+ * @series : #GogSeries
+ * @dim_i : < 0 gets the name
+ * @val : #GOData
+ * @err : #GError
+ *
+ * Absorbs a ref to @val if it is non NULL
+ **/
+void
+gog_dataset_set_dim (GogDataset *set, int dim_i, GOData *val, GError **err)
+{
+ GogDatasetClass *klass = GOG_DATASET_GET_CLASS (set);
+
+ g_return_if_fail (klass != NULL);
+ g_return_if_fail (val == NULL || GO_DATA (val) != NULL);
+
+ /* short circuit */
+ if (val != gog_dataset_get_dim (set, dim_i)) {
+ gog_dataset_set_dim_internal (set, dim_i, val,
+ gog_object_get_graph (GOG_OBJECT (set)));
+
+ if (klass->set_dim)
+ (klass->set_dim) (set, dim_i, val, err);
+ if (klass->dim_changed)
+ (klass->dim_changed) (set, dim_i);
+ }
+
+ /* absorb ref to orig, simplifies life cycle easier for new GODatas */
+ if (val != NULL)
+ g_object_unref (val);
+}
+
+GogDatasetElement *
+gog_dataset_get_elem (GogDataset const *set, int dim_i)
+{
+ GogDatasetClass *klass = GOG_DATASET_GET_CLASS (set);
+ g_return_val_if_fail (klass != NULL, NULL);
+ return (klass->get_elem) (set, dim_i);
+}
+
+static void
+cb_dataset_dim_changed (GOData *data, GogDatasetElement *elem)
+{
+ GogDatasetClass *klass = GOG_DATASET_GET_CLASS (elem->set);
+
+ g_return_if_fail (klass != NULL);
+ if (klass->dim_changed)
+ (klass->dim_changed) (elem->set, elem->dim_i);
+}
+
+/**
+ * gog_dataset_set_dim_internal :
+ *
+ * and internal routine to handle signal setup and teardown
+ **/
+void
+gog_dataset_set_dim_internal (GogDataset *set, int dim_i,
+ GOData *val, GogGraph *graph)
+{
+ GogDatasetElement *elem = gog_dataset_get_elem (set, dim_i);
+
+ g_return_if_fail (elem != NULL);
+
+ if (graph != NULL) {
+ if (val == elem->data)
+ return;
+ if (val != NULL)
+ val = gog_graph_ref_data (graph, val);
+ if (elem->handler != 0) {
+ g_signal_handler_disconnect (G_OBJECT (elem->data),
+ elem->handler);
+ elem->handler = 0;
+ gog_graph_unref_data (graph, elem->data);
+ }
+ if (val != NULL)
+ elem->handler = g_signal_connect (
+ G_OBJECT (val), "changed",
+ G_CALLBACK (cb_dataset_dim_changed), elem);
+ } else {
+ if (val != NULL)
+ g_object_ref (val);
+ if (elem->data != NULL)
+ g_object_unref (elem->data);
+ }
+ elem->data = val;
+ elem->set = set;
+ elem->dim_i = dim_i;
+ gog_object_request_update (GOG_OBJECT (set));
+}
+
+void
+gog_dataset_finalize (GogDataset *set)
+{
+ GogGraph *graph = gog_object_get_graph (GOG_OBJECT (set));
+ int first, last;
+
+ gog_dataset_dims (set, &first, &last);
+ while (first <= last)
+ gog_dataset_set_dim_internal (set, first++, NULL, graph);
+}
+
+void
+gog_dataset_parent_changed (GogDataset *set, gboolean was_set)
+{
+ GogGraph *graph = gog_object_get_graph (GOG_OBJECT (set));
+ GogDatasetElement *elem;
+ GOData *dat;
+ int i, last;
+
+ for (gog_dataset_dims (set, &i, &last); i <= last ; i++) {
+ elem = gog_dataset_get_elem (set, i);
+ if (elem == NULL || elem->data == NULL)
+ continue;
+ dat = elem->data;
+ if (!was_set) {
+ g_object_ref (dat);
+ gog_dataset_set_dim_internal (set, i, NULL, graph);
+ elem->data = dat;
+ } else if (elem->handler == 0) {
+ elem->data = NULL; /* disable the short circuit */
+ gog_dataset_set_dim_internal (set, i, dat, graph);
+ g_object_unref (dat);
+ }
+ }
+ if (was_set)
+ gog_object_request_update (GOG_OBJECT (set));
+}
--- /dev/null
+++ lib/goffice/graph/gog-plot.h
@@ -0,0 +1,70 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-plot.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_PLOT_H
+#define GOG_PLOT_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/utils/goffice-utils.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ struct {
+ double minima, maxima;
+ } val, logical;
+ gboolean is_discrete;
+ gboolean center_on_ticks;
+ GOFormat *fmt;
+} GogPlotBoundInfo;
+
+#define GOG_PLOT_TYPE (gog_plot_get_type ())
+#define GOG_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PLOT_TYPE, GogPlot))
+#define IS_GOG_PLOT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PLOT_TYPE))
+
+GType gog_plot_get_type (void);
+GogPlot *gog_plot_new_by_type (GogPlotType const *type);
+GogPlot *gog_plot_new_by_name (char const *id);
+gboolean gog_plot_make_similar (GogPlot *dst, GogPlot const *src);
+
+void gog_plot_request_cardinality_update (GogPlot *plot);
+void gog_plot_get_cardinality (GogPlot *plot,
+ unsigned *full, unsigned *visible);
+void gog_plot_foreach_elem (GogPlot *plot, gboolean only_visible,
+ GogEnumFunc handler, gpointer data);
+GSList const *gog_plot_get_series (GogPlot const *plot);
+GOData *gog_plot_get_axis_bounds (GogPlot *plot, GogAxisType axis,
+ GogPlotBoundInfo *bounds);
+
+gboolean gog_plot_supports_vary_style_by_element (GogPlot const *plot);
+
+GogSeries *gog_plot_new_series (GogPlot *plot);
+GogPlotDesc const *gog_plot_description (GogPlot const *plot);
+
+GogAxisSet gog_plot_axis_set_pref (GogPlot const *plot);
+gboolean gog_plot_axis_set_is_valid (GogPlot const *plot, GogAxisSet type);
+gboolean gog_plot_axis_set_assign (GogPlot *plot, GogAxisSet type);
+void gog_plot_axis_clear (GogPlot *plot, GogAxisSet filter);
+GogAxis *gog_plot_get_axis (GogPlot const *plot, GogAxisType type);
+
+G_END_DECLS
+
+#endif /* GOG_PLOT_H */
--- /dev/null
+++ lib/goffice/graph/goffice-graph.h
@@ -0,0 +1,173 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * goffice-graph.h:
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOFFICE_GRAPH_H
+#define GOFFICE_GRAPH_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GogObject GogObject;
+typedef struct _GogObjectRole GogObjectRole;
+typedef struct _GogView GogView; /* view of an Object */
+
+typedef struct _GogGraph GogGraph; /* collection of charts */
+typedef struct _GogChart GogChart; /* collection of plots */
+typedef struct _GogPlot GogPlot; /* abstract base for plots */
+typedef struct _GogPlotType GogPlotType; /* visible characterization */
+typedef struct _GogPlotFamily GogPlotFamily; /* a group of plot types */
+typedef struct _GogPlotDesc GogPlotDesc; /* data/axis requirements */
+typedef struct _GogSeries GogSeries; /* single plotable entity */
+typedef struct _GogSeriesDesc GogSeriesDesc; /* data requirements */
+typedef struct _GogSeriesDimDesc GogSeriesDimDesc; /* dimension of a series */
+
+/* Useful objects */
+typedef struct _GogLegend GogLegend;
+typedef struct _GogLabel GogLabel;
+typedef struct _GogStyledObject GogStyledObject;
+typedef struct _GogAxis GogAxis;
+typedef struct _GogGrid GogGrid;
+typedef struct _GogGridLine GogGridLine;
+typedef struct _GogErrorBar GogErrorBar;
+
+/* formating */
+typedef struct _GogTheme GogTheme;
+typedef struct _GogStyle GogStyle;
+typedef GSList GogSeriesElementStyleList;
+
+/* Data */
+typedef struct _GogDataAllocator GogDataAllocator;
+typedef struct _GogDataset GogDataset;
+typedef struct _GOData GOData;
+typedef struct _GODataScalar GODataScalar;
+typedef struct _GODataVector GODataVector;
+typedef struct {
+ int rows; /* negative if dirty, includes missing values */
+ int columns; /* negative if dirty, includes missing values */
+} GOMatrixSize;
+typedef struct _GODataMatrix GODataMatrix;
+
+typedef struct _GogRenderer GogRenderer;
+
+typedef struct {
+ double w, h;
+} GogViewRequisition;
+
+typedef struct {
+ double w, h;
+ double x, y;
+} GogViewAllocation;
+
+typedef struct {
+ double wr, hb;
+ double wl, ht;
+} GogViewPadding;
+
+typedef void (*GogEnumFunc) (unsigned i, GogStyle *style,
+ char const *name, gpointer data);
+
+typedef enum {
+ GOG_AXIS_UNKNOWN = -1,
+ GOG_AXIS_X = 0,
+ GOG_AXIS_Y,
+ GOG_AXIS_Z,
+ GOG_AXIS_CIRCULAR,
+ GOG_AXIS_RADIAL,
+ GOG_AXIS_TYPES,
+ GOG_AXIS_PSEUDO_3D
+} GogAxisType;
+typedef enum {
+ GOG_AXIS_SET_UNKNOWN = -1,
+ GOG_AXIS_SET_NONE = 0,
+ GOG_AXIS_SET_XY = (1 << GOG_AXIS_X) | (1 << GOG_AXIS_Y),
+ GOG_AXIS_SET_XY_pseudo_3d = (1 << GOG_AXIS_X) | (1 << GOG_AXIS_Y) | (1 << GOG_AXIS_PSEUDO_3D),
+ GOG_AXIS_SET_XYZ = (1 << GOG_AXIS_X) | (1 << GOG_AXIS_Y) | (1 << GOG_AXIS_Z),
+ GOG_AXIS_SET_RADAR = (1 << GOG_AXIS_CIRCULAR) | (1 << GOG_AXIS_RADIAL),
+ GOG_AXIS_SET_ALL = ((1 << GOG_AXIS_TYPES) -1)
+} GogAxisSet;
+
+typedef enum {
+ GOG_DIM_INVALID = -1,
+ GOG_DIM_LABEL = 0,
+ GOG_DIM_INDEX,
+ GOG_DIM_VALUE,
+ GOG_DIM_MATRIX,
+ GOG_DIM_TYPES
+} GogDimType;
+
+typedef enum {
+ GOG_DATA_SCALAR,
+ GOG_DATA_VECTOR,
+ GOG_DATA_MATRIX
+} GogDataType;
+
+/* A helper enum to simplify import/export from MS Excel (tm) which uses the
+ * same logical dim names for all plot types. Do _NOT_ reorder, or change the
+ * enumeration without checking the xls code */
+typedef enum {
+ GOG_MS_DIM_LABELS = 0,
+ GOG_MS_DIM_VALUES = 1,
+ GOG_MS_DIM_CATEGORIES = 2,
+ GOG_MS_DIM_BUBBLES = 3, /* undocumented */
+ GOG_MS_DIM_ERR_plus1, /* we made it up */
+ GOG_MS_DIM_ERR_minus1, /* we made it up */
+ GOG_MS_DIM_ERR_plus2, /* we made it up */
+ GOG_MS_DIM_ERR_minus2, /* we made it up */
+ GOG_MS_DIM_TYPES
+} GogMSDimType;
+
+typedef enum {
+ GOG_POSITION_AUTO = 0,
+ GOG_POSITION_N = 1 << 0, /* can be used with E or W */
+ GOG_POSITION_S = 1 << 1, /* can be used with E or W */
+ GOG_POSITION_E = 1 << 2,
+ GOG_POSITION_W = 1 << 3,
+ GOG_POSITION_COMPASS = 0x0f,
+
+ /* modifiers for compass */
+ GOG_POSITION_ALIGN_FILL = 0 << 4,
+ GOG_POSITION_ALIGN_START = 1 << 4,
+ GOG_POSITION_ALIGN_END = 2 << 4,
+ GOG_POSITION_ALIGN_CENTER = 3 << 4,
+ GOG_POSITION_ALIGNMENT = 0x30,
+
+ GOG_POSITION_SPECIAL = 1 << 6,
+
+ GOG_POSITION_MANUAL = 1 << 7,
+ GOG_POSITION_MANUAL_X_ABS = 1 << 8, /* abs vs relative pos */
+ GOG_POSITION_MANUAL_Y_ABS = 1 << 9,
+ GOG_POSITION_MANUAL_X_END = 1 << 10, /* pos relative to start or end */
+ GOG_POSITION_MANUAL_Y_END = 1 << 11,
+ GOG_POSITION_ANY_MANUAL = 0xf80
+} GogObjectPosition;
+
+/* #define NO_DEBUG_CHARTS */
+#ifndef NO_DEBUG_CHARTS
+#define gog_debug(level, code) do { if (goffice_graph_debug_level > level) { code } } while (0)
+#else
+#define gog_debug(level, code)
+#endif
+extern int goffice_graph_debug_level;
+
+G_END_DECLS
+
+#endif /* GOFFICE_GRAPH_H */
--- /dev/null
+++ lib/goffice/graph/gog-plot.c
@@ -0,0 +1,547 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-plot.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-plot-engine.h>
+#include <goffice/graph/gog-series-impl.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-object-xml.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-math.h>
+#include <glib/gi18n.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+#define GOG_PLOT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_PLOT_TYPE, GogPlotClass))
+
+enum {
+ PLOT_PROP_0,
+ PLOT_PROP_VARY_STYLE_BY_ELEMENT
+};
+
+static GObjectClass *plot_parent_klass;
+
+static void
+gog_plot_finalize (GObject *obj)
+{
+ GogPlot *plot = GOG_PLOT (obj);
+
+ g_slist_free (plot->series); /* GogObject does the unref */
+
+ gog_plot_axis_clear (plot, GOG_AXIS_SET_ALL); /* just in case */
+
+ (*plot_parent_klass->finalize) (obj);
+}
+
+static gboolean
+role_series_can_add (GogObject const *parent)
+{
+ GogPlot *plot = GOG_PLOT (parent);
+ return g_slist_length (plot->series) < plot->desc.num_series_max;
+}
+static gboolean
+role_series_can_remove (GogObject const *child)
+{
+ GogPlot const *plot = GOG_PLOT (child->parent);
+ return g_slist_length (plot->series) > plot->desc.num_series_min;
+}
+
+static GogObject *
+role_series_allocate (GogObject *plot)
+{
+ GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
+ GType type = klass->series_type;
+
+ if (type == 0)
+ type = GOG_SERIES_TYPE;
+ return g_object_new (type, NULL);
+}
+
+static void
+role_series_post_add (GogObject *parent, GogObject *child)
+{
+ GogPlot *plot = GOG_PLOT (parent);
+ GogSeries *series = GOG_SERIES (child);
+ unsigned num_dim;
+ num_dim = plot->desc.series.num_dim;
+
+ /* Alias things so that dim -1 is valid */
+ series->values = g_new0 (GogDatasetElement, num_dim+1) + 1;
+ series->plot = plot;
+
+ /* if there are other series associated with the plot, and there are
+ * shared dimensions, clone them over. */
+ if (series->plot->series != NULL) {
+ GogGraph *graph = gog_object_get_graph (GOG_OBJECT (plot));
+ GogSeriesDesc const *desc = &plot->desc.series;
+ GogSeries const *src = plot->series->data;
+ unsigned i;
+
+ for (i = num_dim; i-- > 0 ; ) /* name is never shared */
+ if (desc->dim[i].is_shared)
+ gog_dataset_set_dim_internal (GOG_DATASET (series),
+ i, src->values[i].data, graph);
+
+ gog_series_check_validity (series);
+ }
+
+ /* APPEND to keep order, there won't be that many */
+ plot->series = g_slist_append (plot->series, series);
+ gog_plot_request_cardinality_update (plot);
+}
+
+static void
+role_series_pre_remove (GogObject *parent, GogObject *series)
+{
+ GogPlot *plot = GOG_PLOT (parent);
+ plot->series = g_slist_remove (plot->series, series);
+ gog_plot_request_cardinality_update (plot);
+}
+
+static void
+gog_plot_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogPlot *plot = GOG_PLOT (obj);
+ gboolean b_tmp;
+
+ switch (param_id) {
+ case PLOT_PROP_VARY_STYLE_BY_ELEMENT:
+ b_tmp = g_value_get_boolean (value) &&
+ gog_plot_supports_vary_style_by_element (plot);
+ if (plot->vary_style_by_element ^ b_tmp) {
+ plot->vary_style_by_element = b_tmp;
+ gog_plot_request_cardinality_update (plot);
+ }
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+}
+
+static void
+gog_plot_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogPlot *plot = GOG_PLOT (obj);
+ switch (param_id) {
+ case PLOT_PROP_VARY_STYLE_BY_ELEMENT:
+ g_value_set_boolean (value,
+ plot->vary_style_by_element &&
+ gog_plot_supports_vary_style_by_element (plot));
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_plot_children_reordered (GogObject *obj)
+{
+ GSList *ptr, *accum = NULL;
+ GogPlot *plot = GOG_PLOT (obj);
+
+ for (ptr = obj->children; ptr != NULL ; ptr = ptr->next)
+ if (IS_GOG_SERIES (ptr->data))
+ accum = g_slist_prepend (accum, ptr->data);
+ g_slist_free (plot->series);
+ plot->series = g_slist_reverse (accum);
+
+ gog_plot_request_cardinality_update (plot);
+}
+
+static void
+gog_plot_class_init (GogObjectClass *gog_klass)
+{
+ static GogObjectRole const roles[] = {
+ { N_("Series"), "GogSeries", 0,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_series_can_add, role_series_can_remove,
+ role_series_allocate,
+ role_series_post_add, role_series_pre_remove, NULL },
+ };
+ GObjectClass *gobject_klass = (GObjectClass *) gog_klass;
+
+ plot_parent_klass = g_type_class_peek_parent (gog_klass);
+ gobject_klass->finalize = gog_plot_finalize;
+ gobject_klass->set_property = gog_plot_set_property;
+ gobject_klass->get_property = gog_plot_get_property;
+ g_object_class_install_property (gobject_klass, PLOT_PROP_VARY_STYLE_BY_ELEMENT,
+ g_param_spec_boolean ("vary_style_by_element", "vary_style_by_element",
+ "Use a different style for each segments",
+ FALSE,
+ G_PARAM_READWRITE|GOG_PARAM_PERSISTENT|GOG_PARAM_FORCE_SAVE));
+
+ gog_klass->children_reordered = gog_plot_children_reordered;
+ gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+}
+
+static void
+gog_plot_init (GogPlot *plot, GogPlotClass const *derived_plot_klass)
+{
+ /* keep a local copy so that we can over-ride things if desired */
+ plot->desc = derived_plot_klass->desc;
+ /* start as true so that we can queue an update when it changes */
+ plot->cardinality_valid = TRUE;
+}
+
+GSF_CLASS_ABSTRACT (GogPlot, gog_plot,
+ gog_plot_class_init, gog_plot_init,
+ GOG_OBJECT_TYPE)
+
+GogPlot *
+gog_plot_new_by_type (GogPlotType const *type)
+{
+ GogPlot *res;
+
+ g_return_val_if_fail (type != NULL, NULL);
+
+ res = gog_plot_new_by_name (type->engine);
+ if (res != NULL && type->properties != NULL)
+ g_hash_table_foreach (type->properties,
+ (GHFunc) gog_object_set_arg, res);
+ return res;
+}
+
+/**
+ * gog_plot_make_similar :
+ * @dst :
+ * @src :
+ *
+ * As much as possible have @dst use similar formatting and data allocation to
+ * @src.
+ *
+ * return TRUE on failue
+ **/
+gboolean
+gog_plot_make_similar (GogPlot *dst, GogPlot const *src)
+{
+ g_return_val_if_fail (GOG_PLOT (dst) != NULL, TRUE);
+ g_return_val_if_fail (GOG_PLOT (src) != NULL, TRUE);
+
+ return FALSE;
+}
+
+/* convenience routines */
+GogSeries *
+gog_plot_new_series (GogPlot *plot)
+{
+ GogObject *res = gog_object_add_by_name (GOG_OBJECT (plot), "Series", NULL);
+ return res ? GOG_SERIES (res) : NULL;
+}
+GogPlotDesc const *
+gog_plot_description (GogPlot const *plot)
+{
+ g_return_val_if_fail (GOG_PLOT (plot) != NULL, NULL);
+ return &plot->desc;
+}
+
+static GogChart *
+gog_plot_get_chart (GogPlot const *plot)
+{
+ return GOG_CHART (GOG_OBJECT (plot)->parent);
+}
+
+/* protected */
+void
+gog_plot_request_cardinality_update (GogPlot *plot)
+{
+ g_return_if_fail (GOG_PLOT (plot) != NULL);
+
+ if (plot->cardinality_valid) {
+ GogChart *chart = gog_plot_get_chart (plot);
+ plot->cardinality_valid = FALSE;
+ gog_object_request_update (GOG_OBJECT (plot));
+ if (chart != NULL)
+ gog_chart_request_cardinality_update (chart);
+ }
+}
+
+/**
+ * gog_plot_get_cardinality :
+ * @plot : #GogPlot
+ *
+ * Return the number of logical elements in the plot, updating the cache if
+ * necessary
+ **/
+void
+gog_plot_get_cardinality (GogPlot *plot, unsigned *full, unsigned *visible)
+{
+ g_return_if_fail (GOG_PLOT (plot) != NULL);
+
+ if (!plot->cardinality_valid) {
+ GogSeries *series;
+ GSList *ptr;
+ gboolean is_valid;
+ unsigned size = 0, no_legend = 0, i;
+
+ plot->cardinality_valid = TRUE;
+ gog_chart_get_cardinality (gog_plot_get_chart (plot), NULL, &i);
+ plot->index_num = i;
+
+ for (ptr = plot->series; ptr != NULL ; ptr = ptr->next) {
+ series = GOG_SERIES (ptr->data);
+ is_valid = gog_series_is_valid (GOG_SERIES (series));
+ if (plot->vary_style_by_element) {
+ if (is_valid && size < series->num_elements)
+ size = series->num_elements;
+ gog_series_set_index (series, plot->index_num, FALSE);
+ } else {
+ gog_series_set_index (series, i++, FALSE);
+ if (!gog_series_has_legend (series))
+ no_legend++;
+ }
+ }
+
+ plot->full_cardinality =
+ plot->vary_style_by_element ? size : (i - plot->index_num);
+ plot->visible_cardinality = plot->full_cardinality - no_legend;
+ }
+
+ if (full != NULL)
+ *full = plot->full_cardinality;
+ if (visible != NULL)
+ *visible = plot->visible_cardinality;
+}
+
+void
+gog_plot_foreach_elem (GogPlot *plot, gboolean only_visible,
+ GogEnumFunc func, gpointer data)
+{
+ GSList *ptr;
+ GogSeries const *series;
+ GogStyle *style, *tmp_style;
+ GODataVector *labels;
+ unsigned i, n, num_labels = 0;
+ char *label = NULL;
+ GogTheme *theme = gog_object_get_theme (GOG_OBJECT (plot));
+ GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
+ GList *overrides;
+
+ g_return_if_fail (GOG_PLOT (plot) != NULL);
+ g_return_if_fail (plot->cardinality_valid);
+
+ if (klass->foreach_elem) {
+ klass->foreach_elem (plot, only_visible, func, data);
+ return;
+ }
+
+ ptr = plot->series;
+ if (ptr == NULL)
+ return;
+
+ if (!plot->vary_style_by_element) {
+ unsigned i = plot->index_num;
+ for (; ptr != NULL ; ptr = ptr->next)
+ if (!only_visible || gog_series_has_legend (ptr->data)) {
+ func (i, gog_styled_object_get_style (ptr->data),
+ gog_object_get_name (ptr->data), data);
+ i++;
+ }
+ return;
+ }
+
+ series = ptr->data; /* start with the first */
+ labels = NULL;
+ if (series->values[0].data != NULL) {
+ labels = GO_DATA_VECTOR (series->values[0].data);
+ num_labels = go_data_vector_get_len (labels);
+ }
+ style = gog_style_dup (series->base.style);
+ n = only_visible ? plot->visible_cardinality : plot->full_cardinality;
+ for (overrides = series->overrides, i = 0; i < n ; i++) {
+ if (overrides != NULL &&
+ (GOG_SERIES_ELEMENT (overrides->data)->index == i)) {
+ tmp_style = GOG_STYLED_OBJECT (overrides->data)->style;
+ overrides = overrides->next;
+ } else
+ tmp_style = style;
+
+ gog_theme_fillin_style (theme, tmp_style, GOG_OBJECT (series),
+ plot->index_num + i, FALSE);
+ if (labels != NULL)
+ label = (i < num_labels)
+ ? go_data_vector_get_str (labels, i) : g_strdup ("");
+ else
+ label = NULL;
+ if (label == NULL)
+ label = g_strdup_printf ("%d", i);
+ (func) (i, tmp_style, label, data);
+ g_free (label);
+ }
+ g_object_unref (style);
+}
+
+/**
+ * gog_plot_get_series :
+ * @plot : #GogPlot
+ *
+ * A list of the series in @plot. Do not modify or free the list.
+ **/
+GSList const *
+gog_plot_get_series (GogPlot const *plot)
+{
+ g_return_val_if_fail (GOG_PLOT (plot) != NULL, NULL);
+ return plot->series;
+}
+
+/**
+ * gog_plot_get_axis_bounds :
+ * @plot : #GogPlot
+ * @axis : #GogAxisType
+ * @bounds : #GogPlotBoundInfo
+ *
+ * Queries @plot for its axis preferences for @axis and stores the results in
+ * @bounds. All elements of @bounds are initialized to sane values before the
+ * query _ACCEPT_ ::fmt. The caller is responsible for initializing it. This
+ * is done so that once on plot has selected a format the others need not do
+ * the lookup too if so desired.
+ *
+ * Caller is responsible for unrefing ::fmt.
+ **/
+GOData *
+gog_plot_get_axis_bounds (GogPlot *plot, GogAxisType axis,
+ GogPlotBoundInfo *bounds)
+{
+ GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+ g_return_val_if_fail (bounds != NULL, NULL);
+
+ bounds->val.minima = DBL_MAX;
+ bounds->val.maxima = -DBL_MAX;
+ bounds->logical.maxima = go_nan;
+ bounds->logical.minima = go_nan;
+ bounds->is_discrete = FALSE;
+ bounds->center_on_ticks = FALSE;
+ if (klass->axis_get_bounds == NULL)
+ return NULL;
+ return (klass->axis_get_bounds) (plot, axis, bounds);
+}
+
+gboolean
+gog_plot_supports_vary_style_by_element (GogPlot const *plot)
+{
+ GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
+
+ g_return_val_if_fail (klass != NULL, FALSE);
+
+ if (klass->supports_vary_style_by_element)
+ return (klass->supports_vary_style_by_element) (plot);
+ return TRUE; /* default */
+}
+
+GogAxisSet
+gog_plot_axis_set_pref (GogPlot const *plot)
+{
+ GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
+
+ g_return_val_if_fail (klass != NULL, FALSE);
+ if (klass->axis_set_pref != NULL)
+ return (klass->axis_set_pref) (plot);
+ return GOG_AXIS_SET_NONE;
+}
+
+gboolean
+gog_plot_axis_set_is_valid (GogPlot const *plot, GogAxisSet type)
+{
+ GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
+
+ g_return_val_if_fail (klass != NULL, FALSE);
+ if (klass->axis_set_is_valid != NULL)
+ return (klass->axis_set_is_valid) (plot, type);
+ return type == GOG_AXIS_SET_NONE;
+}
+
+gboolean
+gog_plot_axis_set_assign (GogPlot *plot, GogAxisSet axis_set)
+{
+ GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
+ GogChart const *chart;
+ GogAxisType type;
+
+ g_return_val_if_fail (klass != NULL, FALSE);
+
+ chart = gog_plot_get_chart (plot);
+ for (type = 0 ; type < GOG_AXIS_TYPES ; type++) {
+ if (plot->axis[type] != NULL) {
+ if (!(axis_set & (1 << type))) {
+ gog_axis_del_contributor (plot->axis[type], GOG_OBJECT (plot));
+ plot->axis[type] = NULL;
+ }
+ } else if (axis_set & (1 << type)) {
+ GSList *axes = gog_chart_get_axis (chart, type);
+ if (axes != NULL) {
+ gog_axis_add_contributor (axes->data, GOG_OBJECT (plot));
+ plot->axis[type] = axes->data;
+ g_slist_free (axes);
+ }
+ }
+ }
+
+ if (klass->axis_set_assign == NULL)
+ return axis_set == GOG_AXIS_SET_NONE;
+
+ return (klass->axis_set_assign) (plot, axis_set);
+}
+
+/**
+ * gog_plot_axis_clear :
+ * @plot : #GogPlot
+ * @filter : #GogAxisSet
+ *
+ * A utility method to clear all existing axis associations flagged by @filter
+ **/
+void
+gog_plot_axis_clear (GogPlot *plot, GogAxisSet filter)
+{
+ GogAxisType type;
+
+ g_return_if_fail (GOG_PLOT (plot) != NULL);
+
+ for (type = 0 ; type < GOG_AXIS_TYPES ; type++)
+ if (plot->axis[type] != NULL && ((1 << type) & filter)) {
+ gog_axis_del_contributor (plot->axis[type], GOG_OBJECT (plot));
+ plot->axis[type] = NULL;
+ }
+}
+
+GogAxis *
+gog_plot_get_axis (GogPlot const *plot, GogAxisType type)
+{
+ g_return_val_if_fail (GOG_PLOT (plot) != NULL, NULL);
+ g_return_val_if_fail (type < GOG_AXIS_TYPES, NULL);
+ g_return_val_if_fail (GOG_AXIS_UNKNOWN < type, NULL);
+ return plot->axis[type];
+}
+
+/****************************************************************************/
+/* a placeholder. It seems likely that we will want this eventually */
+GSF_CLASS_ABSTRACT (GogPlotView, gog_plot_view,
+ NULL, NULL,
+ GOG_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-graph-impl.h
@@ -0,0 +1,60 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-graph-impl.h : the top level container for charts
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_GRAPH_IMPL_H
+#define GOG_GRAPH_IMPL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-outlined-object.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogGraph {
+ GogOutlinedObject base;
+
+ GogTheme *theme;
+ GSList *charts;
+ GSList *data;
+
+ unsigned num_cols, num_rows;
+
+ guint idle_handler;
+};
+typedef struct {
+ GogOutlinedObjectClass base;
+
+ /* signals */
+ void (*add_data) (GogGraph *graph, GOData *input);
+ void (*remove_data) (GogGraph *graph, GOData *input);
+} GogGraphClass;
+
+#define GOG_GRAPH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_GRAPH_TYPE, GogGraphClass))
+#define IS_GOG_GRAPH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_GRAPH_TYPE))
+
+/* protected */
+gboolean gog_graph_request_update (GogGraph *graph);
+void gog_graph_force_update (GogGraph *graph);
+
+G_END_DECLS
+
+#endif /* GOG_GRAPH_GROUP_IMPL_H */
--- /dev/null
+++ lib/goffice/graph/gog-legend.h
@@ -0,0 +1,37 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-legend.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_LEGEND_H
+#define GOG_LEGEND_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_LEGEND_TYPE (gog_legend_get_type ())
+#define GOG_LEGEND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_LEGEND_TYPE, GogLegend))
+#define IS_GOG_LEGEND(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_LEGEND_TYPE))
+
+GType gog_legend_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_LEGEND_H */
--- /dev/null
+++ lib/goffice/graph/gog-theme.c
@@ -0,0 +1,625 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-theme.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-gradient.h>
+#include <goffice/utils/go-units.h>
+#include <goffice/utils/go-marker.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+typedef void (*GogThemeStyleMap) (GogStyle *style, unsigned ind);
+typedef struct {
+ /* if role is non-null, it specifies the container class,
+ * if role is null, it specifies object type */
+ char const *klass_name;
+ char const *role_id;
+ GogStyle *style;
+ GogThemeStyleMap map;
+} GogThemeElement;
+struct _GogTheme {
+ GObject base;
+
+ char *name;
+ char *load_from_file; /* optionally NULL */
+ GHashTable *elem_hash_by_role;
+ GHashTable *elem_hash_by_role_id;
+ GHashTable *elem_hash_by_class;
+ GHashTable *elem_hash_by_class_name;
+ GHashTable *class_aliases;
+ GogStyle *default_style;
+};
+typedef GObjectClass GogThemeClass;
+
+static GObjectClass *parent_klass;
+static GSList *themes;
+static GogTheme *default_theme = NULL;
+static GHashTable *global_class_aliases = NULL;
+
+static void
+gog_theme_element_free (GogThemeElement *elem)
+{
+ g_object_unref (elem->style);
+ g_free (elem);
+}
+static guint
+gog_theme_element_hash (GogThemeElement const *elem)
+{
+ return g_str_hash (elem->role_id);
+}
+static gboolean
+gog_theme_element_eq (GogThemeElement const *a, GogThemeElement const *b)
+{
+ if (!g_str_equal (a->role_id, b->role_id))
+ return FALSE;
+ if (a->klass_name == NULL)
+ return b->klass_name == NULL;
+ if (b->klass_name == NULL)
+ return FALSE;
+ return g_str_equal (a->klass_name, b->klass_name);
+}
+
+static void
+gog_theme_finalize (GObject *obj)
+{
+ GogTheme *theme = GOG_THEME (obj);
+
+ g_free (theme->name); theme->name = NULL;
+ g_free (theme->load_from_file); theme->load_from_file = NULL;
+ if (theme->elem_hash_by_role)
+ g_hash_table_destroy (theme->elem_hash_by_role);
+ if (theme->elem_hash_by_role_id)
+ g_hash_table_destroy (theme->elem_hash_by_role_id);
+ if (theme->elem_hash_by_class)
+ g_hash_table_destroy (theme->elem_hash_by_class);
+ if (theme->elem_hash_by_class_name)
+ g_hash_table_destroy (theme->elem_hash_by_class_name);
+ if (theme->class_aliases)
+ g_hash_table_destroy (theme->class_aliases);
+
+ (parent_klass->finalize) (obj);
+}
+
+static void
+gog_theme_class_init (GogThemeClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+
+ parent_klass = g_type_class_peek_parent (klass);
+ gobject_klass->finalize = gog_theme_finalize;
+}
+
+static void
+gog_theme_init (GogTheme *theme)
+{
+ theme->elem_hash_by_role = g_hash_table_new (
+ g_direct_hash, g_direct_equal);
+ theme->elem_hash_by_class = g_hash_table_new (
+ g_direct_hash, g_direct_equal);
+ theme->elem_hash_by_role_id = g_hash_table_new_full (
+ (GHashFunc) gog_theme_element_hash,
+ (GCompareFunc) gog_theme_element_eq,
+ NULL, (GDestroyNotify) gog_theme_element_free);
+ theme->elem_hash_by_class_name = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) gog_theme_element_free);
+ theme->class_aliases = g_hash_table_new (
+ g_str_hash, g_str_equal);
+}
+
+GSF_CLASS (GogTheme, gog_theme,
+ gog_theme_class_init, gog_theme_init,
+ G_TYPE_OBJECT)
+
+static GogThemeElement *
+gog_theme_find_element (GogTheme *theme, GogObject *obj)
+{
+ GogThemeElement *elem = NULL;
+ GObjectClass *klass;
+ char const *name;
+
+ if (theme == NULL)
+ theme = default_theme;
+
+ g_return_val_if_fail (theme != NULL, NULL);
+
+ if (theme->load_from_file != NULL) {
+#warning TODO parse some xml
+ }
+
+ /* 1) have we seen this specific role before */
+ if (obj->role != NULL)
+ elem = g_hash_table_lookup (theme->elem_hash_by_role, obj->role);
+
+ /* 2) have we seen this specific type before */
+ if (elem == NULL) {
+ klass = G_OBJECT_GET_CLASS (obj);
+ elem = g_hash_table_lookup (theme->elem_hash_by_class, klass);
+ }
+
+ /* 3) Search by role */
+ if (elem == NULL && obj->role != NULL && obj->parent != NULL) {
+ GogThemeElement key;
+
+ /* 3.1) Search by specific role */
+ key.role_id = obj->role->id;
+ key.klass_name = G_OBJECT_TYPE_NAME (obj->parent);
+ elem = g_hash_table_lookup (theme->elem_hash_by_role_id, &key);
+
+ /* 3.2) Search by generic role */
+ if (elem == NULL) {
+ key.klass_name = NULL;
+ elem = g_hash_table_lookup (theme->elem_hash_by_role_id, &key);
+ }
+
+ if (elem != NULL) /* if found lets cache the result */
+ g_hash_table_insert (theme->elem_hash_by_role,
+ (gpointer) obj->role, elem);
+ }
+
+ /* 4) Still not found ? search by object type */
+ if (elem == NULL) {
+ do {
+ name = G_OBJECT_CLASS_NAME (klass);
+ elem = g_hash_table_lookup ( /* 4.1) is the type known */
+ theme->elem_hash_by_class_name, name);
+ if (elem == NULL) { /* 4.2) is this a local alias */
+ name = g_hash_table_lookup (
+ theme->class_aliases, name);
+ if (name != NULL)
+ elem = g_hash_table_lookup (
+ theme->elem_hash_by_class_name, name);
+ }
+ if (elem == NULL &&
+ global_class_aliases != NULL) { /* 4.3) is this a global alias */
+ name = g_hash_table_lookup (
+ global_class_aliases, G_OBJECT_CLASS_NAME (klass));
+ if (name != NULL)
+ elem = g_hash_table_lookup (
+ theme->elem_hash_by_class_name, name);
+ }
+ } while (elem == NULL &&
+ (klass = g_type_class_peek_parent (klass)) != NULL);
+ if (elem != NULL) /* if found lets cache the result */
+ g_hash_table_insert (theme->elem_hash_by_class, klass, elem);
+ }
+
+ return elem;
+}
+
+/**
+ * gog_theme_fillin_style :
+ * @theme : #GogTheme
+ * @style : #GogStyle to initialize
+ * @obj : #GogObject The object associated with @style
+ * @ind : an optional index
+ * @complete_overwrite : boolean
+ *
+ * Fill in the auto aspects of @style based on @theme 's element for objects of
+ * type/role similar to @obj with index @ind. If @complete_overwrite is used,
+ * fillin the entire style, not just the auto portions.
+ **/
+void
+gog_theme_fillin_style (GogTheme *theme, GogStyle *style,
+ GogObject *obj, int ind, gboolean complete_overwrite)
+{
+ GogThemeElement *elem = gog_theme_find_element (theme, obj);
+
+ g_return_if_fail (elem != NULL);
+
+ if (complete_overwrite)
+ gog_style_assign (style, elem->style);
+ else
+ gog_style_apply_theme (style, elem->style);
+
+#warning we should handle the applicability here not in the map
+ if (ind >= 0 && elem->map)
+ (elem->map) (style, ind);
+}
+
+void
+gog_theme_register (GogTheme *theme, gboolean is_default)
+{
+ g_return_if_fail (GOG_THEME (theme) != NULL);
+
+ if (is_default) {
+ g_object_ref (theme);
+ if (default_theme != NULL)
+ g_object_unref (default_theme);
+ default_theme = theme;
+ }
+
+ themes = g_slist_prepend (themes, theme);
+}
+
+static GogTheme *
+gog_theme_new (char const *name)
+{
+ GogTheme *theme = g_object_new (GOG_THEME_TYPE, NULL);
+ theme->name = g_strdup (name);
+ return theme;
+}
+
+void
+gog_theme_register_file (char const *name, char const *file)
+{
+ GogTheme *theme = gog_theme_new (name);
+ theme->load_from_file = g_strdup (file);
+}
+
+#if 0
+static void
+gog_theme_add_alias (GogTheme *theme, char const *from, char const *to)
+{
+ g_hash_table_insert (theme->class_aliases, (gpointer)from, (gpointer)to);
+}
+#endif
+
+static void
+gog_theme_add_element (GogTheme *theme, GogStyle *style,
+ GogThemeStyleMap map,
+ char const *klass_name, char const *role_id)
+{
+ GogThemeElement *elem;
+
+ g_return_if_fail (theme != NULL);
+
+ elem = g_new0 (GogThemeElement, 1);
+ elem->klass_name = klass_name;
+ elem->role_id = role_id;
+ elem->style = style;
+ elem->map = map;
+
+ /* Never put an element into both by_role_id & by_class_name */
+ if (role_id != NULL)
+ g_hash_table_insert (theme->elem_hash_by_role_id,
+ (gpointer)elem, elem);
+ else if (klass_name != NULL)
+ g_hash_table_insert (theme->elem_hash_by_class_name,
+ (gpointer)klass_name, elem);
+ else {
+ if (theme->default_style)
+ g_object_unref (theme->default_style);
+ theme->default_style = style;
+ g_free (elem);
+ }
+}
+
+GogTheme *
+gog_theme_lookup (char const *name)
+{
+ GSList *ptr;
+ GogTheme *theme;
+
+ if (name != NULL) {
+ for (ptr = themes ; ptr != NULL ; ptr = ptr->next) {
+ theme = ptr->data;
+ if (!strcmp (theme->name, name))
+ return theme;
+ }
+ g_warning ("No theme named '%s' found, using default", name);
+ }
+ return default_theme;
+}
+
+char const *
+gog_theme_get_name (GogTheme const *theme)
+{
+ g_return_val_if_fail (GOG_THEME (theme) != NULL, "");
+ return theme->name;
+}
+
+/**************************************************************************/
+
+static void
+map_marker (GogStyleMark *mark, unsigned shape, unsigned palette_index,
+ GOColor const *palette)
+{
+ static GOMarkerShape const shape_palette [] = {
+ GO_MARKER_DIAMOND, GO_MARKER_SQUARE,
+ GO_MARKER_TRIANGLE_UP, GO_MARKER_X,
+ GO_MARKER_ASTERISK, GO_MARKER_CIRCLE,
+ GO_MARKER_CROSS, GO_MARKER_HALF_BAR,
+ GO_MARKER_BAR
+ };
+ static gboolean const shape_is_fill_transparent [] = {
+ TRUE, TRUE,
+ TRUE, FALSE,
+ FALSE, TRUE,
+ FALSE, TRUE,
+ TRUE
+ };
+
+ if (shape >= G_N_ELEMENTS (shape_palette))
+ shape %= G_N_ELEMENTS (shape_palette);
+
+ if (mark->auto_shape)
+ go_marker_set_shape (mark->mark, shape_palette [shape]);
+ if (mark->auto_outline_color)
+ go_marker_set_outline_color (mark->mark,
+ palette [palette_index]);
+ if (mark->auto_fill_color)
+ go_marker_set_fill_color (mark->mark,
+ shape_is_fill_transparent [shape]
+ ? palette [palette_index] : 0);
+}
+
+static void
+map_area_series_solid_default (GogStyle *style, unsigned ind)
+{
+ static GOColor const palette [] = {
+ 0x9c9cffff, 0x9c3163ff, 0xffffceff, 0xceffffff, 0x630063ff,
+ 0xff8080ff, 0x0063ceff, 0xceceffff, 0x000080ff, 0xff00ffff,
+ 0xffff00ff, 0x00ffffff, 0x800080ff, 0x800000ff, 0x008080ff,
+ 0x0000ffff, 0x00ceffff, 0xceffffff, 0xceffceff, 0xffff9cff,
+ 0x9cceffff, 0xff9cceff, 0xce9cffff, 0xffce9cff, 0x3163ffff,
+ 0x31ceceff, 0x9cce00ff, 0xffce00ff, 0xff9c00ff, 0xff6300ff,
+ 0x63639cff, 0x949494ff, 0x003163ff, 0x319c63ff, 0x003100ff,
+ 0x313100ff, 0x9c3100ff, 0x9c3163ff, 0x31319cff, 0x313131ff,
+ 0xffffffff, 0xff0000ff, 0x00ff00ff, 0x0000ffff, 0xffff00ff,
+ 0xff00ffff, 0x00ffffff, 0x800000ff, 0x008000ff, 0x000080ff,
+ 0x808000ff, 0x800080ff, 0x008080ff, 0xc6c6c6ff, 0x808080ff
+ };
+
+ unsigned palette_index = ind;
+ if (palette_index >= G_N_ELEMENTS (palette))
+ palette_index %= G_N_ELEMENTS (palette);
+ if (style->fill.auto_back) {
+ style->fill.pattern.back = palette [palette_index];
+
+ /* force the brightness to reinterpolate */
+ if (style->fill.type == GOG_FILL_STYLE_GRADIENT &&
+ style->fill.gradient.brightness >= 0)
+ gog_style_set_fill_brightness (style,
+ style->fill.gradient.brightness);
+ }
+
+ palette_index += 8;
+ if (palette_index >= G_N_ELEMENTS (palette))
+ palette_index -= G_N_ELEMENTS (palette);
+ if (style->line.auto_color && !(style->disable_theming & GOG_STYLE_LINE))
+ style->line.color = palette [palette_index];
+ if (!(style->disable_theming & GOG_STYLE_MARKER))
+ map_marker (&style->marker, ind, palette_index, palette);
+}
+
+static void
+map_area_series_solid_guppi (GogStyle *style, unsigned ind)
+{
+ static GOColor const palette[] = {
+ 0xff3000ff, 0x80ff00ff, 0x00ffcfff, 0x2000ffff,
+ 0xff008fff, 0xffbf00ff, 0x00ff10ff, 0x009fffff,
+ 0xaf00ffff, 0xff0000ff, 0xafff00ff, 0x00ff9fff,
+ 0x0010ffff, 0xff00bfff, 0xff8f00ff, 0x20ff00ff,
+ 0x00cfffff, 0x8000ffff, 0xff0030ff, 0xdfff00ff,
+ 0x00ff70ff, 0x0040ffff, 0xff00efff, 0xff6000ff,
+ 0x50ff00ff, 0x00ffffff, 0x5000ffff, 0xff0060ff,
+ 0xffef00ff, 0x00ff40ff, 0x0070ffff, 0xdf00ffff
+ };
+
+ unsigned palette_index = ind;
+ if (palette_index >= G_N_ELEMENTS (palette))
+ palette_index %= G_N_ELEMENTS (palette);
+ if (style->fill.auto_back) {
+ style->fill.pattern.back = palette [palette_index];
+ if (style->fill.type == GOG_FILL_STYLE_GRADIENT &&
+ style->fill.gradient.brightness >= 0)
+ /* force the brightness to reinterpolate */
+ gog_style_set_fill_brightness (style,
+ style->fill.gradient.brightness);
+ }
+ if (style->line.auto_color && !(style->disable_theming & GOG_STYLE_LINE))
+ style->line.color = palette [palette_index];
+ if (!(style->disable_theming & GOG_STYLE_MARKER))
+ map_marker (&style->marker, ind, palette_index, palette);
+}
+
+/**************************************************************************/
+
+void
+gog_themes_init (void)
+{
+ GogTheme *theme;
+ GogStyle *style;
+
+ if (NULL == global_class_aliases) {
+ global_class_aliases = g_hash_table_new (
+ g_str_hash, g_str_equal);
+ g_hash_table_insert (global_class_aliases,
+ (gpointer)"GogSeriesElement", (gpointer)"GogSeries");
+ }
+
+/* TODO : have a look at apple's themes */
+/* An MS Excel-ish theme */
+ theme = gog_theme_new (N_("Default"));
+ gog_theme_register (theme, TRUE);
+
+ /* graph */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_NONE;
+ style->outline.width = 0;
+ style->outline.color = RGBA_BLACK;
+ style->fill.type = GOG_FILL_STYLE_NONE;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_WHITE);
+ gog_theme_add_element (theme, style, NULL, "GogGraph", NULL);
+
+ /* chart */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->outline.width = 0; /* hairline */
+ style->outline.color = RGBA_BLACK;
+ style->fill.type = GOG_FILL_STYLE_PATTERN;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_WHITE);
+ gog_theme_add_element (theme, style, NULL, "GogChart", NULL);
+
+ /* legend */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->outline.width = 0; /* hairline */
+ style->outline.color = RGBA_BLACK;
+ style->fill.type = GOG_FILL_STYLE_PATTERN;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_WHITE);
+ gog_theme_add_element (theme, style, NULL, "GogLegend", NULL);
+
+ /* Axis */
+ style = gog_style_new ();
+ style->line.width = 0; /* hairline */
+ style->line.color = RGBA_BLACK;
+ gog_theme_add_element (theme, style, NULL, "GogAxis", NULL);
+
+ /* Grid */
+ style = gog_style_new ();
+ style->fill.type = GOG_FILL_STYLE_PATTERN;
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->outline.width = 1.;
+ style->outline.color = 0X848284ff;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_GREY (0xd0));
+ gog_theme_add_element (theme, style, NULL, "GogGrid", NULL);
+
+ /* GridLine */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->line.width = 0.4;
+ style->line.color = RGBA_BLACK;
+ gog_theme_add_element (theme, style, NULL, NULL, "MajorGrid");
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->line.width = 0.2;
+ style->line.color = RGBA_BLACK;
+ gog_theme_add_element (theme, style, NULL, NULL, "MinorGrid");
+
+ /* series */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->outline.width = 0; /* hairline */
+ style->outline.color = RGBA_BLACK;
+ /* FIXME : not really true, will want to split area from line */
+ gog_theme_add_element (theme, style,
+ map_area_series_solid_default, "GogSeries", NULL);
+
+ /* labels */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_NONE;
+ style->outline.width = 0.;
+ style->outline.color = RGBA_BLACK;
+ style->fill.type = GOG_FILL_STYLE_NONE;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_WHITE);
+ gog_theme_add_element (theme, style, NULL, "GogLabel", NULL);
+
+/* Guppi */
+ theme = gog_theme_new (N_("Guppi"));
+ gog_theme_register (theme, FALSE);
+
+ /* graph */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_NONE;
+ style->outline.width = 0;
+ style->outline.color = RGBA_BLACK;
+ style->fill.type = GOG_FILL_STYLE_GRADIENT;
+ style->fill.gradient.dir = GO_GRADIENT_N_TO_S;
+ style->fill.pattern.fore = RGBA_BLUE;
+ style->fill.pattern.back = RGBA_BLACK;
+ gog_theme_add_element (theme, style, NULL, "GogGraph", NULL);
+
+ /* chart */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->outline.width = 0; /* hairline */
+ style->outline.color = RGBA_BLACK;
+ style->fill.type = GOG_FILL_STYLE_NONE;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_WHITE);
+ gog_theme_add_element (theme, style, NULL, "GogChart", NULL);
+
+ /* legend */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->outline.width = 0; /* hairline */
+ style->outline.color = RGBA_BLACK;
+ style->fill.type = GOG_FILL_STYLE_PATTERN;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_GREY (0x20));
+ gog_theme_add_element (theme, style, NULL, "GogLegend", NULL);
+
+ /* Axis */
+ style = gog_style_new ();
+ style->line.dash_type = GO_LINE_SOLID;
+ style->line.width = 0.; /* hairline */
+ style->line.color = RGBA_GREY (0x20);
+ gog_theme_add_element (theme, style, NULL, "GogAxis", NULL);
+
+ /* Grid */
+ style = gog_style_new ();
+ style->fill.type = GOG_FILL_STYLE_PATTERN;
+ style->outline.dash_type = GO_LINE_NONE;
+ style->outline.color = RGBA_BLACK;
+ style->outline.width = 0;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_GREY (0xd0));
+ gog_theme_add_element (theme, style, NULL, "GogGrid", NULL);
+
+ /* GridLine */
+ style = gog_style_new ();
+ style->line.dash_type = GO_LINE_SOLID;
+ style->line.width = 0.; /* hairline */
+ style->line.color = RGBA_GREY (0x96);
+ gog_theme_add_element (theme, style, NULL, NULL, "MajorGrid");
+ style = gog_style_new ();
+ style->line.dash_type = GO_LINE_SOLID;
+ style->line.width = 0.; /* hairline */
+ style->line.color = RGBA_GREY (0xC0);
+ gog_theme_add_element (theme, style, NULL, NULL, "MinorGrid");
+
+ /* series */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_SOLID;
+ style->outline.width = 0.; /* hairline */
+ style->outline.color = RGBA_BLACK;
+ style->fill.type = GOG_FILL_STYLE_PATTERN;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_GREY (0x20));
+ /* FIXME : not really true, will want to split area from line */
+ gog_theme_add_element (theme, style,
+ map_area_series_solid_guppi, "GogSeries", NULL);
+
+ /* labels */
+ style = gog_style_new ();
+ style->outline.dash_type = GO_LINE_NONE;
+ style->outline.width = 0; /* none */
+ style->outline.color = RGBA_BLACK;
+ style->fill.type = GOG_FILL_STYLE_NONE;
+ go_pattern_set_solid (&style->fill.pattern, RGBA_WHITE);
+ gog_theme_add_element (theme, style, NULL, "GogLabel", NULL);
+}
+
+void
+gog_themes_shutdown (void)
+{
+ GSList *ptr;
+
+ if (default_theme != NULL)
+ g_object_unref (default_theme);
+ for (ptr = themes; ptr != NULL ; ptr = ptr->next)
+ g_object_unref (ptr->data);
+}
+
--- /dev/null
+++ lib/goffice/graph/gog-data-allocator.h
@@ -0,0 +1,53 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-data-allocator.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_DATA_ALLOCATOR_H
+#define GOG_DATA_ALLOCATOR_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GTypeInterface base;
+
+ void (*allocate) (GogDataAllocator *a, GogPlot *plot);
+ gpointer (*editor) (GogDataAllocator *a, GogDataset *set,
+ int dim_i, GogDataType data_type);
+} GogDataAllocatorClass;
+
+#define GOG_DATA_ALLOCATOR_TYPE (gog_data_allocator_get_type ())
+#define GOG_DATA_ALLOCATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_DATA_ALLOCATOR_TYPE, GogDataAllocator))
+#define IS_GOG_DATA_ALLOCATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_DATA_ALLOCATOR_TYPE))
+#define GOG_DATA_ALLOCATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_DATA_ALLOCATOR_TYPE, GogDataAllocatorClass))
+#define IS_GOG_DATA_ALLOCATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_DATA_ALLOCATOR_TYPE))
+#define GOG_DATA_ALLOCATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GOG_DATA_ALLOCATOR_TYPE, GogDataAllocatorClass))
+
+GType gog_data_allocator_get_type (void);
+
+void gog_data_allocator_allocate (GogDataAllocator *a, GogPlot *plot);
+gpointer gog_data_allocator_editor (GogDataAllocator *dalloc, GogDataset *set,
+ int dim_i, GogDataType data_type);
+
+G_END_DECLS
+
+#endif /* GOG_DATA_ALLOCATOR_H */
--- /dev/null
+++ lib/goffice/graph/gog-style-prefs.glade
@@ -0,0 +1,1362 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="gog_style_prefs_window">
+ <property name="title" translatable="yes"></property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkHBox" id="gog_style_prefs">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child>
+ <widget class="GtkVBox" id="fill_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="fill_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Fill</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="fill_type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Type:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_type_menu</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="fill_type_menu">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">None
+Pattern
+Gradient
+Image</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="fill_notebook">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkLabel" id="label31">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="fill_pattern_table">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="fill_pattern_type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Pattern:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_pattern_foreground_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Foreground:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_pattern_background_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Background:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label17">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="fill_gradient_table">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">4</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="fill_gradient_direction_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Direction:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_gradient_start_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Start:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_gradient_type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">T_ype:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_gradient_type</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_gradient_end_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_End:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkComboBox" id="fill_gradient_type">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">2 Colors
+Brightness</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="fill_gradient_brightness_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_brighter</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_gradient_brightness</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHScale" id="fill_gradient_brightness">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_TOP</property>
+ <property name="digits">0</property>
+ <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>
+ <property name="inverted">False</property>
+ <property name="adjustment">0 0 100 10 10 10</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_darker</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_gradient_brightness</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label18">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="fill_image_table">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="fill_image_fit_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Fit:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_image_fit</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="fill_image_fit">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">stretched
+wallpaper</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="fill_image_select_picture">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Select...</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkImage" id="fill_image_sample">
+ <property name="width_request">100</property>
+ <property name="height_request">60</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="image-size-label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label19">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="outline_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="outline_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Outline</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="outline_table">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="outline_size_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Size:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">outline_size_spin</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="outline_size_spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 30 0.1 1 1</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label35">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">pts</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="outline_color_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Co_lor:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">St_yle:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="line_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="line_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Line</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="line_table">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="line_color_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Co_lor:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="line_size_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Size:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">line_size_spin</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="line_size_spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 30 0.1 1 1</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label34">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">pts</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">St_yle:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="marker_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="marker_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Marker</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment7">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="marker_table">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="marker_shape_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Sha_pe:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="marker_size_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Si_ze:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">marker_size_spin</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="marker_size_spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">5 1 200 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">pts</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="marker_outline_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">O_utline color:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="marker_fill_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Fill color:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-renderer-pixbuf.h
@@ -0,0 +1,42 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-pixbuf.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_RENDERER_PIXBUF_H
+#define GOG_RENDERER_PIXBUF_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GOG_RENDERER_PIXBUF_TYPE (gog_renderer_pixbuf_get_type ())
+#define GOG_RENDERER_PIXBUF(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RENDERER_PIXBUF_TYPE, GogRendererPixbuf))
+#define IS_GOG_RENDERER_PIXBUF(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RENDERER_PIXBUF_TYPE))
+
+typedef struct _GogRendererPixbuf GogRendererPixbuf;
+
+GType gog_renderer_pixbuf_get_type (void);
+GdkPixbuf *gog_renderer_pixbuf_get (GogRendererPixbuf *prend);
+gboolean gog_renderer_pixbuf_update (GogRendererPixbuf *prend, int w, int h, double zoom);
+
+G_END_DECLS
+
+#endif /* GOG_RENDERER_PIXBUF_H */
--- /dev/null
+++ lib/goffice/graph/go-data.c
@@ -0,0 +1,432 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-data.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/graph/go-data-impl.h>
+#include <goffice/utils/go-math.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#define GO_DATA_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_TYPE, GODataClass))
+#define IS_GO_DATA_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_TYPE))
+#define GO_DATA_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_TYPE, GODataClass))
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static gulong go_data_signals [LAST_SIGNAL] = { 0, };
+
+/* trivial fall back */
+static GOData *
+go_data_dup_real (GOData const *src)
+{
+ char *str = go_data_as_str (src);
+ GOData *dst = g_object_new (G_OBJECT_TYPE (src), NULL);
+ if (dst != NULL)
+ go_data_from_str (dst, str);
+ g_free (str);
+
+ return dst;
+}
+
+static void
+go_data_init (GOData *data)
+{
+ data->flags = 0;
+}
+
+#if 0
+static GObjectClass *parent_klass;
+static void
+go_data_finalize (GOData *obj)
+{
+ g_warning ("finalize");
+ (parent_klass->finalize) (obj);
+}
+#endif
+
+static void
+go_data_class_init (GODataClass *klass)
+{
+ go_data_signals [CHANGED] = g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GODataClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ klass->dup = go_data_dup_real;
+#if 0
+ {
+ GObjectClass *gobj_klass = (GObjectClass *)klass;
+ gobj_klass->finalize = go_data_finalize;
+ parent_klass = g_type_class_peek_parent (klass);
+ }
+#endif
+}
+
+GSF_CLASS_ABSTRACT (GOData, go_data,
+ go_data_class_init, go_data_init,
+ G_TYPE_OBJECT)
+
+/**
+ * go_data_dup :
+ * @src : #GOData
+ *
+ * A deep copy of @src.
+ **/
+GOData *
+go_data_dup (GOData const *src)
+{
+ if (src != NULL) {
+ GODataClass const *klass = GO_DATA_GET_CLASS (src);
+ g_return_val_if_fail (klass != NULL, NULL);
+ return (*klass->dup) (src);
+ }
+ return NULL;
+}
+
+/**
+ * go_data_eq :
+ * @a : #GOData
+ * @b : #GOData
+ *
+ * Returns TRUE if @a and @b are the same
+ **/
+gboolean
+go_data_eq (GOData const *a, GOData const *b)
+{
+ if (a == b)
+ return TRUE;
+ else {
+ GODataClass *a_klass = GO_DATA_GET_CLASS (a);
+ GODataClass *b_klass = GO_DATA_GET_CLASS (b);
+
+ g_return_val_if_fail (a_klass != NULL, FALSE);
+ g_return_val_if_fail (a_klass->eq != NULL, FALSE);
+
+ if (a_klass != b_klass)
+ return FALSE;
+
+ return (*a_klass->eq) (a, b);
+ }
+}
+
+/**
+ * go_data_prefered_fmt :
+ * @dat : #GOData
+ *
+ * Caller is responsible for unrefing the result.
+ * Returns the fmt preferred by the the data
+ **/
+GOFormat *
+go_data_preferred_fmt (GOData const *dat)
+{
+ GODataClass const *klass = GO_DATA_GET_CLASS (dat);
+ g_return_val_if_fail (klass != NULL, NULL);
+ if (klass->preferred_fmt)
+ return (*klass->preferred_fmt) (dat);
+ return NULL;
+}
+
+/**
+ * go_data_as_str :
+ * @dat : #GOData
+ *
+ * Return a string representation of the data source that the caller is
+ * responsible for freeing
+ *
+ * NOTE : This is the _source_ not the content.
+ **/
+char *
+go_data_as_str (GOData const *dat)
+{
+ GODataClass const *klass = GO_DATA_GET_CLASS (dat);
+ g_return_val_if_fail (klass != NULL, NULL);
+ return (*klass->as_str) (dat);
+}
+
+/**
+ * go_data_from_str :
+ * @dat : #GOData
+ * @str :
+ *
+ * De-serializes the source information returned from go_data_as_str.
+ * Returns FALSE on error.
+ **/
+gboolean
+go_data_from_str (GOData *dat, char const *str)
+{
+ GODataClass const *klass = GO_DATA_GET_CLASS (dat);
+ g_return_val_if_fail (klass != NULL, FALSE);
+ return (*klass->from_str) (dat, str);
+}
+
+/**
+ * go_data_emit_changed :
+ * @dat : #GOData
+ *
+ * protected utility to emit a 'changed' signal
+ **/
+void
+go_data_emit_changed (GOData *dat)
+{
+ GODataClass const *klass = GO_DATA_GET_CLASS (dat);
+
+ g_return_if_fail (klass != NULL);
+
+ if (klass->emit_changed)
+ (*klass->emit_changed) (dat);
+
+ g_signal_emit (G_OBJECT (dat), go_data_signals [CHANGED], 0);
+}
+
+/*************************************************************************/
+
+#define GO_DATA_SCALAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_SCALAR_TYPE, GODataScalarClass))
+#define IS_GO_DATA_SCALAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_SCALAR_TYPE))
+#define GO_DATA_SCALAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_SCALAR_TYPE, GODataScalarClass))
+
+GSF_CLASS_ABSTRACT (GODataScalar, go_data_scalar,
+ NULL, NULL,
+ GO_DATA_TYPE)
+
+double
+go_data_scalar_get_value (GODataScalar *scalar)
+{
+ GODataScalarClass const *klass = GO_DATA_SCALAR_GET_CLASS (scalar);
+ g_return_val_if_fail (klass != NULL, 0.); /* TODO : make this a nan */
+ return (*klass->get_value) (scalar);
+}
+
+char const *
+go_data_scalar_get_str (GODataScalar *scalar)
+{
+ GODataScalarClass const *klass = GO_DATA_SCALAR_GET_CLASS (scalar);
+ g_return_val_if_fail (klass != NULL, NULL);
+ return (*klass->get_str) (scalar);
+}
+
+/*************************************************************************/
+
+#define GO_DATA_VECTOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_VECTOR_TYPE, GODataVectorClass))
+#define IS_GO_DATA_VECTOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_VECTOR_TYPE))
+#define GO_DATA_VECTOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_VECTOR_TYPE, GODataVectorClass))
+
+static void
+go_data_vector_emit_changed (GOData *data)
+{
+ data->flags &= ~(GO_DATA_CACHE_IS_VALID | GO_DATA_VECTOR_LEN_CACHED);
+}
+static void
+go_data_vector_class_init (GODataClass *klass)
+{
+ klass->emit_changed = go_data_vector_emit_changed;
+}
+
+GSF_CLASS_ABSTRACT (GODataVector, go_data_vector,
+ go_data_vector_class_init, NULL,
+ GO_DATA_TYPE)
+
+int
+go_data_vector_get_len (GODataVector *vec)
+{
+ if (! (vec->base.flags & GO_DATA_VECTOR_LEN_CACHED)) {
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+
+ g_return_val_if_fail (klass != NULL, 0);
+
+ (*klass->load_len) (vec);
+
+ g_return_val_if_fail (vec->base.flags & GO_DATA_VECTOR_LEN_CACHED, 0);
+ }
+
+ return vec->len;
+}
+
+double *
+go_data_vector_get_values (GODataVector *vec)
+{
+ if (! (vec->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ (*klass->load_values) (vec);
+
+ g_return_val_if_fail (vec->base.flags & GO_DATA_CACHE_IS_VALID, NULL);
+ }
+
+ return vec->values;
+}
+
+double
+go_data_vector_get_value (GODataVector *vec, unsigned i)
+{
+ if (! (vec->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+ g_return_val_if_fail (klass != NULL, go_nan);
+ return (*klass->get_value) (vec, i);
+ }
+
+ g_return_val_if_fail ((int)i < vec->len, go_nan);
+ return vec->values [i];
+}
+
+char *
+go_data_vector_get_str (GODataVector *vec, unsigned i)
+{
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+ char *res;
+
+ g_return_val_if_fail (klass != NULL, g_strdup (""));
+ g_return_val_if_fail ((int)i < vec->len, g_strdup (""));
+
+ res = (*klass->get_str) (vec, i);
+ if (res == NULL)
+ return g_strdup ("");
+ return res;
+}
+
+void
+go_data_vector_get_minmax (GODataVector *vec, double *min, double *max)
+{
+ if (! (vec->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+
+ g_return_if_fail (klass != NULL);
+
+ (*klass->load_values) (vec);
+
+ g_return_if_fail (vec->base.flags & GO_DATA_CACHE_IS_VALID);
+ }
+
+ if (min != NULL)
+ *min = vec->minimum;
+ if (max != NULL)
+ *max = vec->maximum;
+}
+
+/*************************************************************************/
+
+#define GO_DATA_MATRIX_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_MATRIX_TYPE, GODataMatrixClass))
+#define IS_GO_DATA_MATRIX_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_MATRIX_TYPE))
+#define GO_DATA_MATRIX_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_MATRIX_TYPE, GODataMatrixClass))
+
+static void
+go_data_matrix_emit_changed (GOData *data)
+{
+ data->flags &= ~(GO_DATA_CACHE_IS_VALID | GO_DATA_MATRIX_SIZE_CACHED);
+}
+
+static void
+go_data_matrix_class_init (GODataClass *klass)
+{
+ klass->emit_changed = go_data_matrix_emit_changed;
+}
+
+GSF_CLASS_ABSTRACT (GODataMatrix, go_data_matrix,
+ go_data_matrix_class_init, NULL,
+ GO_DATA_TYPE)
+
+GOMatrixSize
+go_data_matrix_get_size (GODataMatrix *mat)
+{
+ if (! (mat->base.flags & GO_DATA_MATRIX_SIZE_CACHED)) {
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+ static GOMatrixSize null_size = {0, 0};
+
+ g_return_val_if_fail (klass != NULL, null_size);
+
+ (*klass->load_size) (mat);
+
+ g_return_val_if_fail (mat->base.flags & GO_DATA_MATRIX_SIZE_CACHED, null_size);
+ }
+
+ return mat->size;
+}
+
+double *
+go_data_matrix_get_values (GODataMatrix *mat)
+{
+ if (! (mat->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ (*klass->load_values) (mat);
+
+ g_return_val_if_fail (mat->base.flags & GO_DATA_CACHE_IS_VALID, NULL);
+ }
+
+ return mat->values;
+}
+
+double
+go_data_matrix_get_value (GODataMatrix *mat, unsigned i, unsigned j)
+{
+ if (! (mat->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+ g_return_val_if_fail (klass != NULL, go_nan);
+ return (*klass->get_value) (mat, i, j);
+ }
+
+ g_return_val_if_fail (((int)i < mat->size.rows) && ((int)j < mat->size.columns), go_nan);
+ return mat->values[i * mat->size.columns + j];
+}
+
+char *
+go_data_matrix_get_str (GODataMatrix *mat, unsigned i, unsigned j)
+{
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+ char *res;
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ res = (*klass->get_str) (mat, i, j);
+ if (res == NULL)
+ return g_strdup ("");
+ return res;
+}
+
+void
+go_data_matrix_get_minmax (GODataMatrix *mat, double *min, double *max)
+{
+ if (! (mat->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+
+ g_return_if_fail (klass != NULL);
+
+ (*klass->load_values) (mat);
+
+ g_return_if_fail (mat->base.flags & GO_DATA_CACHE_IS_VALID);
+ }
+
+ if (min != NULL)
+ *min = mat->minimum;
+ if (max != NULL)
+ *max = mat->maximum;
+}
--- /dev/null
+++ lib/goffice/graph/README
@@ -0,0 +1,30 @@
+GOffice Graph -- A graphing library based on gtk technologies
+Jody Goldberg <jody at gnome.org>
+
+ licensed under the terms of the GNU GPL included in the file COPYING.
+
+Terminology
+-----------
+
+graph == the parent which contains a set of charts layed out in tabular form
+chart == a rectangular region with axis, titles, and legends, and multiple
+ plots
+plot == a set of drawings, overlapping with a chart with axis assigned
+series == 1 displayable set of data of dimensionality related to its
+ associated plot type
+element == 1 'point' in a series
+
+Mailing lists
+-------------
+
+ There is a no mailing list to discuss only the graphing. For now discussion
+ will be on the Gnumeric list, to subscribe send a mail to:
+
+ gnumeric-list-request at gnome.org
+
+ And in the body of the message write "subscribe"
+
+ An archive of the mailing lists is available in:
+
+ http://www.gnome.org/mailing-lists/archives/gnumeric-list/
+
--- /dev/null
+++ lib/goffice/graph/gog-axis.c
@@ -0,0 +1,2453 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-axis.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-grid-line.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-label.h>
+#include <goffice/graph/gog-plot.h>
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-format.h>
+#include <goffice/utils/go-math.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+//#include <src/gui-util.h>
+//#include <src/format.h>
+//#include <src/widgets/widget-format-selector.h>
+#include <gui-util.h>
+#include <format.h>
+#include <widgets/widget-format-selector.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtkcheckbutton.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkcombobox.h>
+#include <gtk/gtkmisc.h>
+#include <glade/glade-xml.h>
+
+#include <string.h>
+
+struct _GogAxis {
+ GogStyledObject base;
+
+ GogAxisType type;
+ GogAxisPosition pos;
+ GSList *contributors;
+
+ GogDatasetElement source [AXIS_ELEM_MAX_ENTRY];
+ double auto_bound [AXIS_ELEM_MAX_ENTRY];
+ struct {
+ gboolean tick_in, tick_out;
+ int size_pts;
+ } major, minor;
+ gboolean major_tick_labeled;
+ gboolean inverted; /* apply to all map type */
+
+ double min_val, max_val;
+ double logical_min_val, logical_max_val;
+ gpointer min_contrib, max_contrib; /* NULL means use the manual sources */
+ gboolean is_discrete;
+ gboolean center_on_ticks;
+ GODataVector *labels;
+ GogPlot *plot_that_supplied_labels;
+ GOFormat *format, *assigned_format;
+
+ GogAxisMapDesc const *map_desc;
+
+ GogAxisTick *ticks;
+ unsigned tick_nbr;
+};
+
+typedef GogStyledObjectClass GogAxisClass;
+
+static GType gog_axis_view_get_type (void);
+
+static GObjectClass *parent_klass;
+
+enum {
+ AXIS_PROP_0,
+ AXIS_PROP_TYPE,
+ AXIS_PROP_POS,
+ AXIS_PROP_POS_STR,
+ AXIS_PROP_INVERT,
+ AXIS_PROP_MAP,
+ AXIS_PROP_MAJOR_TICK_LABELED,
+ AXIS_PROP_MAJOR_TICK_IN,
+ AXIS_PROP_MAJOR_TICK_OUT,
+ AXIS_PROP_MAJOR_TICK_SIZE_PTS,
+ AXIS_PROP_MINOR_TICK_IN,
+ AXIS_PROP_MINOR_TICK_OUT,
+ AXIS_PROP_MINOR_TICK_SIZE_PTS,
+ AXIS_PROP_ASSIGNED_FORMAT_STR_XL
+};
+
+#define TICK_LABEL_PAD_VERT 0
+#define TICK_LABEL_PAD_HORIZ 1
+
+#define GOG_AXIS_MAX_TICK_NBR 1000
+#define GOG_AXIS_LOG_AUTO_MAX_MAJOR_TICK_NBR 8
+#define GOG_AXIS_DISCRETE_AUTO_MAX_MAJOR_TICK_NBR 20
+
+static void gog_axis_set_ticks (GogAxis *axis,int tick_nbr, GogAxisTick *ticks);
+
+static GogAxisTick *
+get_adjusted_tick_array (GogAxisTick *ticks, int allocated_size, int exact_size)
+{
+ GogAxisTick *tmp_ticks;
+
+ if (exact_size > 0) {
+ if (exact_size != allocated_size) {
+ tmp_ticks = g_new (GogAxisTick, exact_size);
+ memcpy (tmp_ticks, ticks, sizeof (GogAxisTick) * exact_size);
+ g_free (ticks);
+ } else
+ tmp_ticks = ticks;
+ } else {
+ g_free (ticks);
+ tmp_ticks = NULL;
+ }
+
+ return tmp_ticks;
+}
+
+static GogAxisTick *
+create_invalid_axis_ticks (double min, double max, gboolean draw_labels) {
+ GogAxisTick *ticks;
+
+ ticks = g_new (GogAxisTick, 2);
+ ticks[0].position = min;
+ ticks[1].position = max;
+ ticks[0].type = ticks[1].type = GOG_AXIS_TICK_MAJOR;
+ ticks[0].label = draw_labels ? g_strdup ("##") : NULL;
+ ticks[1].label = draw_labels ? g_strdup ("##") : NULL;
+
+ return ticks;
+}
+
+/*
+ * Discrete mapping
+ */
+
+typedef struct
+{
+ double min;
+ double max;
+ double scale;
+ double a;
+ double b;
+} MapData;
+
+static gboolean
+map_discrete_init (GogAxisMap *map, double offset, double length)
+{
+ MapData *data;
+
+ map->data = g_new (MapData, 1);
+ data = map->data;
+
+ if (gog_axis_get_bounds (map->axis, &data->min, &data->max)) {
+ data->scale = (map->axis->center_on_ticks)?
+ 1.0 / (data->max - data->min - 1):
+ 1.0 / (data->max - data->min);
+ data->a = data->scale * length;
+ data->b = offset - data->a * data->min;
+ return TRUE;
+ }
+ data->min = 0.0;
+ data->max = 1.0;
+ data->scale = 1.0;
+ data->a = length;
+ data->b = offset;
+ return FALSE;
+}
+
+static double
+map_discrete (GogAxisMap *map, double value)
+{
+ MapData *data = map->data;
+
+ return (value - data->min) * data->scale;
+}
+
+static double
+map_discrete_to_canvas (GogAxisMap *map, double value, gboolean inverted)
+{
+ MapData *data = map->data;
+
+ return inverted ?
+ ((map->axis->center_on_ticks)?
+ (data->min + data->max - 1 - value) * data->a + data->b :
+ (data->min + data->max - value) * data->a + data->b) :
+ value * data->a + data->b;
+}
+
+static void
+map_discrete_auto_bound (GogAxis *axis,
+ double minimum,
+ double maximum,
+ double *bound)
+{
+ if ((maximum - minimum) > GOG_AXIS_DISCRETE_AUTO_MAX_MAJOR_TICK_NBR)
+ bound [AXIS_ELEM_MAJOR_TICK] =
+ bound [AXIS_ELEM_MINOR_TICK] =
+ ceil ((maximum - minimum + 1.0) /
+ (double) GOG_AXIS_DISCRETE_AUTO_MAX_MAJOR_TICK_NBR);
+ else
+ bound [AXIS_ELEM_MAJOR_TICK] =
+ bound [AXIS_ELEM_MINOR_TICK] = 1.;
+
+ bound [AXIS_ELEM_CROSS_POINT] = 1.;
+ bound [AXIS_ELEM_MIN] = minimum;
+ bound [AXIS_ELEM_MAX] = maximum;
+}
+
+static void
+map_discrete_calc_ticks (GogAxis *axis,
+ gboolean draw_labels)
+{
+ GogAxisTick *ticks;
+ gboolean valid;
+ double maximum, minimum;
+ int tick_nbr;
+ int i, count;
+ int major_tick, major_label;
+
+ major_tick = rint (gog_axis_get_entry (axis, AXIS_ELEM_MAJOR_TICK, NULL));
+ major_label = rint (gog_axis_get_entry (axis, AXIS_ELEM_MINOR_TICK, NULL));
+ if (major_tick < 1)
+ major_tick = 1;
+ if (major_label < 1)
+ major_label = 1;
+
+ valid = gog_axis_get_bounds (axis, &minimum, &maximum);
+ if (!valid) {
+ gog_axis_set_ticks (axis, 2, create_invalid_axis_ticks (0.0, 1.0, draw_labels));
+ return;
+ }
+
+ tick_nbr = rint (maximum -minimum) + 1;
+ if (axis->center_on_ticks)
+ tick_nbr--;
+ if (tick_nbr < 1) {
+ gog_axis_set_ticks (axis, 0, NULL);
+ return;
+ }
+ ticks = g_new (GogAxisTick, tick_nbr);
+
+ count = 0;
+ for (i = 0; i < tick_nbr; i++) {
+ ticks[count].position = (double) (i);
+ ticks[count].type = GOG_AXIS_TICK_MINOR;
+ ticks[count].label = NULL;
+
+ if (i % major_tick == 0)
+ ticks[count].type = GOG_AXIS_TICK_MAJOR;
+
+ /* Minimum >= .0 test is a trick to know if it's a barcol or an area/line plot */
+ if (i == 0 && minimum >=.0 && !axis->center_on_ticks)
+ ticks[count].type = GOG_AXIS_TICK_NONE;
+ if ((i % major_label == 0) && draw_labels &&
+ (i < tick_nbr - 1 || minimum >= .0)) {
+ if (axis->labels != NULL) {
+ if (i < go_data_vector_get_len (axis->labels))
+ ticks[count].label = go_data_vector_get_str (axis->labels, i);
+ else
+ ticks[count].label = NULL;
+ }
+ else
+ ticks[count].label = g_strdup_printf ("%d", i + 1);
+ }
+
+ count++;
+ }
+
+ ticks = get_adjusted_tick_array (ticks, tick_nbr, count);
+ gog_axis_set_ticks (axis, count, ticks);
+}
+
+/*
+ * Linear mapping
+ */
+
+static gboolean
+map_linear_init (GogAxisMap *map, double offset, double length)
+{
+ MapData *data;
+
+ map->data = g_new (MapData, 1);
+ data = (MapData *) map->data;
+
+ if (gog_axis_get_bounds (map->axis, &data->min, &data->max)) {
+ data->scale = 1 / (data->max - data->min);
+ data->a = data->scale * length;
+ data->b = offset - data->a * data->min;
+ return TRUE;
+ }
+ data->min = 0.0;
+ data->max = 1.0;
+ data->scale = 1.0;
+ data->a = length;
+ data->b = offset;
+ return FALSE;
+}
+
+static double
+map_linear (GogAxisMap *map, double value)
+{
+ MapData *data = map->data;
+
+ return (value - data->min) * data->scale;
+}
+
+static double
+map_linear_to_canvas (GogAxisMap *map, double value, gboolean inverted)
+{
+ MapData *data = map->data;
+
+ return inverted ?
+ (data->min + data->max - value) * data->a + data->b :
+ value * data->a + data->b;
+}
+
+static void
+map_linear_auto_bound (GogAxis *axis, double minimum, double maximum, double *bound)
+{
+ double step, range, mant;
+ int expon;
+
+ range = fabs (maximum - minimum);
+
+ /* handle singletons */
+ if (go_sub_epsilon (range) <= 0.) {
+ if (maximum > 0)
+ minimum = 0.;
+ else if (minimum < 0.)
+ maximum = 0.;
+ else {
+ maximum = 1;
+ minimum = 0;
+ }
+
+ range = fabs (maximum - minimum);
+ }
+
+ step = pow (10, go_fake_floor (log10 (range)));
+ if (range/step < 1.6)
+ step /= 5.; /* .2 .4 .6 */
+ else if (range/step < 3)
+ step /= 2.; /* 0 5 10 */
+ else if (range/step > 8)
+ step *= 2.; /* 2 4 6 */
+
+ /* we want the bounds to be loose so jump up a step if we get too close */
+ mant = frexp (minimum / step, &expon);
+ bound [AXIS_ELEM_MIN] = step * floor (ldexp (mant - DBL_EPSILON, expon));
+ mant = frexp (maximum / step, &expon);
+ bound [AXIS_ELEM_MAX] = step * ceil (ldexp (mant + DBL_EPSILON, expon));
+ bound [AXIS_ELEM_MAJOR_TICK] = step;
+ bound [AXIS_ELEM_MINOR_TICK] = step / 5.;
+
+ /* pull to zero if its nearby (do not pull both directions to 0) */
+ if (bound [AXIS_ELEM_MIN] > 0 &&
+ (bound [AXIS_ELEM_MIN] - 10. * step) < 0)
+ bound [AXIS_ELEM_MIN] = 0;
+ else if (bound [AXIS_ELEM_MAX] < 0 &&
+ (bound [AXIS_ELEM_MAX] + 10. * step) < 0)
+ bound [AXIS_ELEM_MAX] = 0;
+
+ /* The epsilon shift can pull us away from a zero we want to
+ * keep (eg percentage bars withno negative elements) */
+ if (bound [AXIS_ELEM_MIN] < 0 && minimum >= 0.)
+ bound [AXIS_ELEM_MIN] = 0;
+ else if (bound [AXIS_ELEM_MAX] > 0 && maximum <= 0.)
+ bound [AXIS_ELEM_MAX] = 0;
+}
+
+static void
+map_linear_calc_ticks (GogAxis *axis,
+ gboolean draw_labels)
+{
+ GogAxisTick *ticks;
+ double maximum, minimum;
+ double tick_step;
+ double major_tick, minor_tick;
+ int tick_nbr, ratio, i;
+
+ major_tick = gog_axis_get_entry (axis, AXIS_ELEM_MAJOR_TICK, NULL);
+ minor_tick = gog_axis_get_entry (axis, AXIS_ELEM_MINOR_TICK, NULL);
+ if (minor_tick < major_tick)
+ tick_step = minor_tick;
+ else
+ tick_step = major_tick;
+ ratio = rint (major_tick / tick_step);
+
+ if (!gog_axis_get_bounds (axis, &minimum, &maximum)) {
+ gog_axis_set_ticks (axis, 2, create_invalid_axis_ticks (0.0, 1.0, draw_labels));
+ return;
+ }
+
+ tick_nbr = floor (go_add_epsilon ((maximum - minimum) / tick_step + 1.0));
+ if (tick_nbr < 1 || tick_nbr > GOG_AXIS_MAX_TICK_NBR) {
+ gog_axis_set_ticks (axis, 0, NULL);
+ return;
+ }
+ ticks = g_new0 (GogAxisTick, tick_nbr);
+
+ for (i = 0; i < tick_nbr; i++) {
+ ticks[i].position = minimum + (double) i * tick_step;
+ if (fabs (ticks[i].position) < tick_step / 1E10)
+ ticks[i].position = 0.0;
+ if (rint (fmod (i, ratio)) == 0) {
+ ticks[i].type = GOG_AXIS_TICK_MAJOR;
+ if (draw_labels) {
+ if (axis->assigned_format == NULL ||
+ style_format_is_general (axis->assigned_format))
+ ticks[i].label = go_format_value (axis->format, ticks[i].position);
+ else
+ ticks[i].label = go_format_value (axis->assigned_format, ticks[i].position);
+ }
+ else
+ ticks[i].label = NULL;
+ }
+ else {
+ ticks[i].type = GOG_AXIS_TICK_MINOR;
+ ticks[i].label = NULL;
+ }
+ }
+ gog_axis_set_ticks (axis, tick_nbr, ticks);
+}
+
+/*
+ * Logarithmic mapping
+ */
+
+typedef struct
+{
+ double min;
+ double max;
+ double scale;
+ double a;
+ double b;
+ double a_inv;
+ double b_inv;
+} MapLogData;
+
+static gboolean
+map_log_init (GogAxisMap *map, double offset, double length)
+{
+ MapLogData *data;
+
+ map->data = g_new (MapLogData, 1);
+ data = map->data;
+
+ if (gog_axis_get_bounds (map->axis, &data->min, &data->max))
+ if (data->min > 0.0) {
+ data->min = log (data->min);
+ data->max = log (data->max);
+ data->scale = 1/ (data->max - data->min);
+ data->a = data->scale * length;
+ data->b = offset - data->a * data->min;
+ data->a_inv = -data->scale * length;
+ data->b_inv = offset + length - data->a_inv * data->min;
+ return TRUE;
+ }
+
+ data->min = 0.0;
+ data->max = log (10.0);
+ data->scale = 1 / log(10.0);
+ data->a = data->scale * length;
+ data->b = offset;
+ data->a_inv = -data->scale * length;
+ data->b_inv = offset + length;
+
+ return FALSE;
+}
+
+static double
+map_log (GogAxisMap *map, double value)
+{
+ MapLogData *data = map->data;
+
+ return (log (value) - data->min) * data->scale;
+}
+
+static double
+map_log_to_canvas (GogAxisMap *map, double value, gboolean inverted)
+{
+ MapLogData *data = map->data;
+ double result;
+
+ if (value <= 0.)
+ /* Make libart happy */
+ result = inverted ? -DBL_MAX : DBL_MAX;
+ else
+ result = inverted ?
+ log (value) * data->a_inv + data->b_inv :
+ log (value) * data->a + data->b;
+
+ return result;
+}
+
+static void
+map_log_auto_bound (GogAxis *axis, double minimum, double maximum, double *bound)
+{
+ double step;
+
+ if (maximum <= 0.0)
+ maximum = 1.0;
+ if (minimum <= 0.0)
+ minimum = maximum / 100.0;
+ if (maximum < minimum)
+ maximum = minimum * 100.0;
+
+ maximum = ceil (log10 (maximum));
+ minimum = floor (log10 (minimum));
+
+ step = ceil ((maximum - minimum + 1.0) /
+ (double) GOG_AXIS_LOG_AUTO_MAX_MAJOR_TICK_NBR);
+
+ bound [AXIS_ELEM_MIN] = pow ( 10.0, minimum);
+ bound [AXIS_ELEM_MAX] = pow ( 10.0, maximum);
+ bound [AXIS_ELEM_MAJOR_TICK] = step;
+ bound [AXIS_ELEM_MINOR_TICK] = 8;
+}
+
+static void
+map_log_calc_ticks (GogAxis *axis,
+ gboolean draw_labels)
+{
+ GogAxisTick *ticks;
+ double maximum, minimum;
+ double position;
+ int major_tick, minor_tick, major_label, start_tick;
+ int tick_nbr, i, j;
+ int count;
+
+ major_label = rint (gog_axis_get_entry (axis, AXIS_ELEM_MAJOR_TICK, NULL));
+ minor_tick = rint (gog_axis_get_entry (axis, AXIS_ELEM_MINOR_TICK, NULL) + 1.0);
+
+ if (!gog_axis_get_bounds (axis, &minimum, &maximum) || major_label < 1) {
+ gog_axis_set_ticks (axis, 2, create_invalid_axis_ticks (1.0, 10.0, draw_labels));
+ return;
+ }
+ if (minimum <= 0.0) {
+ gog_axis_set_ticks (axis, 2, create_invalid_axis_ticks (1.0, 10.0, draw_labels));
+ return;
+ }
+
+ start_tick = ceil (log10 (minimum));
+ tick_nbr = major_tick = ceil (ceil (log10 (maximum)) - floor (log10 (minimum)) + 1.0);
+ tick_nbr *= minor_tick;
+ if (tick_nbr < 1 || tick_nbr > GOG_AXIS_MAX_TICK_NBR) {
+ gog_axis_set_ticks (axis, 0, NULL);
+ return;
+ }
+ ticks = g_new0 (GogAxisTick, tick_nbr);
+
+ count = 0;
+ for (i = 0; i < major_tick; i++) {
+ position = pow (10.0, i + start_tick);
+ if (position >= go_sub_epsilon (minimum) && go_sub_epsilon (position) <= maximum) {
+ ticks[count].position = position;
+ if ((i) % major_label == 0 && draw_labels) {
+ ticks[count].type = GOG_AXIS_TICK_MAJOR;
+ if (axis->assigned_format == NULL ||
+ style_format_is_general (axis->assigned_format))
+ ticks[count].label = go_format_value (axis->format, ticks[count].position);
+ else
+ ticks[count].label = go_format_value (axis->assigned_format, ticks[count].position);
+ count++;
+ }
+ else {
+ ticks[count].type = GOG_AXIS_TICK_MINOR;
+ ticks[count].label = NULL;
+ count++;
+ }
+ }
+ for (j = 1; j < minor_tick; j++) {
+ position = pow (10.0, i + start_tick) * (9.0 / (double)minor_tick * (double) j + 1.0);
+ if (position >= go_sub_epsilon (minimum) && go_sub_epsilon (position) <= maximum) {
+ ticks[count].position = position;
+ ticks[count].type = GOG_AXIS_TICK_MINOR;
+ ticks[count].label = NULL;
+ count++;
+ }
+ }
+ }
+
+ ticks = get_adjusted_tick_array (ticks, tick_nbr, count);
+ gog_axis_set_ticks (axis, count, ticks);
+}
+
+static const GogAxisMapDesc map_desc_discrete =
+{
+ map_discrete, map_discrete_to_canvas,
+ map_discrete_init, NULL,
+ map_discrete_auto_bound, map_discrete_calc_ticks,
+ N_("Discrete"), N_("Discrete mapping")
+};
+
+static const GogAxisMapDesc map_descs[] =
+{
+ {
+ map_linear, map_linear_to_canvas,
+ map_linear_init, NULL,
+ map_linear_auto_bound, map_linear_calc_ticks,
+ N_("Linear"), N_("Linear mapping")
+ },
+ {
+ map_log, map_log_to_canvas,
+ map_log_init, NULL,
+ map_log_auto_bound, map_log_calc_ticks,
+ N_("Log"), N_("Logarithm mapping")
+ }
+};
+
+static void
+gog_axis_map_set_by_num (GogAxis *axis, unsigned num)
+{
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ if (num >= 0 && num < G_N_ELEMENTS (map_descs))
+ g_object_set (G_OBJECT (axis), "map-name", map_descs[num].name, NULL);
+ else
+ g_object_set (G_OBJECT (axis), "map-name", "", NULL);
+}
+
+static void
+gog_axis_map_populate_combo (GogAxis *axis, GtkComboBox *combo)
+{
+ unsigned i;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (map_descs); i++) {
+ gtk_combo_box_append_text (combo, _(map_descs[i].name));
+ if (!g_ascii_strcasecmp (map_descs[i].name,
+ axis->map_desc->name))
+ gtk_combo_box_set_active (combo, i);
+ }
+}
+
+static void
+gog_axis_map_set (GogAxis *axis, char const *name)
+{
+ unsigned i, map = 0;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ if (name != NULL)
+ for (i = 0; i < G_N_ELEMENTS(map_descs); i++)
+ if (!g_ascii_strcasecmp (name, map_descs[i].name)) {
+ map = i;
+ break;
+ }
+
+ axis->map_desc = &map_descs[map];
+}
+
+/**
+ * gog_axis_map_is_valid
+ * @axis : #GogAxis
+ *
+ * Return TRUE if map is valid, ie bounds are valid.
+ **/
+
+gboolean
+gog_axis_map_is_valid (GogAxisMap *map)
+{
+ g_return_val_if_fail (map != NULL, FALSE);
+
+ return map->is_valid;
+}
+
+/**
+ * gog_axis_map_new :
+ * @axis : #GogAxis
+ * @offset : start of plot area.
+ * @length : length of plot area.
+ *
+ * Return a new GogAxisMap for data mapping to plot window.
+ * offset and length are optional parameters to be used with
+ * gog_axis_map_to_canvas in order to translates data coordinates
+ * into canvas space.
+ **/
+
+GogAxisMap *
+gog_axis_map_new (GogAxis *axis, double offset, double length)
+{
+ GogAxisMap *map;
+
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, NULL);
+
+ map = g_new0 (GogAxisMap, 1);
+
+ g_object_ref (axis);
+ map->desc = axis->is_discrete ? &map_desc_discrete : axis->map_desc;
+ map->axis = axis;
+ map->data = NULL;
+ map->is_valid = FALSE;
+
+ if (map->desc->init != NULL)
+ map->is_valid = map->desc->init (map, offset, length);
+
+ return map;
+}
+
+/**
+ * gog_axis_map :
+ * @map : #GogAxisMap
+ * value : value to map to plot space.
+ *
+ * Return a value where [0,1.0] means a data within plot
+ * bounds.
+ * */
+
+double
+gog_axis_map (GogAxisMap *map,
+ double value)
+{
+ return (map->axis->inverted ?
+ 1.0 - map->desc->map (map, value) :
+ map->desc->map (map, value));
+}
+
+/**
+ * gog_axis_map_to_canvas :
+ * @map : #GogAxisMap
+ * @value : value to map to canvas space.
+ *
+ * Return a value in canvas coordinates, where
+ * [offset,offset+length] means a data within plot bounds.
+ **/
+
+double
+gog_axis_map_to_canvas (GogAxisMap *map,
+ double value)
+{
+ return map->desc->map_to_canvas (map, value, map->axis->inverted);
+}
+
+/**
+ * gog_axis_map_free :
+ * @map : #GogAxisMap
+ *
+ * Free GogAxisMap object.
+ **/
+
+void
+gog_axis_map_free (GogAxisMap *map)
+{
+ g_return_if_fail (map != NULL);
+
+ if (map->desc->destroy != NULL)
+ map->desc->destroy (map);
+
+ g_object_unref (map->axis);
+ g_free (map->data);
+ g_free (map);
+}
+
+static void
+gog_axis_auto_bound (GogAxis *axis)
+{
+ double maximum, minimum;
+ double tmp;
+ gboolean user_defined;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ minimum = axis->min_val;
+ maximum = axis->max_val;
+
+ tmp = gog_axis_get_entry (axis, AXIS_ELEM_MIN, &user_defined);
+ if (user_defined) minimum = tmp;
+
+ tmp = gog_axis_get_entry (axis, AXIS_ELEM_MAX, &user_defined);
+ if (user_defined) maximum = tmp;
+
+ if (axis->is_discrete)
+ map_desc_discrete.auto_bound (axis, minimum, maximum, axis->auto_bound);
+ else
+ if (axis->map_desc->auto_bound)
+ axis->map_desc->auto_bound (axis, minimum, maximum, axis->auto_bound);
+}
+
+static void
+gog_axis_set_ticks (GogAxis *axis, int tick_nbr, GogAxisTick *ticks)
+{
+ unsigned i;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ if (axis->ticks != NULL) {
+ for (i = 0; i < axis->tick_nbr; i++)
+ g_free (axis->ticks[i].label);
+
+ g_free (axis->ticks);
+ }
+
+ axis->tick_nbr = tick_nbr;
+ axis->ticks = ticks;
+}
+
+static void
+gog_axis_calc_ticks (GogAxis *axis)
+{
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ if (axis->is_discrete)
+ map_desc_discrete.calc_ticks (axis,
+ axis->major_tick_labeled);
+ else
+ if (axis->map_desc->calc_ticks)
+ axis->map_desc->calc_ticks (axis,
+ axis->major_tick_labeled);
+}
+
+/*****************************************************************************/
+
+static void
+role_label_post_add (GogObject *parent, GogObject *label)
+{
+ GogAxis const *axis = GOG_AXIS (parent);
+ if (axis->pos == GOG_AXIS_AT_LOW)
+ label->position = (axis->type == GOG_AXIS_X)
+ ? (GOG_POSITION_S|GOG_POSITION_ALIGN_CENTER)
+ : (GOG_POSITION_W|GOG_POSITION_ALIGN_CENTER);
+ else
+ label->position = (axis->type == GOG_AXIS_X)
+ ? (GOG_POSITION_N|GOG_POSITION_ALIGN_CENTER)
+ : (GOG_POSITION_E|GOG_POSITION_ALIGN_CENTER);
+}
+
+static gboolean
+role_grid_line_can_add (GogObject const *parent, char const *type)
+{
+ GSList *children;
+
+ children = gog_object_get_children (parent,
+ gog_object_find_role_by_name (GOG_OBJECT (parent), type));
+ if (children != NULL) {
+ g_slist_free (children);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+role_grid_line_major_can_add (GogObject const *parent)
+{
+ GogAxis *axis = GOG_AXIS (parent);
+ GogAxisType type = gog_axis_get_atype (axis);
+
+ return ((type == GOG_AXIS_X || type == GOG_AXIS_Y || type == GOG_AXIS_RADIAL) &&
+ role_grid_line_can_add (parent, "MajorGrid"));
+}
+
+static gboolean
+role_grid_line_minor_can_add (GogObject const *parent)
+{
+ GogAxis *axis = GOG_AXIS (parent);
+ GogAxisType type = gog_axis_get_atype (axis);
+
+ return (!gog_axis_is_discrete (GOG_AXIS (parent)) &&
+ (type == GOG_AXIS_X || type == GOG_AXIS_Y || type == GOG_AXIS_RADIAL) &&
+ role_grid_line_can_add (parent, "MinorGrid"));
+}
+
+static void
+role_grid_line_major_post_add (GogObject *parent, GogObject *child)
+{
+ g_object_set (G_OBJECT (child), "is_minor", (gboolean)FALSE, NULL);
+}
+
+static void
+role_grid_line_minor_post_add (GogObject *parent, GogObject *child)
+{
+ g_object_set (G_OBJECT (child), "is_minor", (gboolean)TRUE, NULL);
+}
+
+static gboolean
+gog_axis_set_pos (GogAxis *axis, GogAxisPosition pos)
+{
+ GSList *ptr;
+ if (axis->pos == pos)
+ return FALSE;
+ axis->pos = pos;
+
+ for (ptr = GOG_OBJECT (axis)->children ; ptr != NULL ; ptr = ptr->next)
+ if (IS_GOG_LABEL (ptr->data))
+ role_label_post_add (GOG_OBJECT (axis), ptr->data);
+ return TRUE;
+}
+
+/**
+ * gog_axis_set_format :
+ * @axis : #GogAxis
+ * @fmt : #GOFormat
+ *
+ * Absorbs a reference to @fmt, and accepts NULL.
+ * returns TRUE if things changed
+ **/
+static gboolean
+gog_axis_set_format (GogAxis *axis, GOFormat *fmt)
+{
+ if (go_format_eq (fmt, axis->assigned_format)) {
+ go_format_unref (fmt);
+ return FALSE;
+ }
+ if (axis->assigned_format != NULL)
+ go_format_unref (axis->assigned_format);
+ axis->assigned_format = fmt;
+ return TRUE;
+}
+
+static void
+gog_axis_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogAxis *axis = GOG_AXIS (obj);
+ gboolean resized = FALSE;
+ gboolean calc_ticks = FALSE;
+ gboolean request_update = FALSE;
+ int itmp;
+
+ switch (param_id) {
+ case AXIS_PROP_TYPE:
+ itmp = g_value_get_int (value);
+ if (axis->type != itmp) {
+ axis->type = itmp;
+ resized = TRUE;
+ }
+ break;
+ case AXIS_PROP_POS:
+ resized = gog_axis_set_pos (axis, g_value_get_int (value));
+ break;
+ case AXIS_PROP_POS_STR: {
+ char const *str = g_value_get_string (value);
+ if (str == NULL)
+ return;
+ else if (!g_ascii_strcasecmp (str, "low"))
+ itmp = GOG_AXIS_AT_LOW;
+ else if (!g_ascii_strcasecmp (str, "middle"))
+ itmp = GOG_AXIS_IN_MIDDLE;
+ else if (!g_ascii_strcasecmp (str, "high"))
+ itmp = GOG_AXIS_AT_HIGH;
+ else
+ return;
+ resized = gog_axis_set_pos (axis, itmp);
+ break;
+ }
+ case AXIS_PROP_INVERT:
+ axis->inverted = g_value_get_boolean (value);
+ calc_ticks = TRUE;
+ break;
+ case AXIS_PROP_MAP :
+ gog_axis_map_set (axis, g_value_get_string (value));
+ request_update = TRUE;
+ break;
+
+ case AXIS_PROP_MAJOR_TICK_LABELED:
+ itmp = g_value_get_boolean (value);
+ if (axis->major_tick_labeled != itmp) {
+ axis->major_tick_labeled = itmp;
+ resized = TRUE;
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MAJOR_TICK_IN :
+ itmp = g_value_get_boolean (value);
+ if (axis->major.tick_in != itmp) {
+ axis->major.tick_in = itmp;
+ if (itmp != axis->major.tick_out)
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MAJOR_TICK_OUT :
+ itmp = g_value_get_boolean (value);
+ if (axis->major.tick_out != itmp) {
+ axis->major.tick_out = itmp;
+ resized = axis->major.size_pts > 0;
+ if (itmp != axis->major.tick_in)
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MAJOR_TICK_SIZE_PTS:
+ itmp = g_value_get_int (value);
+ if (axis->major.size_pts != itmp) {
+ axis->major.size_pts = itmp;
+ resized = axis->major.tick_out;
+ }
+ break;
+
+ case AXIS_PROP_MINOR_TICK_IN :
+ itmp = g_value_get_boolean (value);
+ if (axis->minor.tick_in != itmp) {
+ axis->minor.tick_in = itmp;
+ if (itmp != axis->minor.tick_out)
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MINOR_TICK_OUT :
+ itmp = g_value_get_boolean (value);
+ if (axis->minor.tick_out != itmp) {
+ axis->minor.tick_out = itmp;
+ resized = axis->minor.size_pts > 0;
+ if (itmp != axis->minor.tick_in)
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MINOR_TICK_SIZE_PTS:
+ itmp = g_value_get_int (value);
+ if (axis->minor.size_pts != itmp) {
+ axis->minor.size_pts = itmp;
+ resized = axis->minor.tick_out;
+ }
+ break;
+ case AXIS_PROP_ASSIGNED_FORMAT_STR_XL : {
+ char const *str = g_value_get_string (value);
+ resized = gog_axis_set_format (axis, (str != NULL)
+ ? go_format_new_from_XL (str, FALSE)
+ : NULL);
+ calc_ticks = resized;
+ break;
+ }
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ if (request_update)
+ gog_object_request_update (GOG_OBJECT (axis));
+ else {
+ if (calc_ticks)
+ gog_axis_calc_ticks (axis);
+ gog_object_emit_changed (GOG_OBJECT (obj), resized);
+ }
+}
+
+static void
+gog_axis_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogAxis const *axis = GOG_AXIS (obj);
+
+ switch (param_id) {
+ case AXIS_PROP_TYPE:
+ g_value_set_int (value, axis->type);
+ break;
+ case AXIS_PROP_POS:
+ g_value_set_int (value, axis->pos);
+ break;
+ case AXIS_PROP_POS_STR:
+ switch (axis->pos) {
+ case GOG_AXIS_AT_LOW:
+ g_value_set_static_string (value, "low");
+ break;
+ case GOG_AXIS_IN_MIDDLE:
+ g_value_set_static_string (value, "middle");
+ break;
+ case GOG_AXIS_AT_HIGH:
+ g_value_set_static_string (value, "high");
+ break;
+ }
+ break;
+ case AXIS_PROP_INVERT:
+ g_value_set_boolean (value, axis->inverted);
+ break;
+ case AXIS_PROP_MAP:
+ g_value_set_string (value, axis->map_desc->name);
+ break;
+
+ case AXIS_PROP_MAJOR_TICK_LABELED:
+ g_value_set_boolean (value, axis->major_tick_labeled);
+ break;
+ case AXIS_PROP_MAJOR_TICK_IN:
+ g_value_set_boolean (value, axis->major.tick_in);
+ break;
+ case AXIS_PROP_MAJOR_TICK_OUT:
+ g_value_set_boolean (value, axis->major.tick_out);
+ break;
+ case AXIS_PROP_MAJOR_TICK_SIZE_PTS:
+ g_value_set_int (value, axis->major.size_pts);
+ break;
+
+ case AXIS_PROP_MINOR_TICK_IN:
+ g_value_set_boolean (value, axis->minor.tick_in);
+ break;
+ case AXIS_PROP_MINOR_TICK_OUT:
+ g_value_set_boolean (value, axis->minor.tick_out);
+ break;
+ case AXIS_PROP_MINOR_TICK_SIZE_PTS:
+ g_value_set_int (value, axis->minor.size_pts);
+ break;
+ case AXIS_PROP_ASSIGNED_FORMAT_STR_XL :
+ if (axis->assigned_format != NULL)
+ g_value_take_string (value,
+ go_format_as_XL (axis->assigned_format, FALSE));
+ else
+ g_value_set_static_string (value, NULL);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_axis_finalize (GObject *obj)
+{
+ GogAxis *axis = GOG_AXIS (obj);
+
+ gog_axis_clear_contributors (axis);
+
+ g_slist_free (axis->contributors); axis->contributors = NULL;
+ if (axis->labels != NULL) {
+ g_object_unref (axis->labels);
+ axis->labels = NULL;
+ /* this is for information only, no ref */
+ axis->plot_that_supplied_labels = NULL;
+ }
+ if (axis->assigned_format != NULL) {
+ go_format_unref (axis->assigned_format);
+ axis->assigned_format = NULL;
+ }
+ if (axis->format != NULL) {
+ go_format_unref (axis->format);
+ axis->format = NULL;
+ }
+
+ gog_axis_set_ticks (axis, 0, NULL);
+
+ gog_dataset_finalize (GOG_DATASET (axis));
+ (parent_klass->finalize) (obj);
+}
+
+/**
+ * gog_axis_get_entry :
+ * @axis : #GogAxis
+ * @i :
+ * @user_defined : an optionally NULL pointr to gboolean
+ *
+ * Returns the value of axis element @i and sets @user_defined or
+ * NaN on error
+ **/
+double
+gog_axis_get_entry (GogAxis const *axis, GogAxisElemType i, gboolean *user_defined)
+{
+ GOData *dat;
+
+ if (user_defined)
+ *user_defined = FALSE;
+
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, go_nan);
+ g_return_val_if_fail (i >= AXIS_ELEM_MIN && i < AXIS_ELEM_MAX_ENTRY, go_nan);
+
+ dat = axis->source [i].data;
+ if (dat != NULL && IS_GO_DATA_SCALAR (dat)) {
+ double tmp = go_data_scalar_get_value (GO_DATA_SCALAR (dat));
+ if (go_finite (tmp)) {
+ if (user_defined)
+ *user_defined = TRUE;
+ return tmp;
+ }
+ }
+
+ return axis->auto_bound [i];
+}
+
+static void
+gog_axis_update (GogObject *obj)
+{
+ GSList *ptr;
+ GogAxis *axis = GOG_AXIS (obj);
+ double old_min = axis->auto_bound [AXIS_ELEM_MIN];
+ double old_max = axis->auto_bound [AXIS_ELEM_MAX];
+ GOData *labels;
+ GogPlotBoundInfo bounds;
+
+ gog_debug (0, g_warning ("axis::update"););
+
+ if (axis->labels != NULL) {
+ g_object_unref (axis->labels);
+ axis->labels = NULL;
+ axis->plot_that_supplied_labels = NULL;
+ }
+ axis->is_discrete = TRUE;
+ axis->min_val = DBL_MAX;
+ axis->max_val = -DBL_MAX;
+ axis->min_contrib = axis->max_contrib = NULL;
+ if (axis->format != NULL) {
+ go_format_unref (axis->format);
+ axis->format = NULL;
+ }
+
+ /* everything else is initialized in gog_plot_get_axis_bounds */
+ bounds.fmt = NULL;
+ for (ptr = axis->contributors ; ptr != NULL ; ptr = ptr->next) {
+ labels = gog_plot_get_axis_bounds (GOG_PLOT (ptr->data),
+ axis->type, &bounds);
+
+ /* value dimensions have more information than index dimensions.
+ * At least thats what I am guessing today*/
+ if (!bounds.is_discrete)
+ axis->is_discrete = FALSE;
+ else if (axis->labels == NULL && labels != NULL) {
+ g_object_ref (labels);
+ axis->labels = GO_DATA_VECTOR (labels);
+ axis->plot_that_supplied_labels = GOG_PLOT (ptr->data);
+ axis->center_on_ticks = bounds.center_on_ticks;
+ }
+
+ if (axis->min_val > bounds.val.minima) {
+ axis->min_val = bounds.val.minima;
+ axis->logical_min_val = bounds.logical.minima;
+ axis->min_contrib = ptr->data;
+ } else if (axis->min_contrib == ptr->data) {
+ axis->min_contrib = NULL;
+ axis->min_val = bounds.val.minima;
+ }
+
+ if (axis->max_val < bounds.val.maxima) {
+ axis->max_val = bounds.val.maxima;
+ axis->logical_max_val = bounds.logical.maxima;
+ axis->max_contrib = ptr->data;
+ } else if (axis->max_contrib == ptr->data) {
+ axis->max_contrib = NULL;
+ axis->max_val = bounds.val.maxima;
+ }
+ }
+ axis->format = bounds.fmt; /* just absorb the ref if it exists */
+
+ gog_axis_auto_bound (axis);
+
+ if (go_finite (axis->logical_min_val) &&
+ axis->auto_bound [AXIS_ELEM_MIN] < axis->logical_min_val)
+ axis->auto_bound [AXIS_ELEM_MIN] = axis->logical_min_val;
+ if (go_finite (axis->logical_max_val) &&
+ axis->auto_bound [AXIS_ELEM_MAX] > axis->logical_max_val)
+ axis->auto_bound [AXIS_ELEM_MAX] = axis->logical_max_val;
+
+ gog_axis_calc_ticks (axis);
+
+ /* FIXME: there isn't an easy way to know if a discrete axis
+ * needs to emit a changed signal. So allways emit changed signal if
+ * axis is discrete */
+ if (old_min != axis->auto_bound [AXIS_ELEM_MIN] ||
+ old_max != axis->auto_bound [AXIS_ELEM_MAX] ||
+ axis->is_discrete)
+ gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
+}
+
+static void
+cb_pos_changed (GtkToggleButton *toggle_button, GObject *axis)
+{
+ g_object_set (axis,
+ "pos_str", gtk_toggle_button_get_active (toggle_button) ? "low" : "high",
+ NULL);
+}
+
+static void
+cb_axis_toggle_changed (GtkToggleButton *toggle_button, GObject *axis)
+{
+ g_object_set (axis,
+ gtk_widget_get_name (GTK_WIDGET (toggle_button)),
+ gtk_toggle_button_get_active (toggle_button),
+ NULL);
+}
+
+typedef struct {
+ GtkWidget *editor;
+ GtkToggleButton *toggle;
+ GogDataset *set;
+ unsigned dim;
+} ElemToggleData;
+
+static void
+cb_enable_dim (GtkToggleButton *toggle_button, ElemToggleData *closure)
+{
+ gboolean is_auto = gtk_toggle_button_get_active (toggle_button);
+ double bound = GOG_AXIS (closure->set)->auto_bound [closure->dim];
+
+ gtk_widget_set_sensitive (closure->editor, !is_auto);
+
+ if (is_auto) /* clear the data */
+ gog_dataset_set_dim (closure->set, closure->dim, NULL, NULL);
+
+ if (go_finite (bound) && DBL_MAX > bound && bound > -DBL_MAX) {
+ char *str = g_strdup_printf ("%g", bound);
+ g_object_set (closure->editor, "text", str, NULL);
+ g_free (str);
+ } else
+ g_object_set (closure->editor, "text", "", NULL);
+}
+
+static void
+cb_axis_bound_changed (GogObject *axis, gboolean resize, ElemToggleData *closure)
+{
+ if (gtk_toggle_button_get_active (closure->toggle)) {
+ double bound = GOG_AXIS (closure->set)->auto_bound [closure->dim];
+ if (go_finite (bound) && DBL_MAX > bound && bound > -DBL_MAX) {
+ char *str = g_strdup_printf ("%g", bound);
+ g_object_set (closure->editor, "text", str, NULL);
+ g_free (str);
+ } else
+ g_object_set (closure->editor, "text", "", NULL);
+ }
+}
+
+static void
+make_dim_editor (GogDataset *set, GtkTable *table, unsigned dim,
+ GogDataAllocator *dalloc, char const * const *dim_name)
+{
+ ElemToggleData *info;
+ GClosure *closure;
+ GtkWidget *editor = gog_data_allocator_editor (dalloc, set, dim, GOG_DATA_SCALAR);
+ char *txt = g_strconcat (_(dim_name [dim]), ":", NULL);
+ GtkWidget *toggle = gtk_check_button_new_with_mnemonic (txt);
+ g_free (txt);
+
+ info = g_new0 (ElemToggleData, 1);
+ info->editor = editor;
+ info->set = set;
+ info->dim = dim;
+ info->toggle = GTK_TOGGLE_BUTTON (toggle);
+ g_signal_connect (G_OBJECT (toggle),
+ "toggled",
+ G_CALLBACK (cb_enable_dim), info);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ gog_dataset_get_dim (set, dim) == NULL);
+
+ closure = g_cclosure_new (G_CALLBACK (cb_axis_bound_changed),
+ info, (GClosureNotify)g_free);
+ g_object_watch_closure (G_OBJECT (toggle), closure);
+ g_signal_connect_closure (G_OBJECT (set),
+ "changed",
+ closure, FALSE);
+
+ gtk_table_attach (table, toggle,
+ 0, 1, dim + 1, dim + 2, GTK_FILL, 0, 0, 0);
+ gtk_table_attach (table, editor,
+ 1, 2, dim + 1, dim + 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+}
+
+static void
+cb_axis_fmt_changed (G_GNUC_UNUSED GtkWidget *widget,
+ char *fmt,
+ GObject *axis)
+{
+ g_object_set (axis, "assigned-format-string-XL", fmt, NULL);
+}
+
+static void
+cb_map_combo_changed (GtkComboBox *combo,
+ GogAxis *axis)
+{
+ gog_axis_map_set_by_num (axis, gtk_combo_box_get_active (combo));
+}
+
+#if 0
+static void
+cb_axis_fmt_assignment_toggled (GtkToggleButton *toggle_button, GtkNotebook *notebook)
+{
+ /* any time the toggle changes assume the user wanted to select the page too */
+ gtk_notebook_set_current_page (notebook, 0); /* assume it is the first page */
+}
+#endif
+
+static gpointer
+gog_axis_editor (GogObject *gobj, GogDataAllocator *dalloc, GnmCmdContext *cc)
+{
+ static guint axis_pref_page = 0;
+ static char const *toggle_props[] = {
+ "invert-axis",
+ "major-tick-labeled",
+ "major-tick-out",
+ "major-tick-in",
+ "minor-tick-out",
+ "minor-tick-in"
+ };
+ GtkWidget *w, *notebook; /* , *cbox; */
+ GtkTable *table;
+ unsigned i = 0;
+ GogAxis *axis = GOG_AXIS (gobj);
+ GogDataset *set = GOG_DATASET (gobj);
+ GladeXML *gui;
+
+ /* No preferences for circular axis */
+ if (axis->type == GOG_AXIS_CIRCULAR)
+ return NULL;
+
+ gui = gnm_glade_xml_new (cc, "gog-axis-prefs.glade", "axis_pref_box", NULL);
+ if (gui == NULL)
+ return NULL;
+ notebook = gtk_notebook_new ();
+
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook),
+ glade_xml_get_widget (gui, "axis_pref_box"),
+ gtk_label_new (_("Details")));
+ gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, notebook);
+
+ if (!axis->is_discrete) {
+ GtkWidget *w = glade_xml_get_widget (gui, "map_type_combo");
+ gog_axis_map_populate_combo (axis, GTK_COMBO_BOX (w));
+ g_signal_connect_object (G_OBJECT (w),
+ "changed",
+ G_CALLBACK (cb_map_combo_changed),
+ axis, 0);
+ } else {
+ GtkWidget *w = glade_xml_get_widget (gui, "map_type_box");
+ gtk_widget_hide (w);
+ }
+
+ w = glade_xml_get_widget (gui, "axis_low");
+ if (axis->pos == GOG_AXIS_AT_LOW)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), TRUE);
+ else
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
+ glade_xml_get_widget (gui, "axis_high")), TRUE);
+ g_signal_connect_object (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_pos_changed), axis, 0);
+
+ for (i = 0; i < G_N_ELEMENTS (toggle_props) ; i++) {
+ gboolean cur_val;
+ GtkWidget *w = glade_xml_get_widget (gui, toggle_props[i]);
+
+ g_object_get (G_OBJECT (gobj), toggle_props[i], &cur_val, NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), cur_val);
+ g_signal_connect_object (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_axis_toggle_changed), axis, 0);
+ }
+ if (axis->is_discrete) {
+ /* Hide minor tick properties */
+ GtkWidget *w = glade_xml_get_widget (gui, "minor_tick_box");
+ gtk_widget_hide (w);
+ }
+
+ /* Bounds Page */
+ w = gtk_table_new (1, 2, FALSE);
+ table = GTK_TABLE (w);
+ w = gtk_label_new (_("Automatic"));
+ gtk_misc_set_alignment (GTK_MISC (w), 0., .5);
+ gtk_table_set_row_spacings (table, 6);
+ gtk_table_set_col_spacings (table, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_table_attach (table, w, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+ if (axis->is_discrete) {
+ static char const * const dim_names[] = {
+ NULL,
+ NULL,
+ N_("Categories between _ticks"),
+ N_("Categories between _labels"),
+ N_("_Cross at category #")
+ };
+ for (i = AXIS_ELEM_MAJOR_TICK; i < AXIS_ELEM_MAX_ENTRY ; i++)
+ make_dim_editor (set, table, i, dalloc, dim_names);
+ gtk_widget_show_all (GTK_WIDGET (table));
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (table),
+ gtk_label_new (_("Bounds")));
+ } else {
+ static char const * const dim_names[] = {
+ N_("M_in"),
+ N_("M_ax"),
+ N_("Ma_jor Ticks"),
+ N_("Mi_nor Ticks"),
+ N_("_Cross")
+ };
+
+ for (i = AXIS_ELEM_MIN; i < AXIS_ELEM_MAX_ENTRY ; i++)
+ make_dim_editor (set, table, i, dalloc, dim_names);
+ gtk_widget_show_all (GTK_WIDGET (table));
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (table),
+ gtk_label_new (_("Bounds")));
+
+ w = number_format_selector_new ();
+ if (axis->assigned_format != NULL && !style_format_is_general (axis->assigned_format))
+ number_format_selector_set_style_format (NUMBER_FORMAT_SELECTOR (w),
+ axis->assigned_format);
+ else if (axis->format != NULL)
+ number_format_selector_set_style_format (NUMBER_FORMAT_SELECTOR (w),
+ axis->format);
+
+#if 0
+ /* TOO CHEESY to go into production
+ * We need a way to toggle auto vs user formats
+ * but the selector is too tall already
+ * disable for now */
+ cbox = gtk_check_button_new_with_label (_("Format"));
+ g_signal_connect (G_OBJECT (cbox),
+ "toggled",
+ G_CALLBACK (cb_axis_fmt_assignment_toggled), notebook);
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), w, cbox);
+#else
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), w,
+ gtk_label_new (_("Format")));
+#endif
+
+ gtk_widget_show (w);
+ g_signal_connect (G_OBJECT (w),
+ "number_format_changed",
+ G_CALLBACK (cb_axis_fmt_changed), axis);
+ }
+
+ g_object_set_data_full (G_OBJECT (notebook), "gui", gui,
+ (GDestroyNotify)g_object_unref);
+
+ gog_style_handle_notebook (notebook, &axis_pref_page);
+ gtk_widget_show (GTK_WIDGET (notebook));
+ return notebook;
+}
+
+static void
+gog_axis_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_LINE | GOG_STYLE_FONT;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_axis_class_init (GObjectClass *gobject_klass)
+{
+ static GogObjectRole const roles[] = {
+ { N_("Label"), "GogLabel", 0,
+ GOG_POSITION_COMPASS, GOG_POSITION_S|GOG_POSITION_ALIGN_CENTER, GOG_OBJECT_NAME_BY_ROLE,
+ NULL, NULL, NULL, role_label_post_add, NULL, NULL, { -1 } },
+ { N_("MinorGrid"), "GogGridLine", 0,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_grid_line_minor_can_add, NULL, NULL, role_grid_line_minor_post_add, NULL, NULL, { -1 } },
+ { N_("MajorGrid"), "GogGridLine", 1,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_grid_line_major_can_add, NULL, NULL, role_grid_line_major_post_add, NULL, NULL, { -1 } }
+ };
+
+ GogObjectClass *gog_klass = (GogObjectClass *) gobject_klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) gog_klass;
+
+ parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->set_property = gog_axis_set_property;
+ gobject_klass->get_property = gog_axis_get_property;
+ gobject_klass->finalize = gog_axis_finalize;
+
+ /* no need to persist, the role handles that */
+ g_object_class_install_property (gobject_klass, AXIS_PROP_TYPE,
+ g_param_spec_int ("type", "Type",
+ "GogAxisType",
+ GOG_AXIS_UNKNOWN, GOG_AXIS_TYPES, GOG_AXIS_UNKNOWN, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_POS,
+ g_param_spec_int ("pos", "pos",
+ "GogAxisPosition",
+ GOG_AXIS_AT_LOW, GOG_AXIS_AT_HIGH, GOG_AXIS_AT_LOW, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_POS_STR,
+ g_param_spec_string ("pos_str", "pos_str",
+ "Where to position an axis low, high, or crossing",
+ "low", G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ g_object_class_install_property (gobject_klass, AXIS_PROP_INVERT,
+ g_param_spec_boolean ("invert-axis", NULL,
+ "Scale from high to low rather than low to high",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAP,
+ g_param_spec_string ("map-name", "MapName",
+ "The name of the map for scaling",
+ "linear", G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAJOR_TICK_LABELED,
+ g_param_spec_boolean ("major-tick-labeled", NULL,
+ "Show labels for major ticks",
+ TRUE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAJOR_TICK_IN,
+ g_param_spec_boolean ("major-tick-in", NULL,
+ "Major tick marks inside the axis",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAJOR_TICK_OUT,
+ g_param_spec_boolean ("major-tick-out", NULL,
+ "Major tick marks outside the axis",
+ TRUE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAJOR_TICK_SIZE_PTS,
+ g_param_spec_int ("major-tick-size-pts", "major-tick-size-pts",
+ "Size of the major tick marks in pts",
+ 0, 20, 4, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MINOR_TICK_IN,
+ g_param_spec_boolean ("minor-tick-in", NULL,
+ "Minor tick marks inside the axis",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MINOR_TICK_OUT,
+ g_param_spec_boolean ("minor-tick-out", NULL,
+ "Minor tick marks outside the axis",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MINOR_TICK_SIZE_PTS,
+ g_param_spec_int ("minor-tick-size-pts", NULL,
+ "Size of the minor tick marks in pts",
+ 0, 15, 2, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_ASSIGNED_FORMAT_STR_XL,
+ g_param_spec_string ("assigned-format-string-XL", NULL,
+ "The user assigned format to use for non-discrete axis labels (XL format)",
+ "General", G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+ gog_klass->update = gog_axis_update;
+ gog_klass->editor = gog_axis_editor;
+ gog_klass->view_type = gog_axis_view_get_type ();
+ style_klass->init_style = gog_axis_init_style;
+}
+
+static void
+gog_axis_init (GogAxis *axis)
+{
+ axis->type = GOG_AXIS_UNKNOWN;
+ axis->pos = GOG_AXIS_AT_LOW;
+ axis->contributors = NULL;
+ axis->minor.tick_in = axis->minor.tick_out = axis->major.tick_in = FALSE;
+ axis->major.tick_out = TRUE;
+ axis->major_tick_labeled = TRUE;
+ axis->inverted = FALSE;
+ axis->major.size_pts = 4;
+ axis->minor.size_pts = 2;
+
+ /* yes we want min = MAX */
+ axis->min_val = DBL_MAX;
+ axis->max_val = -DBL_MAX;
+ axis->min_contrib = axis->max_contrib = NULL;
+ axis->is_discrete = FALSE;
+ axis->center_on_ticks = FALSE;
+ axis->labels = NULL;
+ axis->plot_that_supplied_labels = NULL;
+ axis->format = axis->assigned_format = NULL;
+
+ gog_axis_map_set (axis, NULL);
+
+ axis->ticks = NULL;
+ axis->tick_nbr = 0;
+}
+
+static void
+gog_axis_dataset_dims (GogDataset const *set, int *first, int *last)
+{
+ *first = AXIS_ELEM_MIN;
+ *last = AXIS_ELEM_CROSS_POINT;
+}
+
+static GogDatasetElement *
+gog_axis_dataset_get_elem (GogDataset const *set, int dim_i)
+{
+ GogAxis *axis = GOG_AXIS (set);
+ if (AXIS_ELEM_MIN <= dim_i && dim_i <= AXIS_ELEM_CROSS_POINT)
+ return &axis->source[dim_i];
+ return NULL;
+}
+
+static void
+gog_axis_dim_changed (GogDataset *set, int dim_i)
+{
+ gog_axis_update (GOG_OBJECT (set));
+ gog_object_emit_changed (GOG_OBJECT (set), TRUE);
+}
+
+static void
+gog_axis_dataset_init (GogDatasetClass *iface)
+{
+ iface->dims = gog_axis_dataset_dims;
+ iface->get_elem = gog_axis_dataset_get_elem;
+ iface->dim_changed = gog_axis_dim_changed;
+}
+
+GSF_CLASS_FULL (GogAxis, gog_axis,
+ gog_axis_class_init, gog_axis_init,
+ GOG_STYLED_OBJECT_TYPE, 0,
+ GSF_INTERFACE (gog_axis_dataset_init, GOG_DATASET_TYPE))
+
+
+GogAxisType
+gog_axis_get_atype (GogAxis const *axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, GOG_AXIS_UNKNOWN);
+ return axis->type;
+}
+
+GogAxisPosition
+gog_axis_get_pos (GogAxis const *axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, GOG_AXIS_IN_MIDDLE);
+ return axis->pos;
+}
+
+/**
+ * gog_axis_is_discrete :
+ * @axis : #GogAxis
+ *
+ * Returns TRUE if @axis enumerates a set of discrete items, rather than a
+ * continuous value
+ **/
+gboolean
+gog_axis_is_discrete (GogAxis const *axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, FALSE);
+ return axis->is_discrete;
+}
+
+/**
+ * gog_axis_get_bounds :
+ * @axis : #GogAxis
+ * @minima : result
+ * @maxima : result
+ *
+ * return TRUE if the bounds stored in @minima and @maxima are sane
+ **/
+gboolean
+gog_axis_get_bounds (GogAxis const *axis, double *minima, double *maxima)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, FALSE);
+ g_return_val_if_fail (minima != NULL, FALSE);
+ g_return_val_if_fail (maxima != NULL, FALSE);
+
+ *minima = gog_axis_get_entry (axis, AXIS_ELEM_MIN, NULL);
+ *maxima = gog_axis_get_entry (axis, AXIS_ELEM_MAX, NULL);
+
+ return go_finite (*minima) && go_finite (*maxima) && *minima < *maxima;
+}
+
+/**
+ * gog_axis_get_ticks :
+ * @axis : #GogAxis
+ * @ticks : result
+ *
+ * Retrieve an array of tick descriptions, and return the number of ticks.
+ **/
+unsigned
+gog_axis_get_ticks (GogAxis *axis, GogAxisTick **ticks)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, 0);
+ g_return_val_if_fail (ticks != NULL, 0);
+
+ *ticks = axis->ticks;
+ return axis->tick_nbr;
+}
+
+/**
+ * gog_axis_get_labels :
+ * @axi : #GogAxis
+ * @plot_that_labeled_axis : #GogPlot
+ *
+ * Return the possibly NULL #GOData used as a label for this axis
+ * along with the plot that it was associated with
+ **/
+GOData *
+gog_axis_get_labels (GogAxis const *axis, GogPlot **plot_that_labeled_axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, NULL);
+
+ if (axis->is_discrete) {
+ if (plot_that_labeled_axis != NULL)
+ *plot_that_labeled_axis = axis->plot_that_supplied_labels;
+ return GO_DATA (axis->labels);
+ }
+ if (plot_that_labeled_axis != NULL)
+ *plot_that_labeled_axis = NULL;
+ return NULL;
+}
+
+/**
+ * gog_axis_add_contributor :
+ * @axis : #GogAxis
+ * @contrib : #GogObject (can we relax this to use an interface ?)
+ *
+ * Register @contrib as taking part in the negotiation of @axis's bounds.
+ **/
+void
+gog_axis_add_contributor (GogAxis *axis, GogObject *contrib)
+{
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+ g_return_if_fail (g_slist_find (axis->contributors, contrib) == NULL);
+
+ axis->contributors = g_slist_prepend (axis->contributors, contrib);
+
+ gog_object_request_update (GOG_OBJECT (axis));
+}
+
+/**
+ * gog_axis_del_contributor :
+ * @axis : #GogAxis
+ * @contrib : #GogObject (can we relax this to use an interface ?)
+ *
+ * @contrib no longer takes part in the negotiation of @axis's bounds.
+ **/
+void
+gog_axis_del_contributor (GogAxis *axis, GogObject *contrib)
+{
+ gboolean update = FALSE;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+ g_return_if_fail (g_slist_find (axis->contributors, contrib) != NULL);
+
+ if (axis->min_contrib == contrib) {
+ axis->min_contrib = NULL;
+ update = TRUE;
+ }
+ if (axis->max_contrib == contrib) {
+ axis->max_contrib = NULL;
+ update = TRUE;
+ }
+ axis->contributors = g_slist_remove (axis->contributors, contrib);
+
+ if (update)
+ gog_object_request_update (GOG_OBJECT (axis));
+}
+
+void
+gog_axis_clear_contributors (GogAxis *axis)
+{
+ GSList *ptr, *list;
+ GogAxisSet filter;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ filter = 1 << axis->type;
+ list = g_slist_copy (axis->contributors);
+ for (ptr = list; ptr != NULL ; ptr = ptr->next)
+ gog_plot_axis_clear (GOG_PLOT (ptr->data), filter);
+ g_slist_free (list);
+}
+
+GSList const *
+gog_axis_contributors (GogAxis *axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, NULL);
+
+ return axis->contributors;
+}
+
+/**
+ * gog_axis_bound_changed :
+ * @axis : #GogAxis
+ * @contrib : #GogObject
+**/
+void
+gog_axis_bound_changed (GogAxis *axis, GogObject *contrib)
+{
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ gog_object_request_update (GOG_OBJECT (axis));
+}
+
+/****************************************************************************/
+
+typedef GogView GogAxisView;
+typedef GogViewClass GogAxisViewClass;
+
+#define GOG_AXIS_VIEW_TYPE (gog_axis_view_get_type ())
+#define GOG_AXIS_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_AXIS_VIEW_TYPE, GogAxisView))
+#define IS_GOG_AXIS_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_AXIS_VIEW_TYPE))
+
+static GogViewClass *aview_parent_klass;
+
+void
+gog_axis_view_padding_request (GogView *v, GogViewPadding *padding,
+ GogViewAllocation *bbox)
+{
+ GogAxis *axis = GOG_AXIS (v->model);
+ GogViewRequisition txt_size;
+ gboolean const is_horiz = axis->type == GOG_AXIS_X;
+ unsigned i;
+ double tick_major = 0., tick_minor = 0.;
+ double txt_max_h, txt_max_w;
+ double line_width = gog_renderer_line_size (
+ v->renderer, axis->base.style->line.width);
+
+ double Xl, Xr, wl, wr, xm;
+ gboolean label_found = FALSE;
+ double position;
+ char *label = NULL;
+ double padding_l, padding_r, padding_label;
+ gboolean is_l_valid, is_r_valid;
+
+ GogAxisMap *map;
+
+ padding->wr = padding->wl = padding->ht = padding->hb = 0.;
+
+ if ((axis->type != GOG_AXIS_X && axis->type != GOG_AXIS_Y))
+ return;
+
+ map = gog_axis_map_new (axis, 0., 1.);
+
+ if (is_horiz) {
+ xm = bbox->w - bbox->x;
+ } else {
+ xm = bbox->h - bbox->y;
+ }
+
+
+ padding_l = padding_r = padding_label = 0.;
+ Xl = DBL_MAX;
+ Xr = -DBL_MAX;
+ wl = wr = 0.;
+ txt_max_w = txt_max_h = 0.;
+ gog_renderer_push_style (v->renderer, axis->base.style);
+ for (i = 0; i < axis->tick_nbr; i++) {
+ label = axis->ticks[i].label;
+ if (label != NULL) {
+ label_found = TRUE;
+ position = gog_axis_map (map, axis->ticks[i].position);
+ gog_renderer_measure_text (v->renderer, label,
+ &txt_size);
+
+ if (txt_max_h < txt_size.h)
+ txt_max_h = txt_size.h;
+ if (txt_max_w < txt_size.w)
+ txt_max_w = txt_size.w;
+
+ if (position < Xl) {
+ Xl = position;
+ wl = (is_horiz ? txt_size.w : txt_size.h) / 2.;
+ }
+ if (position > Xr) {
+ Xr = position;
+ wr = (is_horiz ? txt_size.w : txt_size.h) / 2.;
+ }
+ }
+ }
+ gog_renderer_pop_style (v->renderer);
+
+ if (label_found) {
+ if (Xl != Xr) {
+ padding_l = (Xr * wl + Xl * (wr - xm)) / (Xr - Xl);
+ padding_r = xm - (((Xr - 1.0) * wl + (Xl - 1.0) * (wr - xm)) / (Xr - Xl));
+ is_l_valid = padding_l > 0.;
+ is_r_valid = padding_r > 0.;
+ } else {
+ is_l_valid = Xl > .5;
+ is_r_valid = ! is_l_valid;
+ }
+
+ if (!is_l_valid) {
+ padding_l = 0.;
+ if (Xr > 0.)
+ padding_r = MAX (xm - ((xm - wr) / Xr), 0.);
+ else
+ padding_r = 0.;
+ } else if (!is_r_valid) {
+ padding_r = 0.;
+ if (Xl < 1.0)
+ padding_l = MAX ((wl -Xl * xm) / (1 - Xl), 0.);
+ else
+ padding_l = 0.;
+ }
+ }
+
+ if (is_horiz) {
+ if (line_width > 0) {
+ if (axis->major.tick_out)
+ tick_major = gog_renderer_pt2r_y (v->renderer,
+ axis->major.size_pts);
+ if (axis->minor.tick_out)
+ tick_minor = gog_renderer_pt2r_y (v->renderer,
+ axis->minor.size_pts);
+ }
+ padding_label = (axis->is_discrete ?
+ MAX (txt_max_h, MAX (tick_minor, tick_major)):
+ MAX (tick_major + txt_max_h, tick_minor)) +
+ line_width;
+ } else {
+ if (line_width > 0) {
+ if (axis->major.tick_out)
+ tick_major = gog_renderer_pt2r_x (v->renderer,
+ axis->major.size_pts);
+ if (axis->minor.tick_out)
+ tick_minor = gog_renderer_pt2r_x (v->renderer,
+ axis->minor.size_pts);
+ }
+ padding_label = (axis->is_discrete ?
+ MAX (txt_max_w, MAX (tick_major, tick_minor)):
+ MAX (tick_major + txt_max_w, tick_minor)) +
+ line_width;
+ }
+
+ if (is_horiz) {
+ padding->wl = padding_l;
+ padding->wr = padding_r;
+ if (axis->pos == GOG_AXIS_AT_LOW) {
+ padding->hb = padding_label;
+ padding->ht = line_width;
+ } else {
+ padding->hb = line_width;
+ padding->ht = padding_label;
+ }
+ } else {
+ padding->ht = padding_r;
+ padding->hb = padding_l;
+ if (axis->pos == GOG_AXIS_AT_LOW) {
+ padding->wl = padding_label;
+ padding->wr = line_width;
+ } else {
+ padding->wl = line_width;
+ padding->wr = padding_label;
+ }
+ }
+
+ gog_axis_map_free (map);
+}
+
+static void
+gog_axis_view_size_request (GogView *v, GogViewRequisition *req)
+{
+ gog_view_size_child_request (v, req, req);
+}
+
+static gboolean
+overlap (GogViewAllocation const *bbox1, GogViewAllocation const *bbox2) {
+ return (!((MAX (bbox2->x, bbox2->x + bbox2->w) < MIN (bbox1->x, bbox1->x + bbox1->w)) ||
+ (MAX (bbox2->y, bbox2->y + bbox2->h) < MIN (bbox1->y, bbox1->y + bbox1->h)) ||
+ (MIN (bbox2->x, bbox2->x + bbox2->w) > MAX (bbox1->x, bbox1->x + bbox1->w)) ||
+ (MIN (bbox2->y, bbox2->y + bbox2->h) > MAX (bbox1->y, bbox1->y + bbox1->h))));
+}
+
+static void
+draw_axis_from_a_to_b (GogView *v, GogAxis *axis, double ax, double ay, double bx, double by,
+ gboolean draw_label)
+{
+ GogAxisMap *map = NULL;
+ ArtVpath path[3];
+ double line_width;
+ double axis_length, axis_angle, label_angle;
+ double tick_len;
+ double major_out_x = 0., major_out_y= 0., major_in_x = 0., major_in_y = 0.;
+ double minor_out_x = 0., minor_out_y= 0., minor_in_x = 0., minor_in_y = 0.;
+ double cos_alpha, sin_alpha;
+ double pos, pos_x, pos_y, offset, label_offset;
+ unsigned i;
+ GogViewRequisition txt_size;
+ GogViewAllocation label_pos, label_result, label_old = {0., 0., 0., 0.};
+ gboolean draw_major, draw_minor;
+ gboolean is_line_visible;
+
+ axis_length = sqrt ((ax-bx)*(ax-bx)+(ay-by)*(ay-by));
+ if (bx - ax != 0) {
+ axis_angle = atan ((double)(by-ay)/(double)(bx-ax));
+ if (bx < ax) {
+ axis_angle += M_PI;
+ }
+ } else {
+ if (ay > by) {
+ axis_angle = - M_PI/2.0;
+ } else {
+ axis_angle = M_PI/2.0;
+ }
+ }
+ label_angle = fmod (axis_angle + 2.0 * M_PI, M_PI);
+ if (label_angle > M_PI / 2.0)
+ label_angle = M_PI - label_angle;
+ cos_alpha = cos (axis_angle + M_PI / 2.0);
+ sin_alpha = sin (axis_angle + M_PI / 2.0);
+
+ is_line_visible = gog_style_is_line_visible (axis->base.style);
+ line_width = gog_renderer_line_size (v->renderer, axis->base.style->line.width) / 2;
+ if (is_line_visible)
+ {
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_END;
+
+ path[0].x = ax;
+ path[0].y = ay;
+ path[1].x = bx;
+ path[1].y = by;
+ gog_renderer_draw_path (v->renderer, path, NULL);
+
+ map = gog_axis_map_new (axis, 0., axis_length);
+ }
+
+ draw_major = axis->major.tick_in || axis->major.tick_out;
+ draw_minor = axis->minor.tick_in || axis->minor.tick_out;
+
+ tick_len = gog_renderer_pt2r_x (v->renderer, axis->minor.size_pts) + line_width;
+ minor_out_x = axis->minor.tick_out ? - tick_len * cos_alpha : 0.;
+ minor_out_y = axis->minor.tick_out ? - tick_len * sin_alpha : 0.;
+ minor_in_x = axis->minor.tick_in ? tick_len * cos_alpha : 0.;
+ minor_in_y = axis->minor.tick_in ? tick_len * sin_alpha : 0.;
+ tick_len = gog_renderer_pt2r_x (v->renderer, axis->major.size_pts) + line_width;
+ major_out_x = axis->major.tick_out ? - tick_len * cos_alpha : 0.;
+ major_out_y = axis->major.tick_out ? - tick_len * sin_alpha : 0.;
+ major_in_x = axis->major.tick_in ? tick_len * cos_alpha : 0.;
+ major_in_y = axis->major.tick_in ? tick_len * sin_alpha : 0.;
+ label_offset = gog_renderer_pt2r_x (v->renderer, TICK_LABEL_PAD_HORIZ) +
+ axis->major.tick_in ? tick_len : 0.;
+
+ for (i = 0; i < axis->tick_nbr; i++) {
+ pos = gog_axis_map_to_canvas (map, axis->ticks[i].position);
+ pos_x = ax + pos * cos (axis_angle);
+ pos_y = ay + pos * sin (axis_angle);
+
+ if (is_line_visible) {
+ switch (axis->ticks[i].type) {
+ case GOG_AXIS_TICK_MAJOR:
+ if (draw_major) {
+ path[0].x = major_out_x + pos_x;
+ path[1].x = major_in_x + pos_x;
+ path[0].y = major_out_y + pos_y;
+ path[1].y = major_in_y + pos_y;
+ gog_renderer_draw_path (v->renderer, path, NULL);
+ }
+ break;
+
+ case GOG_AXIS_TICK_MINOR:
+ if (draw_minor) {
+ path[0].x = minor_out_x + pos_x;
+ path[1].x = minor_in_x + pos_x;
+ path[0].y = minor_out_y + pos_y;
+ path[1].y = minor_in_y + pos_y;
+ gog_renderer_draw_path (v->renderer, path, NULL);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (axis->ticks[i].label != NULL &&
+ gog_axis_map (map, axis->ticks[i].position) >= 0.1 &&
+ draw_label) {
+ gog_renderer_measure_text (v->renderer, axis->ticks[i].label, &txt_size);
+ offset = (txt_size.h * cos (label_angle) + txt_size.w * sin (label_angle)) / 2.0 + label_offset;
+ label_pos.x = pos_x + offset * cos_alpha;
+ label_pos.y = pos_y + offset * sin_alpha;
+ label_pos.w = txt_size.w;
+ label_pos.h = txt_size.h;
+ if (!overlap (&label_pos, &label_old)) {
+ gog_renderer_draw_text (v->renderer, axis->ticks[i].label,
+ &label_pos, GTK_ANCHOR_CENTER, &label_result);
+ label_old = label_pos;
+ }
+ }
+ }
+
+ if (is_line_visible)
+ gog_axis_map_free (map);
+}
+
+static void
+gog_axis_view_render_children (GogView *view, GogViewAllocation const *bbox)
+{
+ GSList *ptr;
+
+ /* Render every child except grid lines. Those are rendered
+ * before in gog_chart_view since we don't want to render them
+ * over axis. */
+ for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
+ if (!IS_GOG_GRID_LINE (GOG_VIEW (ptr->data)->model))
+ gog_view_render (ptr->data, bbox);
+ }
+}
+
+static void
+gog_axis_view_render (GogView *v, GogViewAllocation const *bbox)
+{
+ GtkAnchorType anchor;
+ GogViewAllocation const *area;
+ GogViewAllocation label_pos, label_result;
+ GogViewRequisition txt_size;
+ double last_label_pos = .0, last_label_size = -DBL_MAX;
+ ArtVpath path[3];
+ GogAxis *axis = GOG_AXIS (v->model);
+ GogStyle *style = axis->base.style;
+ unsigned i;
+ double tick_len, label_pad, dir, center, label_spacing = 0.;
+ double line_width = gog_renderer_line_size (v->renderer,
+ style->line.width) / 2.;
+ double pos, offset;
+ double major_in = 0., major_out = 0.;
+ double minor_in = 0., minor_out = 0.;
+ gboolean draw_major, draw_minor;
+ gboolean is_line_visible = gog_style_is_line_visible (style);
+
+ gog_axis_view_render_children (v, bbox);
+
+ g_return_if_fail (axis->pos != GOG_AXIS_IN_MIDDLE);
+ g_return_if_fail (v->parent != NULL);
+ area = gog_chart_view_get_plot_area (v->parent);
+ g_return_if_fail (area != NULL);
+
+ gog_renderer_push_style (v->renderer, style);
+
+ draw_major = (axis->major.tick_in || axis->major.tick_out) && is_line_visible;
+ draw_minor = (axis->minor.tick_in || axis->minor.tick_out) && is_line_visible;
+
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_END;
+
+ switch (axis->type) {
+ case GOG_AXIS_X:
+ switch (axis->pos) {
+ default :
+ case GOG_AXIS_AT_LOW:
+ anchor = GTK_ANCHOR_N;
+ dir = 1.;
+ center = area->y + area->h;
+ break;
+
+ case GOG_AXIS_AT_HIGH:
+ anchor = GTK_ANCHOR_S;
+ dir = -1.;
+ center = area->y;
+ break;
+ }
+
+ tick_len = gog_renderer_pt2r_y (v->renderer, axis->major.size_pts);
+ major_out = axis->major.tick_out ? center + dir * (line_width + tick_len) : center;
+ major_in = axis->major.tick_in ? center - dir * (line_width + tick_len) : center;
+ tick_len = gog_renderer_pt2r_y (v->renderer, axis->minor.size_pts);
+ minor_out = axis->minor.tick_out ? center + dir * (line_width + tick_len) : center;
+ minor_in = axis->minor.tick_in ? center - dir * (line_width + tick_len) : center;
+
+ if (is_line_visible) {
+ path[0].y = path[1].y = center;
+ path[0].x = area->x - line_width;
+ path[1].x = area->x + area->w + line_width;
+ gog_renderer_draw_sharp_path (v->renderer, path, NULL);
+ }
+
+ if (axis->major_tick_labeled) {
+ label_pad = gog_renderer_pt2r_y (v->renderer, TICK_LABEL_PAD_VERT);
+ label_pos.y = (axis->major.tick_out && (!axis->is_discrete || axis->center_on_ticks))
+ ? major_out + dir * label_pad
+ : center + dir * (line_width + label_pad);
+ label_pos.h = area->h - line_width;
+ label_pos.w = -1;
+ gog_renderer_measure_text (v->renderer, "0", &txt_size);
+ label_spacing = txt_size.w;
+ }
+
+ if (axis->tick_nbr > 0) {
+ GogAxisMap *map = gog_axis_map_new (axis, area->x, area->w);
+ offset = (axis->is_discrete && !axis->center_on_ticks)? -0.5 : 0.0;
+ for (i = 0; i < axis->tick_nbr; i++) {
+
+ if (is_line_visible) {
+ pos = gog_axis_map_to_canvas (map, axis->ticks[i].position + offset);
+ switch (axis->ticks[i].type) {
+ case GOG_AXIS_TICK_MAJOR:
+ if (draw_major) {
+ path[0].x = path[1].x = pos;
+ path[0].y = major_out;
+ path[1].y = major_in;
+ gog_renderer_draw_sharp_path
+ (v->renderer, path, NULL);
+ }
+ break;
+
+ case GOG_AXIS_TICK_MINOR:
+ if (draw_minor) {
+ path[0].x = path[1].x = pos;
+ path[0].y = minor_out;
+ path[1].y = minor_in;
+ gog_renderer_draw_sharp_path
+ (v->renderer, path, NULL);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (axis->ticks[i].label != NULL) {
+ label_pos.x = gog_axis_map_to_canvas (map, axis->ticks[i].position);
+ if (fabs (last_label_pos - label_pos.x) > last_label_size + label_spacing) {
+ gog_renderer_measure_text (v->renderer, axis->ticks[i].label, &txt_size);
+ txt_size.w /= 2.0;
+ if (fabs (last_label_pos - label_pos.x) > last_label_size + txt_size.w + label_spacing) {
+ gog_renderer_draw_text (v->renderer, axis->ticks[i].label,
+ &label_pos, anchor, &label_result);
+ last_label_pos = label_pos.x;
+ last_label_size = txt_size.w;
+ }
+ }
+ }
+ }
+ gog_axis_map_free (map);
+ }
+
+ break;
+
+ case GOG_AXIS_Y:
+ switch (axis->pos) {
+ default :
+ case GOG_AXIS_AT_LOW:
+ anchor = GTK_ANCHOR_E;
+ dir = -1.;
+ center = area->x;
+ break;
+ case GOG_AXIS_AT_HIGH:
+ anchor = GTK_ANCHOR_W;
+ dir = 1.;
+ center = area->x + area->w;
+ break;
+ }
+
+ tick_len = gog_renderer_pt2r_x (v->renderer, axis->major.size_pts);
+ major_out = axis->major.tick_out ? center + dir * (line_width + tick_len) : center;
+ major_in = axis->major.tick_in ? center - dir * (line_width + tick_len) : center;
+ tick_len = gog_renderer_pt2r_x (v->renderer, axis->minor.size_pts);
+ minor_out = axis->minor.tick_out ? center + dir * (line_width + tick_len) : center;
+ minor_in = axis->minor.tick_in ? center - dir * (line_width + tick_len) : center;
+
+ if (is_line_visible) {
+ path[0].x = path[1].x = center;
+ path[0].y = area->y + area->h + line_width;
+ path[1].y = area->y - line_width;
+ gog_renderer_draw_sharp_path (v->renderer, path, NULL);
+ }
+
+ if (axis->major_tick_labeled) {
+ label_pad = gog_renderer_pt2r_x (v->renderer, TICK_LABEL_PAD_HORIZ);
+ label_pos.x = (axis->major.tick_out && (!axis->is_discrete || axis->center_on_ticks))
+ ? major_out + dir * label_pad
+ : center + dir * (line_width + label_pad);
+ label_pos.w = area->w - line_width;
+ label_pos.h = -1;
+ }
+
+ if (axis->tick_nbr > 0) {
+ GogAxisMap *map = gog_axis_map_new (axis, area->h + area->y, -area->h);
+ offset = (axis->is_discrete && !axis->center_on_ticks)? -0.5 : 0.0;
+ for (i = 0; i < axis->tick_nbr; i++) {
+
+ if (is_line_visible) {
+ pos = gog_axis_map_to_canvas (map, axis->ticks[i].position + offset);
+ switch (axis->ticks[i].type) {
+ case GOG_AXIS_TICK_MAJOR:
+ if (draw_major) {
+ path[0].y = path[1].y = pos;
+ path[0].x = major_out;
+ path[1].x = major_in;
+ gog_renderer_draw_sharp_path
+ (v->renderer, path, NULL);
+ }
+ break;
+
+ case GOG_AXIS_TICK_MINOR:
+ if (draw_minor) {
+ path[0].y = path[1].y = pos;
+ path[0].x = minor_out;
+ path[1].x = minor_in;
+ gog_renderer_draw_sharp_path
+ (v->renderer, path, NULL);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (axis->ticks[i].label != NULL) {
+ label_pos.y = gog_axis_map_to_canvas (map, axis->ticks[i].position);
+ if (fabs (last_label_pos - label_pos.y) > last_label_size) {
+ gog_renderer_measure_text (v->renderer, axis->ticks[i].label, &txt_size);
+ txt_size.h /= 2.0;
+ if (fabs (last_label_pos - label_pos.y) > last_label_size + txt_size.h) {
+ gog_renderer_draw_text (v->renderer, axis->ticks[i].label,
+ &label_pos, anchor, &label_result);
+ last_label_pos = label_pos.y;
+ last_label_size = txt_size.h;
+ }
+ }
+ }
+ }
+ gog_axis_map_free (map);
+ }
+
+ break;
+
+ case GOG_AXIS_CIRCULAR:
+ break;
+
+ case GOG_AXIS_RADIAL: {
+ double center_x, center_y, radius;
+ unsigned i, num_radii;
+ double circular_min, circular_max;
+ GogAxis *circular_axis;
+ GogChart *chart;
+ GSList *axis_list;
+
+ center_x = area->x + (area->w/2);
+ center_y = area->y + (area->h/2);
+ radius = v->allocation.h > v->allocation.w
+ ? v->allocation.w / 2.0
+ : v->allocation.h / 2.0;
+
+ g_return_if_fail (v->parent != NULL);
+ g_return_if_fail (v->parent->model != NULL);
+ g_return_if_fail (IS_GOG_CHART(v->parent->model));
+ chart = GOG_CHART(v->parent->model);
+ axis_list = gog_chart_get_axis (chart, GOG_AXIS_CIRCULAR);
+ g_return_if_fail (axis_list != NULL);
+ g_return_if_fail (axis_list->data != NULL);
+ g_return_if_fail (IS_GOG_AXIS(axis_list->data));
+ circular_axis = GOG_AXIS(axis_list->data);
+ g_slist_free (axis_list);
+ gog_axis_get_bounds (circular_axis, &circular_min, &circular_max);
+ num_radii = rint (circular_max);
+
+ for (i = 0; i < num_radii; i++) {
+ double angle_rad = i * (2.0 * M_PI / num_radii);
+ draw_axis_from_a_to_b (v, axis,
+ center_x, center_y,
+ center_x + radius * sin (angle_rad),
+ center_y - radius * cos (angle_rad),
+ i == 0);
+ }
+ break;
+ }
+ default :
+ break;
+ }
+
+ gog_renderer_pop_style (v->renderer);
+}
+
+static void
+gog_axis_view_class_init (GogAxisViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+
+ aview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->size_request = gog_axis_view_size_request;
+ view_klass->render = gog_axis_view_render;
+}
+
+static GSF_CLASS (GogAxisView, gog_axis_view,
+ gog_axis_view_class_init, NULL,
+ GOG_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-guru-type-selector.glade
@@ -0,0 +1,350 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window1">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkAlignment" id="type_selector">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">6</property>
+ <property name="right_padding">6</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>_Plot type</b></property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">type_treeview</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="type_treeview">
+ <property name="width_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="subtype_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>_Subtype</b></property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">type_treeview</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="canvas_container">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment10">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkButton" id="sample_button">
+ <property name="visible">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">False</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image27">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-info</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="sample_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Show sample</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="description_title_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Description</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <widget class="GtkLabel" id="description_label">
+ <property name="height_request">60</property>
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Test de description</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">6</property>
+ <property name="ypad">6</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-axis.h
@@ -0,0 +1,113 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-axis.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_AXIS_H
+#define GOG_AXIS_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GOG_AXIS_AT_LOW = -1,
+ GOG_AXIS_IN_MIDDLE = 0,
+ GOG_AXIS_AT_HIGH = 1
+} GogAxisPosition;
+
+typedef enum {
+ AXIS_ELEM_MIN = 0,
+ AXIS_ELEM_MAX,
+ AXIS_ELEM_MAJOR_TICK,
+ AXIS_ELEM_MINOR_TICK,
+ AXIS_ELEM_CROSS_POINT,
+ AXIS_ELEM_MAX_ENTRY
+} GogAxisElemType;
+
+typedef enum {
+ GOG_AXIS_TICK_NONE,
+ GOG_AXIS_TICK_MAJOR,
+ GOG_AXIS_TICK_MINOR
+} GogAxisTickTypes;
+
+typedef struct {
+ double position;
+ GogAxisTickTypes type;
+ char *label;
+} GogAxisTick;
+
+#define GOG_AXIS_TYPE (gog_axis_get_type ())
+#define GOG_AXIS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_AXIS_TYPE, GogAxis))
+#define IS_GOG_AXIS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_AXIS_TYPE))
+
+GType gog_axis_get_type (void);
+
+GogAxisType gog_axis_get_atype (GogAxis const *axis);
+GogAxisPosition gog_axis_get_pos (GogAxis const *axis);
+gboolean gog_axis_is_discrete (GogAxis const *axis);
+gboolean gog_axis_get_bounds (GogAxis const *axis,
+ double *minima, double *maxima);
+unsigned gog_axis_get_ticks (GogAxis *axis, GogAxisTick **ticks);
+GOData *gog_axis_get_labels (GogAxis const *axis,
+ GogPlot **plot_that_labeled_axis);
+double gog_axis_get_entry (GogAxis const *axis, GogAxisElemType i,
+ gboolean *user_defined);
+
+void gog_axis_add_contributor (GogAxis *axis, GogObject *contrib);
+void gog_axis_del_contributor (GogAxis *axis, GogObject *contrib);
+GSList const *gog_axis_contributors (GogAxis *axis);
+void gog_axis_clear_contributors (GogAxis *axis);
+void gog_axis_bound_changed (GogAxis *axis, GogObject *contrib);
+
+void gog_axis_view_padding_request (GogView *v, GogViewPadding *padding,
+ GogViewAllocation *bbox);
+
+typedef struct _GogAxisMapDesc GogAxisMapDesc;
+
+typedef struct {
+ GogAxis *axis;
+ GogAxisMapDesc const *desc;
+ gpointer data;
+ gboolean is_valid; /* Default to FALSE if desc::init == NULL */
+} GogAxisMap;
+
+struct _GogAxisMapDesc {
+ double (*map) (GogAxisMap *map, double value);
+ double (*map_to_canvas) (GogAxisMap *map, double value, gboolean inverted);
+ gboolean (*init) (GogAxisMap *map, double offset, double length);
+ void (*destroy) (GogAxisMap *map);
+ void (*auto_bound) (GogAxis *axis,
+ double minimum, double maximum,
+ double *bound);
+ void (*calc_ticks) (GogAxis *axis,
+ gboolean draw_labels);
+ char const *name;
+ char const *description;
+};
+
+GogAxisMap* gog_axis_map_new (GogAxis *axis, double offset, double length);
+double gog_axis_map (GogAxisMap *map, double x);
+double gog_axis_map_to_canvas (GogAxisMap *map, double x);
+void gog_axis_map_free (GogAxisMap *map);
+gboolean gog_axis_map_is_valid (GogAxisMap *map);
+
+G_END_DECLS
+
+#endif /* GOG_AXIS_H */
--- /dev/null
+++ lib/goffice/graph/gog-graph.h
@@ -0,0 +1,54 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-graph.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_GRAPH_H
+#define GOG_GRAPH_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_GRAPH_TYPE (gog_graph_get_type ())
+#define GOG_GRAPH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRAPH_TYPE, GogGraph))
+#define IS_GOG_GRAPH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRAPH_TYPE))
+
+GType gog_graph_get_type (void);
+GType gog_graph_view_get_type (void);
+
+gboolean gog_graph_validate_chart_layout (GogGraph *graph);
+unsigned gog_graph_num_cols (GogGraph const *graph);
+unsigned gog_graph_num_rows (GogGraph const *graph);
+
+/* convenience wrappers */
+GogGraph *gog_graph_dup (GogGraph const *graph);
+GogTheme *gog_graph_get_theme (GogGraph const *graph);
+void gog_graph_set_theme (GogGraph *graph, GogTheme *theme);
+
+/* data management */
+GSList *gog_graph_get_data (GogGraph const *graph);
+
+/* internal routines for use by series */
+GOData *gog_graph_ref_data (GogGraph *graph, GOData *dat);
+void gog_graph_unref_data (GogGraph *graph, GOData *dat);
+
+G_END_DECLS
+
+#endif /* GOG_GRAPH_H */
--- /dev/null
+++ lib/goffice/graph/gog-view.h
@@ -0,0 +1,89 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-view.h : A sized render engine for an item.
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_VIEW_H
+#define GOG_VIEW_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogView {
+ GObject base;
+
+ GogObject *model;
+
+ GogRenderer *renderer; /* not NULL */
+ GogView *parent; /* potentially NULL */
+ GSList *children;
+
+ GogViewAllocation allocation; /* in renderer units */
+ GogViewAllocation residual; /* left over after compass children are placed */
+ unsigned allocation_valid : 1; /* adjust our layout when child changes size */
+ unsigned child_allocations_valid : 1; /* some children need to adjust their layout */
+ unsigned being_updated : 1;
+};
+
+typedef struct {
+ GObjectClass base;
+
+ gboolean clip;
+
+ /* Virtuals */
+ void (*state_init) (GogView *);
+ void (*size_request) (GogView *, GogViewRequisition *r);
+ void (*size_allocate) (GogView *, GogViewAllocation const *a);
+ void (*render) (GogView *, GogViewAllocation const *bbox);
+ gboolean (*info_at_point) (GogView *, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name);
+} GogViewClass;
+
+#define GOG_VIEW_TYPE (gog_view_get_type ())
+#define GOG_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_VIEW_TYPE, GogView))
+#define IS_GOG_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_VIEW_TYPE))
+#define GOG_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_VIEW_TYPE, GogViewClass))
+#define IS_GOG_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_VIEW_TYPE))
+#define GOG_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_VIEW_TYPE, GogViewClass))
+
+GType gog_view_get_type (void);
+
+GogObject *gog_view_get_model (GogView const *view);
+void gog_view_render (GogView *v, GogViewAllocation const *bbox);
+void gog_view_queue_redraw (GogView *v);
+void gog_view_queue_resize (GogView *v);
+void gog_view_size_request (GogView *v, GogViewRequisition *req);
+void gog_view_size_allocate (GogView *v, GogViewAllocation const *a);
+gboolean gog_view_update_sizes (GogView *v);
+gboolean gog_view_info_at_point (GogView *container, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name);
+GogView *gog_view_find_child_view (GogView const *container,
+ GogObject const *target_model);
+
+/* protected */
+void gog_view_size_child_request (GogView *v,
+ GogViewRequisition const *avail,
+ GogViewRequisition *req);
+
+G_END_DECLS
+
+#endif /* GOG_VIEW_H */
--- /dev/null
+++ lib/goffice/graph/gog-chart.h
@@ -0,0 +1,60 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-chart.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_CHART_H
+#define GOG_CHART_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_CHART_TYPE (gog_chart_get_type ())
+#define GOG_CHART(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_CHART_TYPE, GogChart))
+#define IS_GOG_CHART(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_CHART_TYPE))
+
+GType gog_chart_get_type (void);
+
+gboolean gog_chart_get_position (GogChart const *chart, unsigned *x, unsigned *y,
+ unsigned *cols, unsigned *rows);
+void gog_chart_set_position (GogChart *chart, unsigned x, unsigned y,
+ unsigned cols, unsigned rows);
+
+void gog_chart_request_cardinality_update (GogChart *chart);
+void gog_chart_get_cardinality (GogChart *chart,
+ unsigned *full, unsigned *visible);
+void gog_chart_foreach_elem (GogChart *chart, gboolean only_visible,
+ GogEnumFunc handler, gpointer data);
+GSList *gog_chart_get_plots (GogChart const *chart);
+
+GogAxisSet gog_chart_axis_set (GogChart const *chart);
+gboolean gog_chart_axis_set_is_valid (GogChart const *chart, GogAxisSet type);
+gboolean gog_chart_axis_set_assign (GogChart *chart, GogAxisSet type);
+GSList *gog_chart_get_axis (GogChart const *chart, GogAxisType type);
+
+GogGrid *gog_chart_get_grid (GogChart const *chart);
+
+/* View utils */
+GogViewAllocation const *gog_chart_view_get_plot_area (GogView const *view);
+
+G_END_DECLS
+
+#endif /* GOG_CHART_H */
--- /dev/null
+++ lib/goffice/graph/gog-data-set.h
@@ -0,0 +1,69 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-data-set.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_DATA_SET_H
+#define GOG_DATA_SET_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GOData *data;
+ GogDataset *set;
+ int dim_i;
+ gulong handler;
+} GogDatasetElement;
+typedef struct {
+ GTypeInterface base;
+
+ GogDatasetElement *(*get_elem) (GogDataset const *set, int dim_i);
+ void (*set_dim) (GogDataset *set, int dim_i,
+ GOData *val, GError **err);
+ void (*dims) (GogDataset const *set, int *first, int *last);
+ void (*dim_changed) (GogDataset *set, int dim_i);
+} GogDatasetClass;
+
+#define GOG_DATASET_TYPE (gog_dataset_get_type ())
+#define GOG_DATASET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_DATASET_TYPE, GogDataset))
+#define IS_GOG_DATASET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_DATASET_TYPE))
+#define GOG_DATASET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_DATASET_TYPE, GogDatasetClass))
+#define IS_GOG_DATASET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_DATASET_TYPE))
+#define GOG_DATASET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GOG_DATASET_TYPE, GogDatasetClass))
+
+GType gog_dataset_get_type (void);
+
+void gog_dataset_dims (GogDataset const *set, int *first, int *last);
+GOData *gog_dataset_get_dim (GogDataset const *set, int dim_i);
+void gog_dataset_set_dim (GogDataset *set, int dim_i,
+ GOData *val, GError **err);
+
+/* protected */
+void gog_dataset_finalize (GogDataset *set);
+void gog_dataset_parent_changed (GogDataset *set, gboolean was_set);
+GogDatasetElement *gog_dataset_get_elem (GogDataset const *set, int dim_i);
+void gog_dataset_set_dim_internal (GogDataset *set, int dim_i,
+ GOData *val, GogGraph *graph);
+
+G_END_DECLS
+
+#endif /* GOG_DATA_SET_H */
--- /dev/null
+++ lib/goffice/graph/gog-grid.c
@@ -0,0 +1,110 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-grid.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-grid.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-renderer.h>
+
+// #include <src/gui-util.h>
+#include <gui-util.h>
+#include <glib/gi18n.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+struct _GogGrid {
+ GogStyledObject base;
+};
+typedef GogStyledObjectClass GogGridClass;
+
+
+static GType gog_grid_view_get_type (void);
+static GogViewClass *gview_parent_klass;
+
+static void
+gog_grid_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_FILL | GOG_STYLE_OUTLINE;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_grid_class_init (GogGridClass *klass)
+{
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+
+ gog_klass->view_type = gog_grid_view_get_type ();
+ style_klass->init_style = gog_grid_init_style;
+}
+
+GSF_CLASS (GogGrid, gog_grid,
+ gog_grid_class_init, NULL,
+ GOG_STYLED_OBJECT_TYPE)
+
+/************************************************************************/
+
+typedef GogView GogGridView;
+typedef GogViewClass GogGridViewClass;
+
+#define GOG_GRID_VIEW_TYPE (gog_grid_view_get_type ())
+#define GOG_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRID_VIEW_TYPE, GogGridView))
+#define IS_GOG_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRID_VIEW_TYPE))
+
+static void
+gog_grid_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogGrid *grid = GOG_GRID (view->model);
+ ArtVpath path[6];
+
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ path[0].x = path[1].x = path[4].x = view->allocation.x;
+ path[2].x = path[3].x = path[0].x + view->allocation.w;
+ path[0].y = path[3].y = path[4].y = view->allocation.y;
+ path[1].y = path[2].y = path[0].y + view->allocation.h;
+
+ gog_renderer_push_style (view->renderer, grid->base.style);
+ gog_renderer_draw_sharp_polygon (view->renderer, path, FALSE, NULL);
+ gog_renderer_pop_style (view->renderer);
+ (gview_parent_klass->render) (view, bbox);
+}
+
+static void
+gog_grid_view_class_init (GogGridViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+
+ gview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->render = gog_grid_view_render;
+}
+
+static GSF_CLASS (GogGridView, gog_grid_view,
+ gog_grid_view_class_init, NULL,
+ GOG_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-label.c
@@ -0,0 +1,254 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-label.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-label.h>
+#include <goffice/graph/gog-outlined-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/go-data.h>
+
+// #include <src/gui-util.h>
+#include <gui-util.h>
+#include <glib/gi18n.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkalignment.h>
+
+struct _GogLabel {
+ GogOutlinedObject base;
+
+ GogDatasetElement text;
+ gboolean allow_markup;
+};
+typedef GogStyledObjectClass GogLabelClass;
+
+enum {
+ LABEL_PROP_0,
+ LABEL_PROP_ALLOW_MARKUP,
+};
+
+static GType gog_label_view_get_type (void);
+static GObjectClass *label_parent_klass;
+static GogViewClass *lview_parent_klass;
+
+static void
+gog_label_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogLabel *label = GOG_LABEL (obj);
+
+ switch (param_id) {
+ case LABEL_PROP_ALLOW_MARKUP :
+ label->allow_markup = g_value_get_boolean (value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_label_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogLabel *label = GOG_LABEL (obj);
+
+ switch (param_id) {
+ case LABEL_PROP_ALLOW_MARKUP :
+ g_value_set_boolean (value, label->allow_markup);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_label_finalize (GObject *obj)
+{
+ gog_dataset_finalize (GOG_DATASET (obj));
+ (*label_parent_klass->finalize) (obj);
+}
+
+static gpointer
+gog_label_editor (GogObject *gobj, GogDataAllocator *dalloc, GnmCmdContext *cc)
+{
+ static guint label_pref_page = 0;
+ GtkWidget *notebook = gtk_notebook_new ();
+ GtkWidget *hbox = gtk_hbox_new (FALSE, 12);
+ GtkWidget *alignment = gtk_alignment_new (0, 0, 1, 0);
+
+ gtk_container_set_border_width (GTK_CONTAINER (alignment), 12);
+ gtk_box_pack_start (GTK_BOX (hbox),
+ gtk_label_new_with_mnemonic (_("_Text:")), FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox),
+ gog_data_allocator_editor (dalloc, GOG_DATASET (gobj), 0, GOG_DATA_SCALAR),
+ TRUE, TRUE, 0);
+ gtk_container_add (GTK_CONTAINER (alignment), hbox);
+ gtk_widget_show_all (alignment);
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), alignment,
+ gtk_label_new (_("Data")));
+ gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, notebook);
+ gog_style_handle_notebook (notebook, &label_pref_page);
+ return notebook;
+}
+
+static void
+gog_label_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL | GOG_STYLE_FONT;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_label_class_init (GogLabelClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+
+ label_parent_klass = g_type_class_peek_parent (klass);
+ gobject_klass->set_property = gog_label_set_property;
+ gobject_klass->get_property = gog_label_get_property;
+ gobject_klass->finalize = gog_label_finalize;
+ g_object_class_install_property (gobject_klass, LABEL_PROP_ALLOW_MARKUP,
+ g_param_spec_boolean ("allow-markup", "allow-markup",
+ "Support basic html-ish markup",
+ TRUE, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+
+ gog_klass->editor = gog_label_editor;
+ gog_klass->view_type = gog_label_view_get_type ();
+ style_klass->init_style = gog_label_init_style;
+}
+
+static void
+gog_label_dims (GogDataset const *set, int *first, int *last)
+{
+ *first = *last = 0;
+}
+
+static GogDatasetElement *
+gog_label_get_elem (GogDataset const *set, int dim_i)
+{
+ GogLabel *label = GOG_LABEL (set);
+ return &label->text;
+}
+
+static void
+gog_label_dim_changed (GogDataset *set, int dim_i)
+{
+ gog_object_emit_changed (GOG_OBJECT (set), TRUE);
+}
+
+static void
+gog_label_dataset_init (GogDatasetClass *iface)
+{
+ iface->dims = gog_label_dims;
+ iface->get_elem = gog_label_get_elem;
+ iface->dim_changed = gog_label_dim_changed;
+}
+
+GSF_CLASS_FULL (GogLabel, gog_label,
+ gog_label_class_init, NULL,
+ GOG_OUTLINED_OBJECT_TYPE, 0,
+ GSF_INTERFACE (gog_label_dataset_init, GOG_DATASET_TYPE))
+
+/************************************************************************/
+
+typedef GogOutlinedView GogLabelView;
+typedef GogOutlinedViewClass GogLabelViewClass;
+
+#define GOG_LABEL_VIEW_TYPE (gog_label_view_get_type ())
+#define GOG_LABEL_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_LABEL_VIEW_TYPE, GogLabelView))
+#define IS_GOG_LABEL_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_LABEL_VIEW_TYPE))
+
+static void
+gog_label_view_size_request (GogView *v, GogViewRequisition *req)
+{
+ GogLabel *l = GOG_LABEL (v->model);
+
+ req->w = req->h = 0.;
+ if (l->text.data != NULL) {
+ char const *text = go_data_scalar_get_str (GO_DATA_SCALAR (l->text.data));
+ if (text != NULL) {
+ gog_renderer_push_style (v->renderer, l->base.base.style);
+ gog_renderer_measure_text (v->renderer, text, req);
+ gog_renderer_pop_style (v->renderer);
+ }
+ }
+ lview_parent_klass->size_request (v, req);
+}
+
+static void
+gog_label_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogLabel *l = GOG_LABEL (view->model);
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (view->model);
+ GogStyle *style = l->base.base.style;
+
+ gog_renderer_push_style (view->renderer, style);
+ if (l->text.data != NULL) {
+ char const *text = go_data_scalar_get_str (GO_DATA_SCALAR (l->text.data));
+ if (text != NULL) {
+ double outline = gog_renderer_line_size (
+ view->renderer, goo->base.style->outline.width);
+ if (style->fill.type != GOG_FILL_STYLE_NONE || outline > 0.) {
+ GogViewRequisition req;
+ GogViewAllocation rect;
+ double pad_x = gog_renderer_pt2r_x (view->renderer, goo->padding_pts);
+ double pad_y = gog_renderer_pt2r_y (view->renderer, goo->padding_pts);
+
+ gog_renderer_measure_text (view->renderer, text, &req);
+ rect = view->allocation;
+ rect.w = req.w + 2. * outline + pad_x;
+ rect.h = req.h + 2. * outline + pad_y;
+ gog_renderer_draw_sharp_rectangle (view->renderer, &rect, NULL);
+ }
+ gog_renderer_draw_text (view->renderer, text,
+ &view->residual, GTK_ANCHOR_NW, NULL);
+ }
+ }
+ gog_renderer_pop_style (view->renderer);
+}
+
+static void
+gog_label_view_class_init (GogLabelViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+
+ lview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->size_request = gog_label_view_size_request;
+ view_klass->render = gog_label_view_render;
+}
+
+static GSF_CLASS (GogLabelView, gog_label_view,
+ gog_label_view_class_init, NULL,
+ GOG_OUTLINED_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-data-allocator.c
@@ -0,0 +1,61 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-data-allocator.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/go-data.h>
+
+GType
+gog_data_allocator_get_type (void)
+{
+ static GType gog_data_allocator_type = 0;
+
+ if (!gog_data_allocator_type) {
+ static GTypeInfo const gog_data_allocator_info = {
+ sizeof (GogDataAllocatorClass), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ gog_data_allocator_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GogDataAllocator", &gog_data_allocator_info, 0);
+ }
+
+ return gog_data_allocator_type;
+}
+
+void
+gog_data_allocator_allocate (GogDataAllocator *dalloc, GogPlot *plot)
+{
+ g_return_if_fail (IS_GOG_DATA_ALLOCATOR (dalloc));
+ GOG_DATA_ALLOCATOR_GET_CLASS (dalloc)->allocate (dalloc, plot);
+}
+
+gpointer
+gog_data_allocator_editor (GogDataAllocator *dalloc, GogDataset *set,
+ int dim_i, GogDataType data_type)
+{
+ g_return_val_if_fail (IS_GOG_DATA_ALLOCATOR (dalloc), NULL);
+ return GOG_DATA_ALLOCATOR_GET_CLASS (dalloc)->editor (dalloc, set,
+ dim_i, data_type);
+}
--- /dev/null
+++ lib/goffice/graph/gog-grid-line.h
@@ -0,0 +1,46 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-grid-line.h
+ *
+ * Copyright (C) 2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_GRID_LINE_H
+#define GOG_GRID_LINE_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+//typedef enum {
+// GOG_GRID_LINE_MAJOR,
+// GOG_GRID_LINE_MINOR,
+// GOG_GRID_LINE_TYPES
+//} GogGridLineType;
+
+#define GOG_GRID_LINE_TYPE (gog_grid_line_get_type ())
+#define GOG_GRID_LINE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRID_LINE_TYPE, GogGridLine))
+#define IS_GOG_GRID_LINE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRID_LINE_TYPE))
+
+GType gog_grid_line_get_type (void);
+
+gboolean gog_grid_line_is_minor (GogGridLine *ggl);
+
+G_END_DECLS
+
+#endif /* GOG_GRID_LINE_H */
--- /dev/null
+++ lib/goffice/graph/gog-axis-prefs.glade
@@ -0,0 +1,545 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window2">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">window2</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkHBox" id="axis_pref_box">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkVBox" id="position_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Position</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="axis_low">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Low</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="axis_high">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_High</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">axis_low</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="axis_cross">
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Cross</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">axis_low</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="axis_cross_menu">
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="history">-1</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="mapping_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Mapping</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment9">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="invert-axis">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Invert axis</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="map_type_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="map_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Type:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="map_type_combo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes"></property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkVBox" id="major_tick_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="major_tick_header">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Major ticks</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment10">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="major-tick-labeled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Show Labels</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="major-tick-out">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Outside</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="major-tick-in">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Inside</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="minor_tick_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="minor_tick_header">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Minor ticks</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment11">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="minor-tick-out">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">O_utside</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="minor-tick-in">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">I_nside</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-grid-line.c
@@ -0,0 +1,293 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-grid-line.c
+ *
+ * Copyright (C) 2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <math.h>
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-grid-line.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-renderer.h>
+
+//#include <src/gui-util.h>
+#include <gui-util.h>
+#include <glib/gi18n.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+struct _GogGridLine {
+ GogStyledObject base;
+
+ gboolean is_minor;
+};
+
+typedef GogStyledObjectClass GogGridLineClass;
+
+
+static GType gog_grid_line_view_get_type (void);
+static GogViewClass *gview_parent_klass;
+
+enum {
+ GRID_LINE_PROP_0,
+ GRID_LINE_PROP_IS_MINOR,
+};
+
+gboolean
+gog_grid_line_is_minor (GogGridLine *ggl)
+{
+ g_return_val_if_fail (GOG_GRID_LINE (ggl) != NULL, FALSE);
+
+ return ggl->is_minor;
+}
+
+static void
+gog_grid_line_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_LINE;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_grid_line_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogGridLine *grid_line = GOG_GRID_LINE (obj);
+
+ switch (param_id) {
+ case GRID_LINE_PROP_IS_MINOR:
+ grid_line->is_minor = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_grid_line_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogGridLine *grid_line = GOG_GRID_LINE (obj);
+
+ switch (param_id) {
+ case GRID_LINE_PROP_IS_MINOR:
+ g_value_set_boolean (value, grid_line->is_minor);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_grid_line_class_init (GogGridLineClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+
+ gobject_klass->set_property = gog_grid_line_set_property;
+ gobject_klass->get_property = gog_grid_line_get_property;
+ gog_klass->view_type = gog_grid_line_view_get_type ();
+ style_klass->init_style = gog_grid_line_init_style;
+
+ g_object_class_install_property (gobject_klass, GRID_LINE_PROP_IS_MINOR,
+ g_param_spec_boolean ("is_minor", "is_minor",
+ "Are these minor grid lines", FALSE, G_PARAM_READWRITE));
+}
+
+GSF_CLASS (GogGridLine, gog_grid_line,
+ gog_grid_line_class_init, NULL,
+ GOG_STYLED_OBJECT_TYPE)
+
+/************************************************************************/
+
+typedef GogView GogGridLineView;
+typedef GogViewClass GogGridLineViewClass;
+
+#define GOG_GRID_LINE_VIEW_TYPE (gog_grid_line_view_get_type ())
+#define GOG_GRID_LINE8VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRID_LINE_VIEW_TYPE, GogGridLineView))
+#define IS_GOG_GRID_LINE_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRID_LINE_VIEW_TYPE))
+
+static void
+gog_grid_line_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogGridLine *grid_line = GOG_GRID_LINE (view->model);
+ GSList *axis_list;
+ GogAxis *axis, *circular_axis;
+ GogChart *chart;
+ GogView *chart_view;
+ GogStyle *style;
+ GogAxisType axis_type;
+ GogAxisMap *map = NULL;
+ GogAxisTick *ticks;
+ unsigned tick_nbr, i, j;
+ double center_x, center_y, radius;
+ double circular_min, circular_max;
+ double angle, position;
+ unsigned num_radii;
+ ArtVpath path[3];
+ ArtVpath *c_path;
+ GogViewAllocation const *plot_area;
+ double line_width;
+
+ axis = GOG_AXIS (view->model->parent);
+ g_return_if_fail (axis != NULL);
+ chart = GOG_CHART (view->model->parent->parent);
+ g_return_if_fail (chart != NULL);
+ g_return_if_fail (view->parent != NULL);
+ chart_view = GOG_VIEW (view->parent->parent);
+ g_return_if_fail (chart_view != NULL);
+
+ axis_type = gog_axis_get_atype (axis);
+ tick_nbr = gog_axis_get_ticks (axis, &ticks);
+ if (tick_nbr < 1)
+ return;
+
+ plot_area = gog_chart_view_get_plot_area (chart_view);
+ style = gog_styled_object_get_style (GOG_STYLED_OBJECT (grid_line));
+ gog_renderer_push_style (view->renderer, style);
+ line_width = gog_renderer_line_size (view->renderer, style->line.width);
+
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_END;
+
+ if (line_width > 0) {
+ switch (axis_type) {
+ case GOG_AXIS_X:
+ case GOG_AXIS_Y:
+
+ switch (axis_type) {
+ case GOG_AXIS_X:
+ map = gog_axis_map_new (axis, plot_area->x, plot_area->w);
+ break;
+ case GOG_AXIS_Y:
+ map = gog_axis_map_new (axis, plot_area->y +plot_area->h, -plot_area->h);
+ break;
+ default:
+ return;
+ }
+
+ switch (axis_type) {
+ case GOG_AXIS_X:
+ for (i = 0; i < tick_nbr; i++) {
+ if ((ticks[i].type == GOG_AXIS_TICK_MAJOR && !grid_line->is_minor) ||
+ (ticks[i].type == GOG_AXIS_TICK_MINOR && grid_line->is_minor)) {
+ path[0].y = plot_area->y;
+ path[1].y = plot_area->y + plot_area->h;
+ path[0].x =
+ path[1].x = gog_axis_map_to_canvas (map, ticks[i].position);
+ gog_renderer_draw_sharp_path (view->renderer, path, NULL);
+ }
+ }
+ break;
+ case GOG_AXIS_Y:
+ for (i = 0; i < tick_nbr; i++) {
+ if ((ticks[i].type == GOG_AXIS_TICK_MAJOR && !grid_line->is_minor) ||
+ (ticks[i].type == GOG_AXIS_TICK_MINOR && grid_line->is_minor)) {
+ path[0].x = plot_area->x;
+ path[1].x = plot_area->x + plot_area->w;
+ path[0].y =
+ path[1].y = gog_axis_map_to_canvas (map, ticks[i].position);
+ gog_renderer_draw_sharp_path (view->renderer, path, NULL);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ gog_axis_map_free (map);
+ break;
+
+ case GOG_AXIS_RADIAL:
+ center_x = plot_area->x + (plot_area->w/2);
+ center_y = plot_area->y + (plot_area->h/2);
+ radius = plot_area->h > plot_area->w
+ ? plot_area->w / 2.0
+ : plot_area->h / 2.0;
+ map = gog_axis_map_new (axis, 0., radius);
+
+ axis_list = gog_chart_get_axis (chart, GOG_AXIS_CIRCULAR);
+ if (axis_list == NULL)
+ break;
+ circular_axis = GOG_AXIS (axis_list->data);
+ g_slist_free (axis_list);
+ gog_axis_get_bounds (circular_axis, &circular_min, &circular_max);
+ num_radii = rint (circular_max);
+ if (num_radii < 3) {
+ gog_axis_map_free (map);
+ break;
+ }
+ c_path = g_new (ArtVpath, num_radii + 2);
+ c_path[num_radii + 1].code = ART_END;
+
+ for (i = 0; i < tick_nbr; i++) {
+ if ((ticks[i].type == GOG_AXIS_TICK_MAJOR && !grid_line->is_minor) ||
+ (ticks[i].type == GOG_AXIS_TICK_MINOR && grid_line->is_minor)) {
+ position = gog_axis_map_to_canvas (map, ticks[i].position);
+ c_path[0].x = center_x;
+ c_path[0].y = center_y - position;
+ c_path[0].code = ART_MOVETO;
+ for (j = 1; j < num_radii + 1; j++) {
+ angle = j * 2.0 * M_PI/(num_radii) - M_PI / 2.;
+ c_path[j].x = center_x + cos (angle) * position;
+ c_path[j].y = center_y + sin (angle) * position;
+ c_path[j].code = ART_LINETO;
+ }
+ gog_renderer_draw_path (view->renderer, c_path, NULL);
+ }
+ }
+
+ g_free (c_path);
+ gog_axis_map_free (map);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ gog_renderer_pop_style (view->renderer);
+}
+
+static void
+gog_grid_line_view_class_init (GogGridLineViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+
+ gview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->render = gog_grid_line_view_render;
+}
+
+static GSF_CLASS (GogGridLineView, gog_grid_line_view,
+ gog_grid_line_view_class_init, NULL,
+ GOG_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-styled-object.c
@@ -0,0 +1,226 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-styled-object.c : A base class for objects that have associated styles
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-graph.h>
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+enum {
+ STYLED_OBJECT_PROP_0,
+ STYLED_OBJECT_PROP_STYLE
+};
+
+enum {
+ STYLE_CHANGED,
+ LAST_SIGNAL
+};
+static gulong gog_styled_object_signals [LAST_SIGNAL] = { 0, };
+static GObjectClass *parent_klass;
+
+void
+gog_styled_object_style_changed (GogStyledObject *obj)
+{
+ g_signal_emit (G_OBJECT (obj),
+ gog_styled_object_signals [STYLE_CHANGED], 0, obj->style);
+}
+
+static void
+gog_styled_object_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogStyledObject *gso = GOG_STYLED_OBJECT (obj);
+ gboolean resize = FALSE;
+
+ switch (param_id) {
+
+ case STYLED_OBJECT_PROP_STYLE :
+ resize = gog_styled_object_set_style (gso,
+ g_value_get_object (value));
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+ gog_object_emit_changed (GOG_OBJECT (obj), resize);
+}
+
+static void
+gog_styled_object_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogStyledObject *gso = GOG_STYLED_OBJECT (obj);
+
+ switch (param_id) {
+ case STYLED_OBJECT_PROP_STYLE :
+ g_value_set_object (value, gso->style);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_styled_object_finalize (GObject *obj)
+{
+ GogStyledObject *gso = GOG_STYLED_OBJECT (obj);
+
+ if (gso->style != NULL) {
+ g_object_unref (gso->style);
+ gso->style = NULL;
+ }
+
+ (*parent_klass->finalize) (obj);
+}
+
+static gpointer
+styled_object_editor (GogObject *gobj, GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ return gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, NULL);
+}
+
+static void
+gog_styled_object_parent_changed (GogObject *obj, gboolean was_set)
+{
+ GogObjectClass *gog_object_klass = GOG_OBJECT_CLASS (parent_klass);
+ if (was_set) {
+ GogStyledObject *gso = GOG_STYLED_OBJECT (obj);
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ gso->style, GOG_OBJECT (gso), 0, TRUE);
+ gog_styled_object_apply_theme (gso, gso->style);
+ }
+ gog_object_klass->parent_changed (obj, was_set);
+}
+
+static void
+gog_styled_object_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL; /* default */
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_styled_object_class_init (GogObjectClass *gog_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) gog_klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) gog_klass;
+
+ gog_styled_object_signals [STYLE_CHANGED] = g_signal_new ("style-changed",
+ G_TYPE_FROM_CLASS (gog_klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogStyledObjectClass, style_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, G_TYPE_OBJECT);
+
+ parent_klass = g_type_class_peek_parent (gog_klass);
+ gobject_klass->set_property = gog_styled_object_set_property;
+ gobject_klass->get_property = gog_styled_object_get_property;
+ gobject_klass->finalize = gog_styled_object_finalize;
+ gog_klass->editor = styled_object_editor;
+ gog_klass->parent_changed = gog_styled_object_parent_changed;
+ style_klass->init_style = gog_styled_object_init_style;
+
+ g_object_class_install_property (gobject_klass, STYLED_OBJECT_PROP_STYLE,
+ g_param_spec_object ("style", "style",
+ "GogStyle *",
+ GOG_STYLE_TYPE, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+}
+
+static void
+gog_styled_object_init (GogStyledObject *gso)
+{
+ gso->style = gog_style_new (); /* use the defaults */
+}
+
+GSF_CLASS (GogStyledObject, gog_styled_object,
+ gog_styled_object_class_init, gog_styled_object_init,
+ GOG_OBJECT_TYPE)
+
+gboolean
+gog_styled_object_set_style (GogStyledObject *gso,
+ GogStyle *style)
+{
+ gboolean resize;
+
+ g_return_val_if_fail (GOG_STYLED_OBJECT (gso) != NULL, FALSE);
+
+ if (gso->style == style)
+ return FALSE;
+ style = gog_style_dup (style);
+
+ /* which fields are we interested in for this object */
+ gog_styled_object_apply_theme (gso, style);
+ gog_styled_object_style_changed (gso);
+ resize = gog_style_is_different_size (gso->style, style);
+ if (gso->style != NULL)
+ g_object_unref (gso->style);
+ gso->style = style;
+
+ return resize;
+}
+
+/**
+ * gog_styled_object_get_style :
+ * @gso : #GogStyledObject
+ *
+ * Returns a pointer to @gso's style but does not reference it.
+ **/
+GogStyle *
+gog_styled_object_get_style (GogStyledObject *gso)
+{
+ g_return_val_if_fail (GOG_STYLED_OBJECT (gso) != NULL, NULL);
+ return gso->style;
+}
+
+/**
+ * gog_styled_object_get_auto_style :
+ * @gso : #GogStyledObject
+ *
+ * Returns a new style that is initialized with the auto values for @gso.
+ * Caller is responsible for the result.
+ **/
+GogStyle *
+gog_styled_object_get_auto_style (GogStyledObject *gso)
+{
+ GogStyle *res = gog_style_dup (gso->style);
+ gog_style_force_auto (res);
+ gog_styled_object_apply_theme (gso, res);
+ return res;
+}
+
+void
+gog_styled_object_apply_theme (GogStyledObject *gso, GogStyle *style)
+{
+ GogGraph const *graph = gog_object_get_graph (GOG_OBJECT (gso));
+ if (graph != NULL) {
+ GogStyledObjectClass *klass = GOG_STYLED_OBJECT_GET_CLASS (gso);
+ (klass->init_style) (gso, style);
+ }
+}
--- /dev/null
+++ lib/goffice/graph/gog-plot-impl.h
@@ -0,0 +1,95 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-plot-impl.h : implementation details for the abstract 'plot' interface
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_PLOT_IMPL_H
+#define GOG_PLOT_IMPL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-plot.h>
+#include <goffice/graph/gog-series-impl.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-view.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogPlotDesc {
+ unsigned num_series_min, num_series_max;
+ unsigned num_axis;
+ GogSeriesDesc series;
+};
+
+struct _GogPlot {
+ GogObject base;
+ GogChart *chart; /* potentially NULL */
+
+ GSList *series;
+ unsigned full_cardinality, visible_cardinality;
+ gboolean cardinality_valid;
+ unsigned index_num;
+ gboolean vary_style_by_element;
+
+ GogAxis *axis[GOG_AXIS_TYPES];
+
+ /* Usually a copy from the class but it's here to allow a GogPlotType to
+ * override things without requiring a completely new class */
+ GogPlotDesc desc;
+};
+
+typedef struct {
+ GogObjectClass base;
+
+ GogPlotDesc desc;
+ GType series_type;
+
+ /* Virtuals */
+
+ GogAxisSet (*axis_set_pref) (GogPlot const *plot);
+ gboolean (*axis_set_is_valid) (GogPlot const *plot, GogAxisSet type);
+ gboolean (*axis_set_assign) (GogPlot *plot, GogAxisSet type);
+ GOData *(*axis_get_bounds) (GogPlot *plot, GogAxisType axis,
+ GogPlotBoundInfo *bounds);
+
+ gboolean (*supports_vary_style_by_element) (GogPlot const *plot);
+
+ void (*foreach_elem) (GogPlot *plot, gboolean only_visible,
+ GogEnumFunc handler, gpointer data);
+} GogPlotClass;
+
+#define GOG_PLOT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_PLOT_TYPE, GogPlotClass))
+#define IS_GOG_PLOT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_PLOT_TYPE))
+#define GOG_PLOT_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_PLOT_TYPE, GogPlotClass))
+
+/* protected */
+
+/*****************************************************************************/
+
+#define GOG_PLOT_VIEW_TYPE (gog_plot_view_get_type ())
+#define GOG_PLOT_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PLOT_VIEW_TYPE, GogPlotView))
+#define IS_GOG_PLOT_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PLOT_VIEW_TYPE))
+
+typedef GogView GogPlotView;
+typedef GogViewClass GogPlotViewClass;
+GType gog_plot_view_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_PLOT_GROUP_IMPL_H */
--- /dev/null
+++ lib/goffice/graph/gog-control-foocanvas.c
@@ -0,0 +1,265 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-control-foocanvas.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-control-foocanvas.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-object.h>
+#include <libfoocanvas/foo-canvas-util.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <math.h>
+
+enum {
+ CTRL_FOO_PROP_0,
+ CTRL_FOO_PROP_H,
+ CTRL_FOO_PROP_W,
+ CTRL_FOO_PROP_MODEL,
+ CTRL_FOO_PROP_RENDERER,
+ CTRL_FOO_PROP_LOGICAL_WIDTH_PTS,
+ CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS
+};
+
+static GObjectClass *parent_klass;
+
+#define GOG_CONTROL_FOOCANVAS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_CONTROL_FOOCANVAS_TYPE, GogControlFooCanvasClass))
+#define IS_GOG_CONTROL_FOOCANVAS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_CONTROL_FOOCANVAS_TYPE))
+#define GOG_CONTROL_FOOCANVAS_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((k), GOG_CONTROL_FOOCANVAS_TYPE, GogControlFooCanvasClass))
+
+static void
+gog_control_foocanvas_set_property (GObject *gobject, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (gobject);
+ gboolean setup_renderer = (ctrl->renderer == NULL);
+
+ switch (param_id) {
+ case CTRL_FOO_PROP_H: ctrl->new_h = g_value_get_double (value); break;
+ case CTRL_FOO_PROP_W: ctrl->new_w = g_value_get_double (value); break;
+
+ case CTRL_FOO_PROP_MODEL:
+ if (ctrl->renderer != NULL)
+ g_object_unref (ctrl->renderer);
+ ctrl->renderer = g_object_new (GOG_RENDERER_PIXBUF_TYPE,
+ "model", g_value_get_object (value),
+ NULL);
+ break;
+
+ case CTRL_FOO_PROP_RENDERER:
+ if (ctrl->renderer != NULL)
+ g_object_unref (ctrl->renderer);
+ ctrl->renderer = GOG_RENDERER_PIXBUF (g_value_get_object (value));
+ if (ctrl->renderer != NULL)
+ g_object_ref (ctrl->renderer);
+ break;
+ case CTRL_FOO_PROP_LOGICAL_WIDTH_PTS :
+ g_object_set_property (G_OBJECT (ctrl->renderer),
+ "logical_width_pts", value);
+ break;
+ case CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS :
+ g_object_set_property (G_OBJECT (ctrl->renderer),
+ "logical_height_pts", value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ if (setup_renderer && ctrl->renderer != NULL)
+ g_signal_connect_object (G_OBJECT (ctrl->renderer),
+ "request_update",
+ G_CALLBACK (foo_canvas_item_request_update),
+ ctrl, G_CONNECT_SWAPPED);
+ foo_canvas_item_request_update (FOO_CANVAS_ITEM (ctrl));
+}
+
+static void
+gog_control_foocanvas_get_property (GObject *gobject, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (gobject);
+
+ switch (param_id) {
+ case CTRL_FOO_PROP_H: g_value_set_double (value, ctrl->new_h); break;
+ case CTRL_FOO_PROP_W: g_value_set_double (value, ctrl->new_w); break;
+ case CTRL_FOO_PROP_RENDERER : g_value_set_object (value, ctrl->renderer); break;
+ case CTRL_FOO_PROP_LOGICAL_WIDTH_PTS :
+ g_object_get_property (G_OBJECT (ctrl->renderer),
+ "logical_width_pts", value);
+ break;
+ case CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS :
+ g_object_get_property (G_OBJECT (ctrl->renderer),
+ "logical_height_pts", value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_control_foocanvas_finalize (GObject *obj)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (obj);
+
+ if (ctrl->model != NULL) {
+ g_object_unref (ctrl->model);
+ ctrl->model = NULL;
+ }
+ if (ctrl->renderer != NULL) {
+ g_object_unref (ctrl->renderer);
+ ctrl->renderer = NULL;
+ }
+ (*parent_klass->finalize) (obj);
+}
+
+static void
+gog_control_foocanvas_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *ev)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (item);
+ GdkPixbuf *buffer = gog_renderer_pixbuf_get (ctrl->renderer);
+ GdkRectangle display_rect, draw_rect;
+ GdkRegion *draw_region;
+
+ if (buffer) {
+ display_rect.x = item->x1;
+ display_rect.y = item->y1;
+ display_rect.width = item->x2 - item->x1;
+ display_rect.height = item->y2 - item->y1;
+
+ draw_region = gdk_region_rectangle (&display_rect);
+ gdk_region_intersect (draw_region, ev->region);
+ if (!gdk_region_empty (draw_region)) {
+ gdk_region_get_clipbox (draw_region, &draw_rect);
+ gdk_draw_pixbuf (drawable, NULL, buffer,
+ /* pixbuf 0, 0 is at pix_rect.x, pix_rect.y */
+ draw_rect.x - display_rect.x,
+ draw_rect.y - display_rect.y,
+ draw_rect.x,
+ draw_rect.y,
+ draw_rect.width,
+ draw_rect.height,
+ GDK_RGB_DITHER_NORMAL, 0, 0);
+ }
+ gdk_region_destroy (draw_region);
+ }
+
+ /* we are a canvas group, there could be some children */
+ if (FOO_CANVAS_ITEM_CLASS (parent_klass)->draw)
+ (FOO_CANVAS_ITEM_CLASS (parent_klass)->draw) (item, drawable, ev);
+}
+
+static void
+gog_control_foocanvas_update (FooCanvasItem *item,
+ double i2w_dx, double i2w_dy, gint flags)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (item);
+ gboolean redraw;
+ int x1, x2, y1, y2;
+ int orig_x1 = item->x1, orig_x2 = item->x2, orig_y1 = item->y1, orig_y2 = item->y2;
+
+ if (FOO_CANVAS_ITEM_CLASS (parent_klass)->update)
+ (FOO_CANVAS_ITEM_CLASS (parent_klass)->update) (item, i2w_dx, i2w_dy, flags);
+ /* foo_canvas_group_update wipes the bbox */
+ item->x1 = orig_x1; item->x2 = orig_x2;
+ item->y1 = orig_y1; item->y2 = orig_y2;
+
+ foo_canvas_w2c (item->canvas, ctrl->base.xpos, ctrl->base.ypos, &x1, &y1);
+ foo_canvas_w2c (item->canvas, ctrl->base.xpos + ctrl->new_w, ctrl->base.ypos + ctrl->new_h, &x2, &y2);
+
+ redraw = gog_renderer_pixbuf_update (ctrl->renderer, x2-x1, y2-y1,
+ item->canvas->pixels_per_unit);
+ if (item->x1 != x1 || item->y1 != y1 || item->x2 != x2 || item->y2 != y2)
+ foo_canvas_update_bbox (FOO_CANVAS_ITEM (ctrl), x1, y1, x2, y2);
+ else if (redraw)
+ foo_canvas_item_request_redraw (FOO_CANVAS_ITEM (ctrl));
+}
+
+static void
+gog_control_foocanvas_bounds (FooCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ *x1 = item->x1;
+ *x2 = item->x2;
+ *y1 = item->y1;
+ *y2 = item->y2;
+}
+
+static double
+gog_control_foocanvas_point (FooCanvasItem *item, double x, double y, int cx, int cy,
+ FooCanvasItem **actual_item)
+{
+ *actual_item = item;
+ return 0.;
+}
+
+static void
+gog_control_foocanvas_class_init (GogControlFooCanvasClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ FooCanvasItemClass *item_klass = (FooCanvasItemClass *) klass;
+
+ parent_klass = g_type_class_peek_parent (klass);
+
+ gobject_klass->set_property = gog_control_foocanvas_set_property;
+ gobject_klass->get_property = gog_control_foocanvas_get_property;
+ gobject_klass->finalize = gog_control_foocanvas_finalize;
+ item_klass->draw = gog_control_foocanvas_draw;
+ item_klass->update = gog_control_foocanvas_update;
+ item_klass->bounds = gog_control_foocanvas_bounds;
+ item_klass->point = gog_control_foocanvas_point;
+
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_H,
+ g_param_spec_double ("h", _("H"), _("Height"),
+ 0, G_MAXDOUBLE, 100., G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_W,
+ g_param_spec_double ("w", _("W"), _("Width"),
+ 0, G_MAXDOUBLE, 100., G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_MODEL,
+ g_param_spec_object ("model", "model",
+ "the GogObject this object displays",
+ GOG_OBJECT_TYPE, G_PARAM_WRITABLE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_RENDERER,
+ g_param_spec_object ("renderer", "renderer",
+ "the GogRendererPixbuf being displayed",
+ GOG_RENDERER_PIXBUF_TYPE, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_LOGICAL_WIDTH_PTS,
+ g_param_spec_double ("logical_width_pts", "Logical Width Pts",
+ "Logical width of the drawing area in pts",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS,
+ g_param_spec_double ("logical_height_pts", "Logical Height Pts",
+ "Logical height of the drawing area in pts",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
+}
+
+static void
+gog_control_foocanvas_init (GogControlFooCanvas *ctrl)
+{
+ ctrl->new_h = ctrl->new_w = 0.;
+}
+
+GSF_CLASS (GogControlFooCanvas, gog_control_foocanvas,
+ gog_control_foocanvas_class_init, gog_control_foocanvas_init,
+ FOO_TYPE_CANVAS_GROUP)
+
--- /dev/null
+++ lib/goffice/graph/gog-series.c
@@ -0,0 +1,801 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-graph-data.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-series-impl.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/graph/gog-error-bar.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+//#include <src/gui-util.h>
+#include <gui-util.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkhseparator.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkspinbutton.h>
+#include <gtk/gtkcheckbutton.h>
+
+#include <string.h>
+
+int gog_series_get_valid_element_index (GogSeries const *series, int old_index, int desired_index);
+
+/*****************************************************************************/
+static GObjectClass *gse_parent_klass;
+
+enum {
+ ELEMENT_PROP_0,
+ ELEMENT_INDEX
+};
+
+static gint element_compare (GogSeriesElement *gse_a, GogSeriesElement *gse_b)
+{
+ return gse_a->index - gse_b->index;
+}
+
+static void
+gog_series_element_set_index (GogSeriesElement *gse, int ind)
+{
+ gse->index = ind;
+ gog_styled_object_apply_theme (&gse->base, gse->base.style);
+ gog_styled_object_style_changed (&gse->base);
+}
+
+static void
+gog_series_element_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogSeriesElement *gse = GOG_SERIES_ELEMENT (obj);
+ GogObject *gobj = GOG_OBJECT (obj);
+
+ switch (param_id) {
+ case ELEMENT_INDEX :
+ gog_series_element_set_index (gse, g_value_get_int (value));
+ if (gobj->parent != NULL) {
+ GogSeries *series = GOG_SERIES (gobj->parent);
+ series->overrides = g_list_remove (series->overrides, gse);
+ series->overrides = g_list_insert_sorted (series->overrides, gse,
+ (GCompareFunc) element_compare);
+ }
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_series_element_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogSeriesElement *gse = GOG_SERIES_ELEMENT (obj);
+
+ switch (param_id) {
+ case ELEMENT_INDEX :
+ g_value_set_int (value, gse->index);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+cb_index_changed (GtkSpinButton *spin_button, GogSeriesElement *element)
+{
+ int index;
+ int value = gtk_spin_button_get_value (spin_button);
+
+ if ((int) element->index == value)
+ return;
+
+ index = gog_series_get_valid_element_index (
+ GOG_SERIES (gog_object_get_parent (GOG_OBJECT (element))),
+ element->index, value);
+
+ if (index != value)
+ gtk_spin_button_set_value (spin_button, index);
+
+ g_object_set (element, "index", (int) index, NULL);
+}
+
+static gpointer
+gog_series_element_editor (GogObject *gobj,
+ GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ static guint series_element_pref_page = 1;
+ GtkWidget *w, *vbox, *spin_button = NULL;
+ gpointer gse_editor = NULL;
+ GogSeriesElementClass *klass = GOG_SERIES_ELEMENT_GET_CLASS (gobj);
+
+ if (klass->gse_editor)
+ gse_editor = (*klass->gse_editor) (gobj, cc);
+
+ if (gse_editor == NULL)
+ return gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, NULL);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ w = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (w), gtk_label_new (_("Index:")),
+ FALSE, FALSE, 0);
+ spin_button = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_button),
+ GOG_SERIES_ELEMENT(gobj)->index);
+ g_signal_connect (G_OBJECT (spin_button),
+ "value_changed",
+ G_CALLBACK (cb_index_changed), gobj);
+
+ gtk_box_pack_start(GTK_BOX (w), spin_button, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (gse_editor), FALSE, FALSE, 0);
+ gtk_widget_show_all (vbox);
+ w = gtk_notebook_new ();
+ gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, w);
+ gtk_notebook_append_page (GTK_NOTEBOOK (w), vbox,
+ gtk_label_new (_("Settings")));
+ gog_style_handle_notebook (w, &series_element_pref_page);
+
+ return w;
+}
+
+static void
+gog_series_element_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ GogSeries const *series = GOG_SERIES (GOG_OBJECT (gso)->parent);
+ GogStyle *parent_style;
+
+ g_return_if_fail (series != NULL);
+
+ parent_style = gog_styled_object_get_style (GOG_STYLED_OBJECT (series));
+ style->interesting_fields = parent_style->interesting_fields;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), GOG_SERIES_ELEMENT (gso)->index, FALSE);
+}
+
+static void
+gog_series_element_class_init (GogSeriesElementClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+ gse_parent_klass = g_type_class_peek_parent (klass);
+
+ gobject_klass->set_property = gog_series_element_set_property;
+ gobject_klass->get_property = gog_series_element_get_property;
+
+ gog_klass->editor = gog_series_element_editor;
+ style_klass->init_style = gog_series_element_init_style;
+
+ gog_klass->use_parent_as_proxy = TRUE;
+
+ g_object_class_install_property (gobject_klass, ELEMENT_INDEX,
+ g_param_spec_int ("index", "index",
+ "Index of the corresponding data element",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT | GOG_PARAM_FORCE_SAVE));
+}
+
+GSF_CLASS (GogSeriesElement, gog_series_element,
+ gog_series_element_class_init, NULL /*gog_series_element_init*/,
+ GOG_STYLED_OBJECT_TYPE)
+
+
+/*****************************************************************************/
+
+static GObjectClass *series_parent_klass;
+
+enum {
+ SERIES_PROP_0,
+ SERIES_HAS_LEGEND
+};
+
+static gboolean
+role_series_element_can_add (GogObject const *parent)
+{
+ GogSeriesClass *klass = GOG_SERIES_GET_CLASS (parent);
+
+ return ((gog_series_get_valid_element_index(GOG_SERIES (parent), -1, 0) >= 0) &&
+ (klass->series_element_type > 0));
+}
+
+static GogObject *
+role_series_element_allocate (GogObject *series)
+{
+ GogSeriesClass *klass = GOG_SERIES_GET_CLASS (series);
+ GType type = klass->series_element_type;
+ GogObject *gse;
+
+ if (type == 0)
+ type = GOG_SERIES_ELEMENT_TYPE;
+
+ gse = g_object_new (type, NULL);
+ if (gse != NULL)
+ gog_series_element_set_index (GOG_SERIES_ELEMENT (gse),
+ gog_series_get_valid_element_index (GOG_SERIES (series), -1, 0));
+ return gse;
+}
+
+static void
+role_series_element_post_add (GogObject *parent, GogObject *child)
+{
+ GogSeries *series = GOG_SERIES (parent);
+ gog_styled_object_set_style (GOG_STYLED_OBJECT (child),
+ gog_styled_object_get_style (GOG_STYLED_OBJECT (parent)));
+ series->overrides = g_list_insert_sorted (series->overrides, child,
+ (GCompareFunc) element_compare);
+}
+
+static void
+role_series_element_pre_remove (GogObject *parent, GogObject *child)
+{
+ GogSeries *series = GOG_SERIES (parent);
+ series->overrides = g_list_remove (series->overrides, child);
+}
+
+static void
+gog_series_finalize (GObject *obj)
+{
+ GogSeries *series = GOG_SERIES (obj);
+
+ if (series->values != NULL) {
+ gog_dataset_finalize (GOG_DATASET (obj));
+ g_free (series->values - 1); /* it was aliased */
+ series->values = NULL;
+ }
+
+ g_list_free (series->overrides);
+
+ (*series_parent_klass->finalize) (obj);
+}
+
+static void
+gog_series_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogSeries *series = GOG_SERIES (obj);
+ gboolean b_tmp;
+
+ switch (param_id) {
+ case SERIES_HAS_LEGEND :
+ b_tmp = g_value_get_boolean (value);
+ if (series->has_legend ^ b_tmp) {
+ series->has_legend = b_tmp;
+ if (series->plot != NULL)
+ gog_plot_request_cardinality_update (series->plot);
+ }
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_series_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogSeries *series = GOG_SERIES (obj);
+
+ switch (param_id) {
+ case SERIES_HAS_LEGEND :
+ g_value_set_boolean (value, series->has_legend);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static unsigned
+make_dim_editor (GtkTable *table, unsigned row, GtkWidget *editor,
+ char const *name, GogSeriesPriority priority, gboolean is_shared)
+{
+ char *txt = g_strdup_printf (
+ ((priority != GOG_SERIES_REQUIRED) ? "(_%s):" : "_%s:"), _(name));
+ GtkWidget *label = gtk_label_new_with_mnemonic (txt);
+ g_free (txt);
+
+ gtk_table_attach (table, label,
+ 0, 1, row, row+1, GTK_FILL, 0, 0, 0);
+ gtk_table_attach (table, editor,
+ 1, 2, row, row+1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), editor);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+
+ gnm_setup_label_atk (label, editor);
+
+ return row + 1;
+}
+
+static void
+cb_show_in_legend (GtkToggleButton *b, GObject *series)
+{
+ g_object_set (series,
+ "has-legend", gtk_toggle_button_get_active (b),
+ NULL);
+}
+
+static gpointer
+gog_series_editor (GogObject *gobj,
+ GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ static guint series_pref_page = 1;
+ GtkWidget *w;
+ GtkTable *table;
+ unsigned i, row = 0;
+ gboolean has_shared = FALSE;
+ GogSeries *series = GOG_SERIES (gobj);
+ GogDataset *set = GOG_DATASET (gobj);
+ GogSeriesDesc const *desc;
+ GogSeriesClass *klass = GOG_SERIES_GET_CLASS (gobj);
+ GogDataType data_type;
+
+ g_return_val_if_fail (series->plot != NULL, NULL);
+
+ /* Are there any shared dimensions */
+ desc = &series->plot->desc.series;
+ for (i = 0; i < desc->num_dim; i++)
+ if (desc->dim[i].is_shared) {
+ has_shared = TRUE;
+ break;
+ }
+
+ w = gtk_table_new (desc->num_dim + (has_shared ? 2 : 1), 2, FALSE);
+ table = GTK_TABLE (w);
+ gtk_table_set_row_spacings (table, 6);
+ gtk_table_set_col_spacings (table, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+
+ row = make_dim_editor (table, row,
+ gog_data_allocator_editor (dalloc, set, -1, GOG_DATA_SCALAR),
+ N_("Name"), TRUE, FALSE);
+
+ /* first the unshared entries */
+ for (i = 0; i < desc->num_dim; i++) {
+ data_type = (desc->dim[i].val_type == GOG_DIM_MATRIX)?
+ GOG_DATA_MATRIX: GOG_DATA_VECTOR;
+ if (!desc->dim[i].is_shared && (desc->dim[i].priority != GOG_SERIES_ERRORS))
+ row = make_dim_editor (table, row,
+ gog_data_allocator_editor (dalloc, set, i, data_type),
+ desc->dim[i].name, desc->dim[i].priority, FALSE);
+ }
+
+ if (has_shared) {
+ gtk_table_attach (table, gtk_hseparator_new (),
+ 0, 2, row, row+1, GTK_FILL, 0, 0, 0);
+ row++;
+ }
+
+ /* then the shared entries */
+ for (i = 0; i < desc->num_dim; i++) {
+ data_type = (desc->dim[i].val_type == GOG_DIM_MATRIX)?
+ GOG_DATA_MATRIX: GOG_DATA_VECTOR;
+ if (desc->dim[i].is_shared)
+ row = make_dim_editor (table, row,
+ gog_data_allocator_editor (dalloc, set, i, data_type),
+ desc->dim[i].name, desc->dim[i].priority, TRUE);
+ }
+
+ gtk_table_attach (table, gtk_hseparator_new (),
+ 0, 2, row, row+1, GTK_FILL, 0, 0, 0);
+ row++;
+ w = gtk_check_button_new_with_mnemonic ("_Show in Legend");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
+ gog_series_has_legend (series));
+ g_signal_connect (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_show_in_legend), series);
+ gtk_table_attach (table, w,
+ 0, 2, row, row+1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show_all (GTK_WIDGET (table));
+
+ w = gtk_notebook_new ();
+
+ if (klass->populate_editor != NULL)
+ (klass->populate_editor) (GOG_SERIES (set), GTK_NOTEBOOK (w), dalloc, cc);
+
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (w), GTK_WIDGET (table),
+ gtk_label_new (_("Data")));
+ gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, w);
+ gog_style_handle_notebook (w, &series_pref_page);
+ return w;
+}
+
+static void
+gog_series_update (GogObject *obj)
+{
+ GogSeries *series = GOG_SERIES (obj);
+ series->needs_recalc = FALSE;
+}
+
+static void
+gog_series_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ GogSeries const *series = (GogSeries const *)gso;
+ style->interesting_fields = series->plot->desc.series.style_fields;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), series->index, FALSE);
+}
+
+static void
+gog_series_class_init (GogSeriesClass *klass)
+{
+ static GogObjectRole const roles[] = {
+ { N_("Point"), "GogSeriesElement", 0,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_series_element_can_add, NULL,
+ role_series_element_allocate,
+ role_series_element_post_add,
+ role_series_element_pre_remove, NULL },
+ };
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+
+ series_parent_klass = g_type_class_peek_parent (klass);
+ gobject_klass->finalize = gog_series_finalize;
+ gobject_klass->set_property = gog_series_set_property;
+ gobject_klass->get_property = gog_series_get_property;
+
+ gog_klass->editor = gog_series_editor;
+ gog_klass->update = gog_series_update;
+ style_klass->init_style = gog_series_init_style;
+ /* series do not have views, so just forward signals from the plot */
+ gog_klass->use_parent_as_proxy = TRUE;
+
+ gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+
+ g_object_class_install_property (gobject_klass, SERIES_HAS_LEGEND,
+ g_param_spec_boolean ("has-legend", "has-legend",
+ "Should the series show up in legends",
+ TRUE,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+}
+
+static void
+gog_series_init (GogSeries *series)
+{
+ series->is_valid = FALSE;
+ series->has_legend = TRUE;
+ series->plot = NULL;
+ series->values = NULL;
+ series->index = -1;
+}
+
+static void
+gog_series_dataset_dims (GogDataset const *set, int *first, int *last)
+{
+ GogSeries const *series = GOG_SERIES (set);
+ *first = -1;
+ if (series->plot == NULL || series->values == NULL)
+ *last = -2;
+ else
+ *last = (series->plot->desc.series.num_dim - 1);
+}
+
+static GogDatasetElement *
+gog_series_dataset_get_elem (GogDataset const *set, int dim_i)
+{
+ GogSeries const *series = GOG_SERIES (set);
+ g_return_val_if_fail ((int)series->plot->desc.series.num_dim > dim_i, NULL);
+ g_return_val_if_fail (dim_i >= -1, NULL);
+ return series->values + dim_i;
+}
+
+static void
+gog_series_dataset_set_dim (GogDataset *set, int dim_i,
+ GOData *val, GError **err)
+{
+ GogSeriesDesc const *desc;
+ GogSeries *series = GOG_SERIES (set);
+ GogGraph *graph = gog_object_get_graph (GOG_OBJECT (series));
+
+ g_return_if_fail (GOG_PLOT (series->plot) != NULL);
+
+ if (dim_i < 0) {
+ char *name = NULL;
+ if (NULL != series->values[-1].data)
+ name = g_strdup (go_data_scalar_get_str (
+ GO_DATA_SCALAR (series->values[-1].data)));
+ gog_object_set_name (GOG_OBJECT (series), name, err);
+ return;
+ }
+
+ gog_series_check_validity (series);
+
+ /* clone shared dimensions into other series in the plot, and
+ * invalidate if necessary */
+ desc = &series->plot->desc.series;
+ if (desc->dim[dim_i].is_shared) {
+ GSList *ptr = series->plot->series;
+
+ val = series->values[dim_i].data;
+ for (; ptr != NULL ; ptr = ptr->next) {
+ gog_dataset_set_dim_internal (GOG_DATASET (ptr->data),
+ dim_i, val, graph);
+ gog_series_check_validity (GOG_SERIES (ptr->data));
+ }
+ }
+}
+
+static void
+gog_series_dataset_dim_changed (GogDataset *set, int dim_i)
+{
+ GogSeries *series = GOG_SERIES (set);
+ if (dim_i >= 0) {
+ GogSeriesClass *klass = GOG_SERIES_GET_CLASS (set);
+
+ if (!series->needs_recalc) {
+ series->needs_recalc = TRUE;
+ gog_object_emit_changed (GOG_OBJECT (set), FALSE);
+ }
+ if (klass->dim_changed != NULL)
+ (klass->dim_changed) (GOG_SERIES (set), dim_i);
+
+ gog_object_request_update (GOG_OBJECT (set));
+ } else {
+ GOData *name_src = series->values[-1].data;
+ char const *name = (name_src != NULL)
+ ? go_data_scalar_get_str (GO_DATA_SCALAR (name_src)) : NULL;
+ gog_object_set_name (GOG_OBJECT (set), g_strdup (name), NULL);
+ }
+}
+
+static void
+gog_series_dataset_init (GogDatasetClass *iface)
+{
+ iface->get_elem = gog_series_dataset_get_elem;
+ iface->set_dim = gog_series_dataset_set_dim;
+ iface->dims = gog_series_dataset_dims;
+ iface->dim_changed = gog_series_dataset_dim_changed;
+}
+
+GSF_CLASS_FULL (GogSeries, gog_series,
+ gog_series_class_init, gog_series_init,
+ GOG_STYLED_OBJECT_TYPE, 0,
+ GSF_INTERFACE (gog_series_dataset_init, GOG_DATASET_TYPE))
+
+/**
+ * gog_series_get_plot :
+ * @series : #GogSeries
+ *
+ * Returns the possibly NULL plot that contains this series.
+ **/
+GogPlot *
+gog_series_get_plot (GogSeries const *series)
+{
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, NULL);
+ return series->plot;
+}
+
+/**
+ * gog_series_is_valid :
+ * @series : #GogSeries
+ *
+ * Returns the current cached validity. Does not recheck
+ **/
+gboolean
+gog_series_is_valid (GogSeries const *series)
+{
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, FALSE);
+ return series->is_valid;
+}
+
+/**
+ * gog_series_check_validity :
+ * @series : #GogSeries
+ *
+ * Updates the is_valid flag for a series.
+ * This is an internal utility that should not really be necessary for general
+ * usage.
+ **/
+void
+gog_series_check_validity (GogSeries *series)
+{
+ unsigned i;
+ GogSeriesDesc const *desc;
+
+ g_return_if_fail (GOG_SERIES (series) != NULL);
+ g_return_if_fail (GOG_PLOT (series->plot) != NULL);
+
+ desc = &series->plot->desc.series;
+ for (i = series->plot->desc.series.num_dim; i-- > 0; )
+ if (series->values[i].data == NULL &&
+ desc->dim[i].priority == GOG_SERIES_REQUIRED) {
+ series->is_valid = FALSE;
+ return;
+ }
+ series->is_valid = TRUE;
+}
+
+/**
+ * gog_series_has_legend :
+ * @series : #GogSeries
+ *
+ * Returns TRUE if the series has a visible legend entry
+ **/
+gboolean
+gog_series_has_legend (GogSeries const *series)
+{
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, FALSE);
+ return series->has_legend;
+}
+
+/**
+ * gog_series_set_index :
+ * @series : #GogSeries
+ * @index :
+ * @is_manual :
+ *
+ * if @index >= 0 attempt to assign the new index. Auto
+ * indicies (@is_manual == FALSE) will not override the current
+ * index if it is manual. An @index < 0, will reset the index to
+ * automatic and potentially queue a revaluation of the parent
+ * chart's cardinality.
+ **/
+void
+gog_series_set_index (GogSeries *series, int ind, gboolean is_manual)
+{
+ g_return_if_fail (GOG_SERIES (series) != NULL);
+
+ if (ind < 0) {
+ if (series->manual_index && series->plot != NULL)
+ gog_plot_request_cardinality_update (series->plot);
+ series->manual_index = FALSE;
+ return;
+ }
+
+ if (is_manual)
+ series->manual_index = TRUE;
+ else if (series->manual_index)
+ return;
+
+ series->index = ind;
+ gog_styled_object_apply_theme (&series->base, series->base.style);
+ gog_styled_object_style_changed (GOG_STYLED_OBJECT (series));
+}
+
+/**
+ * gog_series_get_name :
+ * @series : #GogSeries
+ *
+ * Returns the _src_ of the name associated with the series
+ * NOTE : this is _NOT_ the actual name
+ * no references are added on the result.
+ **/
+GODataScalar *
+gog_series_get_name (GogSeries const *series)
+{
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, NULL);
+ return GO_DATA_SCALAR (series->values[-1].data);
+}
+
+/**
+ * gog_series_set_name :
+ * @series : #GogSeries
+ * @name_src : #GOData
+ * @err : #Gerror
+ *
+ * Absorbs a ref to @name_src
+ **/
+void
+gog_series_set_name (GogSeries *series, GODataScalar *name_src, GError **err)
+{
+ gog_dataset_set_dim (GOG_DATASET (series),
+ -1, GO_DATA (name_src), NULL);
+}
+
+/**
+ * gog_series_set_dim :
+ * @series : #GogSeries
+ * @dim_i :
+ * @val : #GOData
+ * @err : optional #Gerror pointer
+ *
+ * Absorbs a ref to @val
+ **/
+void
+gog_series_set_dim (GogSeries *series, int dim_i, GOData *val, GError **err)
+{
+ gog_dataset_set_dim (GOG_DATASET (series), dim_i, val, err);
+}
+
+/**
+ * gog_series_num_elements :
+ * @series : #GogSeries
+ *
+ * Returns the number of elements in the series
+ **/
+unsigned
+gog_series_num_elements (GogSeries const *series)
+{
+ return series->num_elements;
+}
+
+GList const *
+gog_series_get_overrides (GogSeries const *series)
+{
+ return series->overrides;
+}
+
+int
+gog_series_get_valid_element_index (GogSeries const *series, int old_index, int desired_index)
+{
+ int index;
+ GList *ptr;
+
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, -1);
+
+ if ((desired_index >= (int) series->num_elements) ||
+ (desired_index < 0))
+ return old_index;
+
+ if (desired_index > old_index)
+ for (ptr = series->overrides; ptr != NULL; ptr = ptr->next) {
+ index = GOG_SERIES_ELEMENT (ptr->data)->index;
+ if (index > desired_index)
+ break;
+ if (index == desired_index)
+ desired_index++;
+ }
+ else
+ for (ptr = g_list_last (series->overrides); ptr != NULL; ptr = ptr->prev) {
+ index = GOG_SERIES_ELEMENT (ptr->data)->index;
+ if (index < desired_index)
+ break;
+ if (index == desired_index)
+ desired_index--;
+ }
+
+ if ((desired_index >= 0) &&
+ (desired_index < (int) series->num_elements))
+ return desired_index;
+
+ return old_index;
+}
+
+GogSeriesElement *
+gog_series_get_element (GogSeries const *series, int index)
+{
+ GList *ptr;
+ GogSeriesElement *element;
+
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, NULL);
+
+ for (ptr = series->overrides; ptr != NULL; ptr = ptr->next) {
+ element = GOG_SERIES_ELEMENT (ptr->data);
+ if ((int) element->index == index)
+ return element;
+ }
+
+ return FALSE;
+}
--- /dev/null
+++ lib/goffice/graph/gog-guru.c
@@ -0,0 +1,1413 @@
+/* vim: set sw=8: */
+
+/*
+ * go-graph-guru.c: The Graph guru
+ *
+ * Copyright (C) 2000-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <goffice/goffice-config.h>
+
+#include <goffice/graph/gog-guru.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-plot.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-plot-engine.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/gog-control-foocanvas.h>
+
+#include <glib/gi18n.h>
+#include <gui-util.h>
+
+#include <libxml/parser.h>
+#include <libfoocanvas/foo-canvas.h>
+#include <libfoocanvas/foo-canvas-pixbuf.h>
+#include <libfoocanvas/foo-canvas-rect-ellipse.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreestore.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkframe.h>
+#include <gtk/gtkviewport.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkcellrendererpixbuf.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkliststore.h>
+#include <string.h>
+
+typedef struct _GraphGuruState GraphGuruState;
+typedef struct _GraphGuruTypeSelector GraphGuruTypeSelector;
+
+struct _GraphGuruState {
+ GogGraph *graph;
+ GogChart *chart;
+ GogPlot *plot;
+
+ GnmCmdContext *cc;
+ GogDataAllocator *dalloc;
+ GClosure *register_closure;
+
+ /* GUI accessors */
+ GladeXML *gui;
+ GtkWidget *dialog;
+ GtkWidget *button_cancel;
+ GtkWidget *button_navigate;
+ GtkWidget *button_ok;
+ GtkNotebook *steps;
+ GtkWidget *add_menu, *delete_button;
+
+ FooCanvasItem *sample_graph_item;
+
+ GtkContainer *prop_container;
+ GtkTreeSelection *prop_selection;
+ GtkTreeView *prop_view;
+ GtkTreeStore *prop_model;
+ GtkTreeIter prop_iter;
+ GogObject *prop_object;
+
+ GraphGuruTypeSelector *type_selector;
+
+ struct {
+ GtkWidget *inc, *dec, *first, *last, *menu;
+ } prec;
+ /* internal state */
+ int current_page, initial_page;
+ gboolean valid;
+ gboolean updating;
+ gboolean fmt_page_initialized;
+ gboolean editing;
+
+ /* hackish reuse of State as a closure */
+ GogObject *search_target, *new_child;
+};
+
+struct _GraphGuruTypeSelector {
+ GladeXML *gui;
+ GtkWidget *canvas;
+ GtkWidget *sample_button;
+ GtkLabel *label;
+ GtkTreeView *list_view;
+ GtkListStore *model;
+ FooCanvasItem *selector;
+
+ FooCanvasItem *sample_graph_item;
+
+ GraphGuruState *state;
+
+ FooCanvasGroup *graph_group;
+
+ xmlNode const *plots;
+ GogPlotFamily *current_family;
+ GogPlotType *current_type;
+ FooCanvasGroup const *current_family_item;
+ FooCanvasItem const *current_minor_item;
+};
+
+enum {
+ PLOT_FAMILY_TYPE_IMAGE,
+ PLOT_FAMILY_TYPE_NAME,
+ PLOT_FAMILY_TYPE_CANVAS_GROUP,
+ PLOT_FAMILY_NUM_COLUMNS
+};
+enum {
+ PLOT_ATTR_NAME,
+ PLOT_ATTR_OBJECT,
+ PLOT_ATTR_NUM_COLUMNS
+};
+
+#define MINOR_PIXMAP_WIDTH 64
+#define MINOR_PIXMAP_HEIGHT 60
+#define BORDER 5
+
+#define PLOT_TYPE_KEY "plot_type"
+#define FIRST_MINOR_TYPE "first_minor_type"
+#define ROLE_KEY "role"
+#define STATE_KEY "plot_type"
+
+static GdkPixbuf *
+get_pixbuf (char const *image_file)
+{
+ static GHashTable *cache = NULL;
+ GdkPixbuf *pixbuf;
+
+ if (cache != NULL) {
+ pixbuf = g_hash_table_lookup (cache, image_file);
+ if (pixbuf != NULL)
+ return pixbuf;
+ } else
+ cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_object_unref);
+
+ pixbuf = gnumeric_load_pixbuf (image_file);
+ g_hash_table_insert (cache, (gpointer)image_file, pixbuf);
+
+ return pixbuf;
+}
+
+static void
+get_pos (int col, int row, double *x, double *y)
+{
+ *x = (col-1) * (MINOR_PIXMAP_WIDTH + BORDER) + BORDER;
+ *y = (row-1) * (MINOR_PIXMAP_HEIGHT + BORDER) + BORDER;
+}
+
+/*
+ * graph_typeselect_minor :
+ *
+ * @typesel :
+ * @item : A CanvasItem in the selector.
+ *
+ * Moves the typesel::selector overlay above the @item.
+ * Assumes that the item is visible
+ */
+static void
+graph_typeselect_minor (GraphGuruTypeSelector *typesel, FooCanvasItem *item)
+{
+ GraphGuruState *s = typesel->state;
+ GogPlotType *type;
+ double x1, y1, x2, y2;
+ gboolean enable_next_button;
+ GogPlot *plot;
+
+ if (typesel->current_minor_item == item)
+ return;
+
+ type = g_object_get_data (G_OBJECT (item), PLOT_TYPE_KEY);
+
+ g_return_if_fail (type != NULL);
+
+ typesel->current_type = type;
+ typesel->current_minor_item = item;
+ foo_canvas_item_get_bounds (item, &x1, &y1, &x2, &y2);
+ foo_canvas_item_set (FOO_CANVAS_ITEM (typesel->selector),
+ "x1", x1-1., "y1", y1-1.,
+ "x2", x2+1., "y2", y2+1.,
+ NULL);
+ gtk_label_set_text (typesel->label, _(type->description));
+ gtk_widget_set_sensitive (typesel->sample_button, TRUE);
+
+ enable_next_button = (s->plot == NULL);
+
+ plot = gog_plot_new_by_type (type);
+
+ g_return_if_fail (plot != NULL);
+
+ if (s->plot != NULL) {
+ GogObject *obj = GOG_OBJECT (s->plot);
+ gog_object_clear_parent (obj);
+ g_object_unref (obj);
+ }
+
+ gog_object_add_by_name (GOG_OBJECT (s->chart),
+ "Plot", GOG_OBJECT (s->plot = plot));
+#if 0
+ if (s->original_plot != NULL &&
+ !gog_plot_make_similar (s->plot, s->original_plot))
+ return;
+#endif
+
+ if (s->dalloc != NULL)
+ gog_data_allocator_allocate (s->dalloc, s->plot);
+
+ if (s->current_page == 0 && enable_next_button)
+ gtk_widget_set_sensitive (s->button_navigate, TRUE);
+}
+
+static gboolean
+graph_typeselect_minor_x_y (GraphGuruTypeSelector *typesel,
+ double x, double y)
+{
+ FooCanvasItem *item = foo_canvas_get_item_at (
+ FOO_CANVAS (typesel->canvas), x, y);
+
+ if (item != NULL) {
+ if(item != typesel->selector)
+ graph_typeselect_minor (typesel, item);
+ foo_canvas_item_grab_focus (item);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+cb_key_press_event (G_GNUC_UNUSED GtkWidget *wrapper,
+ GdkEventKey *event,
+ GraphGuruTypeSelector *typesel)
+{
+ GtkCornerType corner;
+ int col, row;
+ double x, y;
+ GogPlotType const *type = g_object_get_data (
+ G_OBJECT (typesel->current_minor_item), PLOT_TYPE_KEY);
+
+ g_return_val_if_fail (type != NULL, FALSE);
+
+ col = type->col;
+ row = type->row;
+
+ switch (event->keyval){
+ case GDK_KP_Left: case GDK_Left:
+ corner = GTK_CORNER_BOTTOM_RIGHT;
+ --col;
+ break;
+
+ case GDK_KP_Up: case GDK_Up:
+ corner = GTK_CORNER_BOTTOM_RIGHT;
+ --row;
+ break;
+
+ case GDK_KP_Right: case GDK_Right:
+ corner = GTK_CORNER_TOP_LEFT;
+ ++col;
+ break;
+
+ case GDK_KP_Down: case GDK_Down:
+ corner = GTK_CORNER_TOP_LEFT;
+ ++row;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ get_pos (col, row, &x, &y);
+ graph_typeselect_minor_x_y (typesel, x, y);
+
+ return TRUE;
+}
+
+static gint
+cb_button_press_event (GtkWidget *widget, GdkEventButton *event,
+ GraphGuruTypeSelector *typesel)
+{
+ if (event->button == 1) {
+ FooCanvas *c = FOO_CANVAS (widget);
+ double x, y;
+
+ foo_canvas_window_to_world (c, event->x, event->y, &x, &y);
+
+ graph_typeselect_minor_x_y (typesel, x, y);
+ }
+
+ return FALSE;
+}
+
+static void
+cb_selection_changed (GraphGuruTypeSelector *typesel)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (typesel->list_view);
+ GtkTreeIter iter;
+ FooCanvasItem *item;
+ FooCanvasGroup *group;
+
+ if (typesel->current_family_item != NULL)
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->current_family_item));
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+ gtk_tree_model_get (GTK_TREE_MODEL (typesel->model), &iter,
+ PLOT_FAMILY_TYPE_CANVAS_GROUP, &group,
+ -1);
+
+ foo_canvas_item_show (FOO_CANVAS_ITEM (group));
+ typesel->current_family_item = group;
+
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->selector));
+ item = g_object_get_data (G_OBJECT (group), FIRST_MINOR_TYPE);
+ if (item != NULL)
+ graph_typeselect_minor (typesel, item);
+ foo_canvas_item_show (FOO_CANVAS_ITEM (typesel->selector));
+}
+
+static void
+cb_typesel_sample_plot_resize (FooCanvas *canvas,
+ GtkAllocation *alloc, GraphGuruTypeSelector *typesel)
+{
+ /* Use 96dpi and 50% zoom. Hard code the zoom because it is not
+ * active when sample button is not depressed */
+ if (typesel->sample_graph_item != NULL)
+ foo_canvas_item_set (typesel->sample_graph_item,
+ "w", (double)alloc->width * 2.,
+ "h", (double)alloc->height * 2.,
+ "logical_width_pts", (2. * 72. * (double)alloc->width) / 96.,
+ "logical_height_pts", (2. * 72. * (double)alloc->height) / 96.,
+ NULL);
+}
+
+static void
+cb_sample_pressed (GraphGuruTypeSelector *typesel)
+{
+ if (typesel->current_family_item == NULL)
+ return;
+
+ foo_canvas_set_pixels_per_unit (FOO_CANVAS (typesel->canvas), .5); /* 50% zoom */
+ if (typesel->sample_graph_item == NULL) {
+ GtkAllocation *size = >K_WIDGET (typesel->canvas)->allocation;
+ typesel->sample_graph_item = foo_canvas_item_new (typesel->graph_group,
+ GOG_CONTROL_FOOCANVAS_TYPE,
+ "model", typesel->state->graph,
+ NULL);
+ cb_typesel_sample_plot_resize (FOO_CANVAS (typesel->canvas),
+ size, typesel);
+
+ g_return_if_fail (typesel->sample_graph_item != NULL);
+ }
+
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->current_family_item));
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->selector));
+ foo_canvas_item_show (FOO_CANVAS_ITEM (typesel->graph_group));
+}
+
+static void
+cb_sample_released (GraphGuruTypeSelector *typesel)
+{
+ if (typesel->current_family_item == NULL)
+ return;
+
+ foo_canvas_set_pixels_per_unit (FOO_CANVAS (typesel->canvas), 1.);
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->graph_group));
+ foo_canvas_item_show (FOO_CANVAS_ITEM (typesel->current_family_item));
+ foo_canvas_item_show (FOO_CANVAS_ITEM (typesel->selector));
+}
+
+typedef struct {
+ GraphGuruTypeSelector *typesel;
+ FooCanvasGroup *group;
+ FooCanvasItem *current_item;
+ GogPlotType *current_type;
+ int col, row;
+} type_list_closure;
+
+static void
+cb_plot_types_init (char const *id, GogPlotType *type,
+ type_list_closure *closure)
+{
+ double x1, y1, w, h;
+ FooCanvasItem *item;
+ int col, row;
+ GdkPixbuf *image = get_pixbuf (type->sample_image_file);
+
+ g_return_if_fail (image != NULL);
+
+ col = type->col;
+ row = type->row;
+ get_pos (col, row, &x1, &y1);
+ w = gdk_pixbuf_get_width (image);
+ if (w > MINOR_PIXMAP_WIDTH)
+ w = MINOR_PIXMAP_WIDTH;
+ h = gdk_pixbuf_get_height (image);
+ if (h > MINOR_PIXMAP_HEIGHT)
+ h = MINOR_PIXMAP_HEIGHT;
+
+ item = foo_canvas_item_new (closure->group,
+ foo_canvas_pixbuf_get_type (),
+ "x", x1, "y", y1,
+ "width", w, "height", h,
+ "pixbuf", image,
+ "point_ignores_alpha", TRUE,
+ NULL);
+ g_object_set_data (G_OBJECT (item), PLOT_TYPE_KEY, (gpointer)type);
+
+ if (closure->current_type == NULL ||
+ closure->row > row ||
+ (closure->row == row && closure->col > col)) {
+ closure->current_type = type;
+ closure->current_item = item;
+ closure->col = col;
+ closure->row = row;
+ }
+}
+
+static void
+cb_plot_families_init (char const *id, GogPlotFamily *family,
+ GraphGuruTypeSelector *typesel)
+{
+ FooCanvasGroup *group;
+ GtkTreeIter iter;
+ type_list_closure closure;
+
+ if (g_hash_table_size (family->types) <= 0)
+ return;
+
+ /* Define a canvas group for all the minor types */
+ group = FOO_CANVAS_GROUP (foo_canvas_item_new (
+ foo_canvas_root (FOO_CANVAS (typesel->canvas)),
+ foo_canvas_group_get_type (),
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (group));
+
+ gtk_list_store_append (typesel->model, &iter);
+ gtk_list_store_set (typesel->model, &iter,
+ PLOT_FAMILY_TYPE_IMAGE, get_pixbuf (family->sample_image_file),
+ PLOT_FAMILY_TYPE_NAME, _(family->name),
+ PLOT_FAMILY_TYPE_CANVAS_GROUP, group,
+ -1);
+
+ closure.typesel = typesel;
+ closure.group = group;
+ closure.current_type = NULL;
+ closure.current_item = NULL;
+
+ /* Init the list and the canvas group for each family */
+ g_hash_table_foreach (family->types,
+ (GHFunc) cb_plot_types_init, &closure);
+ g_object_set_data (G_OBJECT (group), FIRST_MINOR_TYPE,
+ closure.current_item);
+}
+
+static void
+cb_canvas_realized (GtkLayout *widget)
+{
+ gdk_window_set_back_pixmap (widget->bin_window, NULL, FALSE);
+}
+
+static void
+graph_guru_state_destroy (GraphGuruState *state)
+{
+ g_return_if_fail (state != NULL);
+
+ if (state->graph != NULL) {
+ g_object_unref (state->graph);
+ state->graph = NULL;
+ }
+
+ if (state->gui != NULL) {
+ g_object_unref (state->gui);
+ state->gui = NULL;
+ }
+
+ if (state->register_closure != NULL) {
+ g_closure_unref (state->register_closure);
+ state->register_closure = NULL;
+ }
+
+ state->dialog = NULL;
+ g_free (state);
+}
+
+static void populate_graph_item_list (GogObject *obj, GogObject *select,
+ GraphGuruState *s, GtkTreeIter *parent,
+ gboolean insert);
+
+static void
+cb_graph_guru_add_item (GtkWidget *w, GraphGuruState *s)
+{
+ gog_object_add_by_role (s->prop_object,
+ g_object_get_data (G_OBJECT (w), ROLE_KEY), NULL);
+}
+
+static void
+cb_graph_guru_delete_item (GraphGuruState *s)
+{
+ if (s->prop_object != NULL) {
+ GtkTreeIter iter;
+ GogObject *obj = s->prop_object;
+
+ /* select the parent before we delete */
+ gtk_tree_model_iter_parent (GTK_TREE_MODEL (s->prop_model),
+ &iter, &s->prop_iter);
+ gtk_tree_selection_select_iter (s->prop_selection, &iter);
+ gog_object_clear_parent (obj);
+ g_object_unref (obj);
+ }
+}
+
+static void
+update_prec_menu (GraphGuruState *s, gboolean inc_ok, gboolean dec_ok)
+{
+ gtk_widget_set_sensitive (s->prec.first, inc_ok);
+ gtk_widget_set_sensitive (s->prec.inc, inc_ok);
+ gtk_widget_set_sensitive (s->prec.dec, dec_ok);
+ gtk_widget_set_sensitive (s->prec.last, dec_ok);
+ gtk_widget_set_sensitive (s->prec.menu, dec_ok | inc_ok);
+}
+
+static gboolean
+cb_reordered_find (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ gtk_tree_store_move_after (s->prop_model, &s->prop_iter, iter);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+static void
+reorder (GraphGuruState *s, gboolean inc, gboolean goto_max)
+{
+ gboolean inc_ok, dec_ok;
+ GogObject *after;
+
+ g_return_if_fail (s->search_target == NULL);
+
+ after = gog_object_reorder (s->prop_object, inc, goto_max);
+ if (after != NULL) {
+ s->search_target = after;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_reordered_find, s);
+ s->search_target = NULL;
+ } else
+ gtk_tree_store_move_after (s->prop_model, &s->prop_iter, NULL);
+
+ gog_object_can_reorder (s->prop_object, &inc_ok, &dec_ok);
+ update_prec_menu (s, inc_ok, dec_ok);
+}
+static void cb_prec_first (GraphGuruState *s) { reorder (s, TRUE, TRUE); }
+static void cb_prec_inc (GraphGuruState *s) { reorder (s, TRUE, FALSE); }
+static void cb_prec_dec (GraphGuruState *s) { reorder (s, FALSE, FALSE); }
+static void cb_prec_last (GraphGuruState *s) { reorder (s, FALSE, TRUE); }
+
+struct type_menu_create {
+ GraphGuruState *state;
+ GtkWidget *menu;
+ gboolean non_blank;
+};
+
+
+static gint
+cb_cmp_plot_type (GogPlotType const *a, GogPlotType const *b)
+{
+ if (a->row == b->row)
+ return a->col - b->col;
+ return a->row - b->row;
+}
+
+static void
+cb_plot_type_list (char const *id, GogPlotType *type, GSList **list)
+{
+ *list = g_slist_insert_sorted (*list, type,
+ (GCompareFunc) cb_cmp_plot_type);
+}
+
+static void
+cb_graph_guru_add_plot (GtkWidget *w, GraphGuruState *s)
+{
+ GogPlotType *type = g_object_get_data (G_OBJECT (w), PLOT_TYPE_KEY);
+ GogPlot *plot = gog_plot_new_by_type (type);
+ gog_object_add_by_name (GOG_OBJECT (s->prop_object),
+ "Plot", GOG_OBJECT (plot));
+ /* as a convenience add a series to the newly created plot */
+ gog_object_add_by_name (GOG_OBJECT (plot), "Series", NULL);
+}
+
+static void
+cb_plot_family_menu_create (char const *id, GogPlotFamily *family,
+ struct type_menu_create *closure)
+{
+ GtkWidget *w, *menu;
+ GSList *ptr, *types = NULL;
+ GogPlotType *type;
+
+ if (g_hash_table_size (family->types) <= 0)
+ return;
+
+ menu = gtk_image_menu_item_new_with_label (_(family->name));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu),
+ gtk_image_new_from_pixbuf (
+ get_pixbuf (family->sample_image_file)));
+ gtk_menu_shell_append (GTK_MENU_SHELL (closure->menu), menu);
+ closure->non_blank = TRUE;
+
+ w = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), w);
+
+ menu = w;
+ g_hash_table_foreach (family->types,
+ (GHFunc) cb_plot_type_list, &types);
+ for (ptr = types ; ptr != NULL ; ptr = ptr->next) {
+ type = ptr->data;
+ w = gtk_image_menu_item_new_with_label (_(type->name));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (w),
+ gtk_image_new_from_pixbuf (
+ get_pixbuf (type->sample_image_file)));
+ g_object_set_data (G_OBJECT (w), PLOT_TYPE_KEY, type);
+ g_signal_connect (G_OBJECT (w),
+ "activate",
+ G_CALLBACK (cb_graph_guru_add_plot), closure->state);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), w);
+ }
+ g_slist_free (types);
+}
+
+/* return TRUE if there are plot types to add */
+static GtkWidget *
+plot_type_menu_create (GraphGuruState *s)
+{
+ struct type_menu_create closure;
+ closure.state = s;
+ closure.menu = gtk_menu_new ();
+ closure.non_blank = FALSE;
+
+ g_hash_table_foreach ((GHashTable *)gog_plot_families (),
+ (GHFunc) cb_plot_family_menu_create, &closure);
+
+ if (closure.non_blank)
+ return closure.menu;
+ gtk_object_destroy (GTK_OBJECT (closure.menu));
+ return NULL;
+}
+
+static void
+cb_attr_tree_selection_change (GraphGuruState *s)
+{
+ gboolean add_ok = FALSE;
+ gboolean delete_ok = FALSE;
+ gboolean inc_ok = FALSE;
+ gboolean dec_ok = FALSE;
+ GtkTreeModel *model;
+ GogObject *obj = NULL;
+ GtkWidget *w, *editor, *notebook;
+ GtkTreePath *path;
+
+ if (gtk_tree_selection_get_selected (s->prop_selection, &model, &s->prop_iter))
+ gtk_tree_model_get (model, &s->prop_iter,
+ PLOT_ATTR_OBJECT, &obj,
+ -1);
+
+ if (s->prop_object == obj)
+ return;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (s->prop_model), &s->prop_iter);
+ gtk_tree_view_scroll_to_cell (s->prop_view, path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free (path);
+
+ /* remove the old prop page */
+ s->prop_object = obj;
+ w = gtk_bin_get_child (GTK_BIN (s->prop_container));
+ if (w != NULL)
+ gtk_container_remove (s->prop_container, w);
+
+ if (s->prop_object != NULL) {
+ /* Setup up the additions menu */
+ GSList *additions = gog_object_possible_additions (s->prop_object);
+ if (additions != NULL) {
+ GogObjectRole const *role;
+ GSList *ptr;
+ GtkWidget *tmp, *menu;
+
+ menu = gtk_menu_new ();
+ for (ptr = additions ; ptr != NULL ; ptr = ptr->next) {
+ role = ptr->data;
+ /* somewhat hackish, but I do not see a need
+ * for anything more general yet */
+ if (strcmp (role->id, "Plot")) {
+ tmp = gtk_menu_item_new_with_label (_(role->id));
+ g_object_set_data (G_OBJECT (tmp), ROLE_KEY,
+ (gpointer)role);
+ g_signal_connect (G_OBJECT (tmp),
+ "activate",
+ G_CALLBACK (cb_graph_guru_add_item), s);
+ } else {
+ GtkWidget *submenu = plot_type_menu_create (s);
+ if (submenu != NULL) {
+ tmp = gtk_menu_item_new_with_label (_(role->id));
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (tmp), submenu);
+ } else
+ continue;
+ }
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), tmp);
+
+ }
+ add_ok = (additions != NULL);
+ g_slist_free (additions);
+
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (s->add_menu), menu);
+ gtk_widget_show_all (s->add_menu);
+ }
+
+ /* if we ever go back to the typeselector be sure to
+ * add the plot to the last selected chart */
+ s->chart = (GogChart *)
+ gog_object_get_parent_typed (obj, GOG_CHART_TYPE);
+ s->plot = (GogPlot *)
+ gog_object_get_parent_typed (obj, GOG_PLOT_TYPE);
+
+ if (s->plot == NULL) {
+ if (s->chart == NULL && s->graph != NULL) {
+ GSList *charts = gog_object_get_children (GOG_OBJECT (s->graph),
+ gog_object_find_role_by_name (GOG_OBJECT (s->graph), "Chart"));
+ if (charts != NULL && charts->next == NULL)
+ s->chart = charts->data;
+ g_slist_free (charts);
+ }
+ if (s->chart != NULL) {
+ GSList *plots = gog_object_get_children (GOG_OBJECT (s->chart),
+ gog_object_find_role_by_name (GOG_OBJECT (s->chart), "Plot"));
+ if (plots != NULL && plots->next == NULL)
+ s->plot = plots->data;
+ g_slist_free (plots);
+ }
+ }
+ gtk_widget_set_sensitive (s->button_navigate, s->chart != NULL);
+
+ delete_ok = gog_object_is_deletable (s->prop_object);
+ gog_object_can_reorder (obj, &inc_ok, &dec_ok);
+
+ /* create a prefs page for the graph obj */
+ editor = gog_object_get_editor (obj, s->dalloc, s->cc);
+ if (GTK_IS_NOTEBOOK (editor)) {
+ notebook = editor;
+ } else {
+ notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
+ if (editor == NULL)
+ editor = gtk_label_new (NULL); /* dummy widget for empty page */
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), editor, NULL);
+ gtk_widget_show (editor);
+ }
+ gtk_container_add (s->prop_container, notebook);
+ gtk_widget_show (notebook);
+ }
+
+ gtk_widget_set_sensitive (s->delete_button, delete_ok);
+ gtk_widget_set_sensitive (s->add_menu, add_ok);
+ update_prec_menu (s, inc_ok, dec_ok);
+}
+
+static gboolean
+cb_find_renamed_item (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ s->search_target = NULL;
+ gtk_tree_store_set (s->prop_model, iter,
+ PLOT_ATTR_NAME, gog_object_get_name (obj),
+ -1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+cb_obj_name_changed (GogObject *obj, GraphGuruState *s)
+{
+ s->search_target = obj;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_find_renamed_item, s);
+}
+
+static gboolean
+cb_find_child_added (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ s->search_target = NULL;
+ populate_graph_item_list (s->new_child, s->new_child, s, iter, TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+cb_obj_child_added (GogObject *parent, GogObject *child, GraphGuruState *s)
+{
+ s->search_target = parent;
+ s->new_child = child;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_find_child_added, s);
+ s->new_child = NULL;
+}
+
+static gboolean
+cb_find_child_removed (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+ GtkWidget *w;
+
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ s->search_target = NULL;
+ /* remove the tree element and the prop page */
+ gtk_tree_store_remove (s->prop_model, iter);
+ w = gtk_bin_get_child (GTK_BIN (s->prop_container));
+ if (w != NULL)
+ gtk_container_remove (s->prop_container, w);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+static void
+cb_obj_child_removed (GogObject *parent, GogObject *child, GraphGuruState *s)
+{
+ s->search_target = child;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_find_child_removed, s);
+
+ if (s->chart == (gpointer)child) {
+ s->chart = NULL;
+ s->plot = NULL;
+ gtk_widget_set_sensitive (s->button_navigate, FALSE);
+ } else if (s->plot == (gpointer)child)
+ s->plot = NULL;
+}
+
+static void
+populate_graph_item_list (GogObject *obj, GogObject *select, GraphGuruState *s,
+ GtkTreeIter *parent, gboolean insert)
+{
+ GSList *children, *ptr;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GClosure *closure;
+
+ if (insert) {
+ GogObject *gparent = gog_object_get_parent (obj);
+ gint i = g_slist_index (gparent->children, obj);
+ if (i > 0) {
+ GtkTreeIter sibling;
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (s->prop_model),
+ &sibling, parent, i-1);
+ gtk_tree_store_insert_after (s->prop_model, &iter,
+ parent, &sibling);
+ } else
+ gtk_tree_store_prepend (s->prop_model, &iter, parent);
+ } else
+ gtk_tree_store_append (s->prop_model, &iter, parent);
+
+ gtk_tree_store_set (s->prop_model, &iter,
+ PLOT_ATTR_OBJECT, obj,
+ PLOT_ATTR_NAME, gog_object_get_name (obj),
+ -1);
+
+ /* remove the signal handlers when the guru goes away */
+ closure = g_cclosure_new (G_CALLBACK (cb_obj_name_changed), s, NULL);
+ g_object_watch_closure (G_OBJECT (s->prop_view), closure);
+ g_signal_connect_closure (G_OBJECT (obj),
+ "name-changed",
+ closure, FALSE);
+ closure = g_cclosure_new (G_CALLBACK (cb_obj_child_added), s, NULL);
+ g_object_watch_closure (G_OBJECT (s->prop_view), closure);
+ g_signal_connect_closure (G_OBJECT (obj),
+ "child-added",
+ closure, FALSE);
+ closure = g_cclosure_new (G_CALLBACK (cb_obj_child_removed), s, NULL);
+ g_object_watch_closure (G_OBJECT (s->prop_view), closure);
+ g_signal_connect_closure (G_OBJECT (obj),
+ "child-removed",
+ closure, FALSE);
+
+ children = gog_object_get_children (obj, NULL);
+ for (ptr = children ; ptr != NULL ; ptr = ptr->next)
+ populate_graph_item_list (ptr->data, select, s, &iter, FALSE);
+ g_slist_free (children);
+
+ /* ensure that new items are visible */
+ path = gtk_tree_model_get_path (
+ GTK_TREE_MODEL (s->prop_model), &iter);
+ gtk_tree_view_expand_to_path (s->prop_view, path);
+ gtk_tree_path_free (path);
+
+ if (obj == select)
+ /* select after expanding so that we do not lose selection due
+ * to visibility */
+ gtk_tree_selection_select_iter (s->prop_selection, &iter);
+}
+
+static gboolean
+cb_find_item (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ gtk_tree_selection_select_iter (s->prop_selection, iter);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint
+cb_canvas_select_item (FooCanvas *canvas, GdkEventButton *event,
+ GraphGuruState *s)
+{
+ GogView *view;
+ GogRenderer *rend;
+ double x, y;
+
+ g_return_val_if_fail (FOO_IS_CANVAS (canvas), FALSE);
+
+ if (canvas->current_item == NULL)
+ return FALSE;
+
+ g_object_get (G_OBJECT (s->sample_graph_item), "renderer", &rend, NULL);
+ g_object_get (G_OBJECT (rend), "view", &view, NULL);
+ foo_canvas_window_to_world (canvas, event->x, event->y, &x, &y);
+ gog_view_info_at_point (view,
+ x * canvas->pixels_per_unit, y * canvas->pixels_per_unit,
+ s->prop_object, &s->search_target, NULL);
+ if (s->search_target == NULL)
+ return FALSE;
+
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_find_item, s);
+ s->search_target = NULL;
+ return TRUE;
+}
+
+static void
+cb_sample_plot_resize (FooCanvas *canvas,
+ GtkAllocation *alloc, GraphGuruState *s)
+{
+ /* Use 96dpi and zoom */
+ double zoom = 1. / canvas->pixels_per_unit;
+ foo_canvas_item_set (s->sample_graph_item,
+ "w", (double)alloc->width * zoom,
+ "h", (double)alloc->height * zoom,
+ "logical_width_pts", (72. * zoom * (double)alloc->width) / 96.,
+ "logical_height_pts", (72. * zoom * (double)alloc->height) / 96.,
+ NULL);
+}
+
+static void
+graph_guru_init_format_page (GraphGuruState *s)
+{
+ GtkWidget *w;
+ GtkTreeViewColumn *column;
+
+ if (s->fmt_page_initialized)
+ return;
+ s->fmt_page_initialized = TRUE;
+ s->add_menu = glade_xml_get_widget (s->gui, "add_menu");
+ s->delete_button = glade_xml_get_widget (s->gui, "delete");
+ s->prec.menu = glade_xml_get_widget (s->gui, "precedence_menu");
+ s->prec.inc = glade_xml_get_widget (s->gui, "inc_precedence");
+ s->prec.dec = glade_xml_get_widget (s->gui, "dec_precedence");
+ s->prec.first = glade_xml_get_widget (s->gui, "first_precedence");
+ s->prec.last = glade_xml_get_widget (s->gui, "last_precedence");
+
+ g_signal_connect_swapped (G_OBJECT (s->delete_button),
+ "clicked",
+ G_CALLBACK (cb_graph_guru_delete_item), s);
+ g_signal_connect_swapped (G_OBJECT (s->prec.first),
+ "activate",
+ G_CALLBACK (cb_prec_first), s);
+ g_signal_connect_swapped (G_OBJECT (s->prec.inc),
+ "activate",
+ G_CALLBACK (cb_prec_inc), s);
+ g_signal_connect_swapped (G_OBJECT (s->prec.dec),
+ "activate",
+ G_CALLBACK (cb_prec_dec), s);
+ g_signal_connect_swapped (G_OBJECT (s->prec.last),
+ "activate",
+ G_CALLBACK (cb_prec_last), s);
+
+ /* Load up the sample view and make it fill the entire canvas */
+ w = glade_xml_get_widget (s->gui, "sample_canvas");
+ foo_canvas_set_pixels_per_unit (FOO_CANVAS (w), .5); /* 50% zoom */
+ s->sample_graph_item = foo_canvas_item_new (
+ foo_canvas_root (FOO_CANVAS (w)), GOG_CONTROL_FOOCANVAS_TYPE,
+ "model", s->graph,
+ NULL);
+ cb_sample_plot_resize (FOO_CANVAS (w), &w->allocation, s);
+ g_signal_connect (G_OBJECT (w),
+ "size_allocate",
+ G_CALLBACK (cb_sample_plot_resize), s);
+ g_signal_connect_after (G_OBJECT (w),
+ "button_press_event",
+ G_CALLBACK (cb_canvas_select_item), s);
+ gtk_widget_show (w);
+
+ w = glade_xml_get_widget (s->gui, "prop_alignment");
+ s->prop_container = GTK_CONTAINER (w);
+ s->prop_model = gtk_tree_store_new (PLOT_ATTR_NUM_COLUMNS,
+ G_TYPE_STRING, G_TYPE_POINTER);
+ s->prop_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (
+ GTK_TREE_MODEL (s->prop_model)));
+ s->prop_selection = gtk_tree_view_get_selection (s->prop_view);
+ gtk_tree_selection_set_mode (s->prop_selection, GTK_SELECTION_BROWSE);
+ g_signal_connect_swapped (s->prop_selection,
+ "changed",
+ G_CALLBACK (cb_attr_tree_selection_change), s);
+
+ column = gtk_tree_view_column_new_with_attributes (_("Name"),
+ gtk_cell_renderer_text_new (),
+ "text", PLOT_ATTR_NAME, NULL);
+ gtk_tree_view_append_column (s->prop_view, column);
+
+ gtk_tree_view_set_headers_visible (s->prop_view, FALSE);
+
+ gtk_tree_store_clear (s->prop_model);
+ populate_graph_item_list (GOG_OBJECT (s->graph), GOG_OBJECT (s->graph),
+ s, NULL, FALSE);
+ gtk_tree_view_expand_all (s->prop_view);
+
+ w = glade_xml_get_widget (s->gui, "attr_window");
+ gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (s->prop_view));
+ gtk_widget_show_all (w);
+}
+
+static void
+graph_guru_set_page (GraphGuruState *s, int page)
+{
+ char *name;
+
+ if (s->current_page == page)
+ return;
+
+ switch (page) {
+ case 0: name = _("Step 1 of 2: Select Chart Type");
+ gtk_widget_set_sensitive (s->button_navigate, s->plot != NULL);
+ gtk_button_set_label (GTK_BUTTON (s->button_navigate),
+ GTK_STOCK_GO_FORWARD);
+ break;
+
+ case 1:
+ if (s->initial_page == 0) {
+ name = _("Step 2 of 2: Customize Chart");
+ gtk_widget_set_sensitive (s->button_navigate, s->chart != NULL);
+ gtk_button_set_label (GTK_BUTTON (s->button_navigate),
+ GTK_STOCK_GO_BACK);
+ } else {
+ name = _("Customize Chart");
+ gtk_widget_hide (s->button_navigate);
+ }
+ graph_guru_init_format_page (s);
+ break;
+
+ default:
+ g_warning ("Invalid Chart Guru page");
+ return;
+ }
+
+ s->current_page = page;
+ gtk_notebook_set_current_page (s->steps, page - s->initial_page);
+ gtk_window_set_title (GTK_WINDOW (s->dialog), name);
+}
+
+static void
+cb_graph_guru_clicked (GtkWidget *button, GraphGuruState *s)
+{
+ if (s->dialog == NULL)
+ return;
+
+ if (button == s->button_navigate) {
+ graph_guru_set_page (s, (s->current_page + 1) % 2);
+ return;
+ } else if (button == s->button_ok &&
+ s->register_closure != NULL && s->graph != NULL) {
+ /* Invoking closure */
+ GValue instance_and_params[2];
+ gpointer data = s->register_closure->is_invalid ?
+ NULL : s->register_closure->data;
+
+ instance_and_params[0].g_type = 0;
+ g_value_init (&instance_and_params[0], GOG_GRAPH_TYPE);
+ g_value_set_instance (&instance_and_params[0], s->graph);
+
+ instance_and_params[1].g_type = 0;
+ g_value_init (&instance_and_params[1], G_TYPE_POINTER);
+ g_value_set_pointer (&instance_and_params[1], data);
+
+ g_closure_set_marshal (s->register_closure,
+ g_cclosure_marshal_VOID__POINTER);
+
+ g_closure_invoke (s->register_closure,
+ NULL,
+ 2,
+ instance_and_params,
+ NULL);
+ g_value_unset (instance_and_params + 0);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (s->dialog));
+}
+
+static GtkWidget *
+graph_guru_init_button (GraphGuruState *s, char const *widget_name)
+{
+ GtkWidget *button = glade_xml_get_widget (s->gui, widget_name);
+ g_signal_connect (G_OBJECT (button),
+ "clicked",
+ G_CALLBACK (cb_graph_guru_clicked), s);
+ return button;
+}
+
+static GtkWidget *
+graph_guru_init_ok_button (GraphGuruState *s)
+{
+ GtkButton *button = GTK_BUTTON (glade_xml_get_widget
+ (s->gui, "button_ok"));
+
+ if (s->editing) {
+ gtk_button_set_label (button, GTK_STOCK_APPLY);
+ gtk_button_set_use_stock (button, TRUE);
+ } else {
+ gtk_button_set_use_stock (button, FALSE);
+ gtk_button_set_use_underline (button, TRUE);
+ gtk_button_set_label (button, _("_Insert"));
+ }
+ g_signal_connect (G_OBJECT (button),
+ "clicked",
+ G_CALLBACK (cb_graph_guru_clicked), s);
+ return GTK_WIDGET (button);
+}
+
+static void
+typesel_set_selection_color (GraphGuruTypeSelector *typesel)
+{
+ GtkWidget *w = gtk_entry_new ();
+ GdkColor *color = &w->style->base [GTK_WIDGET_HAS_FOCUS (typesel->canvas)
+ ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE];
+ guint32 select_color = 0;
+
+ select_color |= ((color->red >> 8) & 0xff) << 24;
+ select_color |= ((color->green >> 8) & 0xff) << 16;
+ select_color |= ((color->blue >> 8) & 0xff) << 8;
+ select_color |= 0x40; /* alpha of 25% */
+ foo_canvas_item_set (typesel->selector,
+ "fill_color_rgba", select_color,
+ NULL);
+ gtk_object_destroy (GTK_OBJECT (w));
+}
+
+static GtkWidget *
+graph_guru_type_selector_new (GraphGuruState *s)
+{
+ GtkTreeSelection *selection;
+ GraphGuruTypeSelector *typesel;
+ GtkWidget *selector;
+ GladeXML *gui;
+
+ gui = gnm_glade_xml_new (s->cc, "gog-guru-type-selector.glade", "type_selector", NULL);
+
+ typesel = g_new0 (GraphGuruTypeSelector, 1);
+ typesel->state = s;
+ typesel->current_family_item = NULL;
+ typesel->current_minor_item = NULL;
+ typesel->current_type = NULL;
+ typesel->sample_graph_item = NULL;
+
+ selector = glade_xml_get_widget (gui, "type_selector");
+
+ /* List of family types */
+ typesel->model = gtk_list_store_new (PLOT_FAMILY_NUM_COLUMNS,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_POINTER);
+ typesel->list_view = GTK_TREE_VIEW (glade_xml_get_widget (gui, "type_treeview"));
+ gtk_tree_view_set_model (typesel->list_view, GTK_TREE_MODEL (typesel->model));
+ gtk_tree_view_append_column (typesel->list_view,
+ gtk_tree_view_column_new_with_attributes ("",
+ gtk_cell_renderer_pixbuf_new (),
+ "pixbuf", PLOT_FAMILY_TYPE_IMAGE,
+ NULL));
+ gtk_tree_view_append_column (typesel->list_view,
+ gtk_tree_view_column_new_with_attributes (_("_Plot Type"),
+ gtk_cell_renderer_text_new (),
+ "text", PLOT_FAMILY_TYPE_NAME,
+ NULL));
+ selection = gtk_tree_view_get_selection (typesel->list_view);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ g_signal_connect_swapped (selection,
+ "changed",
+ G_CALLBACK (cb_selection_changed), typesel);
+
+ /* Setup an canvas to display the sample image & the sample plot. */
+ typesel->canvas = foo_canvas_new ();
+ typesel->graph_group = FOO_CANVAS_GROUP (foo_canvas_item_new (
+ foo_canvas_root (FOO_CANVAS (typesel->canvas)),
+ foo_canvas_group_get_type (),
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+ g_object_connect (typesel->canvas,
+ "signal::realize", G_CALLBACK (cb_canvas_realized), typesel,
+ "signal::size_allocate", G_CALLBACK (cb_typesel_sample_plot_resize), typesel,
+ "signal_after::key_press_event", G_CALLBACK (cb_key_press_event), typesel,
+ "signal::button_press_event", G_CALLBACK (cb_button_press_event), typesel,
+ "swapped_signal::focus_in_event", G_CALLBACK (typesel_set_selection_color), typesel,
+ "swapped_signal::focus_out_event", G_CALLBACK (typesel_set_selection_color), typesel,
+ NULL);
+ gtk_widget_set_size_request (typesel->canvas,
+ MINOR_PIXMAP_WIDTH*3 + BORDER*5,
+ MINOR_PIXMAP_HEIGHT*3 + BORDER*5);
+ foo_canvas_scroll_to (FOO_CANVAS (typesel->canvas), 0, 0);
+ gtk_container_add (GTK_CONTAINER (glade_xml_get_widget (gui, "canvas_container")),
+ typesel->canvas);
+
+ /* Init the list and the canvas group for each family */
+ g_hash_table_foreach ((GHashTable *)gog_plot_families (),
+ (GHFunc) cb_plot_families_init, typesel);
+
+ /* The alpha blended selection box */
+ typesel->selector = foo_canvas_item_new (
+ foo_canvas_root (FOO_CANVAS (typesel->canvas)),
+ foo_canvas_rect_get_type (),
+ "outline_color_rgba", 0x000000ff, /* black */
+ "width_pixels", 1,
+ NULL);
+ typesel_set_selection_color (typesel);
+
+ /* Setup the description label */
+ typesel->label = GTK_LABEL (glade_xml_get_widget (gui, "description_label"));
+
+ /* Set up sample button */
+ typesel->sample_button = glade_xml_get_widget (gui, "sample_button");
+ g_signal_connect_swapped (G_OBJECT (typesel->sample_button),
+ "pressed",
+ G_CALLBACK (cb_sample_pressed), typesel);
+ g_signal_connect_swapped (G_OBJECT (typesel->sample_button),
+ "released",
+ G_CALLBACK (cb_sample_released), typesel);
+
+ g_object_set_data_full (G_OBJECT (selector),
+ "state", typesel, (GDestroyNotify) g_free);
+
+ g_object_unref (G_OBJECT (gui));
+
+ return selector;
+}
+
+static gboolean
+graph_guru_init (GraphGuruState *s)
+{
+ s->gui = gnm_glade_xml_new (s->cc, "gog-guru.glade", NULL, NULL);
+ if (s->gui == NULL)
+ return TRUE;
+
+ s->dialog = glade_xml_get_widget (s->gui, "GraphGuru");
+ s->steps = GTK_NOTEBOOK (glade_xml_get_widget (s->gui, "notebook"));
+
+ /* Buttons */
+ s->button_cancel = graph_guru_init_button (s, "button_cancel");
+ s->button_navigate = graph_guru_init_button (s, "button_navigate");
+ s->button_ok = graph_guru_init_ok_button (s);
+
+ gnumeric_init_help_button (
+ glade_xml_get_widget (s->gui, "help_button"),
+ "sect-graphics-plots");
+
+ return FALSE;
+}
+
+/**
+ * dialog_graph_guru
+ * @wb : The workbook to use as a parent window.
+ * @graph : the graph to edit
+ * @page : the page to start on.
+ *
+ * Pop up a graph guru.
+ */
+GtkWidget *
+gog_guru (GogGraph *graph, GogDataAllocator *dalloc,
+ GnmCmdContext *cc, GtkWindow *toplevel,
+ GClosure *closure)
+{
+ int page = (graph != NULL) ? 1 : 0;
+ GraphGuruState *state;
+
+ state = g_new0 (GraphGuruState, 1);
+ state->valid = FALSE;
+ state->updating = FALSE;
+ state->fmt_page_initialized = FALSE;
+ state->editing = (graph != NULL);
+ state->gui = NULL;
+ state->cc = cc;
+ state->dalloc = dalloc;
+ state->current_page = -1;
+ state->register_closure = closure;
+ g_closure_ref (closure);
+
+ if (graph != NULL) {
+ g_return_val_if_fail (IS_GOG_GRAPH (graph), NULL);
+
+ state->graph = gog_graph_dup (graph);
+ state->chart = NULL;
+ state->plot = NULL;
+ } else {
+ state->plot = NULL;
+ state->graph = g_object_new (GOG_GRAPH_TYPE, NULL);
+ state->chart = GOG_CHART (gog_object_add_by_name (
+ GOG_OBJECT (state->graph), "Chart", NULL));
+ }
+
+ if (state->graph == NULL || graph_guru_init (state)) {
+ graph_guru_state_destroy (state);
+ return NULL;
+ }
+
+ /* Ok everything is hooked up. Let-er rip */
+ state->valid = TRUE;
+
+ state->initial_page = page;
+ if (page == 0) {
+ GtkWidget *w = graph_guru_type_selector_new (state);
+ gtk_notebook_prepend_page (state->steps, w, NULL);
+ gtk_widget_show_all (w);
+ }
+
+ graph_guru_set_page (state, page);
+
+ /* a candidate for merging into attach guru */
+ g_object_set_data_full (G_OBJECT (state->dialog),
+ "state", state, (GDestroyNotify) graph_guru_state_destroy);
+ gnumeric_non_modal_dialog (toplevel, GTK_WINDOW (state->dialog));
+ gtk_widget_show (GTK_WIDGET (state->dialog));
+
+ return state->dialog;
+}
+
+#if 0
+gtk_tree_sortable_set_sort_func (store, column, compare_rows,
+ GUINT_TO_POINTER (column), NULL);
+
+for each column of your database table.
+
+int
+compare_rows (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ int column = GPOINTER_TO_UINT (user_data);
+ MyRow *row_a, row_b;
+
+ gtk_tree_model_get (model, DATA_COLUMN, a, &row_a, -1);
+ gtk_tree_model_get (model, DATA_COLUMN, b, &row_b, -1);
+
+ return compare_cells (row_a->cells[column], row_b->cells[column]);
+}
+
+
+#endif
--- /dev/null
+++ lib/goffice/graph/gog-object-xml.c
@@ -0,0 +1,543 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-object-xml.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-object-xml.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-plot.h>
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-color.h>
+
+#include <glade/glade-build.h> /* for the xml utils */
+#include <string.h>
+#include <stdlib.h>
+
+GType
+gog_persist_get_type (void)
+{
+ static GType gog_persist_type = 0;
+
+ if (!gog_persist_type) {
+ static GTypeInfo const gog_persist_info = {
+ sizeof (GogPersistClass), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ gog_persist_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GogPersist", &gog_persist_info, 0);
+ }
+
+ return gog_persist_type;
+}
+
+gboolean
+gog_persist_dom_load (GogPersist *gp, xmlNode *node)
+{
+ g_return_val_if_fail (IS_GOG_PERSIST (gp), FALSE);
+ return GOG_PERSIST_GET_CLASS (gp)->dom_load (gp, node);
+}
+
+void
+gog_persist_dom_save (GogPersist const *gp, xmlNode *parent)
+{
+ g_return_if_fail (IS_GOG_PERSIST (gp));
+ GOG_PERSIST_GET_CLASS (gp)->dom_save (gp, parent);
+}
+void
+gog_persist_sax_save (GogPersist const *gp, GsfXMLOut *output)
+{
+ g_return_if_fail (IS_GOG_PERSIST (gp));
+ GOG_PERSIST_GET_CLASS (gp)->sax_save (gp, output);
+}
+
+static void
+gog_object_set_arg_full (char const *name, char const *val, GogObject *obj, xmlNode *xml_node)
+{
+ GParamSpec *pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name);
+ GType prop_type;
+ GValue res = { 0 };
+ gboolean success = TRUE;
+
+ if (pspec == NULL) {
+ g_warning ("unknown property `%s' for class `%s'",
+ name, G_OBJECT_TYPE_NAME (obj));
+ return;
+ }
+
+ prop_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+ if (val == NULL &&
+ G_TYPE_FUNDAMENTAL (prop_type) != G_TYPE_BOOLEAN) {
+ g_warning ("could not convert NULL to type `%s' for property `%s'",
+ g_type_name (prop_type), pspec->name);
+ return;
+ }
+
+ g_value_init (&res, prop_type);
+ switch (G_TYPE_FUNDAMENTAL (prop_type)) {
+ case G_TYPE_CHAR:
+ g_value_set_char (&res, val[0]);
+ break;
+ case G_TYPE_UCHAR:
+ g_value_set_uchar (&res, (guchar)val[0]);
+ break;
+ case G_TYPE_BOOLEAN:
+ g_value_set_boolean (&res,
+ val == NULL ||
+ g_ascii_tolower (val[0]) == 't' ||
+ g_ascii_tolower (val[0]) == 'y' ||
+ strtol (val, NULL, 0));
+ break;
+ case G_TYPE_INT:
+ g_value_set_int (&res, strtol (val, NULL, 0));
+ break;
+ case G_TYPE_UINT:
+ g_value_set_uint (&res, strtoul (val, NULL, 0));
+ break;
+ case G_TYPE_LONG:
+ g_value_set_long (&res, strtol (val, NULL, 0));
+ break;
+ case G_TYPE_ULONG:
+ g_value_set_ulong (&res, strtoul (val, NULL, 0));
+ break;
+ case G_TYPE_ENUM:
+ g_value_set_enum (&res, glade_enum_from_string (prop_type, val));
+ break;
+ case G_TYPE_FLAGS:
+ g_value_set_flags (&res, glade_flags_from_string (prop_type, val));
+ break;
+ case G_TYPE_FLOAT:
+ g_value_set_float (&res, g_strtod (val, NULL));
+ break;
+ case G_TYPE_DOUBLE:
+ g_value_set_double (&res, g_strtod (val, NULL));
+ break;
+ case G_TYPE_STRING:
+ g_value_set_string (&res, val);
+ break;
+
+ case G_TYPE_OBJECT:
+ if (g_type_is_a (prop_type, G_TYPE_OBJECT)) {
+ xmlChar *type_name;
+ GType type = 0;
+ GObject *val_obj;
+
+ success = FALSE;
+ type_name = xmlGetProp (xml_node,
+ (xmlChar const *) "type");
+ if (type_name != NULL) {
+ type = g_type_from_name (type_name);
+ }
+ xmlFree (type_name);
+ if (type != 0) {
+ val_obj = g_object_new (type, NULL);
+ if (IS_GOG_PERSIST (val_obj) &&
+ gog_persist_dom_load (GOG_PERSIST (val_obj), xml_node)) {
+ g_value_set_object (&res, val_obj);
+ success = TRUE;
+ }
+ g_object_unref (val_obj);
+ }
+ }
+ break;
+
+ default:
+ success = FALSE;
+ }
+
+ if (!success) {
+ g_warning ("could not convert string to type `%s' for property `%s'",
+ g_type_name (prop_type), pspec->name);
+ } else
+ g_object_set_property (G_OBJECT (obj), name, &res);
+ g_value_unset (&res);
+}
+
+void
+gog_object_set_arg (char const *name, char const *val, GogObject *obj)
+{
+ gog_object_set_arg_full (name, val, obj, NULL);
+}
+
+static void
+gog_object_write_property_sax (GogObject const *obj, GParamSpec *pspec, GsfXMLOut *output)
+{
+ GObject *val_obj;
+ GType prop_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+ GValue value = { 0 };
+
+ g_value_init (&value, prop_type);
+ g_object_get_property (G_OBJECT (obj), pspec->name, &value);
+
+ /* No need to save default values */
+ if (!(pspec->flags & GOG_PARAM_FORCE_SAVE) &&
+ g_param_value_defaults (pspec, &value)) {
+ g_value_unset (&value);
+ return;
+ }
+
+ switch (G_TYPE_FUNDAMENTAL (prop_type)) {
+ case G_TYPE_CHAR:
+ case G_TYPE_UCHAR:
+ case G_TYPE_BOOLEAN:
+ case G_TYPE_INT:
+ case G_TYPE_UINT:
+ case G_TYPE_LONG:
+ case G_TYPE_ULONG:
+ case G_TYPE_FLOAT:
+ case G_TYPE_DOUBLE: {
+ GValue str = { 0 };
+ g_value_init (&str, G_TYPE_STRING);
+ g_value_transform (&value, &str);
+ gsf_xml_out_start_element (output, "property");
+ gsf_xml_out_add_cstr_unchecked (output, "name", pspec->name);
+ gsf_xml_out_add_cstr (output, NULL, g_value_get_string (&str));
+ gsf_xml_out_end_element (output); /* </property> */
+ g_value_unset (&str);
+ break;
+ }
+
+ case G_TYPE_STRING: {
+ char const *str = g_value_get_string (&value);
+ if (str != NULL) {
+ gsf_xml_out_start_element (output, "property");
+ gsf_xml_out_add_cstr_unchecked (output, "name", pspec->name);
+ gsf_xml_out_add_cstr (output, NULL, str);
+ gsf_xml_out_end_element (output); /* </property> */
+ }
+ break;
+ }
+
+ case G_TYPE_OBJECT:
+ val_obj = g_value_get_object (&value);
+ if (val_obj != NULL) {
+ if (IS_GOG_PERSIST (val_obj)) {
+ gsf_xml_out_start_element (output, "property");
+ gsf_xml_out_add_cstr_unchecked (output, "name", pspec->name);
+ gog_persist_sax_save (GOG_PERSIST (val_obj), output);
+ gsf_xml_out_end_element (output); /* </property> */
+ } else
+ g_warning ("How are we supposed to persist this ??");
+ }
+ break;
+
+ default:
+ ;
+ }
+ g_value_unset (&value);
+}
+
+static void
+gog_object_write_property (GogObject *obj, GParamSpec *pspec, xmlNode *parent)
+{
+ GObject *val_obj;
+ GType prop_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+ gboolean success = TRUE;
+ GValue value = { 0 };
+ xmlNode *node;
+
+ g_value_init (&value, prop_type);
+ g_object_get_property (G_OBJECT (obj), pspec->name, &value);
+
+ /* No need to save default values */
+ if (!(pspec->flags & GOG_PARAM_FORCE_SAVE) &&
+ g_param_value_defaults (pspec, &value)) {
+ g_value_unset (&value);
+ return;
+ }
+
+ node = xmlNewDocNode (parent->doc, NULL,
+ (xmlChar const *)"property", NULL);
+
+ switch (G_TYPE_FUNDAMENTAL (prop_type)) {
+ case G_TYPE_CHAR:
+ case G_TYPE_UCHAR:
+ case G_TYPE_BOOLEAN:
+ case G_TYPE_INT:
+ case G_TYPE_UINT:
+ case G_TYPE_LONG:
+ case G_TYPE_ULONG:
+ case G_TYPE_FLOAT:
+ case G_TYPE_DOUBLE: {
+ GValue str = { 0 };
+ g_value_init (&str, G_TYPE_STRING);
+ g_value_transform (&value, &str);
+ xmlNodeSetContent (node, g_value_get_string (&str));
+ g_value_unset (&str);
+ break;
+ }
+
+ case G_TYPE_STRING: {
+ char const *str = g_value_get_string (&value);
+ if (str != NULL)
+ xmlNodeSetContent (node, str);
+ else
+ success = FALSE;
+ break;
+ }
+
+ case G_TYPE_OBJECT:
+ val_obj = g_value_get_object (&value);
+ if (val_obj != NULL) {
+ if (IS_GOG_PERSIST (val_obj)) {
+ gog_persist_dom_save (GOG_PERSIST (val_obj), node);
+ } else
+ g_warning ("How are we supposed to persist this ??");
+ } else
+ success = FALSE;
+ break;
+
+ default:
+ success = FALSE;
+ }
+ g_value_unset (&value);
+
+ if (success) {
+ xmlSetProp (node, (xmlChar const *) "name", pspec->name);
+ xmlAddChild (parent, node);
+ } else
+ xmlFreeNode (node);
+}
+
+static void
+gog_dataset_load (GogDataset *set, xmlNode *node)
+{
+ xmlNode *ptr;
+ xmlChar *id, *val, *type;
+
+ for (ptr = node->xmlChildrenNode ; ptr != NULL ; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL)
+ continue;
+ if (!strcmp (ptr->name, "data"))
+ break;
+ }
+ if (ptr == NULL)
+ return;
+ for (ptr = ptr->xmlChildrenNode ; ptr != NULL ; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL)
+ continue;
+ if (!strcmp (ptr->name, "dimension")) {
+ id = xmlGetProp (ptr, (xmlChar const *) "id");
+ type = xmlGetProp (ptr, (xmlChar const *) "type");
+ val = xmlNodeGetContent (ptr);
+ if (id != NULL && type != NULL && val != NULL) {
+ unsigned dim_id = strtoul (id, NULL, 0);
+ GOData *dat = g_object_new (g_type_from_name (type), NULL);
+ if (dat != NULL && go_data_from_str (dat, val))
+ gog_dataset_set_dim (set, dim_id, dat, NULL);
+ }
+
+ if (id != NULL) xmlFree (id);
+ if (type != NULL) xmlFree (type);
+ if (val != NULL) xmlFree (val);
+ }
+ }
+}
+
+static void
+gog_dataset_sax_save (GogDataset const *set, GsfXMLOut *output)
+{
+ GOData *dat;
+ char *tmp, buffer[10];
+ int i, last;
+
+ gsf_xml_out_start_element (output, "data");
+ gog_dataset_dims (set, &i, &last);
+ for ( ; i <= last ; i++) {
+ dat = gog_dataset_get_dim (set, i);
+ if (dat == NULL)
+ continue;
+
+ gsf_xml_out_start_element (output, "dimension");
+ snprintf (buffer, sizeof buffer, "%d", i);
+ gsf_xml_out_add_cstr (output, "id", buffer);
+ gsf_xml_out_add_cstr (output, "type",
+ G_OBJECT_TYPE_NAME (dat));
+ tmp = go_data_as_str (dat);
+ gsf_xml_out_add_cstr (output, NULL, tmp);
+ g_free (tmp);
+ gsf_xml_out_end_element (output); /* </dimension> */
+ }
+ gsf_xml_out_end_element (output); /* </data> */
+
+}
+static void
+gog_dataset_dom_save (GogDataset *set, xmlNode *parent)
+{
+ xmlNode *node, *child;
+ char *tmp, buffer[10];
+ GOData *dat;
+ int i, last;
+
+ node = xmlNewDocNode (parent->doc, NULL, (xmlChar const *)"data", NULL);
+ gog_dataset_dims (set, &i, &last);
+ for ( ; i <= last ; i++) {
+ dat = gog_dataset_get_dim (set, i);
+ if (dat == NULL)
+ continue;
+
+ tmp = go_data_as_str (dat);
+ child = xmlNewChild (node, NULL,
+ (xmlChar const *) ("dimension"), tmp);
+ g_free (tmp);
+
+ snprintf (buffer, sizeof buffer, "%d", i);
+ xmlSetProp (child, (xmlChar const *) "id", buffer);
+ xmlSetProp (child, (xmlChar const *) "type",
+ G_OBJECT_TYPE_NAME (dat));
+ }
+
+ xmlAddChild (parent, node);
+}
+
+void
+gog_object_write_xml_sax (GogObject const *obj, GsfXMLOut *output)
+{
+ gint n;
+ GParamSpec **props;
+ GSList *ptr;
+
+ gsf_xml_out_start_element (output, "GogObject");
+
+ /* Primary details */
+ if (obj->role != NULL)
+ gsf_xml_out_add_cstr (output, "role", obj->role->id);
+ if (obj->explicitly_typed_role || obj->role == NULL)
+ gsf_xml_out_add_cstr (output, "type", G_OBJECT_TYPE_NAME (obj));
+
+ /* properties */
+ props = g_object_class_list_properties (G_OBJECT_GET_CLASS (obj), &n);
+ while (n-- > 0)
+ if (props[n]->flags & GOG_PARAM_PERSISTENT)
+ gog_object_write_property_sax (obj, props[n], output);
+
+ g_free (props);
+
+ if (IS_GOG_PERSIST (obj)) /* anything special for this class */
+ gog_persist_sax_save (GOG_PERSIST (obj), output);
+ if (IS_GOG_DATASET (obj)) /* convenience to save data */
+ gog_dataset_sax_save (GOG_DATASET (obj), output);
+
+ /* the children */
+ for (ptr = obj->children; ptr != NULL ; ptr = ptr->next)
+ gog_object_write_xml_sax (ptr->data, output);
+
+ gsf_xml_out_end_element (output); /* </GogObject> */
+}
+
+xmlNode *
+gog_object_write_xml (GogObject *obj, xmlDoc *doc)
+{
+ gint n;
+ GParamSpec **props;
+ GSList *ptr;
+ xmlNode *node = xmlNewDocNode (doc, NULL,
+ (xmlChar const *)"GogObject", NULL);
+
+ /* Primary details */
+ if (obj->role != NULL)
+ xmlSetProp (node, (xmlChar const *) "role", obj->role->id);
+ if (obj->explicitly_typed_role || obj->role == NULL)
+ xmlSetProp (node, (xmlChar const *) "type", G_OBJECT_TYPE_NAME (obj));
+
+ /* properties */
+ props = g_object_class_list_properties (G_OBJECT_GET_CLASS (obj), &n);
+ while (n-- > 0)
+ if (props[n]->flags & GOG_PARAM_PERSISTENT)
+ gog_object_write_property (obj, props[n], node);
+
+ g_free (props);
+
+ if (IS_GOG_PERSIST (obj)) /* anything special for this class */
+ gog_persist_dom_save (GOG_PERSIST (obj), node);
+ if (IS_GOG_DATASET (obj)) /* convenience to save data */
+ gog_dataset_dom_save (GOG_DATASET (obj), node);
+
+ /* the children */
+ for (ptr = obj->children; ptr != NULL ; ptr = ptr->next)
+ xmlAddChild (node, gog_object_write_xml (ptr->data, doc));
+
+ return node;
+}
+
+GogObject *
+gog_object_new_from_xml (GogObject *parent, xmlNode *node)
+{
+ xmlChar *role, *name, *val, *type_name;
+ xmlNode *ptr;
+ GogObject *res = NULL;
+ gboolean explicitly_typed_role = FALSE;
+
+ type_name = xmlGetProp (node, (xmlChar const *) "type");
+ if (type_name != NULL) {
+ GType type = g_type_from_name (type_name);
+ if (type == 0) {
+ GogPlot *plot = gog_plot_new_by_name (type_name);
+ res = GOG_OBJECT (plot);
+ } else
+ res = g_object_new (type, NULL);
+ xmlFree (type_name);
+ explicitly_typed_role = TRUE;
+ }
+ role = xmlGetProp (node, (xmlChar const *) "role");
+ if (role == NULL) {
+ g_return_val_if_fail (parent == NULL, NULL);
+ } else {
+ res = gog_object_add_by_name (parent, role, res);
+ xmlFree (role);
+ }
+
+ g_return_val_if_fail (res != NULL, NULL);
+
+ res->explicitly_typed_role = explicitly_typed_role;
+
+ if (IS_GOG_PERSIST (res))
+ gog_persist_dom_load (GOG_PERSIST (res), node);
+ if (IS_GOG_DATASET (res)) /* convenience to save data */
+ gog_dataset_load (GOG_DATASET (res), node);
+
+ for (ptr = node->xmlChildrenNode ; ptr != NULL ; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL)
+ continue;
+ if (!strcmp (ptr->name, "property")) {
+ name = xmlGetProp (ptr, "name");
+ if (name == NULL) {
+ g_warning ("missing name for property entry");
+ continue;
+ }
+ val = xmlNodeGetContent (ptr);
+ gog_object_set_arg_full (name, val, res, ptr);
+ xmlFree (val);
+ xmlFree (name);
+ } else if (!strcmp (ptr->name, "GogObject"))
+ gog_object_new_from_xml (res, ptr);
+ }
+ return res;
+}
+
+void
+go_xml_out_add_color (GsfXMLOut *output, char const *id, GOColor c)
+{
+ char *str = go_color_as_str (c);
+ gsf_xml_out_add_cstr_unchecked (output, id, str);
+ g_free (str);
+}
--- /dev/null
+++ lib/goffice/graph/gog-label.h
@@ -0,0 +1,37 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-label.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_LABEL_H
+#define GOG_LABEL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_LABEL_TYPE (gog_label_get_type ())
+#define GOG_LABEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_LABEL_TYPE, GogLabel))
+#define IS_GOG_LABEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_LABEL_TYPE))
+
+GType gog_label_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_LABEL_H */
--- /dev/null
+++ lib/goffice/graph/gog-object-xml.h
@@ -0,0 +1,64 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-object-xml.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_OBJECT_XML_H
+#define GOG_OBJECT_XML_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/utils/goffice-utils.h>
+#include <glib-object.h>
+#include <libxml/tree.h>
+#include <gsf/gsf-libxml.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GogPersist GogPersist;
+
+typedef struct {
+ GTypeInterface base;
+
+ gboolean (*dom_load) (GogPersist *gp, xmlNode *node);
+ void (*dom_save) (GogPersist const *gp, xmlNode *parent);
+ void (*sax_save) (GogPersist const *gp, GsfXMLOut *output);
+} GogPersistClass;
+
+#define GOG_PERSIST_TYPE (gog_persist_get_type ())
+#define GOG_PERSIST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PERSIST_TYPE, GogPersist))
+#define IS_GOG_PERSIST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PERSIST_TYPE))
+#define GOG_PERSIST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_PERSIST_TYPE, GogPersistClass))
+#define IS_GOG_PERSIST_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_PERSIST_TYPE))
+#define GOG_PERSIST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GOG_PERSIST_TYPE, GogPersistClass))
+
+GType gog_persist_get_type (void);
+
+gboolean gog_persist_dom_load (GogPersist *gp, xmlNode *node);
+void gog_persist_dom_save (GogPersist const *gp, xmlNode *parent);
+void gog_persist_sax_save (GogPersist const *gp, GsfXMLOut *output);
+
+void gog_object_set_arg (char const *name, char const *val, GogObject *obj);
+void gog_object_write_xml_sax(GogObject const *obj, GsfXMLOut *output);
+xmlNode *gog_object_write_xml (GogObject *obj, xmlDoc *doc);
+GogObject *gog_object_new_from_xml (GogObject *parent, xmlNode *node);
+
+void go_xml_out_add_color (GsfXMLOut *out, char const *id, GOColor c);
+
+G_END_DECLS
+
+#endif /* GOG_OBJECT_XML_H */
--- /dev/null
+++ lib/goffice/graph/gog-guru.glade
@@ -0,0 +1,426 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="GraphGuru">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">GNOME Office Graph</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_MOUSE</property>
+ <property name="modal">False</property>
+ <property name="default_width">530</property>
+ <property name="default_height">625</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area4">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="help_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-11</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button_cancel">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button_navigate">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-go-forward</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">2</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button_ok">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="notebook">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkVPaned" id="v_fmt_pane">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">1</property>
+
+ <child>
+ <widget class="GtkHPaned" id="h_fmt_pane">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">1</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">6</property>
+ <property name="left_padding">6</property>
+ <property name="right_padding">6</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="attr_window">
+ <property name="height_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkMenuBar" id="add_menubar">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="add_menu">
+ <property name="visible">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="precedence_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Order</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image71">
+ <property name="visible">True</property>
+ <property name="stock">gtk-sort-ascending</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenu" id="precedence_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="first_precedence">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Push to _back</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image72">
+ <property name="visible">True</property>
+ <property name="stock">gtk-goto-top</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="inc_precedence">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Push b_ackward</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image73">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-up</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="dec_precedence">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Pull f_orward</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image74">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-down</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="last_precedence">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Pull to _front</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image75">
+ <property name="visible">True</property>
+ <property name="stock">gtk-goto-bottom</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="delete">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NONE</property>
+ <property name="focus_on_click">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">False</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="width_request">200</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">6</property>
+ <property name="left_padding">6</property>
+ <property name="right_padding">6</property>
+
+ <child>
+ <widget class="Custom" id="sample_canvas">
+ <property name="visible">True</property>
+ <property name="creation_function">foo_canvas_new</property>
+ <property name="int1">0</property>
+ <property name="int2">0</property>
+ <property name="last_modification_time">Thu, 03 Apr 2003 17:05:22 GMT</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">False</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">False</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="prop_alignment">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">6</property>
+ <property name="right_padding">6</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">False</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <placeholder/>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-renderer-svg.h
@@ -0,0 +1,35 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-svg.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_RENDERER_SVG_H
+#define GOG_RENDERER_SVG_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <gsf/gsf.h>
+
+G_BEGIN_DECLS
+
+gboolean gog_graph_export_to_svg (GogGraph *graph,
+ GsfOutput *output,
+ double width, double height, double scale);
+
+G_END_DECLS
+
+#endif /* GOG_RENDERER_SVG_H */
--- /dev/null
+++ lib/goffice/graph/gog-error-bar.h
@@ -0,0 +1,81 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-error-bar.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_ERROR_BAR_H
+#define GOG_ERROR_BAR_H
+
+#include <goffice/utils/go-color.h>
+#include "gog-series.h"
+#include "gog-data-set.h"
+#include "gog-axis.h"
+
+typedef enum {
+ GOG_ERROR_BAR_TYPE_NONE,
+ GOG_ERROR_BAR_TYPE_ABSOLUTE,
+ GOG_ERROR_BAR_TYPE_RELATIVE,
+ GOG_ERROR_BAR_TYPE_PERCENT
+} GogErrorBarType;
+
+typedef enum {
+ GOG_ERROR_BAR_DISPLAY_NONE,
+ GOG_ERROR_BAR_DISPLAY_POSITIVE,
+ GOG_ERROR_BAR_DISPLAY_NEGATIVE,
+ GOG_ERROR_BAR_DISPLAY_BOTH
+} GogErrorBarDisplay;
+
+struct _GogErrorBar{
+ GObject base;
+ GogErrorBarType type;
+ GogSeries *series;
+ int dim_i;
+ int error_i;
+ GogErrorBarDisplay display;
+ float width;
+ GogStyle* style;
+};
+
+#define GOG_ERROR_BAR_TYPE (gog_error_bar_get_type ())
+#define GOG_ERROR_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_ERROR_BAR_TYPE, GogErrorBar))
+#define IS_GOG_ERROR_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_ERROR_BAR_TYPE))
+
+GType gog_error_bar_get_type (void);
+
+GogErrorBar *gog_error_bar_dup (GogErrorBar const *bar);
+
+#ifdef WITH_GTK
+gpointer gog_error_bar_prefs (GogSeries *series, char const* property,
+ gboolean horizontal, GogDataAllocator *dalloc,
+ GnmCmdContext *cc);
+#endif
+
+gboolean gog_error_bar_get_bounds (const GogErrorBar *bar, int index,
+ double *min, double *max);
+void gog_error_bar_get_minmax (const GogErrorBar *bar,
+ double *min, double *max);
+void gog_error_bar_render (const GogErrorBar *bar, GogRenderer *rend,
+ GogAxisMap *x_map, GogAxisMap *y_map,
+ double x, double y,
+ double minus,
+ double plus,
+ gboolean horizontal);
+gboolean gog_error_bar_is_visible (GogErrorBar *bar);
+
+#endif
--- /dev/null
+++ lib/goffice/graph/gog-renderer-impl.h
@@ -0,0 +1,99 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-impl.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_RENDERER_IMPL_H
+#define GOG_RENDERER_IMPL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/utils/goffice-utils.h>
+#include <goffice/utils/go-line.h>
+#include <goffice/graph/gog-renderer.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GogViewAllocation area;
+ gpointer data;
+} GogRendererClip;
+
+struct _GogRenderer {
+ GObject base;
+
+ GogGraph *model;
+ GogView *view;
+ double logical_width_pts;
+ double logical_height_pts;
+ float scale, scale_x, scale_y;
+ float zoom;
+
+ GogRendererClip const *cur_clip;
+ GSList *clip_stack;
+
+ GClosure *font_watcher;
+ gboolean needs_update;
+
+ GogStyle const *cur_style;
+ GSList *style_stack;
+
+ ArtVpathDash *line_dash;
+ ArtVpathDash *outline_dash;
+};
+
+typedef struct {
+ GObjectClass base;
+
+ /* Virtuals */
+ void (*font_removed) (GogRenderer *renderer, GOFont const *font);
+ void (*push_style) (GogRenderer *renderer, GogStyle const *style);
+ void (*pop_style) (GogRenderer *renderer);
+
+ void (*clip_push) (GogRenderer *renderer, GogRendererClip *clip);
+ void (*clip_pop) (GogRenderer *renderer, GogRendererClip *clip);
+
+ void (*sharp_path) (GogRenderer *renderer, ArtVpath *path, double line_width);
+
+ void (*draw_path) (GogRenderer *renderer, ArtVpath const *path,
+ GogViewAllocation const *bound);
+ void (*draw_polygon) (GogRenderer *renderer, ArtVpath const *path, gboolean narrow,
+ GogViewAllocation const *bound);
+ void (*draw_text) (GogRenderer *rend, char const *text,
+ GogViewAllocation const *pos, GtkAnchorType anchor,
+ GogViewAllocation *result);
+ void (*draw_marker) (GogRenderer *rend, double x, double y);
+
+ void (*measure_text) (GogRenderer *rend,
+ char const *text, GogViewRequisition *size);
+
+ double (*line_size) (GogRenderer const *rend, double width);
+
+ /* Signals */
+ void (*request_update) (GogRenderer *renderer);
+} GogRendererClass;
+
+#define GOG_RENDERER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_RENDERER_TYPE, GogRendererClass))
+#define IS_GOG_RENDERER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_RENDERER_TYPE))
+
+/* protected */
+void gog_renderer_invalidate_size_requests (GogRenderer *rend);
+
+G_END_DECLS
+
+#endif /* GOG_RENDERER_GROUP_IMPL_H */
--- /dev/null
+++ lib/goffice/graph/gog-plot-engine.c
@@ -0,0 +1,453 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-plot-engine.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-plot-engine.h>
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-theme.h>
+#include <glib/gi18n.h>
+#include <xml-io.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+#include <string.h>
+
+static GSList *refd_plugins;
+
+/***************************************************************************/
+/* Support plot engines in plugins */
+
+#include <plugin-service-impl.h>
+
+#define GOG_PLOT_ENGINE_SERVICE_TYPE (gog_plot_engine_service_get_type ())
+#define GOG_PLOT_ENGINE_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PLOT_ENGINE_SERVICE_TYPE, GogPlotEngineService))
+#define IS_GOG_PLOT_ENGINE_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PLOT_ENGINE_SERVICE_TYPE))
+
+static GType gog_plot_engine_service_get_type (void);
+
+typedef PluginServiceGObjectLoader GogPlotEngineService;
+typedef PluginServiceGObjectLoaderClass GogPlotEngineServiceClass;
+
+static GHashTable *pending_engines = NULL;
+
+static char *
+gog_plot_engine_service_get_description (GnmPluginService *service)
+{
+ return g_strdup (_("Plot Engine"));
+}
+
+static void
+gog_plot_engine_service_class_init (PluginServiceGObjectLoaderClass *gobj_loader_class)
+{
+ GnmPluginServiceClass *ps_class = GPS_CLASS (gobj_loader_class);
+
+ ps_class->get_description = gog_plot_engine_service_get_description;
+
+ gobj_loader_class->pending =
+ pending_engines = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+GSF_CLASS (GogPlotEngineService, gog_plot_engine_service,
+ gog_plot_engine_service_class_init, NULL,
+ GNM_PLUGIN_SERVICE_GOBJECT_LOADER_TYPE)
+
+GogPlot *
+gog_plot_new_by_name (char const *id)
+{
+ GType type = g_type_from_name (id);
+
+ if (type == 0) {
+ ErrorInfo *err = NULL;
+ GnmPluginService *service =
+ pending_engines
+ ? g_hash_table_lookup (pending_engines, id)
+ : NULL;
+ GnmPlugin *plugin;
+
+ if (!service || !service->is_active)
+ return NULL;
+
+ g_return_val_if_fail (!service->is_loaded, NULL);
+
+ plugin_service_load (service, &err);
+ type = g_type_from_name (id);
+
+ if (err != NULL) {
+ error_info_print (err);
+ error_info_free (err);
+ }
+
+ g_return_val_if_fail (type != 0, NULL);
+
+ /*
+ * The plugin defined a gtype so it must not be unloaded.
+ */
+ plugin = plugin_service_get_plugin (service);
+ refd_plugins = g_slist_prepend (refd_plugins, plugin);
+ g_object_ref (plugin);
+ gnm_plugin_use_ref (plugin);
+ }
+
+ return g_object_new (type, NULL);
+}
+
+/***************************************************************************/
+/* Use a plugin service to define where to find plot types */
+
+#define GOG_PLOT_TYPE_SERVICE_TYPE (gog_plot_type_service_get_type ())
+#define GOG_PLOT_TYPE_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PLOT_TYPE_SERVICE_TYPE, GogPlotTypeService))
+#define IS_GOG_PLOT_TYPE_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PLOT_TYPE_SERVICE_TYPE))
+
+GType gog_plot_type_service_get_type (void);
+
+typedef struct {
+ PluginServiceSimple base;
+
+ GSList *families, *types;
+} GogPlotTypeService;
+
+typedef struct{
+ PluginServiceSimpleClass base;
+} GogPlotTypeServiceClass;
+
+static GHashTable *pending_plot_type_files = NULL;
+static GObjectClass *plot_type_parent_klass;
+
+static void
+cb_pending_plot_types_load (char const *path,
+ GogPlotTypeService *service,
+ G_GNUC_UNUSED gpointer ignored)
+{
+ xmlNode *ptr, *prop;
+ xmlDoc *doc = xmlParseFile (path);
+ GogPlotFamily *family = NULL;
+ GogPlotType *type;
+ int col, row;
+ xmlChar *name, *image_file, *description, *engine;
+
+ g_return_if_fail (doc != NULL && doc->xmlRootNode != NULL);
+
+ /* do the families before the types so that they are available */
+ for (ptr = doc->xmlRootNode->xmlChildrenNode; ptr ; ptr = ptr->next)
+ if (!xmlIsBlankNode (ptr) && ptr->name && !strcmp (ptr->name, "Family")) {
+ name = xmlGetProp (ptr, "_name");
+ image_file = xmlGetProp (ptr, "sample_image_file");
+ family = gog_plot_family_register (name, image_file);
+ if (family != NULL)
+ service->families = g_slist_prepend (service->families, family);
+ if (name != NULL) xmlFree (name);
+ if (image_file != NULL) xmlFree (image_file);
+ }
+
+ for (ptr = doc->xmlRootNode->xmlChildrenNode; ptr ; ptr = ptr->next)
+ if (!xmlIsBlankNode (ptr) && ptr->name && !strcmp (ptr->name, "Type")) {
+ name = xmlGetProp (ptr, "family");
+ if (name != NULL) {
+ family = gog_plot_family_by_name (name);
+ xmlFree (name);
+ if (family == NULL)
+ continue;
+ }
+
+ name = xmlGetProp (ptr, "_name");
+ image_file = xmlGetProp (ptr, "sample_image_file");
+ description = xmlGetProp (ptr, "_description");
+ engine = xmlGetProp (ptr, "engine");
+ if (xml_node_get_int (ptr, "col", &col) &&
+ xml_node_get_int (ptr, "row", &row)) {
+ type = gog_plot_type_register (family, col, row,
+ name, image_file, description, engine);
+ if (type != NULL) {
+ service->types = g_slist_prepend (service->types, type);
+ for (prop = ptr->xmlChildrenNode ; prop != NULL ; prop = prop->next)
+ if (!xmlIsBlankNode (prop) &&
+ prop->name && !strcmp (prop->name, "property")) {
+ xmlChar *prop_name = xmlGetProp (prop, "name");
+
+ if (prop_name == NULL) {
+ g_warning ("missing name for property entry");
+ continue;
+ }
+
+ if (type->properties == NULL)
+ type->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
+ xmlFree, xmlFree);
+ g_hash_table_replace (type->properties,
+ prop_name, xmlNodeGetContent (prop));
+ }
+ }
+ }
+ if (name != NULL) xmlFree (name);
+ if (image_file != NULL) xmlFree (image_file);
+ if (description != NULL) xmlFree (description);
+ if (engine != NULL) xmlFree (engine);
+ }
+
+ xmlFreeDoc (doc);
+}
+
+static void
+pending_plot_types_load (void)
+{
+ if (pending_plot_type_files != NULL) {
+ GHashTable *tmp = pending_plot_type_files;
+ pending_plot_type_files = NULL;
+ g_hash_table_foreach (tmp,
+ (GHFunc) cb_pending_plot_types_load, NULL);
+ g_hash_table_destroy (tmp);
+ }
+}
+
+static void
+gog_plot_type_service_read_xml (GnmPluginService *service, xmlNode *tree, ErrorInfo **ret_error)
+{
+ char *path;
+ xmlNode *ptr;
+
+ for (ptr = tree->xmlChildrenNode; ptr != NULL; ptr = ptr->next)
+ if (0 == xmlStrcmp (ptr->name, "file") &&
+ NULL != (path = xmlNodeGetContent (ptr))) {
+ if (!g_path_is_absolute (path)) {
+ char const *dir = gnm_plugin_get_dir_name (
+ plugin_service_get_plugin (service));
+ char *tmp = g_build_filename (dir, path, NULL);
+ g_free (path);
+ path = tmp;
+ }
+ if (pending_plot_type_files == NULL)
+ pending_plot_type_files = g_hash_table_new_full (
+ g_str_hash, g_str_equal, g_free, g_object_unref);
+ g_object_ref (service);
+ g_hash_table_replace (pending_plot_type_files, path, service);
+ }
+}
+
+static char *
+gog_plot_type_service_get_description (GnmPluginService *service)
+{
+ return g_strdup (_("Plot Type"));
+}
+
+static void
+gog_plot_type_service_finalize (GObject *obj)
+{
+ GogPlotTypeService *service = GOG_PLOT_TYPE_SERVICE (obj);
+ GSList *ptr;
+
+ for (ptr = service->families ; ptr != NULL ; ptr = ptr->next) {
+ }
+ g_slist_free (service->families);
+ service->families = NULL;
+
+ for (ptr = service->types ; ptr != NULL ; ptr = ptr->next) {
+ }
+ g_slist_free (service->types);
+ service->types = NULL;
+
+ (plot_type_parent_klass->finalize) (obj);
+}
+
+static void
+gog_plot_type_service_init (GObject *obj)
+{
+ GogPlotTypeService *service = GOG_PLOT_TYPE_SERVICE (obj);
+
+ service->families = NULL;
+ service->types = NULL;
+}
+
+static void
+gog_plot_type_service_class_init (GObjectClass *gobject_klass)
+{
+ GnmPluginServiceClass *ps_class = GPS_CLASS (gobject_klass);
+
+ plot_type_parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = gog_plot_type_service_finalize;
+ ps_class->read_xml = gog_plot_type_service_read_xml;
+ ps_class->get_description = gog_plot_type_service_get_description;
+}
+
+GSF_CLASS (GogPlotTypeService, gog_plot_type_service,
+ gog_plot_type_service_class_init, gog_plot_type_service_init,
+ GNM_PLUGIN_SERVICE_SIMPLE_TYPE)
+
+/***************************************************************************/
+/* Use a plugin service to define themes */
+
+#define GOG_THEME_SERVICE_TYPE (gog_theme_service_get_type ())
+#define GOG_THEME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_THEME_SERVICE_TYPE, GogThemeService))
+#define IS_GOG_THEME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_THEME_SERVICE_TYPE))
+
+GType gog_theme_service_get_type (void);
+
+typedef PluginServiceSimple GogThemeService;
+typedef PluginServiceSimpleClass GogThemeServiceClass;
+
+static void
+gog_theme_service_read_xml (GnmPluginService *service, xmlNode *tree, ErrorInfo **ret_error)
+{
+ char *path;
+ xmlNode *ptr;
+
+ for (ptr = tree->xmlChildrenNode; ptr != NULL; ptr = ptr->next)
+ if (0 == xmlStrcmp (ptr->name, "file") &&
+ NULL != (path = xmlNodeGetContent (ptr))) {
+ if (!g_path_is_absolute (path)) {
+ char const *dir = gnm_plugin_get_dir_name (
+ plugin_service_get_plugin (service));
+ char *tmp = g_build_filename (dir, path, NULL);
+ g_free (path);
+ path = tmp;
+ }
+ gog_theme_register_file (
+ plugin_service_get_description (service), path);
+ }
+}
+
+static char *
+gog_theme_service_get_description (GnmPluginService *service)
+{
+ return g_strdup (_("Chart Theme"));
+}
+
+static void
+gog_theme_service_class_init (GnmPluginServiceClass *ps_class)
+{
+ ps_class->read_xml = gog_theme_service_read_xml;
+ ps_class->get_description = gog_theme_service_get_description;
+}
+
+GSF_CLASS (GogThemeService, gog_theme_service,
+ gog_theme_service_class_init, NULL,
+ GNM_PLUGIN_SERVICE_SIMPLE_TYPE)
+
+/***************************************************************************/
+
+void
+gog_plugin_services_init (void)
+{
+ plugin_service_define ("plot_engine", &gog_plot_engine_service_get_type);
+ plugin_service_define ("plot_type", &gog_plot_type_service_get_type);
+ plugin_service_define ("chart_theme", &gog_theme_service_get_type);
+}
+
+void
+gog_plugin_services_shutdown (void)
+{
+ g_slist_foreach (refd_plugins, (GFunc)gnm_plugin_use_unref, NULL);
+ g_slist_foreach (refd_plugins, (GFunc)g_object_unref, NULL);
+ g_slist_free (refd_plugins);
+}
+
+/***************************************************************************/
+
+static GHashTable *plot_families = NULL;
+
+static void
+gog_plot_family_free (GogPlotFamily *family)
+{
+ g_free (family->name); family->name = NULL;
+ g_free (family->sample_image_file); family->sample_image_file = NULL;
+ if (family->types) {
+ g_hash_table_destroy (family->types);
+ family->types = NULL;
+ }
+ g_free (family);
+}
+
+static void
+gog_plot_type_free (GogPlotType *type)
+{
+ g_free (type->name);
+ g_free (type->sample_image_file);
+ g_free (type->description);
+ g_free (type->engine);
+ g_free (type);
+}
+
+static void
+create_plot_families (void)
+{
+ if (!plot_families)
+ plot_families = g_hash_table_new_full
+ (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) gog_plot_family_free);
+}
+
+GHashTable const *
+gog_plot_families (void)
+{
+ create_plot_families ();
+ pending_plot_types_load ();
+ return plot_families;
+}
+GogPlotFamily *
+gog_plot_family_by_name (char const *name)
+{
+ create_plot_families ();
+ pending_plot_types_load ();
+ return g_hash_table_lookup (plot_families, name);
+}
+
+GogPlotFamily *
+gog_plot_family_register (char const *name, char const *sample_image_file)
+{
+ GogPlotFamily *res;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (sample_image_file != NULL, NULL);
+
+ create_plot_families ();
+ g_return_val_if_fail (g_hash_table_lookup (plot_families, name) == NULL, NULL);
+
+ res = g_new0 (GogPlotFamily, 1);
+ res->name = g_strdup (name);
+ res->sample_image_file = g_strdup (sample_image_file);
+ res->types = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) gog_plot_type_free);
+ g_hash_table_insert (plot_families, res->name, res);
+
+ return res;
+}
+
+GogPlotType *
+gog_plot_type_register (GogPlotFamily *family, int col, int row,
+ char const *name, char const *sample_image_file,
+ char const *description, char const *engine)
+{
+ GogPlotType *res;
+
+ g_return_val_if_fail (family != NULL, NULL);
+
+ res = g_new0 (GogPlotType, 1);
+ res->name = g_strdup (name);
+ res->sample_image_file = g_strdup (sample_image_file);
+ res->description = g_strdup (description);
+ res->engine = g_strdup (engine);
+
+ res->col = col;
+ res->row = row;
+ res->family = family;
+ g_hash_table_replace (family->types, res->name, res);
+
+ return res;
+}
+
--- /dev/null
+++ lib/goffice/graph/gog-outlined-object.h
@@ -0,0 +1,61 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-outlined-object.h : some utility classes for objects with outlines.
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_OUTLINED_OBJECT_H
+#define GOG_OUTLINED_OBJECT_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-view.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GogStyledObject base;
+ double padding_pts;
+} GogOutlinedObject;
+
+typedef GogStyledObjectClass GogOutlinedObjectClass;
+
+#define GOG_OUTLINED_OBJECT_TYPE (gog_outlined_object_get_type ())
+#define GOG_OUTLINED_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_OUTLINED_OBJECT_TYPE, GogOutlinedObject))
+#define IS_GOG_OUTLINED_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_OUTLINED_OBJECT_TYPE))
+
+GType gog_outlined_object_get_type (void);
+double gog_outlined_object_get_pad (GogOutlinedObject const *goo);
+
+/****************************************************************************/
+
+typedef GogView GogOutlinedView;
+typedef struct {
+ GogViewClass base;
+ gboolean call_parent_render;
+} GogOutlinedViewClass;
+
+#define GOG_OUTLINED_VIEW_TYPE (gog_outlined_view_get_type ())
+#define GOG_OUTLINED_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_OUTLINED_VIEW_TYPE, GogOutlinedView))
+#define IS_GOG_OUTLINED_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_OUTLINED_VIEW_TYPE))
+#define GOG_OUTLINED_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_OUTLINED_VIEW_TYPE, GogOutlinedViewClass))
+
+GType gog_outlined_view_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_OUTLINED_VIEW_H */
--- /dev/null
+++ lib/goffice/graph/Makefile.am
@@ -0,0 +1,93 @@
+SUBDIRS = plugins
+
+# SUBDIRS += tests
+## Don't put comment on an assignment line! It can trigger an automake bug.
+
+noinst_LTLIBRARIES = libgoffice-graph.la
+
+AM_CFLAGS = ${GLIB_CFLAGS} ${GSF_CFLAGS} ${ART_CFLAGS} ${GLADE_CFLAGS} ${GNOME_CFLAGS} ${PRINT_CFLAGS}
+
+libgoffice_graph_la_SOURCES = \
+ goffice-graph.h \
+ gog-object.c \
+ gog-object.h \
+ gog-object-xml.c \
+ gog-object-xml.h \
+ gog-styled-object.c \
+ gog-styled-object.h \
+ gog-outlined-object.c \
+ gog-outlined-object.h \
+ gog-view.c \
+ gog-view.h \
+ \
+ gog-graph.c \
+ gog-graph.h \
+ gog-graph-impl.h \
+ gog-chart.c \
+ gog-chart.h \
+ gog-chart-impl.h \
+ \
+ gog-axis.c \
+ gog-axis.h \
+ gog-legend.c \
+ gog-legend.h \
+ gog-label.c \
+ gog-label.h \
+ gog-grid.c \
+ gog-grid.h \
+ gog-grid-line.c \
+ gog-grid-line.h \
+ \
+ gog-style.c \
+ gog-style.h \
+ gog-theme.c \
+ gog-theme.h \
+ \
+ gog-plot.c \
+ gog-plot.h \
+ gog-plot-impl.h \
+ gog-plot-engine.h \
+ gog-plot-engine.c \
+ gog-series.c \
+ gog-series.h \
+ gog-series-impl.h \
+ gog-error-bar.c \
+ gog-error-bar.h \
+ gog-data-allocator.c \
+ gog-data-allocator.h \
+ gog-data-set.c \
+ gog-data-set.h \
+ \
+ go-data.c \
+ go-data.h \
+ go-data-impl.h \
+ go-data-simple.c \
+ go-data-simple.h \
+ \
+ gog-renderer.c \
+ gog-renderer.h \
+ gog-renderer-impl.h \
+ gog-renderer-pixbuf.c \
+ gog-renderer-pixbuf.h \
+ gog-renderer-gnome-print.c \
+ gog-renderer-gnome-print.h \
+ gog-renderer-svg.c \
+ gog-renderer-svg.h
+
+#
+# gog-guru.c \
+# gog-guru.h \
+# gog-control-foocanvas.c \
+# gog-control-foocanvas.h \
+#
+
+#graph_glade_DATA = \
+# gog-guru.glade \
+# gog-guru-type-selector.glade \
+# gog-style-prefs.glade \
+# gog-axis-prefs.glade \
+# gog-error-bar-prefs.glade
+
+# EXTRA_DIST = $(graph_glade_DATA)
+
+include $(srcdir)/../goffice.mk
--- /dev/null
+++ lib/goffice/graph/plugins/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = plot_pie plot_barcol plot_xy plot_radar plot_surface
--- /dev/null
+++ lib/goffice/utils/go-math.h
@@ -0,0 +1,46 @@
+#ifndef __GO_MATH_H
+#define __GO_MATH_H
+
+#include <math.h>
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#ifdef HAVE_IEEE754_H
+#include <ieee754.h>
+#endif
+
+/* What a circus! */
+#ifdef HAVE_FINITE
+#define go_finite finite
+#elif defined(HAVE_ISFINITE)
+#define go_finite isfinite
+#elif defined(FINITE)
+#define go_finite FINITE
+#else
+#error "I don't know an equivalent of finite for your system; you lose"
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+extern double go_nan;
+extern double go_pinf;
+extern double go_ninf;
+
+/* ------------------------------------------------------------------------- */
+
+double go_add_epsilon (double x);
+double go_sub_epsilon (double x);
+
+/* ------------------------------------------------------------------------- */
+
+double go_fake_floor (double x);
+double go_fake_ceil (double x);
+double go_fake_trunc (double x);
+
+/* ------------------------------------------------------------------------- */
+
+void go_math_init (void);
+
+/* ------------------------------------------------------------------------- */
+
+#endif /* __GO_MATH_H */
--- /dev/null
+++ lib/goffice/utils/go-pattern.h
@@ -0,0 +1,77 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-pattern.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_PATTERN_H
+#define GO_PATTERN_H
+
+#include <glib.h>
+#include <goffice/utils/goffice-utils.h>
+#include <libart_lgpl/art_render.h>
+#include <libart_lgpl/art_svp.h>
+
+G_BEGIN_DECLS
+
+struct _GOPattern {
+ GOColor fore, back;
+ unsigned pattern;
+};
+
+/* Useful for themes to explicitly name the pattern */
+typedef enum {
+ GO_PATTERN_SOLID,
+ GO_PATTERN_GREY75,
+ GO_PATTERN_GREY50,
+ GO_PATTERN_GREY25,
+ GO_PATTERN_GREY125,
+ GO_PATTERN_GREY625,
+ GO_PATTERN_HORIZ,
+ GO_PATTERN_VERT,
+ GO_PATTERN_REV_DIAG,
+ GO_PATTERN_DIAG,
+ GO_PATTERN_DIAG_CROSS,
+ GO_PATTERN_THICK_DIAG_CROSS,
+ GO_PATTERN_THIN_HORIZ,
+ GO_PATTERN_THIN_VERT,
+ GO_PATTERN_THIN_REV_DIAG,
+ GO_PATTERN_THIN_DIAG,
+ GO_PATTERN_THIN_HORIZ_CROSS,
+ GO_PATTERN_THIN_DIAG_CROSS,
+ GO_PATTERN_FOREGROUND_SOLID,
+ GO_PATTERN_SMALL_CIRCLES,
+ GO_PATTERN_SEMI_CIRCLES,
+ GO_PATTERN_THATCH,
+ GO_PATTERN_LARGE_CIRCLES,
+ GO_PATTERN_BRICKS,
+ GO_PATTERN_MAX
+} GOPatternType;
+
+GOPatternType go_pattern_from_str (char const *name);
+char const *go_pattern_as_str (GOPatternType pattern);
+gboolean go_pattern_is_solid (GOPattern const *pat, GOColor *color);
+void go_pattern_set_solid (GOPattern *pat, GOColor fore);
+void go_pattern_render_svp (GOPattern const *pat, ArtSVP const *svp,
+ int x0, int y0, int x1, int y1,
+ art_u8 *buf, int rowstride);
+gpointer go_pattern_selector (GOColor fore, GOColor back,
+ GOPatternType default_pat);
+
+G_END_DECLS
+
+#endif /* GO_PATTERN_H */
--- /dev/null
+++ lib/goffice/utils/go-locale.h
@@ -0,0 +1,32 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-locale.h :
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_LOCALE_H
+#define GO_LOCALE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+GList const *go_locale_languages (void);
+
+G_END_DECLS
+
+#endif /* GO_LOCALE_H */
--- /dev/null
+++ lib/goffice/utils/go-gradient.h
@@ -0,0 +1,67 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-gradient.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_GRADIENT_H
+#define GO_GRADIENT_H
+
+#include <glib.h>
+#include <goffice/utils/goffice-utils.h>
+#include <libart_lgpl/libart.h>
+#include <libart_lgpl/art_render_gradient.h>
+
+#ifdef WITH_GTK
+#include <gtk/gtkwidget.h>
+#endif
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GO_GRADIENT_N_TO_S,
+ GO_GRADIENT_S_TO_N,
+ GO_GRADIENT_N_TO_S_MIRRORED,
+ GO_GRADIENT_S_TO_N_MIRRORED,
+ GO_GRADIENT_W_TO_E,
+ GO_GRADIENT_E_TO_W,
+ GO_GRADIENT_W_TO_E_MIRRORED,
+ GO_GRADIENT_E_TO_W_MIRRORED,
+ GO_GRADIENT_NW_TO_SE,
+ GO_GRADIENT_SE_TO_NW,
+ GO_GRADIENT_NW_TO_SE_MIRRORED,
+ GO_GRADIENT_SE_TO_NW_MIRRORED,
+ GO_GRADIENT_NE_TO_SW,
+ GO_GRADIENT_SW_TO_NE,
+ GO_GRADIENT_SW_TO_NE_MIRRORED,
+ GO_GRADIENT_NE_TO_SW_MIRRORED
+} GOGradientDirection;
+
+GOGradientDirection go_gradient_dir_from_str (const gchar *name);
+const gchar *go_gradient_dir_as_str (GOGradientDirection dir);
+void go_gradient_setup (ArtGradientLinear *gradient,
+ GOGradientDirection dir, GOColor col0, GOColor col1,
+ double x0, double y0, double x1, double y1,
+ ArtGradientStop *stops);
+
+#ifdef WITH_GTK
+GtkWidget *go_gradient_selector (GOColor fore, GOColor back);
+#endif
+
+G_END_DECLS
+
+#endif /* GO_GRADIENT_H */
--- /dev/null
+++ lib/goffice/utils/go-line.c
@@ -0,0 +1,315 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-line.c :
+ *
+ * Copyright (C) 2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+
+#include "go-color.h"
+#include "go-line.h"
+
+#ifdef WITH_GTK
+#include <goffice/gui-utils/go-combo-pixmaps.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+typedef struct {
+ int n_dash;
+ double dash[6];
+} GOLineDashDesc;
+
+static const GOLineDashDesc line_dash_desc = {2, { 18, 6 } };
+static const GOLineDashDesc line_dot_desc = {2, { 3, 3 } };
+static const GOLineDashDesc line_dash_dot_desc = {4, { 9, 3, 6, 3 } };
+static const GOLineDashDesc line_dash_dot_dot_desc = {6, { 9, 3, 3, 3, 3, 3 } };
+
+struct {
+ GOLineDashType type;
+ char const *label;
+ char const *name;
+ const GOLineDashDesc *dash_desc;
+} line_dashes[GO_LINE_MAX] =
+{
+ { GO_LINE_NONE, N_("None"), "none", NULL },
+ { GO_LINE_SOLID, N_("Solid"), "solid", NULL },
+ { GO_LINE_DASH, N_("Dash"), "dash", &line_dash_desc },
+ { GO_LINE_DOT, N_("Dot"), "dot", &line_dot_desc},
+ { GO_LINE_DASH_DOT, N_("Dash dot"), "dash-dot", &line_dash_dot_desc },
+ { GO_LINE_DASH_DOT_DOT, N_("Dash dot dot"), "dash-dot-dot", &line_dash_dot_dot_desc }
+};
+
+GOLineDashType
+go_line_dash_from_str (char const *name)
+{
+ unsigned i;
+ GOLineDashType ret = GO_LINE_NONE;
+
+ for (i = 0; i < GO_LINE_MAX; i++) {
+ if (strcmp (line_dashes[i].name, name) == 0) {
+ ret = line_dashes[i].type;
+ break;
+ }
+ }
+ return ret;
+}
+
+char const *
+go_line_dash_as_str (GOLineDashType type)
+{
+ unsigned i;
+ char const *ret = "none";
+
+ for (i = 0; i < GO_LINE_MAX; i++) {
+ if (line_dashes[i].type == type) {
+ ret = line_dashes[i].name;
+ break;
+ }
+ }
+ return ret;
+}
+
+void
+go_line_vpath_dash_free (ArtVpathDash *dash)
+{
+ if (dash != NULL)
+ g_free (dash->dash);
+ g_free (dash);
+}
+
+ArtVpathDash *
+go_line_get_vpath_dash (GOLineDashType type, double scale)
+{
+ int i;
+ ArtVpathDash *dash = NULL;
+ const GOLineDashDesc *dash_desc;
+
+ if (type < 0 || type >= G_N_ELEMENTS (line_dashes))
+ return NULL;
+
+ dash_desc = line_dashes[type].dash_desc;
+ if (dash_desc != NULL) {
+ dash = g_new (ArtVpathDash, 1);
+ dash->offset = 0.5;
+ dash->n_dash = dash_desc->n_dash;
+ dash->dash = g_new (double, dash->n_dash);
+ for (i = 0; i < dash->n_dash; i++)
+ dash->dash[i] = scale * dash_desc->dash[i];
+ }
+
+ return dash;
+}
+
+/* Liang-Barsky line clipping */
+ArtVpath *
+go_line_clip_vpath (ArtVpath const *vpath, GogViewAllocation const *clip_area)
+{
+ double x1, y1, x2, y2;
+ double p[4], q[4], r, t1 = 0., t2 = 1., delta_x, delta_y;
+ double x_min, x_max, y_min, y_max;
+ unsigned i = 0, j;
+ gboolean clip_last, clip_first;
+ ArtVpath *result_path;
+ int n_result, n_result_max;
+
+ x_min = clip_area->x;
+ x_max = x_min + clip_area->w;
+ y_min = clip_area->y;
+ y_max = y_min + clip_area->h;
+
+ n_result = 0;
+ n_result_max = 16;
+ result_path = art_new (ArtVpath, n_result_max);
+
+ /* TODO clip_first computation isn't needed if previous clip_last was FALSE */
+ while (vpath[i].code != ART_END) {
+ gboolean reject = FALSE;
+ clip_last = TRUE;
+ while (vpath[i+1].code == ART_LINETO) {
+
+ t1 = 0.;
+ t2 = 1.;
+
+ x1 = vpath[i].x;
+ y1 = vpath[i].y;
+ x2 = vpath[i+1].x;
+ y2 = vpath[i+1].y;
+
+ delta_x = x2 - x1;
+ delta_y = y2 - y1;
+
+ p[0] = - delta_x;
+ q[0] = x1 - x_min;
+ p[1] = delta_x;
+ q[1] = x_max - x1;
+ p[2] = - delta_y;
+ q[2] = y1 - y_min;
+ p[3] = delta_y;
+ q[3] = y_max - y1;
+
+ clip_last = FALSE;
+ clip_first = FALSE;
+
+ for (j = 0; j < 4; j++) {
+ if (p[j] < 0.) {
+ r = q[j] / p[j];
+ if (r > t1) {
+ t1 = r;
+ clip_first = TRUE;
+ }
+ }
+ else if (p[j] > 0.) {
+ r = q[j] / p[j];
+ if (r < t2) {
+ t2 = r;
+ clip_last = TRUE;
+ }
+ }
+ }
+
+ if (t1 <= t2) {
+ reject = FALSE;
+ if (clip_first)
+ art_vpath_add_point (&result_path, &n_result, &n_result_max,
+ ART_MOVETO,
+ x1 + t1 * delta_x,
+ y1 + t1 * delta_y);
+ else
+ art_vpath_add_point (&result_path, &n_result, &n_result_max,
+ vpath[i].code,
+ vpath[i].x,
+ vpath[i].y);
+ if (clip_last)
+ art_vpath_add_point (&result_path, &n_result, &n_result_max,
+ ART_LINETO,
+ x1 + t2 * delta_x,
+ y1 + t2 * delta_y);
+ } else
+ reject = TRUE;
+
+ i++;
+ }
+ if (!clip_last && !reject)
+ art_vpath_add_point (&result_path, &n_result, &n_result_max,
+ ART_LINETO, vpath[i].x, vpath[i].y);
+
+ i++;
+ }
+ art_vpath_add_point (&result_path, &n_result, &n_result_max,
+ ART_END, 0., 0.);
+
+ return result_path;
+}
+
+ArtVpath *
+go_line_dash_vpath (ArtVpath const *path,
+ ArtVpathDash const *dash,
+ GogViewAllocation const *bbox)
+{
+ ArtVpath *dashed;
+
+ if (dash == NULL)
+ return NULL;
+
+ if (bbox != NULL) {
+ ArtVpath *clipped = go_line_clip_vpath (path, bbox);
+ dashed = art_vpath_dash (clipped, dash);
+ g_free (clipped);
+ } else
+ dashed = art_vpath_dash (path, dash);
+
+ return dashed;
+}
+
+#ifdef WITH_GTK
+
+#define LINE_SAMPLE_HEIGHT 5
+#define LINE_SAMPLE_WIDTH 60
+
+gpointer
+go_line_dash_selector (GOLineDashType default_type)
+{
+ static GOLineDashType const elements[] = {
+ GO_LINE_NONE,
+ GO_LINE_SOLID,
+ GO_LINE_DASH,
+ GO_LINE_DOT,
+ GO_LINE_DASH_DOT,
+ GO_LINE_DASH_DOT_DOT,
+ GO_LINE_MAX
+ };
+
+ unsigned i;
+ gboolean is_auto;
+ GOComboPixmaps *w;
+ GdkPixbuf *pixbuf;
+ ArtVpathDash *dash;
+ GOLineDashType dash_type;
+ ArtVpath line[3], *path;
+ ArtSVP *svp;
+ GogViewAllocation bbox;
+
+ bbox.x = 0;
+ bbox.y = 0;
+ bbox.w = LINE_SAMPLE_WIDTH;
+ bbox.h = LINE_SAMPLE_HEIGHT;
+
+ line[0].code = ART_MOVETO;
+ line[1].code = ART_LINETO;
+ line[2].code = ART_END;
+ line[0].x = 0.5;
+ line[1].x = LINE_SAMPLE_WIDTH - 0.5;
+ line[0].y = line[1].y = LINE_SAMPLE_HEIGHT / 2.0;
+
+ w = go_combo_pixmaps_new (1);
+ for (i = 0; i < G_N_ELEMENTS (elements); i++) {
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ LINE_SAMPLE_WIDTH, LINE_SAMPLE_HEIGHT);
+ gdk_pixbuf_fill (pixbuf, 0); /* in case the fill colours have alpha = 0 */
+ is_auto = elements[i] == GO_LINE_MAX;
+ dash_type = is_auto ? default_type : i;
+ if (dash_type != GO_LINE_NONE) {
+ dash = go_line_get_vpath_dash (dash_type, 1.0);
+ path = dash != NULL ? art_vpath_dash (line, dash) : line;
+ svp = art_svp_vpath_stroke (path,
+ ART_PATH_STROKE_JOIN_MITER, ART_PATH_STROKE_CAP_ROUND,
+ 1.0, 4.0, 0.5);
+ if (dash != NULL) {
+ go_line_vpath_dash_free (dash);
+ g_free (path);
+ }
+ go_color_render_svp (0x000000FF, svp, 0, 0, LINE_SAMPLE_WIDTH, LINE_SAMPLE_HEIGHT,
+ gdk_pixbuf_get_pixels (pixbuf),
+ gdk_pixbuf_get_rowstride (pixbuf));
+ art_svp_free (svp);
+ }
+ if (is_auto) {
+ /* xgettext : this will appear as 'Automatic (patternname)' */
+ char *name = g_strdup_printf (_("Automatic (%s)"),
+ _(line_dashes [default_type].label));
+ go_combo_pixmaps_add_element (w, pixbuf, -default_type, name);
+ g_free (name);
+ } else
+ go_combo_pixmaps_add_element (w, pixbuf, dash_type,
+ _(line_dashes[dash_type].label));
+ }
+ return w;
+}
+#endif /*WITH_GTK*/
--- /dev/null
+++ lib/goffice/utils/go-color.h
@@ -0,0 +1,125 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-color.h
+ *
+ * Copyright (C) 1999, 2000 EMC Capital Management, Inc.
+ *
+ * Developed by Jon Trowbridge <trow at gnu.org> and
+ * Havoc Pennington <hp at pobox.com>.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GO_COLOR_H
+#define GO_COLOR_H
+
+#include <glib.h>
+#include <goffice/utils/goffice-utils.h>
+#include <libart_lgpl/art_render.h>
+#include <libart_lgpl/art_svp.h>
+#include <pango/pango.h>
+
+#ifdef WITH_GTK
+#include <gdk/gdktypes.h>
+#endif
+
+G_BEGIN_DECLS
+
+/*
+ Some convenient macros for drawing into an RGB buffer.
+ Beware of side effects, code-bloat, and all of the other classic
+ cpp-perils...
+*/
+
+#define GDK_TO_UINT(c) RGBA_TO_UINT(((c).red>>8), ((c).green>>8), ((c).blue>>8), 0xff)
+
+#define RGB_TO_UINT(r,g,b) ((((guint)(r))<<16)|(((guint)(g))<<8)|((guint)(b)))
+#define RGB_TO_RGBA(x,a) (((x) << 8) | ((((guint)a) & 0xff)))
+#define RGB_WHITE RGB_TO_UINT(0xff, 0xff, 0xff)
+#define RGB_BLACK RGB_TO_UINT(0x00, 0x00, 0x00)
+#define RGB_RED RGB_TO_UINT(0xff, 0x00, 0x00)
+#define RGB_GREEN RGB_TO_UINT(0x00, 0xff, 0x00)
+#define RGB_BLUE RGB_TO_UINT(0x00, 0x00, 0xff)
+#define RGB_YELLOW RGB_TO_UINT(0xff, 0xff, 0x00)
+#define RGB_VIOLET RGB_TO_UINT(0xff, 0x00, 0xff)
+#define RGB_CYAN RGB_TO_UINT(0x00, 0xff, 0xff)
+#define RGB_GREY(x) RGB_TO_UINT(x,x,x)
+
+#define RGBA_TO_UINT(r,g,b,a) ((((guint)(r))<<24)|(((guint)(g))<<16)|(((guint)(b))<<8)|(guint)(a))
+#define RGBA_WHITE RGB_TO_RGBA(RGB_WHITE, 0xff)
+#define RGBA_BLACK RGB_TO_RGBA(RGB_BLACK, 0xff)
+#define RGBA_RED RGB_TO_RGBA(RGB_RED, 0xff)
+#define RGBA_GREEN RGB_TO_RGBA(RGB_GREEN, 0xff)
+#define RGBA_BLUE RGB_TO_RGBA(RGB_BLUE, 0xff)
+#define RGBA_YELLOW RGB_TO_RGBA(RGB_YELLOW, 0xff)
+#define RGBA_VIOLET RGB_TO_RGBA(RGB_VIOLET, 0xff)
+#define RGBA_CYAN RGB_TO_RGBA(RGB_CYAN, 0xff)
+#define RGBA_GREY(x) RGB_TO_RGBA(RGB_GREY(x), 0xff)
+
+#define UINT_RGBA_R(x) (((guint)(x))>>24)
+#define UINT_RGBA_G(x) ((((guint)(x))>>16)&0xff)
+#define UINT_RGBA_B(x) ((((guint)(x))>>8)&0xff)
+#define UINT_RGBA_A(x) (((guint)(x))&0xff)
+#define UINT_RGBA_CHANGE_R(x, r) (((x)&(~(0xff<<24)))|(((r)&0xff)<<24))
+#define UINT_RGBA_CHANGE_G(x, g) (((x)&(~(0xff<<16)))|(((g)&0xff)<<16))
+#define UINT_RGBA_CHANGE_B(x, b) (((x)&(~(0xff<<8)))|(((b)&0xff)<<8))
+#define UINT_RGBA_CHANGE_A(x, a) (((x)&(~0xff))|((a)&0xff))
+#define UINT_TO_RGB(u,r,g,b) \
+{ (*(r)) = ((u)>>16)&0xff; (*(g)) = ((u)>>8)&0xff; (*(b)) = (u)&0xff; }
+#define UINT_TO_RGBA(u,r,g,b,a) \
+{ UINT_TO_RGB(((u)>>8),r,g,b); (*(a)) = (u)&0xff; }
+#define MONO_INTERPOLATE(v1, v2, t) ((gint)rint((v2)*(t)+(v1)*(1-(t))))
+#define UINT_INTERPOLATE(c1, c2, t) \
+ RGBA_TO_UINT( MONO_INTERPOLATE(UINT_RGBA_R(c1), UINT_RGBA_R(c2), t), \
+ MONO_INTERPOLATE(UINT_RGBA_G(c1), UINT_RGBA_G(c2), t), \
+ MONO_INTERPOLATE(UINT_RGBA_B(c1), UINT_RGBA_B(c2), t), \
+ MONO_INTERPOLATE(UINT_RGBA_A(c1), UINT_RGBA_A(c2), t) )
+#define PIXEL_RGB(p, r, g, b) \
+{((guchar*)(p))[0]=(r); ((guchar*)(p))[1]=(g); ((guchar*)(p))[2]=(b);}
+#define PIXEL_RGBA(p, r, g, b, a) \
+{ if ((a)>=0xff) { PIXEL_RGB(p,r,g,b) } \
+ else if ((a)>0) { \
+ guint pixel_tmp; \
+ pixel_tmp = ((guchar*)(p))[0]; \
+ ((guchar*)(p))[0] = pixel_tmp + ((((r)-pixel_tmp)*(a)+0x80) >> 8); \
+ pixel_tmp = ((guchar*)(p))[1]; \
+ ((guchar*)(p))[1] = pixel_tmp + ((((g)-pixel_tmp)*(a)+0x80) >> 8); \
+ pixel_tmp = ((guchar*)(p))[2]; \
+ ((guchar*)(p))[2] = pixel_tmp + ((((b)-pixel_tmp)*(a)+0x80) >> 8); }}
+#define PIXEL_RGB_UINT(p, i) \
+UINT_TO_RGB((i), ((guchar*)p), ((guchar*)p)+1, ((guchar*)p)+2)
+#define PIXEL_RGBA_UINT(p, i) \
+ PIXEL_RGBA((p), ((i)>>24)&0xff, ((i)>>16)&0xff, ((i)>>8)&0xff, (i)&0xff)
+#define PIXEL_BLACK(p) PIXEL_RGB(p,0,0,0)
+#define PIXEL_WHITE(p) PIXEL_RGB(p,0xff,0xff,0xff)
+#define PIXEL_GREY(p,g) PIXEL_RGB(p,g,g,g)
+#define PIXEL_GREYA(p,g,a) PIXEL_RGBA(p,g,g,g,a)
+
+void go_color_to_artpix (ArtPixMaxDepth *res, GOColor rgba);
+void go_color_render_svp (GOColor color, ArtSVP const *svp,
+ int x0, int y0, int x1, int y1,
+ art_u8 *buf, int rowstride);
+
+GOColor go_color_from_str (char const *string);
+gchar *go_color_as_str (GOColor color);
+PangoAttribute *go_color_to_pango (GOColor color, gboolean is_fore);
+#ifdef WITH_GTK
+GdkColor *go_color_to_gdk (GOColor color, GdkColor *res);
+#endif
+
+G_END_DECLS
+
+#endif /* GO_COLOR_H */
--- /dev/null
+++ lib/goffice/utils/go-format.c
@@ -0,0 +1,128 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-format.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-format.h"
+//#include <src/format.h>
+//#include <src/value.h>
+//#include <src/datetime.h>
+#include <format.h>
+#include <value.h>
+#include <datetime.h>
+
+GOFormat *
+go_format_new_from_XL (char const *descriptor_string, gboolean delocalize)
+{
+ return style_format_new_XL (descriptor_string, delocalize);
+}
+
+char *
+go_format_as_XL (GOFormat const *fmt, gboolean localized)
+{
+ return style_format_as_XL (fmt, localized);
+}
+
+GOFormat *
+go_format_ref (GOFormat *fmt)
+{
+ style_format_ref (fmt);
+ return fmt;
+}
+
+void
+go_format_unref (GOFormat *fmt)
+{
+ style_format_unref (fmt);
+}
+
+char *
+go_format_value (GOFormat const *fmt, double val)
+{
+ static GnmValueFloat tmp = { VALUE_FLOAT, NULL, 0. };
+ static GnmDateConventions conv = { FALSE };
+ tmp.val = val;
+ return format_value (fmt, (GnmValue *)&tmp, NULL, -1, &conv);
+}
+
+gboolean
+go_format_eq (GOFormat const *a, GOFormat const *b)
+{
+ if (a == NULL)
+ return b == NULL;
+ if (b == NULL)
+ return FALSE;
+ return style_format_equal (a, b);
+}
+
+/**
+ * go_format_general :
+ *
+ * Returns the 'General' #GOFormat but does not add a reference
+ **/
+GOFormat *
+go_format_general (void)
+{
+ return style_format_general ();
+}
+
+/**
+ * go_format_default_date :
+ *
+ * Returns the default date #GOFormat but does not add a reference
+ **/
+GOFormat *
+go_format_default_date (void)
+{
+ return style_format_default_date ();
+}
+
+/**
+ * go_format_default_time :
+ *
+ * Returns the default time #GOFormat but does not add a reference
+ **/
+GOFormat *
+go_format_default_time (void)
+{
+ return style_format_default_time ();
+}
+
+/**
+ * go_format_default_percentage :
+ *
+ * Returns the default percentage #GOFormat but does not add a reference
+ **/
+GOFormat *
+go_format_default_percentage (void)
+{
+ return style_format_default_percentage ();
+}
+
+/**
+ * go_format_default_money :
+ *
+ * Returns the default money #GOFormat but does not add a reference
+ **/
+GOFormat *
+go_format_default_money (void)
+{
+ return style_format_default_money ();
+}
--- /dev/null
+++ lib/goffice/utils/go-pattern.c
@@ -0,0 +1,567 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-pattern.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-pattern.h"
+#include "go-color.h"
+#ifdef WITH_GTK
+#include <goffice/gui-utils/go-combo-pixmaps.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+#endif
+
+#include <libart_lgpl/libart.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+typedef struct {
+ int const x, y;
+ char const pattern[8];
+} GOPatternSpec;
+
+static GOPatternSpec const go_patterns [] = {
+ { 8, 8, /* Solid */
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } },
+ { 8, 8, /* 75% */
+ { 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee } },
+ { 8, 8, /* 50% */
+ { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 } },
+ { 8, 8, /* 25% */
+ { 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88 } },
+ { 8, 8, /* 12.5% */
+ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 } },
+ { 8, 8, /* 6.25% */
+ { 0x20, 0x00, 0x02, 0x00, 0x20, 0x00, 0x02, 0x00 } },
+ { 8, 8, /* Horizontal Stripe */
+ { 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff } },
+ { 8, 8, /* Vertical Stripe */
+ { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33 } },
+ { 8, 8, /* Reverse Diagonal Stripe */
+ { 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc, 0x99 } },
+ { 8, 8, /* Diagonal Stripe */
+ { 0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99 } },
+ { 8, 8, /* Diagonal Crosshatch */
+ { 0x99, 0x66, 0x66, 0x99, 0x99, 0x66, 0x66, 0x99 } },
+ { 8, 8, /* Thick Diagonal Crosshatch */
+ { 0xff, 0x66, 0xff, 0x99, 0xff, 0x66, 0xff, 0x99 } },
+ { 8, 8, /* Thin Horizontal Stripe */
+ { 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00 } },
+ { 8, 8, /* Thin Vertical Stripe */
+ { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 } },
+ { 8, 8, /* Thin Reverse Diagonal Stripe */
+ { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 } },
+ { 8, 8, /* Thin Diagonal Stripe */
+ { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 } },
+ { 8, 8, /* Thin Crosshatch */
+ { 0x22, 0x22, 0xff, 0x22, 0x22, 0x22, 0xff, 0x22 } },
+ { 8, 8, /* Thin Diagonal Crosshatch */
+ { 0x88, 0x55, 0x22, 0x55, 0x88, 0x55, 0x22, 0x55 } },
+ { 8, 8, /* 100% */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 8, 8, /* Applix small circle */
+ { 0x99, 0x55, 0x33, 0xff, 0x99, 0x55, 0x33, 0xff } },
+ { 8, 8, /* Applix semicircle */
+ { 0x10, 0x10, 0x28, 0xc7, 0x01, 0x01, 0x82, 0x7c } },
+ { 8, 8, /* Applix small thatch */
+ { 0x22, 0x74, 0xf8, 0x71, 0x22, 0x17, 0x8f, 0x47 } },
+ { 8, 8, /* Applix round thatch */
+ { 0xc1, 0x80, 0x1c, 0x3e, 0x3e, 0x3e, 0x1c, 0x80 } },
+ { 8, 8, /* Applix Brick */
+ { 0x20, 0x20, 0x20, 0xff, 0x02, 0x02, 0x02, 0xff } }
+};
+
+static struct {
+ GOPatternType pattern;
+ char const *label;
+ char const *name;
+} pattern_names[] = {
+ { GO_PATTERN_SOLID, N_("Solid"), "solid" },
+ { GO_PATTERN_GREY75, N_("75% Grey"), "grey75" },
+ { GO_PATTERN_GREY50, N_("50% Grey"), "grey50" },
+ { GO_PATTERN_GREY25, N_("25% Grey"), "grey25" },
+ { GO_PATTERN_GREY125, N_("12.5% Grey"), "grey12.5" },
+ { GO_PATTERN_GREY625, N_("6.25% Grey"), "grey6.25" },
+ { GO_PATTERN_HORIZ, N_("Horizontal Stripe"), "horiz" },
+ { GO_PATTERN_VERT, N_("Vertical Stripe"), "vert" },
+ { GO_PATTERN_REV_DIAG, N_("Reverse Diagonal Stripe"), "rev-diag" },
+ { GO_PATTERN_DIAG, N_("Diagonal Stripe"), "diag" },
+ { GO_PATTERN_DIAG_CROSS, N_("Diagonal Crosshatch"), "diag-cross" },
+ { GO_PATTERN_THICK_DIAG_CROSS, N_("Thick Diagonal Crosshatch"), "thick-diag-cross" },
+ { GO_PATTERN_THIN_HORIZ, N_("Thin Horizontal Stripe"), "thin-horiz" },
+ { GO_PATTERN_THIN_VERT, N_("Thin Vertical Stripe"), "thin-vert" },
+ { GO_PATTERN_THIN_REV_DIAG, N_("Thin Reverse Diagonal Stripe"), "rev-diag" },
+ { GO_PATTERN_THIN_DIAG, N_("Thin Diagonal Stripe"), "thin-diag" },
+ { GO_PATTERN_THIN_HORIZ_CROSS, N_("Thin Horizontal Crosshatch"),"thin-horiz-cross" },
+ { GO_PATTERN_THIN_DIAG_CROSS, N_("Thin Diagonal Crosshatch"), "thin-diag-cross" },
+ { GO_PATTERN_FOREGROUND_SOLID, N_("Foreground Solid"), "foreground-solid" },
+ { GO_PATTERN_SMALL_CIRCLES, N_("Small Circles"), "small-circles" },
+ { GO_PATTERN_SEMI_CIRCLES, N_("Semi Circles"), "semi-circles" },
+ { GO_PATTERN_THATCH, N_("Thatch"), "thatch" },
+ { GO_PATTERN_LARGE_CIRCLES, N_("Large Circles"), "large-circles" },
+ { GO_PATTERN_BRICKS, N_("Bricks"), "bricks" }
+};
+
+
+GOPatternType
+go_pattern_from_str (char const *name)
+{
+ unsigned i;
+ GOPatternType ret = GO_PATTERN_SOLID;
+
+ for (i = 0; i < sizeof pattern_names / sizeof pattern_names[0]; i++) {
+ if (strcmp (pattern_names[i].name, name) == 0) {
+ ret = pattern_names[i].pattern;
+ break;
+ }
+ }
+ return ret;
+}
+char const *
+go_pattern_as_str (GOPatternType pattern)
+{
+ unsigned i;
+ char const *ret = "none";
+
+ for (i = 0; i < sizeof pattern_names / sizeof pattern_names[0]; i++) {
+ if (pattern_names[i].pattern == pattern) {
+ ret = pattern_names[i].name;
+ break;
+ }
+ }
+ return ret;
+}
+
+/**
+ * go_pattern_is_solid :
+ * @pat : #GOPattern
+ * @color : #GOColor
+ *
+ * Returns if @pat is solid, and stores the color in @color.
+ * If @pat is not solid @color is not touched.
+ **/
+gboolean
+go_pattern_is_solid (GOPattern const *pat, GOColor *color)
+{
+ g_return_val_if_fail (pat != NULL, FALSE);
+
+ if (pat->pattern == GO_PATTERN_SOLID || pat->fore == pat->back) {
+ *color = pat->back;
+ return TRUE;
+ }
+ if (pat->pattern == GO_PATTERN_FOREGROUND_SOLID) {
+ *color = pat->fore;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * go_pattern_set_solid :
+ * @pat : #GOPattern
+ * @fore : #GOColor
+ *
+ * Makes @pat a solid pattern with colour @fore.
+ **/
+void
+go_pattern_set_solid (GOPattern *pat, GOColor fore)
+{
+ g_return_if_fail (pat != NULL);
+ pat->pattern = GO_PATTERN_SOLID;
+ pat->fore = RGBA_BLACK;
+ pat->back = fore;
+}
+
+#ifdef WITH_GTK
+gpointer
+go_pattern_selector (GOColor fore, GOColor back,
+ GOPatternType default_pat)
+{
+ static GOPatternType const elements[] = {
+ GO_PATTERN_SOLID, GO_PATTERN_GREY75, GO_PATTERN_GREY50, GO_PATTERN_GREY25,
+ GO_PATTERN_GREY125, GO_PATTERN_GREY625, GO_PATTERN_HORIZ, GO_PATTERN_VERT,
+ GO_PATTERN_REV_DIAG, GO_PATTERN_DIAG, GO_PATTERN_DIAG_CROSS, GO_PATTERN_THICK_DIAG_CROSS,
+ GO_PATTERN_THIN_HORIZ, GO_PATTERN_THIN_VERT,
+ GO_PATTERN_THIN_REV_DIAG, GO_PATTERN_THIN_DIAG,
+ GO_PATTERN_THIN_HORIZ_CROSS, GO_PATTERN_THIN_DIAG_CROSS,
+ GO_PATTERN_FOREGROUND_SOLID, GO_PATTERN_SMALL_CIRCLES,
+ GO_PATTERN_SEMI_CIRCLES, GO_PATTERN_THATCH, GO_PATTERN_LARGE_CIRCLES, GO_PATTERN_BRICKS,
+ GO_PATTERN_MAX /* fill with auto */
+ };
+ int const W = 20, H = 20;
+ unsigned i;
+ gboolean is_auto;
+ GOComboPixmaps *w;
+ GdkPixbuf *pixbuf;
+ GOPattern pat;
+ ArtVpath path[6];
+ ArtSVP *svp;
+
+ pat.fore = fore;
+ pat.back = back;
+
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ path[0].x = path[1].x = path[4].x = 0;
+ path[2].x = path[3].x = W;
+ path[0].y = path[3].y = path[4].y = 0;
+ path[1].y = path[2].y = H;
+ svp = art_svp_from_vpath (path);
+
+ w = go_combo_pixmaps_new (5);
+ for (i = 0; i < G_N_ELEMENTS (elements); i++) {
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, W, H);
+ gdk_pixbuf_fill (pixbuf, 0); /* in case the fill colours have alpha = 0 */
+ is_auto = elements[i] == GO_PATTERN_MAX;
+ pat.pattern = is_auto ? default_pat : i;
+ go_pattern_render_svp (&pat, svp, 0, 0, W, H,
+ gdk_pixbuf_get_pixels (pixbuf),
+ gdk_pixbuf_get_rowstride (pixbuf));
+ if (is_auto) {
+ /* xgettext : this will appear as 'Automatic (patternname)' */
+ char *name = g_strdup_printf (_("Automatic (%s)"),
+ _(pattern_names [default_pat].label));
+ go_combo_pixmaps_add_element (w, pixbuf,
+ -default_pat, name);
+ g_free (name);
+ } else
+ go_combo_pixmaps_add_element (w, pixbuf, pat.pattern,
+ _(pattern_names[pat.pattern].label));
+ }
+ art_svp_free (svp);
+ return w;
+}
+#endif /* WITH_GTK */
+
+/*
+ * A slightly modified version of art_rgb_svp to render into rgba buffer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors:
+ * Raph Levien <raph at acm.org>
+ * Lauris Kaplinski <lauris at ariman.ee>
+ *
+ * Copyright (C) 1998 Raph Levien
+ *
+ */
+
+/* Render a sorted vector path into an RGBA buffer. */
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_render_aa.h>
+#include <libart_lgpl/art_rgb.h>
+
+typedef struct {
+ struct {
+ art_u8 r, g, b;
+ int alphatab[256];
+ } fore, back;
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+ guint8 const *pattern;
+} pattern_data;
+
+static void
+pattern_solid (pattern_data const *state, guint8 pat, int offset, int n)
+{
+ art_u8 r1, g1, b1, r2, g2, b2;
+ art_u8 *buf = state->buf + 4*offset;
+ int mask = 1 << (offset % 8);
+
+ r1 = state->fore.r;
+ g1 = state->fore.g;
+ b1 = state->fore.b;
+ r2 = state->back.r;
+ g2 = state->back.g;
+ b2 = state->back.b;
+ while (n-- > 0) {
+ if (pat & mask) {
+ *buf++ = r1;
+ *buf++ = g1;
+ *buf++ = b1;
+ } else {
+ *buf++ = r2;
+ *buf++ = g2;
+ *buf++ = b2;
+ }
+ * buf++ = 255;
+
+ if (mask != 0x80)
+ mask <<= 1;
+ else
+ mask = 1;
+ }
+}
+
+static void
+pattern_blend (pattern_data const *state, guint8 pat, int offset, int alpha, int n)
+{
+ int br, bg, bb, ba;
+ int cr, cg, cb;
+ int r1, g1, b1, r2, g2, b2;
+ art_u8 *buf = state->buf + 4*offset;
+ int mask = 1 << (offset % 8);
+ int alpha1 = state->fore.alphatab[alpha];
+ int alpha2 = state->back.alphatab[alpha];
+
+ r1 = state->fore.r;
+ g1 = state->fore.g;
+ b1 = state->fore.b;
+ r2 = state->back.r;
+ g2 = state->back.g;
+ b2 = state->back.b;
+
+ while (n-- > 0) {
+ br = * (buf + 0);
+ bg = * (buf + 1);
+ bb = * (buf + 2);
+ ba = * (buf + 3);
+
+ cr = (br * ba + 0x80) >> 8;
+ cg = (bg * ba + 0x80) >> 8;
+ cb = (bb * ba + 0x80) >> 8;
+
+ if (pat & mask) {
+ *buf++ = cr + (((r1 - cr) * alpha1 + 0x80) >> 8);
+ *buf++ = cg + (((g1 - cg) * alpha1 + 0x80) >> 8);
+ *buf++ = cb + (((b1 - cb) * alpha1 + 0x80) >> 8);
+ } else {
+ *buf++ = cr + (((r2 - cr) * alpha2 + 0x80) >> 8);
+ *buf++ = cg + (((g2 - cg) * alpha2 + 0x80) >> 8);
+ *buf++ = cb + (((b2 - cb) * alpha2 + 0x80) >> 8);
+ }
+ *buf++ = ba + (((255 - ba) * alpha + 0x80) >> 8);
+
+ if (mask != 0x80)
+ mask <<= 1;
+ else
+ mask = 1;
+ }
+}
+
+static void
+cb_pattern_alpha (void *callback_data, int y, int start,
+ ArtSVPRenderAAStep *steps, int n_steps)
+{
+ pattern_data *state = callback_data;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int k;
+ int alpha;
+ int x0 = state->x0;
+ int x1 = state->x1;
+ int pat = state->pattern [y % 8];
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ pattern_blend (state, pat, 0, alpha, run_x1 - x0);
+ }
+
+ /* render the steps into tmpbuf */
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ pattern_blend (state, pat, run_x0 - x0,
+ alpha, run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ pattern_blend (state, pat, run_x1 - x0,
+ alpha, x1 - run_x1);
+ }
+ } else {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ pattern_blend (state, pat, 0, alpha, x1 - x0);
+ }
+
+ state->buf += state->rowstride;
+}
+
+static void
+cb_pattern_opaque (void *callback_data, int y, int start,
+ ArtSVPRenderAAStep *steps, int n_steps)
+{
+ pattern_data *state = callback_data;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ int alpha;
+ int pat = state->pattern [y % 8];
+
+ x0 = state->x0;
+ x1 = state->x1;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ pattern_solid (state, pat, 0, run_x1 - x0);
+ else
+ pattern_blend (state, pat, 0, alpha, run_x1 - x0);
+ }
+ }
+
+ /* render the steps into tmpbuf */
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ pattern_solid (state, pat, run_x0 - x0,
+ run_x1 - run_x0);
+ else
+ pattern_blend (state, pat, run_x0 - x0,
+ alpha, run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ pattern_solid (state, pat, run_x1 - x0,
+ x1 - run_x1);
+ else
+ pattern_blend (state, pat, run_x1 - x0,
+ alpha, x1 - run_x1);
+ }
+ }
+ } else {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ pattern_solid (state, pat, 0, x1 - x0);
+ else
+ pattern_blend (state, pat, 0, alpha, x1 - x0);
+ }
+ }
+
+ state->buf += state->rowstride;
+}
+
+/**
+ * go_pattern_render_svp:
+ * @pat : #GOPattern
+ * @svp: The source sorted vector path.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @buf: Destination RGBA buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ *
+ * Renders the shape specified with @svp over the @buf RGB buffer.
+ * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @pat argument specifies the pattern for the rendering. Pixels of
+ * entirely 0 winding number are left untouched. Pixels of entirely
+ * 1 winding number have the pattern @pat composited over them (ie,
+ * are replaced by the red, green, blue components of @pat->fore or @pat->back
+ * depending on the stipple associated with @pat->pattern. if the alpha
+ * component is 0xff). Pixels of intermediate coverage are linearly
+ * interpolated.
+ **/
+void
+go_pattern_render_svp (GOPattern const *pat, ArtSVP const *svp,
+ int x0, int y0, int x1, int y1,
+ art_u8 *buf, int rowstride)
+{
+ pattern_data state;
+ int i, a, da;
+ gboolean opaque = TRUE;
+ GOColor c;
+
+ g_return_if_fail (pat != NULL);
+
+ if (go_pattern_is_solid (pat, &c)) {
+ go_color_render_svp (c, svp,
+ x0, y0, x1, y1, buf, rowstride);
+ return;
+ }
+
+ state.fore.r = UINT_RGBA_R (pat->fore);
+ state.fore.g = UINT_RGBA_G (pat->fore);
+ state.fore.b = UINT_RGBA_B (pat->fore);
+ state.back.r = UINT_RGBA_R (pat->back);
+ state.back.g = UINT_RGBA_G (pat->back);
+ state.back.b = UINT_RGBA_B (pat->back);
+ state.buf = buf;
+ state.rowstride = rowstride;
+ state.x0 = x0;
+ state.x1 = x1;
+ state.pattern = go_patterns [pat->pattern].pattern;
+
+ a = 0x8000;
+ da = (UINT_RGBA_A (pat->fore) * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+ if (da != 65793) opaque = FALSE;
+ for (i = 0; i < 256; i++) {
+ state.fore.alphatab[i] = a >> 16;
+ a += da;
+ }
+ a = 0x8000;
+ da = (UINT_RGBA_A (pat->back) * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+ if (da != 65793) opaque = FALSE;
+ for (i = 0; i < 256; i++) {
+ state.back.alphatab[i] = a >> 16;
+ a += da;
+ }
+
+ art_svp_render_aa (svp, x0, y0, x1, y1,
+ (opaque ? &cb_pattern_opaque : &cb_pattern_alpha), &state);
+}
--- /dev/null
+++ lib/goffice/utils/go-line.h
@@ -0,0 +1,55 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-line.h :
+ *
+ * Copyright (C) 2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GO_LINE_H
+#define GO_LINE_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <libart_lgpl/libart.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GO_LINE_NONE,
+ GO_LINE_SOLID,
+ GO_LINE_DASH,
+ GO_LINE_DOT,
+ GO_LINE_DASH_DOT,
+ GO_LINE_DASH_DOT_DOT,
+ GO_LINE_MAX
+} GOLineDashType;
+
+GOLineDashType go_line_dash_from_str (char const *name);
+char const *go_line_dash_as_str (GOLineDashType type);
+
+void go_line_vpath_dash_free (ArtVpathDash *dash);
+ArtVpathDash *go_line_get_vpath_dash (GOLineDashType type, double scale);
+
+ArtVpath *go_line_clip_vpath (ArtVpath const *path, GogViewAllocation const *bbox);
+ArtVpath *go_line_dash_vpath (ArtVpath const *path, ArtVpathDash const *dash,
+ GogViewAllocation const *bbox);
+
+gpointer go_line_dash_selector (GOLineDashType default_type);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+++ lib/goffice/utils/go-font.c
@@ -0,0 +1,207 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-font.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-font.h"
+#include <gmodule.h>
+
+static GHashTable *font_hash;
+static GPtrArray *font_array;
+static GSList *font_watchers;
+static GOFont const *font_default;
+
+#if 0
+#define ref_debug(x) x
+#else
+#define ref_debug(x) do { } while (0)
+#endif
+
+static void
+go_font_free (GOFont *font)
+{
+ g_return_if_fail (font->ref_count == 1);
+ pango_font_description_free (font->desc);
+ g_free (font);
+}
+
+/**
+ * go_font_new_by_desc:
+ * @desc : #PangoFontDescription
+ *
+ * Aborbs @desc and returns a ref to a font that matches it.
+ **/
+GOFont const *
+go_font_new_by_desc (PangoFontDescription *desc)
+{
+ GOFont *font = g_hash_table_lookup (font_hash, desc);
+
+ if (font == NULL) {
+ int i = font_array->len;
+
+ while (i-- > 0 && g_ptr_array_index (font_array, i) != NULL)
+ ;
+
+ font = g_new0 (GOFont, 1);
+ font->desc = desc; /* absorb it */
+ font->ref_count = 1; /* one for the hash */
+ ref_debug (g_warning ("created %p = 1", font););
+ if (i < 0) {
+ i = font_array->len;
+ g_ptr_array_add (font_array, font);
+ } else
+ g_ptr_array_index (font_array, i) = font;
+ font->font_index = i;
+ g_hash_table_insert (font_hash, font->desc, font);
+ } else
+ pango_font_description_free (desc); /* free it */
+
+ return go_font_ref (font); /* and another ref for the result */
+}
+
+GOFont const *
+go_font_new_by_name (char const *str)
+{
+ return go_font_new_by_desc (pango_font_description_from_string (str));
+}
+
+GOFont const *
+go_font_new_by_index (unsigned i)
+{
+ g_return_val_if_fail (i < font_array->len, NULL);
+ return go_font_ref (g_ptr_array_index (font_array, i));
+}
+
+char *
+go_font_as_str (GOFont const *font)
+{
+ g_return_val_if_fail (font != NULL, g_strdup (""));
+ return pango_font_description_to_string (font->desc);
+}
+
+GOFont const *
+go_font_ref (GOFont const *font)
+{
+ g_return_val_if_fail (font != NULL, NULL);
+ ((GOFont *)font)->ref_count++;
+ ref_debug (g_warning ("ref added %p = %d", font, font->ref_count););
+ return font;
+}
+
+void
+go_font_unref (GOFont const *font)
+{
+ g_return_if_fail (font != NULL);
+
+ if (--((GOFont *)font)->ref_count == 1) {
+ GValue instance_and_params[2];
+ GSList *ptr;
+
+ for (ptr = font_watchers; ptr != NULL ; ptr = ptr->next) {
+ GClosure *watcher = ptr->data;
+ gpointer data = watcher->is_invalid ?
+ NULL : watcher->data;
+
+ instance_and_params[0].g_type = 0;
+ g_value_init (&instance_and_params[0], G_TYPE_POINTER);
+ g_value_set_pointer (&instance_and_params[0], (gpointer)font);
+
+ instance_and_params[1].g_type = 0;
+ g_value_init (&instance_and_params[1], G_TYPE_POINTER);
+ g_value_set_pointer (&instance_and_params[1], data);
+
+ g_closure_invoke (watcher, NULL, 2,
+ instance_and_params, NULL);
+ }
+ g_ptr_array_index (font_array, font->font_index) = NULL;
+ g_hash_table_remove (font_hash, font->desc);
+ ref_debug (g_warning ("unref removed %p = 1 (and deleted)", font););
+ } else
+ ref_debug (g_warning ("unref removed %p = %d", font, font->ref_count););
+}
+
+gboolean
+go_font_eq (GOFont const *a, GOFont const *b)
+{
+ return pango_font_description_equal (a->desc, b->desc);
+}
+
+void
+go_font_cache_register (GClosure *watcher)
+{
+ g_return_if_fail (watcher != NULL);
+
+ font_watchers = g_slist_prepend (font_watchers, watcher);
+ g_closure_set_marshal (watcher,
+ g_cclosure_marshal_VOID__POINTER);
+}
+
+void
+go_font_cache_unregister (GClosure *watcher)
+{
+ font_watchers = g_slist_remove (font_watchers, watcher);
+}
+
+static void (*fake_pango_fc_font_map_cache_clear) (PangoFcFontMap *);
+
+void
+go_pango_fc_font_map_cache_clear (PangoFcFontMap *font_map)
+{
+ if (fake_pango_fc_font_map_cache_clear)
+ fake_pango_fc_font_map_cache_clear (font_map);
+}
+
+/* private */
+void
+go_font_init (void)
+{
+ GModule *self = g_module_open (NULL, 0);
+ if (self) {
+ gpointer sym;
+ if (g_module_symbol (self, "pango_fc_font_map_cache_clear", &sym))
+ fake_pango_fc_font_map_cache_clear = sym;
+ g_module_close (self);
+ }
+
+ font_array = g_ptr_array_new ();
+ font_hash = g_hash_table_new_full (
+ (GHashFunc)pango_font_description_hash,
+ (GEqualFunc)pango_font_description_equal,
+ NULL, (GDestroyNotify) go_font_free);
+ font_default = go_font_new_by_desc (
+ pango_font_description_from_string ("Sans 8"));
+}
+
+void
+go_font_shutdown (void)
+{
+ go_font_unref (font_default);
+ font_default = NULL;
+ g_ptr_array_free (font_array, TRUE);
+ font_array = NULL;
+ g_hash_table_destroy (font_hash);
+ font_hash = NULL;
+
+ if (font_watchers != NULL) {
+ g_warning ("Missing calls to go_font_cache_unregister");
+ /* be careful and _leak_ the closured in case they are already freed */
+ g_slist_free (font_watchers);
+ }
+}
--- /dev/null
+++ lib/goffice/utils/go-file.h
@@ -0,0 +1,46 @@
+/*
+ * go-file.h :
+ *
+ * Copyright (C) 2004 Morten Welinder (terra at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_FILE_H
+#define GO_FILE_H
+
+#include <glib.h>
+#include <gsf/gsf.h>
+
+G_BEGIN_DECLS
+
+char *go_filename_from_uri (const char *uri);
+char *go_filename_to_uri (const char *filename);
+char *go_shell_arg_to_uri (const char *arg);
+char *go_basename_from_uri (const char *uri);
+char *go_dirname_from_uri (const char *uri, gboolean brief);
+
+GsfInput *go_file_open (char const *uri, GError **err);
+GsfOutput *go_file_create (char const *uri, GError **err);
+
+GSList *go_file_split_uris (const char *data);
+
+gchar *go_url_decode (gchar const *text);
+gchar *go_url_encode (gchar const *text);
+GError *go_url_show (gchar const *url);
+GError *go_url_mailto (gchar const *url);
+
+G_END_DECLS
+
+#endif /* GO_FILE_H */
--- /dev/null
+++ lib/goffice/utils/goffice-utils.h
@@ -0,0 +1,39 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * goffice-utils.h:
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOFFICE_UTILS_H
+#define GOFFICE_UTILS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef guint32 GOColor;
+typedef struct _GOFont GOFont;
+typedef struct _GOPattern GOPattern;
+typedef struct _GOMarker GOMarker;
+typedef struct _GnmFormat GOFormat; /* pull this down after rewrite */
+
+typedef const char *(*GOTranslateFunc)(char const *path, gpointer func_data);
+
+G_END_DECLS
+
+#endif /* GOFFICE_UTILS_H */
--- /dev/null
+++ lib/goffice/utils/go-units.h
@@ -0,0 +1,83 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-units.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_UNITS_H
+#define GO_UNITS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/* Conversion factors */
+/* The following number is the least common multiplier of 254 (1/10mm), 72(pt), 100000, and 576 */
+/* This way inch, pt, and mm are all integer multipliers (in fact, a nanometer is.) */
+/* (Of course that is only true because we use the lobotomized pt size so that
+ 1inch is exactly 72pt.) */
+#define PT_PER_IN 72
+#define CM_PER_IN 254
+#define EMU_PER_IN 914400
+
+#define UN_PER_IN 228600000
+#define UN_PER_EMU (UN_PER_IN/EMU_PER_IN)
+#define UN_PER_PT (UN_PER_IN/PT_PER_IN)
+#define UN_PER_CM (UN_PER_IN/CM_PER_IN)
+
+#define GO_IN_TO_UN(inch) ((inch)*UN_PER_IN)
+#define GO_IN_TO_PT(inch) ((inch)*PT_PER_IN)
+#define GO_IN_TO_CM(inch) ((inch)*CM_PER_IN/100)
+#define GO_IN_TO_EMU(inch) ((inch)*EMU_PER_IN)
+
+#define GO_UN_TO_IN(unit) ((unit)/UN_PER_IN)
+#define GO_UN_TO_PT(unit) ((unit)/UN_PER_PT)
+#define GO_UN_TO_CM(unit) ((unit)/UN_PER_CM/100)
+#define GO_UN_TO_EMU(unit) ((unit)/UN_PER_EMU)
+
+#define GO_PT_TO_UN(pt) ((pt)* UN_PER_PT)
+#define GO_PT_TO_IN(pt) ((pt) /PT_PER_IN)
+#define GO_PT_TO_CM(pt) ((pt)* CM_PER_IN/PT_PER_IN/100)
+#define GO_PT_TO_EMU(pt) ((pt)*EMU_PER_IN/PT_PER_IN)
+
+#define GO_CM_TO_UN(cm) ((cm)*100*UN_PER_CM)
+#define GO_CM_TO_IN(cm) ((cm)*100 /CM_PER_IN)
+#define GO_CM_TO_PT(cm) ((cm)*100*PT_PER_IN/CM_PER_IN)
+#define GO_CM_TO_EMU(cm) ((cm)*100*PT_PER_IN/EMU_PER_IN)
+
+#define GO_EMU_TO_UN(emu) ((emu)*UN_PER_EMU)
+#define GO_EMU_TO_IN(emu) ((emu) /EMU_PER_IN)
+#define GO_EMU_TO_PT(emu) ((emu)*PT_PER_IN/EMU_PER_IN)
+#define GO_EMU_TO_CM(emu) ((emu)*CM_PER_IN/EMU_PER_IN/100)
+
+typedef gint64 go_unit_t;
+
+typedef struct {
+ go_unit_t x;
+ go_unit_t y;
+} GoPoint;
+
+typedef struct {
+ go_unit_t top;
+ go_unit_t left;
+ go_unit_t bottom;
+ go_unit_t right;
+} GoRect;
+
+G_END_DECLS
+
+#endif /* GO_UNITS_H */
--- /dev/null
+++ lib/goffice/utils/go-locale.c
@@ -0,0 +1,318 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-locale.c :
+ *
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Gnome Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-locale.h"
+#include <stdio.h>
+#include <string.h>
+
+#ifndef WITH_GNOME
+static GHashTable *alias_table = NULL;
+
+/*read an alias file for the locales*/
+static void
+read_aliases (char const *file)
+{
+ FILE *fp;
+ char buf[256];
+ if (!alias_table)
+ alias_table = g_hash_table_new (g_str_hash, g_str_equal);
+ fp = fopen (file,"r");
+ if (!fp)
+ return;
+ while (fgets (buf,256,fp))
+ {
+ char *p;
+ g_strstrip(buf);
+ if (buf[0]=='#' || buf[0]=='\0')
+ continue;
+ p = strtok (buf,"\t ");
+ if (!p)
+ continue;
+ p = strtok (NULL,"\t ");
+ if(!p)
+ continue;
+ if (!g_hash_table_lookup (alias_table, buf))
+ g_hash_table_insert (alias_table, g_strdup(buf), g_strdup(p));
+ }
+ fclose (fp);
+}
+
+/*return the un-aliased language as a newly allocated string*/
+static char *
+unalias_lang (char *lang)
+{
+ char *p;
+ int i;
+ if (!alias_table)
+ {
+ read_aliases ("/usr/share/locale/locale.alias");
+ read_aliases ("/usr/local/share/locale/locale.alias");
+ read_aliases ("/usr/lib/X11/locale/locale.alias");
+ read_aliases ("/usr/openwin/lib/locale/locale.alias");
+ }
+ i = 0;
+ while ((p=g_hash_table_lookup(alias_table,lang)) && strcmp(p, lang))
+ {
+ lang = p;
+ if (i++ == 30)
+ {
+ static gboolean said_before = FALSE;
+ if (!said_before)
+ g_warning ("Too many alias levels for a locale, may indicate a loop");
+ said_before = TRUE;
+ return lang;
+ }
+ }
+ return lang;
+}
+
+/* Mask for components of locale spec. The ordering here is from
+ * least significant to most significant
+ */
+enum
+{
+ COMPONENT_CODESET = 1 << 0,
+ COMPONENT_TERRITORY = 1 << 1,
+ COMPONENT_MODIFIER = 1 << 2
+};
+
+/* Break an X/Open style locale specification into components
+ */
+static guint
+explode_locale (const gchar *locale,
+ gchar **language,
+ gchar **territory,
+ gchar **codeset,
+ gchar **modifier)
+{
+ const gchar *uscore_pos;
+ const gchar *at_pos;
+ const gchar *dot_pos;
+
+ guint mask = 0;
+
+ uscore_pos = strchr (locale, '_');
+ dot_pos = strchr (uscore_pos ? uscore_pos : locale, '.');
+ at_pos = strchr (dot_pos ? dot_pos : (uscore_pos ? uscore_pos : locale), '@');
+
+ if (at_pos)
+ {
+ mask |= COMPONENT_MODIFIER;
+ *modifier = g_strdup (at_pos);
+ }
+ else
+ at_pos = locale + strlen (locale);
+
+ if (dot_pos)
+ {
+ mask |= COMPONENT_CODESET;
+ *codeset = g_new (gchar, 1 + at_pos - dot_pos);
+ strncpy (*codeset, dot_pos, at_pos - dot_pos);
+ (*codeset)[at_pos - dot_pos] = '\0';
+ }
+ else
+ dot_pos = at_pos;
+
+ if (uscore_pos)
+ {
+ mask |= COMPONENT_TERRITORY;
+ *territory = g_new (gchar, 1 + dot_pos - uscore_pos);
+ strncpy (*territory, uscore_pos, dot_pos - uscore_pos);
+ (*territory)[dot_pos - uscore_pos] = '\0';
+ }
+ else
+ uscore_pos = dot_pos;
+
+ *language = g_new (gchar, 1 + uscore_pos - locale);
+ strncpy (*language, locale, uscore_pos - locale);
+ (*language)[uscore_pos - locale] = '\0';
+
+ return mask;
+}
+
+/*
+ * Compute all interesting variants for a given locale name -
+ * by stripping off different components of the value.
+ *
+ * For simplicity, we assume that the locale is in
+ * X/Open format: language[_territory][.codeset][@modifier]
+ *
+ * TODO: Extend this to handle the CEN format (see the GNUlibc docs)
+ * as well. We could just copy the code from glibc wholesale
+ * but it is big, ugly, and complicated, so I'm reluctant
+ * to do so when this should handle 99% of the time...
+ */
+static GList *
+compute_locale_variants (const gchar *locale)
+{
+ GList *retval = NULL;
+
+ gchar *language;
+ gchar *territory;
+ gchar *codeset;
+ gchar *modifier;
+
+ guint mask;
+ guint i;
+
+ g_return_val_if_fail (locale != NULL, NULL);
+
+ mask = explode_locale (locale, &language, &territory, &codeset, &modifier);
+
+ /* Iterate through all possible combinations, from least attractive
+ * to most attractive.
+ */
+ for (i=0; i<=mask; i++)
+ if ((i & ~mask) == 0)
+ {
+ gchar *val = g_strconcat(language,
+ (i & COMPONENT_TERRITORY) ? territory : "",
+ (i & COMPONENT_CODESET) ? codeset : "",
+ (i & COMPONENT_MODIFIER) ? modifier : "",
+ NULL);
+ retval = g_list_prepend (retval, val);
+ }
+
+ g_free (language);
+ if (mask & COMPONENT_CODESET)
+ g_free (codeset);
+ if (mask & COMPONENT_TERRITORY)
+ g_free (territory);
+ if (mask & COMPONENT_MODIFIER)
+ g_free (modifier);
+
+ return retval;
+}
+
+/* The following is (partly) taken from the gettext package.
+ Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. */
+
+static const gchar *
+guess_category_value (void)
+{
+ const gchar *retval;
+
+ /* The highest priority value is the `LANGUAGE' environment
+ variable. This is a GNU extension. */
+ retval = g_getenv ("LANGUAGE");
+ if (retval != NULL && retval[0] != '\0')
+ return retval;
+
+ /* `LANGUAGE' is not set. So we have to proceed with the POSIX
+ methods of looking to `LC_ALL', `LC_xxx', and `LANG'. On some
+ systems this can be done by the `setlocale' function itself. */
+
+ /* Setting of LC_ALL overwrites all other. */
+ retval = g_getenv ("LC_ALL");
+ if (retval != NULL && retval[0] != '\0')
+ return retval;
+
+ /* Next comes the name of the desired category. */
+ retval = g_getenv ("LC_MESSAGES");
+ if (retval != NULL && retval[0] != '\0')
+ return retval;
+
+ /* Last possibility is the LANG environment variable. */
+ retval = g_getenv ("LANG");
+ if (retval != NULL && retval[0] != '\0')
+ return retval;
+
+ return NULL;
+}
+
+
+/**
+ * go_locale_languages:
+ *
+ * This computes a list of language strings that the user wants. It searches in
+ * the standard environment variables to find the list, which is sorted in order
+ * from most desirable to least desirable. The `C' locale is appended to the
+ * list if it does not already appear (other routines depend on this
+ * behaviour). If @category_name is %NULL, then %LC_ALL is assumed.
+ *
+ * Return value: the list of languages, this list should not be freed
+ * owned by gnome-i18n.
+ **/
+GList const *
+go_locale_languages (void)
+{
+ static GList *list = NULL;
+
+ if (list == NULL )
+ {
+ gint c_locale_defined= FALSE;
+
+ const gchar *category_value;
+ gchar *category_memory, *orig_category_memory;
+
+ category_value = guess_category_value ();
+ if (! category_value)
+ category_value = "C";
+ orig_category_memory = category_memory =
+ g_malloc (strlen (category_value)+1);
+
+ while (category_value[0] != '\0')
+ {
+ while (category_value[0] != '\0' && category_value[0] == ':')
+ ++category_value;
+
+ if (category_value[0] != '\0')
+ {
+ char *cp= category_memory;
+
+ while (category_value[0] != '\0' && category_value[0] != ':')
+ *category_memory++= *category_value++;
+
+ category_memory[0]= '\0';
+ category_memory++;
+
+ cp = unalias_lang(cp);
+
+ if (strcmp (cp, "C") == 0)
+ c_locale_defined= TRUE;
+
+ list= g_list_concat (list, compute_locale_variants (cp));
+ }
+ }
+
+ g_free (orig_category_memory);
+
+ if (!c_locale_defined)
+ list = g_list_append (list, (gpointer)"C");
+ }
+
+ return list;
+}
+#else
+#include <libgnome/gnome-i18n.h>
+
+GList const *
+go_locale_languages (void)
+{
+ return gnome_i18n_get_language_list ("LC_MESSAGES");
+}
+
+#endif
--- /dev/null
+++ lib/goffice/utils/go-marker.c
@@ -0,0 +1,615 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-marker.c :
+ *
+ * Copyright (C) 2003-2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-marker.h"
+#include "go-color.h"
+#include <goffice/utils/go-math.h>
+
+#ifdef WITH_GTK
+#include <goffice/gui-utils/go-combo-color.h>
+#include <goffice/gui-utils/go-combo-pixmaps.h>
+#include <libart_lgpl/art_render_gradient.h>
+#include <libart_lgpl/art_render_svp.h>
+#include <libart_lgpl/art_render_mask.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_affine.h>
+#include <libart_lgpl/art_rgb_svp.h>
+#include <glade/glade-xml.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+#define MARKER_DEFAULT_SIZE 5
+#define MARKER_OUTLINE_WIDTH 0.1
+
+typedef struct {
+ GObjectClass base;
+} GOMarkerClass;
+
+#define GO_MARKER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GO_MARKER_TYPE, GOMarkerClass))
+
+static ArtVpath const square_path[] = {
+ {ART_MOVETO, -1.0, -1.0},
+ {ART_LINETO, -1.0, 1.0},
+ {ART_LINETO, 1.0, 1.0},
+ {ART_LINETO, 1.0, -1.0},
+ {ART_LINETO, -1.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const diamond_path[] = {
+ {ART_MOVETO, 0.0, -1.0},
+ {ART_LINETO, 1.0, 0.0},
+ {ART_LINETO, 0.0, 1.0},
+ {ART_LINETO, -1.0, 0.0},
+ {ART_LINETO, 0.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const triangle_down_path[] = {
+ {ART_MOVETO, -1.0, -1.0},
+ {ART_LINETO, 1.0, -1.0},
+ {ART_LINETO, 0.0, 1.0},
+ {ART_LINETO, -1.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const triangle_up_path[] = {
+ {ART_MOVETO, 0.0, -1.0},
+ {ART_LINETO, 1.0, 1.0},
+ {ART_LINETO, -1.0, 1.0},
+ {ART_LINETO, 0.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const triangle_right_path[] = {
+ {ART_MOVETO, -1.0, -1.0},
+ {ART_LINETO, 1.0, 0.0},
+ {ART_LINETO, -1.0, 1.0},
+ {ART_LINETO, -1.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const triangle_left_path[] = {
+ {ART_MOVETO, 1.0, -1.0},
+ {ART_LINETO, -1.0, 0.0},
+ {ART_LINETO, 1.0, 1.0},
+ {ART_LINETO, 1.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const circle_path[] = {
+ {ART_MOVETO, 1.000 , 0.000 },
+ {ART_LINETO, 0.985 , 0.174 },
+ {ART_LINETO, 0.940 , 0.342 },
+ {ART_LINETO, 0.866 , 0.500 },
+ {ART_LINETO, 0.766 , 0.643 },
+ {ART_LINETO, 0.643 , 0.766 },
+ {ART_LINETO, 0.500 , 0.866 },
+ {ART_LINETO, 0.342 , 0.940 },
+ {ART_LINETO, 0.174 , 0.985 },
+ {ART_LINETO, 0.000 , 1.000 },
+ {ART_LINETO, -0.174 , 0.985 },
+ {ART_LINETO, -0.342 , 0.940 },
+ {ART_LINETO, -0.500 , 0.866 },
+ {ART_LINETO, -0.643 , 0.766 },
+ {ART_LINETO, -0.766 , 0.643 },
+ {ART_LINETO, -0.866 , 0.500 },
+ {ART_LINETO, -0.940 , 0.342 },
+ {ART_LINETO, -0.985 , 0.174 },
+ {ART_LINETO, -1.000 , 0.000 },
+ {ART_LINETO, -0.985 , -0.174 },
+ {ART_LINETO, -0.940 , -0.342 },
+ {ART_LINETO, -0.866 , -0.500 },
+ {ART_LINETO, -0.766 , -0.643 },
+ {ART_LINETO, -0.643 , -0.766 },
+ {ART_LINETO, -0.500 , -0.866 },
+ {ART_LINETO, -0.342 , -0.940 },
+ {ART_LINETO, -0.174 , -0.985 },
+ {ART_LINETO, -0.000 , -1.000 },
+ {ART_LINETO, 0.174 , -0.985 },
+ {ART_LINETO, 0.342 , -0.940 },
+ {ART_LINETO, 0.500 , -0.866 },
+ {ART_LINETO, 0.643 , -0.766 },
+ {ART_LINETO, 0.766 , -0.643 },
+ {ART_LINETO, 0.866 , -0.500 },
+ {ART_LINETO, 0.940 , -0.342 },
+ {ART_LINETO, 0.985 , -0.174 },
+ {ART_LINETO, 1.000 , 0.000 },
+ {ART_END, 0.000 , 0.000 }
+};
+
+static ArtVpath const x_path[] = {
+ {ART_MOVETO, 1.0, 1.0},
+ {ART_LINETO, -1.0, -1.0},
+ {ART_MOVETO, 1.0, -1.0},
+ {ART_LINETO, -1.0, 1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const cross_path[] = {
+ {ART_MOVETO, 1.0, 0.0},
+ {ART_LINETO, -1.0, 0.0},
+ {ART_MOVETO, 0.0, 1.0},
+ {ART_LINETO, 0.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const asterisk_path[] = {
+ {ART_MOVETO, 0.7, 0.7},
+ {ART_LINETO, -0.7, -0.7},
+ {ART_MOVETO, 0.7, -0.7},
+ {ART_LINETO, -0.7, 0.7},
+ {ART_MOVETO, 1.0, 0.0},
+ {ART_LINETO, -1.0, 0.0},
+ {ART_MOVETO, 0.0, 1.0},
+ {ART_LINETO, 0.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const bar_path[] = {
+ {ART_MOVETO, -1.0, -0.2},
+ {ART_LINETO, 1.0, -0.2},
+ {ART_LINETO, 1.0, 0.2},
+ {ART_LINETO, -1.0, 0.2},
+ {ART_LINETO, -1.0, -0.2},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const half_bar_path[] = {
+ {ART_MOVETO, 0.0, -0.2},
+ {ART_LINETO, 1.0, -0.2},
+ {ART_LINETO, 1.0, 0.2},
+ {ART_LINETO, 0.0, 0.2},
+ {ART_LINETO, 0.0, -0.2},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const butterfly_path[] = {
+ {ART_MOVETO, -1.0, -1.0},
+ {ART_LINETO, -1.0, 1.0},
+ {ART_LINETO, 0.0, 0.0},
+ {ART_LINETO, 1.0, 1.0},
+ {ART_LINETO, 1.0, -1.0},
+ {ART_LINETO, 0.0, 0.0},
+ {ART_LINETO, -1.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+static ArtVpath const hourglass_path[] = {
+ {ART_MOVETO, -1.0, -1.0},
+ {ART_LINETO, 1.0, -1.0},
+ {ART_LINETO, 0.0, 0.0},
+ {ART_LINETO, 1.0, 1.0},
+ {ART_LINETO, -1.0, 1.0},
+ {ART_LINETO, 0.0, 0.0},
+ {ART_LINETO, -1.0, -1.0},
+ {ART_END , 0.0, 0.0}
+};
+
+typedef struct
+{
+ char const *name;
+ ArtVpath const *outline_path;
+ ArtVpath const *fill_path;
+} MarkerShape;
+
+static MarkerShape const marker_shapes[GO_MARKER_MAX] = {
+ { N_("none"), NULL, NULL},
+ { N_("square"), square_path, square_path},
+ { N_("diamond"), diamond_path, diamond_path},
+ { N_("triangle down"), triangle_down_path, triangle_down_path},
+ { N_("triangle up"), triangle_up_path, triangle_up_path},
+ { N_("triangle right"), triangle_right_path, triangle_right_path},
+ { N_("triangle left"), triangle_left_path, triangle_left_path},
+ { N_("circle"), circle_path, circle_path},
+ { N_("x"), x_path, square_path},
+ { N_("cross"), cross_path, square_path},
+ { N_("asterisk"), asterisk_path, square_path},
+ { N_("bar"), bar_path, bar_path},
+ { N_("half bar"), half_bar_path, half_bar_path},
+ { N_("butterfly"), butterfly_path, butterfly_path},
+ { N_("hourglass"), hourglass_path, hourglass_path}
+};
+
+static struct {
+ GOMarkerShape shape;
+ char const *name;
+} marker_shape_names[] = {
+ { GO_MARKER_NONE, "none" },
+ { GO_MARKER_SQUARE, "square" },
+ { GO_MARKER_DIAMOND, "diamond" },
+ { GO_MARKER_TRIANGLE_DOWN, "triangle-down" },
+ { GO_MARKER_TRIANGLE_UP, "triangle-up" },
+ { GO_MARKER_TRIANGLE_RIGHT, "triangle-right" },
+ { GO_MARKER_TRIANGLE_LEFT, "triangle-left" },
+ { GO_MARKER_CIRCLE, "circle" },
+ { GO_MARKER_X, "x" },
+ { GO_MARKER_CROSS, "cross" },
+ { GO_MARKER_ASTERISK, "asterisk" },
+ { GO_MARKER_BAR, "bar" },
+ { GO_MARKER_HALF_BAR, "half-bar" },
+ { GO_MARKER_BUTTERFLY, "butterfly" },
+ { GO_MARKER_HOURGLASS, "hourglass" }
+};
+
+static GObjectClass *marker_parent_klass;
+
+#ifdef WITH_GTK
+static GdkPixbuf *
+new_blank_pixbuf (GOMarker *marker, guint size)
+{
+ int offset = ceil ((double)size * MARKER_OUTLINE_WIDTH / 2.0);
+ int pixbuf_size = size + 1 + 2 * offset;
+ GdkPixbuf *res = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ pixbuf_size, pixbuf_size);
+ gdk_pixbuf_fill (res, 0); /* in case the fill colours have alpha = 0 */
+ return res;
+}
+
+static GdkPixbuf *
+marker_create_pixbuf_with_size (GOMarker *marker, guint size)
+{
+ double scaling[6], translation[6], affine[6];
+ guchar *pixels;
+ int rowstride;
+ ArtSVP *outline, *fill;
+ double half_size;
+ int pixbuf_size, offset;
+ ArtVpath *outline_path;
+ ArtVpath *fill_path;
+ GdkPixbuf *pixbuf;
+
+ size = rint (marker->scale * size);
+
+ if (size < 1 || marker->shape == GO_MARKER_NONE)
+ return NULL;
+
+ /* FIXME : markers look bad due to grey outline */
+
+ /* keep in sync with new_blank_pixbuf */
+ offset = ceil ((double)size * MARKER_OUTLINE_WIDTH / 2.0);
+ pixbuf_size = size + 1 + 2 * offset;
+ half_size = (double)size / 2.0;
+
+ art_affine_scale (scaling, half_size, half_size);
+ art_affine_translate (translation, half_size + offset + .5, half_size + offset + .5);
+ art_affine_multiply (affine, scaling, translation);
+
+ outline_path = art_vpath_affine_transform (marker_shapes[marker->shape].outline_path, affine);
+ fill_path = art_vpath_affine_transform (marker_shapes[marker->shape].fill_path, affine);
+
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, pixbuf_size, pixbuf_size);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+
+ gdk_pixbuf_fill (pixbuf, 0xffffff00);
+ outline = art_svp_vpath_stroke (outline_path,
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_CAP_SQUARE,
+ MARKER_OUTLINE_WIDTH * (double)size, 4, 0.5);
+ fill = art_svp_from_vpath (fill_path);
+
+ go_color_render_svp (marker->fill_color, fill, 0, 0, pixbuf_size, pixbuf_size,
+ pixels, rowstride);
+ go_color_render_svp (marker->outline_color, outline, 0, 0, pixbuf_size, pixbuf_size,
+ pixels, rowstride);
+
+ art_svp_free (fill);
+ art_svp_free (outline);
+
+ g_free (outline_path);
+ g_free (fill_path);
+
+/* {*/
+/* GError * error = NULL;*/
+
+/* if (!gdk_pixbuf_save (pixbuf, "test.png", "png", &error, NULL))*/
+/* {*/
+/* g_warning("%s", error->message);*/
+/* g_error_free (error);*/
+/* }*/
+/* }*/
+
+ return pixbuf;
+}
+
+static void
+marker_update_pixbuf (GOMarker * marker)
+{
+ if (marker->pixbuf != NULL) {
+ g_object_unref (G_OBJECT (marker->pixbuf));
+ marker->pixbuf = NULL;
+ }
+
+ marker->pixbuf = marker_create_pixbuf_with_size (marker, marker->size);
+}
+#endif /* WITH_GTK */
+
+static void
+go_marker_finalize (GObject *obj)
+{
+ GOMarker * marker = GO_MARKER (obj);
+
+ if (marker->pixbuf != NULL) {
+ g_object_unref (G_OBJECT (marker->pixbuf));
+ marker->pixbuf = NULL;
+ }
+
+ marker_parent_klass->finalize (obj);
+}
+
+static void
+go_marker_init (GOMarker * marker)
+{
+ marker->shape = GO_MARKER_NONE;
+ marker->outline_color = RGBA_BLACK;
+ marker->fill_color = RGBA_WHITE;
+ marker->size = MARKER_DEFAULT_SIZE;
+ marker->pixbuf = NULL;
+ marker->scale = 1.;
+}
+
+static void
+go_marker_class_init (GObjectClass *gobject_klass)
+{
+ marker_parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = go_marker_finalize;
+}
+
+GOMarkerShape
+go_marker_shape_from_str (char const *name)
+{
+ unsigned i = G_N_ELEMENTS (marker_shape_names);
+ while (i-- > 0)
+ if (g_ascii_strcasecmp (marker_shape_names[i].name, name) == 0)
+ return marker_shape_names[i].shape;
+ return GO_MARKER_NONE;
+}
+
+char const *
+go_marker_shape_as_str (GOMarkerShape shape)
+{
+ unsigned i = G_N_ELEMENTS (marker_shape_names);
+ while (i-- > 0)
+ if (marker_shape_names[i].shape == shape)
+ return marker_shape_names[i].name;
+ return "pattern";
+}
+
+void
+go_marker_get_paths (GOMarker * marker,
+ ArtVpath const **outline_path,
+ ArtVpath const **fill_path)
+{
+ *outline_path = marker_shapes[marker->shape].outline_path;
+ *fill_path = marker_shapes[marker->shape].fill_path;
+}
+
+#ifdef WITH_GTK
+GdkPixbuf const *
+go_marker_get_pixbuf (GOMarker * marker, double scale)
+{
+ g_return_val_if_fail (IS_GO_MARKER (marker), NULL);
+
+ if (marker->pixbuf == NULL ||
+ marker->scale != scale) {
+ marker->scale = scale;
+ marker_update_pixbuf (marker);
+ }
+ return marker->pixbuf;
+}
+
+GdkPixbuf const *
+go_marker_get_pixbuf_with_size (GOMarker *marker, guint size)
+{
+ g_return_val_if_fail (IS_GO_MARKER (marker), NULL);
+
+ return marker_create_pixbuf_with_size (marker, size);
+}
+#endif /* WITH_GTK */
+
+GOMarkerShape
+go_marker_get_shape (GOMarker * marker)
+{
+ return marker->shape;
+}
+
+void
+go_marker_set_shape (GOMarker *marker, GOMarkerShape shape)
+{
+ g_return_if_fail (IS_GO_MARKER (marker));
+
+ if (marker->shape == shape)
+ return;
+
+ marker->shape = shape;
+ if (marker->pixbuf != NULL) {
+ g_object_unref (marker->pixbuf);
+ marker->pixbuf = NULL;
+ }
+}
+
+GOColor
+go_marker_get_outline_color (GOMarker * marker)
+{
+ return marker->outline_color;
+}
+
+void
+go_marker_set_outline_color (GOMarker *marker, GOColor color)
+{
+ g_return_if_fail (IS_GO_MARKER (marker));
+ if (marker->outline_color == color)
+ return;
+
+ marker->outline_color = color;
+ if (marker->pixbuf != NULL) {
+ g_object_unref (marker->pixbuf);
+ marker->pixbuf = NULL;
+ }
+}
+
+GOColor
+go_marker_get_fill_color (GOMarker * marker)
+{
+ return marker->fill_color;
+}
+
+void
+go_marker_set_fill_color (GOMarker *marker, GOColor color)
+{
+ g_return_if_fail (IS_GO_MARKER (marker));
+
+ if (marker->fill_color == color)
+ return;
+ marker->fill_color = color;
+ if (marker->pixbuf != NULL) {
+ g_object_unref (marker->pixbuf);
+ marker->pixbuf = NULL;
+ }
+}
+
+int
+go_marker_get_size (GOMarker * marker)
+{
+ return marker->size;
+}
+
+double
+go_marker_get_outline_width (GOMarker * marker)
+{
+ return (double)marker->size * MARKER_OUTLINE_WIDTH;
+}
+
+void
+go_marker_set_size (GOMarker *marker, int size)
+{
+ g_return_if_fail (IS_GO_MARKER (marker));
+ g_return_if_fail (size >= 0);
+
+ if (marker->size == size)
+ return;
+ marker->size = size;
+ if (marker->pixbuf != NULL) {
+ g_object_unref (marker->pixbuf);
+ marker->pixbuf = NULL;
+ }
+}
+
+void
+go_marker_assign (GOMarker *dst, GOMarker const *src)
+{
+ if (src == dst)
+ return;
+
+ g_return_if_fail (GO_MARKER (src) != NULL);
+ g_return_if_fail (GO_MARKER (dst) != NULL);
+
+ dst->size = src->size;
+ dst->shape = src->shape;
+ dst->outline_color = src->outline_color;
+ dst->fill_color = src->fill_color;
+
+ if (dst->pixbuf != NULL)
+ g_object_unref (G_OBJECT (src->pixbuf));
+ dst->pixbuf = src->pixbuf;
+ if (dst->pixbuf != NULL)
+ g_object_ref (dst->pixbuf);
+}
+
+GOMarker *
+go_marker_dup (GOMarker *src)
+{
+ GOMarker *dst = go_marker_new ();
+ go_marker_assign (dst, src);
+ return dst;
+}
+
+GOMarker *
+go_marker_new (void)
+{
+ return g_object_new (GO_MARKER_TYPE, NULL);
+}
+
+GSF_CLASS (GOMarker, go_marker,
+ go_marker_class_init, go_marker_init,
+ G_TYPE_OBJECT)
+
+/*---------------------------------------------------------------------------*/
+
+#ifdef WITH_GTK
+#define SELECTOR_PIXBUF_SIZE 20
+#define SELECTOR_MARKER_SIZE 15
+
+gpointer
+go_marker_selector (GOColor outline_color, GOColor fill_color,
+ GOMarkerShape default_shape)
+{
+ static GOMarkerShape elements[] = {
+ GO_MARKER_NONE, GO_MARKER_TRIANGLE_UP, GO_MARKER_BUTTERFLY,
+ GO_MARKER_TRIANGLE_LEFT, GO_MARKER_DIAMOND, GO_MARKER_TRIANGLE_RIGHT,
+ GO_MARKER_BAR, GO_MARKER_TRIANGLE_DOWN, GO_MARKER_HOURGLASS,
+ GO_MARKER_HALF_BAR, GO_MARKER_SQUARE, GO_MARKER_CIRCLE,
+ GO_MARKER_X, GO_MARKER_CROSS, GO_MARKER_ASTERISK,
+ GO_MARKER_MAX /* fill with auto */
+ };
+
+ unsigned i;
+ gboolean is_auto;
+ GOComboPixmaps *w;
+ GOMarker *marker = go_marker_new ();
+ GOMarkerShape shape;
+ GdkPixbuf const *pixbuf;
+
+ go_marker_set_fill_color (marker, fill_color);
+ go_marker_set_outline_color (marker, outline_color);
+ go_marker_set_size (marker, 15);
+
+ w = go_combo_pixmaps_new (4);
+ for (i = 0; i < G_N_ELEMENTS (elements); i++) {
+ shape = elements[i];
+ is_auto = (shape == GO_MARKER_MAX);
+ go_marker_set_shape (marker, is_auto ? default_shape : shape);
+ pixbuf = go_marker_get_pixbuf (marker, 1.0);
+ if (pixbuf == NULL) /* handle none */
+ pixbuf = new_blank_pixbuf (marker, marker->size);
+ else /* add_element absorbs ref */
+ g_object_ref (G_OBJECT (pixbuf));
+ if (is_auto) {
+ /* xgettext : this will appear as 'Automatic (shapename)' */
+ char *name = g_strdup_printf (_("Automatic (%s)"),
+ _(marker_shapes [default_shape].name));
+ go_combo_pixmaps_add_element (w, pixbuf,
+ -default_shape, name);
+ } else
+ go_combo_pixmaps_add_element (w, pixbuf,
+ shape, _(marker_shapes [shape].name));
+ }
+ g_object_unref (marker);
+
+ return GTK_WIDGET (w);
+}
+#endif /* WITH_GTK */
--- /dev/null
+++ lib/goffice/utils/go-gradient.c
@@ -0,0 +1,182 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-gradient.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-gradient.h"
+#include "go-color.h"
+
+#ifdef WITH_GTK
+#include <goffice/gui-utils/go-combo-pixmaps.h>
+#include <gdk-pixbuf/gdk-pixdata.h>
+#endif
+
+#include <glib/gi18n.h>
+#include <string.h>
+
+
+static const struct {
+ GOGradientDirection dir;
+ char const *name;
+} grad_dir_names[] = {
+ { GO_GRADIENT_N_TO_S, "n-s" },
+ { GO_GRADIENT_S_TO_N, "s-n" },
+ { GO_GRADIENT_N_TO_S_MIRRORED, "n-s-mirrored" },
+ { GO_GRADIENT_S_TO_N_MIRRORED, "s-n-mirrored" },
+ { GO_GRADIENT_W_TO_E, "w-e" },
+ { GO_GRADIENT_E_TO_W, "e-w" },
+ { GO_GRADIENT_W_TO_E_MIRRORED, "w-e-mirrored" },
+ { GO_GRADIENT_E_TO_W_MIRRORED, "e-w-mirrored" },
+ { GO_GRADIENT_NW_TO_SE, "nw-se" },
+ { GO_GRADIENT_SE_TO_NW, "se-nw" },
+ { GO_GRADIENT_NW_TO_SE_MIRRORED, "nw-se-mirrored" },
+ { GO_GRADIENT_SE_TO_NW_MIRRORED, "se-nw-mirrored" },
+ { GO_GRADIENT_NE_TO_SW, "ne-sw" },
+ { GO_GRADIENT_SW_TO_NE, "sw-ne" },
+ { GO_GRADIENT_SW_TO_NE_MIRRORED, "sw-ne-mirrored" },
+ { GO_GRADIENT_NE_TO_SW_MIRRORED, "ne-sw-mirrored" },
+};
+
+GOGradientDirection
+go_gradient_dir_from_str (char const *name)
+{
+ unsigned i;
+ GOGradientDirection ret = GO_GRADIENT_N_TO_S;
+
+ for (i = 0; i < G_N_ELEMENTS (grad_dir_names); i++) {
+ if (strcmp (grad_dir_names[i].name, name) == 0) {
+ ret = grad_dir_names[i].dir;
+ break;
+ }
+ }
+ return ret;
+}
+
+char const *
+go_gradient_dir_as_str (GOGradientDirection dir)
+{
+ unsigned i;
+ char const *ret = "pattern";
+
+ for (i = 0; i < G_N_ELEMENTS (grad_dir_names); i++) {
+ if (grad_dir_names[i].dir == dir) {
+ ret = grad_dir_names[i].name;
+ break;
+ }
+ }
+ return ret;
+}
+
+#ifdef WITH_GTK
+GtkWidget *
+go_gradient_selector (GOColor start, GOColor end)
+{
+ int const W = 20, H = 20;
+ unsigned i;
+ GOComboPixmaps *w;
+ GdkPixbuf *pixbuf;
+ ArtRender *render;
+ ArtGradientLinear gradient;
+ ArtGradientStop stops[2];
+
+ w = go_combo_pixmaps_new (4);
+ for (i = 0; i < G_N_ELEMENTS (grad_dir_names); i++) {
+ GOGradientDirection dir = grad_dir_names[i].dir;
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, W, H);
+ gdk_pixbuf_fill (pixbuf, 0); /* in case the fill colours have alpha = 0 */
+ render = art_render_new (0, 0, W, H,
+ gdk_pixbuf_get_pixels (pixbuf),
+ gdk_pixbuf_get_rowstride (pixbuf),
+ gdk_pixbuf_get_n_channels (pixbuf) - 1,
+ 8, ART_ALPHA_SEPARATE, NULL);
+ go_gradient_setup (&gradient, dir, start, end, 0, 0,
+ W, H, stops);
+ art_render_gradient_linear (render,
+ &gradient, ART_FILTER_NEAREST);
+ art_render_invoke (render);
+ go_combo_pixmaps_add_element (w, pixbuf, dir, NULL);
+ }
+
+ return GTK_WIDGET (w);
+}
+#endif /* WITH_GTK */
+
+void
+go_gradient_setup (ArtGradientLinear *gradient,
+ GOGradientDirection dir, GOColor col0, GOColor col1,
+ double x0, double y0, double x1, double y1,
+ ArtGradientStop *stops)
+{
+ double dx = x1 - x0;
+ double dy = y1 - y0;
+
+ if (dir < 4) {
+ gradient->a = 0.;
+ gradient->b = 1. / (dy ? dy : 1);
+ gradient->c = - 1.e-10 - (gradient->a * x0 + gradient->b * y0);
+ } else if (dir < 8) {
+ gradient->a = 1. / (dx ? dx : 1);
+ gradient->b = 0.;
+ gradient->c = -(gradient->a * x0 + gradient->b * y0);
+ } else if (dir < 12) {
+ gradient->a = .5 / (dx ? dx : 1);
+ gradient->b = .5 / (dy ? dy : 1);
+ gradient->c = -(gradient->a * x0 + gradient->b * y0);
+ } else {
+ gradient->a = -.5 / (dx ? dx : 1);
+ gradient->b = .5 / (dy ? dy : 1);
+ /* Note: this gradient is anchored at (x1,y0). */
+ gradient->c = -(gradient->a * x1 + gradient->b * y0);
+ }
+
+ gradient->stops = stops;
+ gradient->n_stops = 2;
+ stops[0].offset = 0;
+ stops[1].offset = 1;
+
+ switch (dir % 4) {
+ case 0:
+ gradient->spread = ART_GRADIENT_PAD;
+ go_color_to_artpix (stops[0].color, col0);
+ go_color_to_artpix (stops[1].color, col1);
+ break;
+ case 1:
+ gradient->spread = ART_GRADIENT_PAD;
+ go_color_to_artpix (stops[0].color, col1);
+ go_color_to_artpix (stops[1].color, col0);
+ break;
+ case 2:
+ gradient->spread = ART_GRADIENT_REFLECT;
+ go_color_to_artpix (stops[0].color, col0);
+ go_color_to_artpix (stops[1].color, col1);
+ gradient->a *= 2;
+ gradient->b *= 2;
+ gradient->c *= 2;
+ break;
+ case 3:
+ gradient->spread = ART_GRADIENT_REFLECT;
+ go_color_to_artpix (stops[0].color, col1);
+ go_color_to_artpix (stops[1].color, col0);
+ gradient->a *= 2;
+ gradient->b *= 2;
+ gradient->c *= 2;
+ break;
+ }
+}
--- /dev/null
+++ lib/goffice/utils/go-color.c
@@ -0,0 +1,386 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-color.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-color.h"
+
+#include <stdio.h>
+
+void
+go_color_to_artpix (ArtPixMaxDepth *res, GOColor rgba)
+{
+ guint8 r = UINT_RGBA_R (rgba);
+ guint8 g = UINT_RGBA_G (rgba);
+ guint8 b = UINT_RGBA_B (rgba);
+ guint8 a = UINT_RGBA_A (rgba);
+ res[0] = ART_PIX_MAX_FROM_8 (r);
+ res[1] = ART_PIX_MAX_FROM_8 (g);
+ res[2] = ART_PIX_MAX_FROM_8 (b);
+ res[3] = ART_PIX_MAX_FROM_8 (a);
+}
+
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * A slightly modified version of art_rgb_svp to render into rgba buffer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors:
+ * Raph Levien <raph at acm.org>
+ * Lauris Kaplinski <lauris at ariman.ee>
+ *
+ * Copyright (C) 1998 Raph Levien
+ *
+ */
+
+/* Render a sorted vector path into an RGBA buffer. */
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_svp_render_aa.h>
+#include <libart_lgpl/art_rgb.h>
+
+typedef struct {
+ int const *alphatab;
+ art_u8 r, g, b;
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+} solid_data;
+
+static void
+fill_solid (art_u8 * buf, art_u8 r, art_u8 g, art_u8 b, int n)
+{
+ while (n-- > 0) {
+ * buf++ = r;
+ * buf++ = g;
+ * buf++ = b;
+ * buf++ = 255;
+ }
+}
+
+static void
+fill_blend (art_u8 * buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
+{
+ int br, bg, bb, ba;
+ int cr, cg, cb;
+
+ while (n-- > 0) {
+ br = * (buf + 0);
+ bg = * (buf + 1);
+ bb = * (buf + 2);
+ ba = * (buf + 3);
+
+ cr = (br * ba + 0x80) >> 8;
+ cg = (bg * ba + 0x80) >> 8;
+ cb = (bb * ba + 0x80) >> 8;
+
+ * buf++ = cr + (((r - cr) * alpha + 0x80) >> 8);
+ * buf++ = cg + (((g - cg) * alpha + 0x80) >> 8);
+ * buf++ = cb + (((b - cb) * alpha + 0x80) >> 8);
+ * buf++ = ba + (((255 - ba) * alpha + 0x80) >> 8);
+ }
+}
+
+static void
+cb_fill_alpha (void *callback_data, int y, int start,
+ ArtSVPRenderAAStep *steps, int n_steps)
+{
+ solid_data *data = callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int const *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ fill_blend (linebuf, r, g, b, alphatab[alpha],
+ run_x1 - x0);
+ }
+
+ /* render the steps into tmpbuf */
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ fill_blend (linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ fill_blend (linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha],
+ x1 - run_x1);
+ }
+ } else {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ fill_blend (linebuf, r, g, b, alphatab[alpha],
+ x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+static void
+cb_fill_opaque (void *callback_data, int y, int start,
+ ArtSVPRenderAAStep *steps, int n_steps)
+{
+ solid_data *data = callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int const *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0) {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ fill_solid (linebuf, r, g, b,
+ run_x1 - x0);
+ else
+ fill_blend (linebuf, r, g, b, alphatab[alpha],
+ run_x1 - x0);
+ }
+ }
+
+ /* render the steps into tmpbuf */
+ for (k = 0; k < n_steps - 1; k++) {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ fill_solid (linebuf + (run_x0 - x0) * 4, r, g, b,
+ run_x1 - run_x0);
+ else
+ fill_blend (linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1) {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ fill_solid (linebuf + (run_x1 - x0) * 4, r, g, b,
+ x1 - run_x1);
+ else
+ fill_blend (linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha],
+ x1 - run_x1);
+ }
+ }
+ } else {
+ alpha = running_sum >> 16;
+ if (alpha) {
+ if (alpha >= 255)
+ fill_solid (linebuf, r, g, b, x1 - x0);
+ else
+ fill_blend (linebuf, r, g, b, alphatab[alpha], x1 - x0);
+ }
+ }
+
+ data->buf += data->rowstride;
+}
+
+/**
+ * go_color_render_svp: Alpha-composite sorted vector path over RGBA buffer.
+ * @color : Color in 0xRRGGBBAA format.
+ * @svp : The source sorted vector path.
+ * @x0 : Left coordinate of destination rectangle.
+ * @y0 : Top coordinate of destination rectangle.
+ * @x1 : Right coordinate of destination rectangle.
+ * @y1 : Bottom coordinate of destination rectangle.
+ * @buf : Destination RGB buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ *
+ * Renders the shape specified with @svp over the @buf RGB buffer.
+ * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @color argument specifies the color for the rendering. Pixels of
+ * entirely 0 winding number are left untouched. Pixels of entirely
+ * 1 winding number have the color @color composited over them (ie,
+ * are replaced by the red, green, blue components of @color if the alpha
+ * component is 0xff). Pixels of intermediate coverage are linearly
+ * interpolated.
+ **/
+void
+go_color_render_svp (GOColor color, ArtSVP const *svp,
+ int x0, int y0, int x1, int y1,
+ art_u8 *buf, int rowstride)
+{
+ solid_data data;
+ int alpha, i, a, da;
+ int alphatab[256];
+
+ data.rowstride = rowstride;
+ data.buf = buf;
+ data.x0 = x0;
+ data.x1 = x1;
+ data.r = UINT_RGBA_R (color);
+ data.g = UINT_RGBA_G (color);
+ data.b = UINT_RGBA_B (color);
+ alpha = UINT_RGBA_A (color);
+
+ a = 0x8000;
+ da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+ if (alpha != 0xff) {
+ for (i = 0; i < 256; i++) {
+ alphatab[i] = a >> 16;
+ a += da;
+ }
+ data.alphatab = alphatab;
+ art_svp_render_aa (svp, x0, y0, x1, y1, &cb_fill_alpha, &data);
+ } else {
+ /* Hard code the most common table */
+ static int const opaque[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
+ 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
+ 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195,
+ 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,
+ 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256
+ };
+ data.alphatab = opaque;
+ art_svp_render_aa (svp, x0, y0, x1, y1, &cb_fill_opaque, &data);
+ }
+}
+
+GOColor
+go_color_from_str (gchar const *string)
+{
+ unsigned r, g, b, a;
+ GOColor color = 0;
+
+ if (sscanf ((char const *) string, "%X:%X:%X:%X", &r, &g, &b, &a) == 4)
+ color = RGBA_TO_UINT (r, g, b, a);
+ return color;
+}
+
+gchar *
+go_color_as_str (GOColor color)
+{
+ unsigned r, g, b, a;
+
+ UINT_TO_RGBA (color, &r, &g, &b, &a);
+ return g_strdup_printf ("%X:%X:%X:%X", r, g, b, a);
+}
+
+PangoAttribute *
+go_color_to_pango (GOColor color, gboolean is_fore)
+{
+ guint16 r, g, b;
+ r = UINT_RGBA_R (color);
+ r |= (r << 8);
+ g = UINT_RGBA_G (color);
+ g |= (g << 8);
+ b = UINT_RGBA_B (color);
+ b |= (b << 8);
+
+ if (is_fore)
+ return pango_attr_foreground_new (r, g, b);
+ else
+ return pango_attr_background_new (r, g, b);
+}
+
+#ifdef WITH_GTK
+#include <gdk/gdkcolor.h>
+
+GdkColor *
+go_color_to_gdk (GOColor color, GdkColor *res)
+{
+ res->red = UINT_RGBA_R (color);
+ res->red |= (res->red << 8);
+ res->green = UINT_RGBA_G (color);
+ res->green |= (res->green << 8);
+ res->blue = UINT_RGBA_B (color);
+ res->blue |= (res->blue << 8);
+
+ return res;
+}
+#endif /* WITH_GTK */
--- /dev/null
+++ lib/goffice/utils/go-marker.h
@@ -0,0 +1,108 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-marker.h :
+ *
+ * Copyright (C) 2003-2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GO_MARKER_H
+#define GO_MARKER_H
+
+#include <glib-object.h>
+#include <goffice/utils/goffice-utils.h>
+#include <libart_lgpl/art_vpath.h>
+
+#ifdef WITH_GTK
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#endif
+
+G_BEGIN_DECLS
+
+#define GO_MARKER_TYPE (go_marker_get_type ())
+#define GO_MARKER(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GO_MARKER_TYPE, GOMarker))
+#define IS_GO_MARKER(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GO_MARKER_TYPE))
+
+typedef enum {
+ GO_MARKER_NONE,
+ GO_MARKER_SQUARE,
+ GO_MARKER_DIAMOND,
+ GO_MARKER_TRIANGLE_DOWN,
+ GO_MARKER_TRIANGLE_UP,
+ GO_MARKER_TRIANGLE_RIGHT,
+ GO_MARKER_TRIANGLE_LEFT,
+ GO_MARKER_CIRCLE,
+ GO_MARKER_X,
+ GO_MARKER_CROSS,
+ GO_MARKER_ASTERISK,
+ GO_MARKER_BAR,
+ GO_MARKER_HALF_BAR,
+ GO_MARKER_BUTTERFLY,
+ GO_MARKER_HOURGLASS,
+ GO_MARKER_MAX
+} GOMarkerShape;
+
+struct _GOMarker {
+ GObject base;
+
+ int size;
+ double scale;
+ GOMarkerShape shape;
+ GOColor outline_color;
+ GOColor fill_color;
+#ifdef WITH_GTK
+ GdkPixbuf *pixbuf;
+#else
+ gpointer pixbuf;
+#endif
+};
+
+GType go_marker_get_type (void);
+
+
+GOMarkerShape go_marker_shape_from_str (char const *name);
+char const *go_marker_shape_as_str (GOMarkerShape shape);
+void go_marker_get_paths (GOMarker * marker,
+ ArtVpath const **outline_path,
+ ArtVpath const **fill_path);
+GOMarkerShape go_marker_get_shape (GOMarker *m);
+void go_marker_set_shape (GOMarker *m, GOMarkerShape shape);
+GOColor go_marker_get_outline_color (GOMarker *m);
+void go_marker_set_outline_color (GOMarker *m, GOColor color);
+GOColor go_marker_get_fill_color (GOMarker *m);
+void go_marker_set_fill_color (GOMarker *m, GOColor color);
+int go_marker_get_size (GOMarker *m);
+void go_marker_set_size (GOMarker *m, int size);
+double go_marker_get_outline_width (GOMarker *m);
+
+void go_marker_assign (GOMarker *dst, GOMarker const *src);
+GOMarker * go_marker_dup (GOMarker *src);
+GOMarker * go_marker_new (void);
+
+#ifdef WITH_GTK
+GdkPixbuf const *go_marker_get_pixbuf (GOMarker *m, double scale);
+GdkPixbuf const *go_marker_get_pixbuf_with_size (GOMarker *m, guint size);
+
+gpointer go_marker_selector (GOColor outline_color,
+ GOColor fill_color,
+ GOMarkerShape default_shape);
+GOMarkerShape go_marker_selector_get_shape (gpointer selector,
+ int index, gboolean *is_auto);
+#endif
+
+G_END_DECLS
+
+#endif /* GO_MARKER_H */
--- /dev/null
+++ lib/goffice/utils/go-math.c
@@ -0,0 +1,164 @@
+/*
+ * go-math.c: Mathematical functions.
+ *
+ * Authors:
+ * Morten Welinder <terra at gnome.org>
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-math.h"
+#include <glib/gmessages.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <locale.h>
+#include <signal.h>
+#include <errno.h>
+
+#if defined (HAVE_IEEEFP_H) || defined (HAVE_IEEE754_H)
+/* Make sure we have this symbol defined, since the existance of either
+ header file implies it. */
+#ifndef IEEE_754
+#define IEEE_754
+#endif
+#endif
+
+#define ML_UNDERFLOW (GO_EPSILON * GO_EPSILON)
+
+double go_nan;
+double go_pinf;
+double go_ninf;
+
+void
+go_math_init (void)
+{
+ const char *bug_url = "http://bugzilla.gnome.org/enter_bug.cgi?product=gnumeric";
+ char *old_locale;
+ double d;
+#ifdef SIGFPE
+ void (*signal_handler)(int) = (void (*)(int))signal (SIGFPE, SIG_IGN);
+#endif
+
+ go_pinf = HUGE_VAL;
+ if (go_pinf > 0 && !go_finite (go_pinf))
+ goto have_pinf;
+
+#if defined(INFINITY) && defined(__STDC_IEC_559__)
+ go_pinf = INFINITY;
+ if (go_pinf > 0 && !go_finite (go_pinf))
+ goto have_pinf;
+#endif
+
+ /* Try sscanf with fixed strings. */
+ old_locale = setlocale (LC_ALL, "C");
+ if (sscanf ("Inf", "%lf", &d) != 1 &&
+ sscanf ("+Inf", "%lf", &d) != 1)
+ d = 0;
+ setlocale (LC_ALL, old_locale);
+ go_pinf = d;
+ if (go_pinf > 0 && !go_finite (go_pinf))
+ goto have_pinf;
+
+ /* Try overflow. */
+ go_pinf = (HUGE_VAL * HUGE_VAL);
+ if (go_pinf > 0 && !go_finite (go_pinf))
+ goto have_pinf;
+
+ g_error ("Failed to generate +Inf. Please report at %s",
+ bug_url);
+ abort ();
+
+ have_pinf:
+ /* ---------------------------------------- */
+
+ go_ninf = -go_pinf;
+ if (go_ninf < 0 && !go_finite (go_ninf))
+ goto have_ninf;
+
+ g_error ("Failed to generate -Inf. Please report at %s",
+ bug_url);
+ abort ();
+
+ have_ninf:
+ /* ---------------------------------------- */
+
+ go_nan = go_pinf * 0.0;
+ if (isnan (go_nan))
+ goto have_nan;
+
+ /* Try sscanf with fixed strings. */
+ old_locale = setlocale (LC_ALL, "C");
+ if (sscanf ("NaN", "%lf", &d) != 1 &&
+ sscanf ("NAN", "%lf", &d) != 1 &&
+ sscanf ("+NaN", "%lf", &d) != 1 &&
+ sscanf ("+NAN", "%lf", &d) != 1)
+ d = 0;
+ setlocale (LC_ALL, old_locale);
+ go_nan = d;
+ if (isnan (go_nan))
+ goto have_nan;
+
+ go_nan = go_pinf / go_pinf;
+ if (isnan (go_nan))
+ goto have_nan;
+
+ g_error ("Failed to generate NaN. Please report at %s",
+ bug_url);
+ abort ();
+
+ have_nan:
+#ifdef SIGFPE
+ signal (SIGFPE, signal_handler);
+#endif
+ return;
+}
+
+/*
+ * In preparation for truncation, make the value a tiny bit larger (seen
+ * absolutely). This makes ROUND (etc.) behave a little closer to what
+ * people want, even if it is a bit bogus.
+ */
+double
+go_add_epsilon (double x)
+{
+ if (!go_finite (x) || x == 0)
+ return x;
+ else {
+ int exp;
+ double mant = frexp (fabs (x), &exp);
+ double absres = ldexp (mant + DBL_EPSILON, exp);
+ return (x < 0) ? -absres : absres;
+ }
+}
+
+double
+go_sub_epsilon (double x)
+{
+ if (!go_finite (x) || x == 0)
+ return x;
+ else {
+ int exp;
+ double mant = frexp (fabs (x), &exp);
+ double absres = ldexp (mant - DBL_EPSILON, exp);
+ return (x < 0) ? -absres : absres;
+ }
+}
+
+double
+go_fake_floor (double x)
+{
+ return floor (go_add_epsilon (x));
+}
+
+double
+go_fake_ceil (double x)
+{
+ return ceil (go_sub_epsilon (x));
+}
+
+double
+go_fake_trunc (double x)
+{
+ return (x >= 0)
+ ? go_fake_floor (x)
+ : -go_fake_floor (-x);
+}
--- /dev/null
+++ lib/goffice/utils/go-file.c
@@ -0,0 +1,519 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-file.c :
+ *
+ * Copyright (C) 2004 Morten Welinder (terra at gnome.org)
+ * Copyright (C) 2004 Yukihiro Nakai <nakai at gnome.gr.jp>
+ * Copyright (C) 2003, Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-file.h"
+#include <gsf/gsf-input-memory.h>
+#include <gsf/gsf-input-stdio.h>
+#include <gsf/gsf-output-stdio.h>
+#ifdef WITH_GNOME
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <gsf-gnome/gsf-input-gnomevfs.h>
+#include <gsf-gnome/gsf-output-gnomevfs.h>
+#include <libgnome/gnome-url.h>
+#else
+#ifdef G_OS_WIN32
+#include <windows.h>
+#include <winreg.h>
+#endif
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+/* ------------------------------------------------------------------------- */
+
+char *
+go_filename_from_uri (const char *uri)
+{
+#ifdef WITH_GNOME
+ return gnome_vfs_get_local_path_from_uri (uri);
+#else
+ return g_filename_from_uri (uri, NULL, NULL);
+#endif
+}
+
+
+char *
+go_filename_to_uri (const char *filename)
+{
+ if (g_path_is_absolute (filename)) {
+ char *uri;
+ char *simp = g_strdup (filename);
+ char *p, *q;
+
+ for (p = q = simp; *p;) {
+ if (p != simp &&
+ p[0] == G_DIR_SEPARATOR &&
+ p[1] == G_DIR_SEPARATOR) {
+ /* "//" --> "/", except initially. */
+ p++;
+ continue;
+ }
+
+ if (p[0] == G_DIR_SEPARATOR &&
+ p[1] == '.' &&
+ p[2] == G_DIR_SEPARATOR) {
+ /* "/./" -> "/". */
+ p += 2;
+ continue;
+ }
+
+ *q++ = *p++;
+ }
+ *q = 0;
+
+ /* FIXME: Resolve ".." parts. */
+#ifdef WITH_GNOME
+ uri = gnome_vfs_get_uri_from_local_path (simp);
+#else
+ uri = g_filename_to_uri (simp, NULL, NULL);
+#endif
+ g_free (simp);
+ return uri;
+ } else {
+ char *uri;
+ char *current_dir = g_get_current_dir ();
+ char *abs_filename =
+ g_build_filename (current_dir, filename, NULL);
+ g_return_val_if_fail (g_path_is_absolute (abs_filename), NULL);
+ uri = go_filename_to_uri (abs_filename);
+ g_free (current_dir);
+ g_free (abs_filename);
+ return uri;
+ }
+}
+
+
+char *
+go_shell_arg_to_uri (const char *arg)
+{
+#ifdef WITH_GNOME
+ return gnome_vfs_make_uri_from_shell_arg (arg);
+#else
+ if (g_path_is_absolute (arg))
+ return go_filename_to_uri (arg);
+ else {
+ /* See if it's a file: uri. */
+ char *tmp = go_filename_from_uri (arg);
+ if (tmp) {
+ g_free (tmp);
+ return g_strdup (arg);
+ }
+ }
+
+ /* Just assume it's a filename. */
+ return go_filename_to_uri (arg);
+#endif
+}
+
+/**
+ * go_basename_from_uri:
+ * @uri :
+ *
+ * Decode the final path component. Returns as UTF-8 encoded.
+ **/
+char *
+go_basename_from_uri (const char *uri)
+{
+#ifdef WITH_GNOME
+ char *raw_uri = gnome_vfs_unescape_string (uri, G_DIR_SEPARATOR_S);
+ char *basename = raw_uri ? g_path_get_basename (raw_uri) : NULL;
+ g_free (raw_uri);
+#else
+ char *uri_basename = g_path_get_basename (uri);
+ char *fake_uri = g_strconcat ("file:///", uri_basename, NULL);
+ char *filename = go_filename_from_uri (fake_uri);
+ char *basename = filename ? g_path_get_basename (filename) : NULL;
+ g_free (uri_basename);
+ g_free (fake_uri);
+ g_free (filename);
+
+#endif
+ {
+ char *basename_utf8 = basename
+ ? g_filename_to_utf8 (basename, -1, NULL, NULL, NULL)
+ : NULL;
+ g_free (basename);
+ return basename_utf8;
+ }
+}
+
+/**
+ * go_dirname_from_uri:
+ * @uri :
+ * @brief: if TRUE, hide "file://" if present.
+ *
+ * Decode the all but the final path component. Returns as UTF-8 encoded.
+ **/
+char *
+go_dirname_from_uri (const char *uri, gboolean brief)
+{
+ char *dirname_utf8, *dirname;
+
+#ifdef WITH_GNOME
+ char *raw_uri = gnome_vfs_unescape_string (uri, G_DIR_SEPARATOR_S);
+ dirname = raw_uri ? g_path_get_dirname (raw_uri) : NULL;
+ g_free (raw_uri);
+#else
+ char *uri_dirname = g_path_get_dirname (uri);
+ char *dir = uri_dirname ? go_filename_from_uri (uri_dirname) : NULL;
+ dirname = dirname ? g_strconcat ("file://", dirname, NULL) : NULL;
+ g_free (dir);
+ g_free (uri_dirname);
+#endif
+
+ if (brief && dirname &&
+ g_ascii_strncasecmp (dirname, "file:///", 8) == 0) {
+ char *temp = g_strdup (dirname + 7);
+ g_free (dirname);
+ dirname = temp;
+ }
+
+ dirname_utf8 = dirname
+ ? g_filename_to_utf8 (dirname, -1, NULL, NULL, NULL)
+ : NULL;
+ g_free (dirname);
+ return dirname_utf8;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static GsfInput *
+open_plain_file (const char *path, GError **err)
+{
+ GsfInput *input = gsf_input_mmap_new (path, NULL);
+ if (input != NULL)
+ return input;
+ /* Only report error if stdio fails too */
+ return gsf_input_stdio_new (path, err);
+}
+
+
+/**
+ * go_file_open :
+ * @uri :
+ * @err : #GError
+ *
+ * Try all available methods to open a file or return an error
+ **/
+GsfInput *
+go_file_open (char const *uri, GError **err)
+{
+ char *filename;
+
+ if (err != NULL)
+ *err = NULL;
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ if (uri[0] == G_DIR_SEPARATOR) {
+ g_warning ("Got plain filename %s in go_file_open.", uri);
+ return open_plain_file (uri, err);
+ }
+
+ filename = go_filename_from_uri (uri);
+ if (filename) {
+ GsfInput *result = open_plain_file (filename, err);
+ g_free (filename);
+ return result;
+ }
+
+#ifdef WITH_GNOME
+ return gsf_input_gnomevfs_new (uri, err);
+#else
+ g_set_error (err, gsf_input_error (), 0,
+ "Invalid or non-supported URI");
+ return NULL;
+#endif
+}
+
+GsfOutput *
+go_file_create (char const *uri, GError **err)
+{
+ char *filename;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ filename = go_filename_from_uri (uri);
+ if (filename) {
+ GsfOutput *result = gsf_output_stdio_new (filename, err);
+ g_free (filename);
+ return result;
+ }
+
+#ifdef WITH_GNOME
+ return gsf_output_gnomevfs_new (uri, err);
+#else
+ g_set_error (err, gsf_output_error_id (), 0,
+ "Invalid or non-supported URI");
+ return NULL;
+#endif
+}
+
+/* ------------------------------------------------------------------------- */
+/* Adapted from gtkfilechooserdefault.c. Unfortunately it is static there. */
+
+GSList *
+go_file_split_uris (const char *data)
+{
+ GSList *uris;
+ const char *p, *q;
+
+ uris = NULL;
+
+ p = data;
+
+ /* We don't actually try to validate the URI according to RFC
+ * 2396, or even check for allowed characters - we just ignore
+ * comments and trim whitespace off the ends. We also
+ * allow LF delimination as well as the specified CRLF.
+ *
+ * We do allow comments like specified in RFC 2483.
+ */
+ while (p)
+ {
+ if (*p != '#')
+ {
+ while (g_ascii_isspace (*p))
+ p++;
+
+ q = p;
+ while (*q && (*q != '\n') && (*q != '\r'))
+ q++;
+
+ if (q > p)
+ {
+ q--;
+ while (q > p && g_ascii_isspace (*q))
+ q--;
+
+ if (q > p)
+ uris = g_slist_prepend (uris, g_strndup (p, q - p + 1));
+ }
+ }
+ p = strchr (p, '\n');
+ if (p)
+ p++;
+ }
+
+ uris = g_slist_reverse (uris);
+ return uris;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * go_url_decode: decode the result of go_url_encode.
+ */
+gchar*
+go_url_decode (gchar const *text)
+{
+ GString *result;
+
+ g_return_val_if_fail (text != NULL, NULL);
+ g_return_val_if_fail (*text != '\0', NULL);
+
+ result = g_string_new (NULL);
+ while (*text) {
+ unsigned char c = *text++;
+ if (c == '%') {
+ if (g_ascii_isxdigit (text[0]) && g_ascii_isxdigit (text[1])) {
+ g_string_append_c (result,
+ (g_ascii_xdigit_value (text[0]) << 4) |
+ g_ascii_xdigit_value (text[1]));
+ text += 2;
+ } else {
+ /* Bogus. */
+ return g_string_free (result, TRUE);
+ }
+ } else
+ g_string_append_c (result, c);
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+/**
+ * go_url_encode: url-encode a string according to RFC 2368.
+ */
+gchar*
+go_url_encode (gchar const *text)
+{
+ static const char hex[16] = "0123456789ABCDEF";
+ GString* result;
+
+ g_return_val_if_fail (text != NULL, NULL);
+ g_return_val_if_fail (*text != '\0', NULL);
+
+ result = g_string_new (NULL);
+ while (*text) {
+ unsigned char c = *text++;
+ switch (c) {
+ case '.': case '-': case '_': case '@':
+ g_string_append_c (result, c);
+ break;
+ default:
+ if (g_ascii_isalnum (c))
+ g_string_append_c (result, c);
+ else {
+ g_string_append_c (result, '%');
+ g_string_append_c (result, hex[c >> 4]);
+ g_string_append_c (result, hex[c & 0xf]);
+ }
+ }
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+#ifndef WITH_GNOME
+static char *
+check_program (char const *prog)
+{
+ if (NULL == prog)
+ return NULL;
+ if (g_path_is_absolute (prog)) {
+ if (!g_file_test (prog, G_FILE_TEST_IS_EXECUTABLE))
+ return NULL;
+ } else if (!g_find_program_in_path (prog))
+ return NULL;
+ return g_strdup (prog);
+}
+#endif
+
+GError *
+go_url_show (gchar const *url)
+{
+ GError *err = NULL;
+#ifdef WITH_GNOME
+ gnome_url_show (url, &err);
+ return err;
+#else
+ guint8 *browser = NULL;
+ guint8 *clean_url = NULL;
+
+ /* 1) Check BROWSER env var */
+ browser = check_program (getenv ("BROWSER"));
+
+#ifdef G_OS_WIN32
+{
+ char *ptr, *longpath;
+ HKEY hKey;
+ unsigned long lType;
+ DWORD dwSize;
+
+ /* 2) Check registry */
+ if (browser == NULL &&
+ RegOpenKeyEx (HKEY_CLASSES_ROOT, "http\\shell\\open\\command", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
+ if(RegQueryValueEx (hKey, NULL, NULL, &lType, NULL, &dwSize) == ERROR_SUCCESS) {
+ unsigned char *buf = g_new (unsigned char, dwSize + 1);
+ RegQueryValueEx (hKey, NULL, NULL, &lType, buf, &dwSize);
+ browser = check_program (buf);
+ g_free (buf);
+ }
+ RegCloseKey(hKey);
+ }
+
+ /* some win32 specific url cleanup */
+ /* If this is a file:// URL, strip off file:// and make it backslashed */
+ if (g_ascii_strncasecmp (url, "file://", 7) == 0) {
+ url += 7;
+
+ /* View as WebPage likes to throw in an extra /\ just for fun,
+ * strip it off */
+ if (strncmp (url, "/\\", 2) == 0)
+ url += 2;
+
+ longpath = g_strdup (url);
+ /* s/forward-slash/back-slash/ */
+ for (ptr = longpath ; *ptr ; ptr++)
+ if (*ptr == '/')
+ *ptr = '\\';
+
+ clean_url = g_new (char, MAX_PATH);
+ /* Convert to 8.3 in case of spaces in path */
+ GetShortPathName (longpath, clean_url, MAX_PATH);
+ g_free (longpath);
+ }
+}
+#endif
+
+ if (browser == NULL) {
+ static char const * const browsers[] = {
+ "sensible-browser", /* debian */
+ "epiphany", /* primary gnome */
+ "galeon", /* secondary gnome */
+ "encompass",
+ "firefox",
+ "mozilla-firebird",
+ "mozilla",
+ "netscape",
+ "konqueror",
+ "xterm -e w3m",
+ "xterm -e lynx",
+ "xterm -e links"
+ };
+ unsigned i;
+ for (i = 0 ; i < G_N_ELEMENTS (browsers) ; i++)
+ if (NULL != (browser = check_program (browsers[i])))
+ break;
+ }
+
+ if (browser != NULL) {
+ gint argc;
+ gchar **argv = NULL;
+ char *cmd_line = g_strconcat (browser, " %1", NULL);
+
+ if (g_shell_parse_argv (cmd_line, &argc, &argv, &err)) {
+ /* check for '%1' in an argument and substitute the url
+ * otherwise append it */
+ gint i;
+ char *tmp;
+
+ for (i = 1 ; i < argc ; i++)
+ if (NULL != (tmp = strstr (argv[i], "%1"))) {
+ *tmp = '\0';
+ tmp = g_strconcat (argv[i],
+ (clean_url != NULL) ? (char const *)clean_url : url,
+ tmp+2, NULL);
+ g_free (argv[i]);
+ argv[i] = tmp;
+ break;
+ }
+
+ /* there was actually a %1, drop the one we added */
+ if (i != argc-1) {
+ g_free (argv[argc-1]);
+ argv[argc-1] = NULL;
+ }
+ g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, &err);
+ g_strfreev (argv);
+ }
+ g_free (cmd_line);
+ }
+ g_free (browser);
+ g_free (clean_url);
+ return err;
+#endif
+}
--- /dev/null
+++ lib/goffice/utils/Makefile.am
@@ -0,0 +1,29 @@
+noinst_LTLIBRARIES = libgoffice-utils.la
+
+AM_CFLAGS = ${GLIB_CFLAGS} ${ART_CFLAGS} ${GNOME_CFLAGS} ${GSF_CFLAGS} ${GLADE_CFLAGS}
+
+libgoffice_utils_la_SOURCES = \
+ goffice-utils.h \
+ go-color.c \
+ go-color.h \
+ go-file.c \
+ go-file.h \
+ go-font.c \
+ go-font.h \
+ go-format.c \
+ go-format.h \
+ go-gradient.c \
+ go-gradient.h \
+ go-line.c \
+ go-line.h \
+ go-marker.c \
+ go-marker.h \
+ go-pattern.c \
+ go-pattern.h \
+ go-locale.c \
+ go-locale.h \
+ go-units.h \
+ go-math.c \
+ go-math.h
+
+include $(srcdir)/../goffice.mk
--- /dev/null
+++ lib/goffice/utils/go-font.h
@@ -0,0 +1,58 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-font.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_FONT_H
+#define GO_FONT_H
+
+#include <glib.h>
+#include <goffice/utils/goffice-utils.h>
+#include <pango/pango-font.h>
+#include <pango/pangofc-fontmap.h>
+
+G_BEGIN_DECLS
+
+struct _GOFont {
+ PangoFontDescription *desc;
+ int ref_count;
+ int font_index; /* each renderer keeps an array for lookup */
+};
+
+GOFont const *go_font_new_by_desc (PangoFontDescription *desc);
+GOFont const *go_font_new_by_name (char const *str);
+GOFont const *go_font_new_by_index (unsigned i);
+char *go_font_as_str (GOFont const *font);
+GOFont const *go_font_ref (GOFont const *font);
+void go_font_unref (GOFont const *font);
+gboolean go_font_eq (GOFont const *a, GOFont const *b);
+
+/* cache notification */
+void go_font_cache_register (GClosure *callback);
+void go_font_cache_unregister (GClosure *callback);
+
+/* private */
+void go_font_init (void);
+void go_font_shutdown (void);
+
+/* See http://bugzilla.gnome.org/show_bug.cgi?id=143542 */
+void go_pango_fc_font_map_cache_clear (PangoFcFontMap *font_map);
+
+G_END_DECLS
+
+#endif /* GO_FONT_H */
--- /dev/null
+++ lib/goffice/utils/go-format.h
@@ -0,0 +1,43 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-format.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_FORMAT_H
+#define GO_FORMAT_H
+
+#include <goffice/utils/goffice-utils.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+GOFormat *go_format_new_from_XL (char const *descriptor_string, gboolean delocalize);
+char *go_format_as_XL (GOFormat const *fmt, gboolean localized);
+GOFormat *go_format_ref (GOFormat *fmt);
+void go_format_unref (GOFormat *fmt);
+char *go_format_value (GOFormat const *fmt, double val);
+gboolean go_format_eq (GOFormat const *a, GOFormat const *b);
+GOFormat *go_format_general (void);
+GOFormat *go_format_default_date (void);
+GOFormat *go_format_default_time (void);
+GOFormat *go_format_default_percentage (void);
+GOFormat *go_format_default_money (void);
+
+G_END_DECLS
+
+#endif /* GO_FORMAT_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/gog-barcol-prefs.glade
@@ -0,0 +1,176 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window1">
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkTable" id="gog_barcol_prefs">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Gap:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">gap_spinner</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label63">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">%</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label64">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">O_verlap:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">overlap_spinner</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label65">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">%</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="gap_spinner">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Separation between groups as a percentage of bar/col width</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">10</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">True</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 500 10 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="overlap_spinner">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">How far the bars/cols overlap as a percentage of the width</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">0.1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">True</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 -100 100 10 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/gog-1.5d.c
@@ -0,0 +1,513 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-1.5d.c
+ *
+ * Copyright (C) 2003-2004
+ * Jody Goldberg (jody at gnome.org)
+ * Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-1.5d.h"
+#include "gog-line.h"
+#include "gog-barcol.h"
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-format.h>
+#include <goffice/utils/go-math.h>
+
+#include <module-plugin-defs.h>
+#include <glib/gi18n.h>
+#include <gtk/gtklabel.h>
+#include <gsf/gsf-impl-utils.h>
+
+GNUMERIC_MODULE_PLUGIN_INFO_DECL;
+
+enum {
+ GOG_1_5D_PROP_0,
+ GOG_1_5D_PROP_TYPE,
+ GOG_1_5D_PROP_IN_3D /* place holder for XL */
+};
+
+static GogObjectClass *plot1_5d_parent_klass;
+
+static void
+gog_plot_1_5d_clear_formats (GogPlot1_5d *plot)
+{
+ if (plot->fmt != NULL) {
+ go_format_unref (plot->fmt);
+ plot->fmt = NULL;
+ }
+}
+
+static void
+gog_plot1_5d_finalize (GObject *obj)
+{
+ gog_plot_1_5d_clear_formats (GOG_PLOT1_5D (obj));
+ G_OBJECT_CLASS (plot1_5d_parent_klass)->finalize (obj);
+}
+
+static void
+gog_plot1_5d_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogPlot1_5d *gog_1_5d = GOG_PLOT1_5D (obj);
+ gboolean tmp;
+
+ switch (param_id) {
+ case GOG_1_5D_PROP_TYPE: {
+ char const *str = g_value_get_string (value);
+ if (str == NULL)
+ return;
+ else if (!g_ascii_strcasecmp (str, "normal"))
+ gog_1_5d->type = GOG_1_5D_NORMAL;
+ else if (!g_ascii_strcasecmp (str, "stacked"))
+ gog_1_5d->type = GOG_1_5D_STACKED;
+ else if (!g_ascii_strcasecmp (str, "as_percentage"))
+ gog_1_5d->type = GOG_1_5D_AS_PERCENTAGE;
+ else
+ return;
+ break;
+ case GOG_1_5D_PROP_IN_3D :
+ tmp = g_value_get_boolean (value);
+ if ((gog_1_5d->in_3d != 0) == (tmp != 0))
+ return;
+ gog_1_5d->in_3d = tmp;
+ }
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+ gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
+}
+
+static void
+gog_plot1_5d_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogPlot1_5d *gog_1_5d = GOG_PLOT1_5D (obj);
+
+ switch (param_id) {
+ case GOG_1_5D_PROP_TYPE:
+ switch (gog_1_5d->type) {
+ case GOG_1_5D_NORMAL:
+ g_value_set_static_string (value, "normal");
+ break;
+ case GOG_1_5D_STACKED:
+ g_value_set_static_string (value, "stacked");
+ break;
+ case GOG_1_5D_AS_PERCENTAGE:
+ g_value_set_static_string (value, "as_percentage");
+ break;
+ }
+ break;
+ case GOG_1_5D_PROP_IN_3D :
+ g_value_set_boolean (value, gog_1_5d->in_3d);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static GogAxis *
+gog_plot1_5d_get_value_axis (GogPlot1_5d *model)
+{
+ GogPlot1_5dClass *klass = GOG_PLOT1_5D_GET_CLASS (model);
+ if (klass->swap_x_and_y && (*klass->swap_x_and_y ) (model))
+ return model->base.axis [GOG_AXIS_X];
+ return model->base.axis [GOG_AXIS_Y];
+}
+
+GogAxis *
+gog_plot1_5d_get_index_axis (GogPlot1_5d *model)
+{
+ GogPlot1_5dClass *klass = GOG_PLOT1_5D_GET_CLASS (model);
+ if (klass->swap_x_and_y && (*klass->swap_x_and_y ) (model))
+ return model->base.axis [GOG_AXIS_Y];
+ return model->base.axis [GOG_AXIS_X];
+}
+
+static void
+gog_plot1_5d_update (GogObject *obj)
+{
+ GogPlot1_5d *model = GOG_PLOT1_5D (obj);
+ GogPlot1_5dClass *klass = GOG_PLOT1_5D_GET_CLASS (obj);
+ GogSeries1_5d const *series;
+ unsigned i, num_elements, num_series;
+ double **vals, minima, maxima;
+ double old_minima, old_maxima;
+ unsigned *lengths;
+ GSList *ptr;
+ GOData *index_dim = NULL;
+ GogPlot *plot_that_labeled_axis;
+ GogAxis *axis;
+ GogErrorBar **errors;
+ gboolean index_changed = FALSE;
+
+ old_minima = model->minima;
+ old_maxima = model->maxima;
+ model->minima = DBL_MAX;
+ model->maxima = -DBL_MAX;
+ gog_plot_1_5d_clear_formats (model);
+
+ num_elements = num_series = 0;
+ for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
+ series = ptr->data;
+ if (!gog_series_is_valid (GOG_SERIES (series)))
+ continue;
+ num_series++;
+
+ if (GOG_SERIES1_5D (series)->index_changed) {
+ GOG_SERIES1_5D (series)->index_changed = FALSE;
+ index_changed = TRUE;
+ }
+
+ if (num_elements < series->base.num_elements)
+ num_elements = series->base.num_elements;
+ if (GOG_1_5D_NORMAL == model->type) {
+ if (gog_error_bar_is_visible (series->errors))
+ gog_error_bar_get_minmax (series->errors, &minima, &maxima);
+ else
+ go_data_vector_get_minmax (GO_DATA_VECTOR (
+ series->base.values[1].data), &minima, &maxima);
+ if (model->minima > minima)
+ model->minima = minima;
+ if (model->maxima < maxima)
+ model->maxima = maxima;
+ }
+ if (model->fmt == NULL)
+ model->fmt = go_data_preferred_fmt (series->base.values[1].data);
+ index_dim = GOG_SERIES (series)->values[0].data;
+ }
+ axis = gog_plot1_5d_get_index_axis (model);
+ if (model->num_elements != num_elements ||
+ model->implicit_index ^ (index_dim == NULL) ||
+ (index_dim != gog_axis_get_labels (axis, &plot_that_labeled_axis) &&
+ GOG_PLOT (model) == plot_that_labeled_axis)) {
+ model->num_elements = num_elements;
+ model->implicit_index = (index_dim == NULL);
+ gog_axis_bound_changed (axis, GOG_OBJECT (model));
+ } else {
+ if (index_changed)
+ gog_axis_bound_changed (axis, GOG_OBJECT (model));
+ }
+
+ model->num_series = num_series;
+
+ if (num_elements <= 0 || num_series <= 0)
+ model->minima = model->maxima = 0.;
+ else if (model->type != GOG_1_5D_NORMAL) {
+ vals = g_alloca (num_series * sizeof (double *));
+ errors = g_alloca (num_series * sizeof (GogErrorBar *));
+ lengths = g_alloca (num_series * sizeof (unsigned));
+ i = 0;
+ for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next, i++) {
+ series = ptr->data;
+ /* we are guaranteed that at least 1 series is valid above */
+ if (!gog_series_is_valid (GOG_SERIES (series)))
+ continue;
+ vals[i] = go_data_vector_get_values (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ g_object_get (G_OBJECT (series), "errors", errors + i, NULL);
+ lengths[i] = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ }
+
+ klass->update_stacked_and_percentage (model, vals, errors, lengths);
+ }
+
+ if (old_minima != model->minima || old_maxima != model->maxima)
+ gog_axis_bound_changed (
+ gog_plot1_5d_get_value_axis (model), GOG_OBJECT (model));
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+ if (plot1_5d_parent_klass->update)
+ plot1_5d_parent_klass->update (obj);
+}
+
+static GOData *
+gog_plot1_5d_axis_get_bounds (GogPlot *plot, GogAxisType axis,
+ GogPlotBoundInfo *bounds)
+{
+ GogPlot1_5d *model = GOG_PLOT1_5D (plot);
+ if (axis == gog_axis_get_atype (gog_plot1_5d_get_value_axis (model))) {
+ bounds->val.minima = model->minima;
+ bounds->val.maxima = model->maxima;
+ if (model->type == GOG_1_5D_AS_PERCENTAGE) {
+ if (model->minima >= -1.)
+ bounds->logical.minima = -1.;
+ if (model->maxima <= 1.)
+ bounds->logical.maxima = 1.;
+ if (bounds->fmt == NULL) {
+ bounds->fmt = go_format_ref (
+ go_format_default_percentage ());
+ }
+ } else if (bounds->fmt == NULL && model->fmt != NULL)
+ bounds->fmt = go_format_ref (model->fmt);
+ return NULL;
+ } else if (axis == gog_axis_get_atype (gog_plot1_5d_get_index_axis (model))) {
+ GSList *ptr;
+
+ bounds->val.minima = 0.;
+ bounds->val.maxima = model->num_elements - 1.;
+ bounds->logical.minima = 0.;
+ bounds->logical.maxima = go_nan;
+ bounds->is_discrete = TRUE;
+
+ for (ptr = plot->series; ptr != NULL ; ptr = ptr->next)
+ if (gog_series_is_valid (GOG_SERIES (ptr->data)))
+ return GOG_SERIES (ptr->data)->values[0].data;
+ return NULL;
+ }
+
+ g_warning ("not reached");
+ return NULL;
+}
+
+static GogAxisSet
+gog_plot1_5d_axis_set_pref (GogPlot const *plot)
+{
+ return GOG_AXIS_SET_XY; /* do some magic later for 3d */
+}
+
+static gboolean
+gog_plot1_5d_axis_set_is_valid (GogPlot const *plot, GogAxisSet type)
+{
+ return type == GOG_AXIS_SET_XY; /* do some magic later for 3d */
+}
+
+static gboolean
+gog_plot1_5d_axis_set_assign (GogPlot *plot, GogAxisSet type)
+{
+ return type == GOG_AXIS_SET_XY; /* do some magic later for 3d */
+}
+
+static gboolean
+gog_1_5d_supports_vary_style_by_element (GogPlot const *plot)
+{
+ GogPlot1_5d *gog_1_5d = GOG_PLOT1_5D (plot);
+ return gog_1_5d->type == GOG_1_5D_NORMAL;
+}
+
+static void
+gog_plot1_5d_class_init (GogPlotClass *plot_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) plot_klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) plot_klass;
+
+ plot1_5d_parent_klass = g_type_class_peek_parent (plot_klass);
+ gobject_klass->set_property = gog_plot1_5d_set_property;
+ gobject_klass->get_property = gog_plot1_5d_get_property;
+ gobject_klass->finalize = gog_plot1_5d_finalize;
+
+ g_object_class_install_property (gobject_klass, GOG_1_5D_PROP_TYPE,
+ g_param_spec_string ("type", "type",
+ "How to group multiple series, normal, stacked, as_percentage",
+ "normal", G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, GOG_1_5D_PROP_IN_3D,
+ g_param_spec_boolean ("in_3d", "in_3d",
+ "Place holder to all us to round trip pseudo 3d state",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ gog_klass->update = gog_plot1_5d_update;
+
+ {
+ static GogSeriesDimDesc dimensions[] = {
+ { N_("Labels"), GOG_SERIES_SUGGESTED, TRUE,
+ GOG_DIM_LABEL, GOG_MS_DIM_CATEGORIES },
+ { N_("Values"), GOG_SERIES_REQUIRED, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_VALUES },
+/* Names of the error data are not translated since they are not used */
+ { "+err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_plus1 },
+ { "-err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_minus1 }
+ };
+ plot_klass->desc.series.dim = dimensions;
+ plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
+ }
+ plot_klass->desc.num_series_min = 1;
+ plot_klass->desc.num_series_max = G_MAXINT;
+ plot_klass->series_type = gog_series1_5d_get_type ();
+ plot_klass->axis_get_bounds = gog_plot1_5d_axis_get_bounds;
+ plot_klass->axis_set_pref = gog_plot1_5d_axis_set_pref;
+ plot_klass->axis_set_is_valid = gog_plot1_5d_axis_set_is_valid;
+ plot_klass->axis_set_assign = gog_plot1_5d_axis_set_assign;
+ plot_klass->supports_vary_style_by_element = gog_1_5d_supports_vary_style_by_element;
+}
+
+static void
+gog_plot1_5d_init (GogPlot1_5d *plot)
+{
+ plot->fmt = NULL;
+ plot->in_3d = FALSE;
+}
+
+GSF_CLASS_ABSTRACT (GogPlot1_5d, gog_plot1_5d,
+ gog_plot1_5d_class_init, gog_plot1_5d_init,
+ GOG_PLOT_TYPE)
+
+/*****************************************************************************/
+
+static GogObjectClass *gog_series1_5d_parent_klass;
+
+enum {
+ SERIES_PROP_0,
+ SERIES_PROP_ERRORS
+};
+
+static void
+gog_series1_5d_dim_changed (GogSeries *series, int dim_i)
+{
+ if (dim_i == 0)
+ GOG_SERIES1_5D (series)->index_changed = TRUE;
+}
+
+static void
+gog_series1_5d_update (GogObject *obj)
+{
+ double *vals;
+ int len = 0;
+ GogSeries1_5d *series = GOG_SERIES1_5D (obj);
+ unsigned old_num = series->base.num_elements;
+
+ if (series->base.values[1].data != NULL) {
+ vals = go_data_vector_get_values (GO_DATA_VECTOR (series->base.values[1].data));
+ len = go_data_vector_get_len
+ (GO_DATA_VECTOR (series->base.values[1].data));
+ }
+ series->base.num_elements = len;
+
+ /* queue plot for redraw */
+ gog_object_request_update (GOG_OBJECT (series->base.plot));
+ if (old_num != series->base.num_elements)
+ gog_plot_request_cardinality_update (series->base.plot);
+
+ if (gog_series1_5d_parent_klass->update)
+ gog_series1_5d_parent_klass->update (obj);
+}
+
+static void
+gog_series1_5d_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogSeries1_5d *series= GOG_SERIES1_5D (obj);
+ GogErrorBar* bar;
+
+ switch (param_id) {
+ case SERIES_PROP_ERRORS :
+ bar = g_value_get_object (value);
+ if (series->errors == bar)
+ return;
+ if (bar) {
+ bar = gog_error_bar_dup (bar);
+ bar->series = GOG_SERIES (series);
+ bar->dim_i = 1;
+ bar->error_i = 2;
+ }
+ if (!series->base.needs_recalc) {
+ series->base.needs_recalc = TRUE;
+ gog_object_emit_changed (GOG_OBJECT (series), FALSE);
+ }
+ if (series->errors != NULL)
+ g_object_unref (series->errors);
+ series->errors = bar;
+ break;
+ }
+}
+
+static void
+gog_series1_5d_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogSeries1_5d *series= GOG_SERIES1_5D (obj);
+
+ switch (param_id) {
+ case SERIES_PROP_ERRORS :
+ g_value_set_object (value, series->errors);
+ break;
+ }
+}
+
+static void
+gog_series1_5d_populate_editor (GogSeries *series,
+ GtkNotebook *book,
+ GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ GtkWidget * error_page;
+ gboolean horizontal;
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (series->plot), "horizontal") == NULL)
+ horizontal = FALSE;
+ else
+ g_object_get (G_OBJECT (series->plot), "horizontal", &horizontal, NULL);
+ error_page = gog_error_bar_prefs (series, "errors", horizontal, dalloc, cc);
+ gtk_notebook_prepend_page (book, error_page, gtk_label_new (_("Error bars")));
+}
+
+static void
+gog_series1_5d_class_init (GogObjectClass *obj_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) obj_klass;
+ GogSeriesClass *gog_series_klass = (GogSeriesClass*) obj_klass;
+
+ gog_series1_5d_parent_klass = g_type_class_peek_parent (obj_klass);
+ obj_klass->update = gog_series1_5d_update;
+ gobject_klass->set_property = gog_series1_5d_set_property;
+ gobject_klass->get_property = gog_series1_5d_get_property;
+ gog_series_klass->populate_editor = gog_series1_5d_populate_editor;
+ gog_series_klass->dim_changed = gog_series1_5d_dim_changed;
+
+ g_object_class_install_property (gobject_klass, SERIES_PROP_ERRORS,
+ g_param_spec_object ("errors", "errors",
+ "GogErrorBar *",
+ GOG_ERROR_BAR_TYPE, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+}
+
+static void
+gog_series1_5d_init (GObject *obj)
+{
+ GogSeries1_5d *series= GOG_SERIES1_5D (obj);
+
+ series->errors = NULL;
+ series->index_changed = FALSE;
+}
+
+GSF_CLASS (GogSeries1_5d, gog_series1_5d,
+ gog_series1_5d_class_init, gog_series1_5d_init,
+ GOG_SERIES_TYPE)
+
+/* Plugin initialization */
+
+void
+plugin_init (void)
+{
+ gog_plot1_5d_get_type ();
+ gog_line_plot_get_type ();
+ gog_area_plot_get_type ();
+ gog_barcol_plot_get_type ();
+}
+
+void
+plugin_cleanup (void)
+{
+}
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/gog-barcol.h
@@ -0,0 +1,47 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-barcol.h
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_BARCOL_H
+#define GOG_BARCOL_H
+
+#include "gog-1.5d.h"
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GogPlot1_5d base;
+
+ gboolean horizontal;
+ int overlap_percentage;
+ int gap_percentage;
+
+} GogBarColPlot;
+typedef GogPlot1_5dClass GogBarColPlotClass;
+
+#define GOG_BARCOL_PLOT_TYPE (gog_barcol_plot_get_type ())
+#define GOG_BARCOL_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_BARCOL_PLOT_TYPE, GogBarColPlot))
+#define GOG_IS_PLOT_BARCOL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_BARCOL_PLOT_TYPE))
+
+GType gog_barcol_plot_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_BARCOL_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/gog-line.h
@@ -0,0 +1,51 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-line.h
+ *
+ * Copyright (C) 2003-2004 Emmanuel Pacaud (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_LINE_H
+#define GOG_LINE_H
+
+#include "gog-1.5d.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GogLinePlot GogLinePlot;
+typedef GogPlot1_5dClass GogLinePlotClass;
+
+#define GOG_LINE_PLOT_TYPE (gog_line_plot_get_type ())
+#define GOG_LINE_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_LINE_PLOT_TYPE, GogLinePlot))
+#define GOG_IS_PLOT_LINE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_LINE_PLOT_TYPE))
+
+GType gog_line_plot_get_type (void);
+
+/*************************************************************************/
+
+#define GOG_AREA_PLOT_TYPE (gog_area_plot_get_type ())
+#define GOG_AREA_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_AREA_PLOT_TYPE, GogAreaPlot))
+#define GOG_IS_PLOT_AREA(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_AREA_PLOT_TYPE))
+
+typedef GogLinePlot GogAreaPlot;
+typedef GogLinePlotClass GogAreaPlotClass;
+
+GType gog_area_plot_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_LINE_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/gog-line.c
@@ -0,0 +1,528 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-line.c
+ *
+ * Copyright (C) 2003-2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-line.h"
+#include "gog-1.5d.h"
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-marker.h>
+#include <goffice/utils/go-math.h>
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+struct _GogLinePlot {
+ GogPlot1_5d base;
+ gboolean default_style_has_markers;
+};
+
+static GType gog_line_view_get_type (void);
+
+enum {
+ GOG_LINE_PROP_0,
+ GOG_LINE_PROP_DEFAULT_STYLE_HAS_MARKERS
+};
+
+typedef GogSeries1_5d GogLineSeries;
+typedef GogSeries1_5dClass GogLineSeriesClass;
+
+static GogStyledObjectClass *series_parent_klass;
+
+static void
+gog_line_series_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ GogSeries *series = GOG_SERIES (gso);
+ GogLinePlot const *plot;
+
+ series_parent_klass->init_style (gso, style);
+ if (series->plot == NULL)
+ return;
+
+ plot = GOG_LINE_PLOT (series->plot);
+ if (!plot->default_style_has_markers) {
+ style->disable_theming |= GOG_STYLE_MARKER;
+ if (style->marker.auto_shape) {
+ GOMarker *m = go_marker_new ();
+ go_marker_set_shape (m, GO_MARKER_NONE);
+ gog_style_set_marker (style, m);
+ }
+ }
+}
+
+static void
+gog_line_series_class_init (GogStyledObjectClass *gso_klass)
+{
+ series_parent_klass = g_type_class_peek_parent (gso_klass);
+ gso_klass->init_style = gog_line_series_init_style;
+}
+static GSF_CLASS (GogLineSeries, gog_line_series,
+ gog_line_series_class_init, NULL,
+ GOG_SERIES1_5D_TYPE)
+
+static char const *
+gog_line_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ /* xgettext : the base for how to name bar/col plot objects
+ * eg The 2nd line plot in a chart will be called
+ * PlotLine2
+ */
+ return N_("PlotLine");
+}
+
+static void
+gog_line_update_stacked_and_percentage (GogPlot1_5d *model,
+ double **vals, GogErrorBar **errors, unsigned const *lengths)
+{
+ unsigned i, j;
+ double abs_sum, minima, maxima, sum, tmp, errplus, errminus;
+
+ for (i = model->num_elements ; i-- > 0 ; ) {
+ abs_sum = sum = 0.;
+ minima = DBL_MAX;
+ maxima = -DBL_MAX;
+ for (j = 0 ; j < model->num_series ; j++) {
+ if (i >= lengths[j])
+ continue;
+ tmp = vals[j][i];
+ if (!go_finite (tmp))
+ continue;
+ if (gog_error_bar_is_visible (errors[j])) {
+ gog_error_bar_get_bounds (errors[j], i, &errminus, &errplus);
+ errminus = errminus > 0. ? errminus: 0.;
+ errplus = errplus > 0. ? errplus : 0.;
+ } else
+ errplus = errminus = 0.;
+ sum += tmp;
+ abs_sum += fabs (tmp);
+ if (minima > sum - errminus)
+ minima = sum - errminus;
+ if (maxima < sum + errplus)
+ maxima = sum + errplus;
+ }
+ if ((model->type == GOG_1_5D_AS_PERCENTAGE) &&
+ (go_sub_epsilon (abs_sum) > 0.)) {
+ if (model->minima > minima / abs_sum)
+ model->minima = minima / abs_sum;
+ if (model->maxima < maxima / abs_sum)
+ model->maxima = maxima / abs_sum;
+ } else {
+ if (model->minima > minima)
+ model->minima = minima;
+ if (model->maxima < maxima)
+ model->maxima = maxima;
+ }
+ }
+}
+
+static void
+gog_line_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogLinePlot *line = GOG_LINE_PLOT (obj);
+ switch (param_id) {
+ case GOG_LINE_PROP_DEFAULT_STYLE_HAS_MARKERS:
+ line->default_style_has_markers = g_value_get_boolean (value);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+static void
+gog_line_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogLinePlot const *line = GOG_LINE_PLOT (obj);
+ switch (param_id) {
+ case GOG_LINE_PROP_DEFAULT_STYLE_HAS_MARKERS:
+ g_value_set_boolean (value, line->default_style_has_markers);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_line_plot_class_init (GogPlot1_5dClass *gog_plot_1_5d_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) gog_plot_1_5d_klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) gog_plot_1_5d_klass;
+ GogPlotClass *plot_klass = (GogPlotClass *) gog_plot_1_5d_klass;
+
+ gobject_klass->set_property = gog_line_set_property;
+ gobject_klass->get_property = gog_line_get_property;
+
+ g_object_class_install_property (gobject_klass, GOG_LINE_PROP_DEFAULT_STYLE_HAS_MARKERS,
+ g_param_spec_boolean ("default-style-has-markers", NULL,
+ "Should the default style of a series include markers",
+ TRUE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ gog_klass->type_name = gog_line_plot_type_name;
+ gog_klass->view_type = gog_line_view_get_type ();
+
+ plot_klass->desc.series.style_fields = GOG_STYLE_LINE | GOG_STYLE_MARKER;
+ plot_klass->series_type = gog_line_series_get_type ();
+
+ gog_plot_1_5d_klass->update_stacked_and_percentage =
+ gog_line_update_stacked_and_percentage;
+}
+
+static void
+gog_line_plot_init (GogLinePlot *plot)
+{
+ plot->default_style_has_markers = TRUE;
+}
+
+GSF_CLASS (GogLinePlot, gog_line_plot,
+ gog_line_plot_class_init, gog_line_plot_init,
+ GOG_PLOT1_5D_TYPE)
+
+/*****************************************************************************/
+
+static char const *
+gog_area_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ /* xgettext : the base for how to name bar/col plot objects
+ * eg The 2nd line plot in a chart will be called
+ * PlotArea2
+ */
+ return N_("PlotArea");
+}
+static void
+gog_area_plot_class_init (GogObjectClass *gog_klass)
+{
+ GogPlotClass *plot_klass = (GogPlotClass *) gog_klass;
+
+ plot_klass->desc.series.style_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL;
+ plot_klass->series_type = gog_series1_5d_get_type ();
+
+ gog_klass->type_name = gog_area_plot_type_name;
+}
+GSF_CLASS (GogAreaPlot, gog_area_plot,
+ gog_area_plot_class_init, NULL,
+ GOG_LINE_PLOT_TYPE)
+
+/*****************************************************************************/
+
+typedef struct {
+ double x;
+ double y;
+ double plus;
+ double minus;
+} ErrorBarData;
+
+typedef GogPlotView GogLineView;
+typedef GogPlotViewClass GogLineViewClass;
+
+static void
+gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogPlot1_5d const *model = GOG_PLOT1_5D (view->model);
+ GogPlot1_5dType const type = model->type;
+ GogSeries1_5d const *series;
+ unsigned i, j, k;
+ unsigned num_elements = model->num_elements;
+ unsigned num_series = model->num_series;
+ GSList *ptr;
+ double plus, minus;
+
+ double **vals;
+ ErrorBarData **error_data;
+ GogStyle **styles;
+ unsigned *lengths;
+ ArtVpath **path;
+ GogErrorBar **errors;
+
+ double y_zero;
+ double abs_sum, sum, value;
+ gboolean is_null, is_area_plot;
+
+ GogAxisMap *x_map, *y_map;
+
+ is_area_plot = GOG_IS_PLOT_AREA (model);
+
+ if (num_elements <= 0 || num_series <= 0)
+ return;
+
+ x_map = gog_axis_map_new (model->base.axis[0],
+ view->allocation.x, view->allocation.w);
+ y_map = gog_axis_map_new (model->base.axis[1],
+ view->allocation.y + view->allocation.h,
+ -view->allocation.h);
+
+ if (!(gog_axis_map_is_valid (x_map) &&
+ gog_axis_map_is_valid (y_map))) {
+ gog_axis_map_free (x_map);
+ gog_axis_map_free (y_map);
+ return;
+ }
+
+ y_zero = gog_axis_map_to_canvas (y_map, .0);
+
+ vals = g_alloca (num_series * sizeof (double *));
+ error_data = g_alloca (num_series * sizeof (ErrorBarData *));
+ lengths = g_alloca (num_series * sizeof (unsigned));
+ styles = g_alloca (num_series * sizeof (GogStyle *));
+ path = g_alloca (num_series * sizeof (ArtVpath *));
+ errors = g_alloca (num_series * sizeof (GogErrorBar *));
+
+ i = 0;
+ for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
+ series = ptr->data;
+
+ if (!gog_series_is_valid (GOG_SERIES (series)))
+ continue;
+
+ vals[i] = go_data_vector_get_values (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ lengths[i] = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ styles[i] = GOG_STYLED_OBJECT (series)->style;
+
+ if (!is_area_plot)
+ path[i] = g_malloc (sizeof (ArtVpath) * (lengths[i] + 2));
+ else if (type == GOG_1_5D_NORMAL)
+ path[i] = g_malloc (sizeof (ArtVpath) * (lengths[i] + 5));
+ else
+ path[i] = g_malloc (sizeof (ArtVpath) * (2 * lengths[i] + 3));
+
+ errors[i] = series->errors;
+ if (gog_error_bar_is_visible (series->errors))
+ error_data[i] = g_malloc (sizeof (ErrorBarData) * lengths[i]);
+ else
+ error_data[i] = NULL;
+ i++;
+ }
+
+ for (j = 1; j <= num_elements; j++) {
+ sum = abs_sum = 0.0;
+ if (type == GOG_1_5D_AS_PERCENTAGE) {
+ for (i = 0; i < num_series; i++)
+ if (go_finite (vals[i][j-1]))
+ abs_sum += fabs (vals[i][j-1]);
+ is_null = (go_sub_epsilon (abs_sum) <= 0.);
+ } else
+ is_null = TRUE;
+
+ for (i = 0; i < num_series; i++) {
+ if (j > lengths[i])
+ continue;
+
+ if (vals[i] && go_finite (vals[i][j-1])) {
+ value = vals[i][j-1];
+ if (gog_error_bar_is_visible (errors[i])) {
+ gog_error_bar_get_bounds (errors[i], j - 1, &minus, &plus);
+ }
+ } else {
+ value = 0.0;
+ minus = -1.0;
+ plus = -1.;
+ }
+ k = 2 * lengths[i] - j + 1;
+
+ if (is_area_plot && (type != GOG_1_5D_NORMAL)) {
+ path[i][k].x = gog_axis_map_to_canvas (x_map, j - 1);
+ path[i][k].code = ART_LINETO;
+
+ if (type == GOG_1_5D_STACKED)
+ path[i][k].y = gog_axis_map_to_canvas (y_map, sum);
+ else
+ path[i][k].y = is_null ?
+ y_zero :
+ gog_axis_map_to_canvas (y_map, sum / abs_sum);
+ }
+
+ path[i][j].x = gog_axis_map_to_canvas (x_map, j - 1);
+ if (type == GOG_1_5D_NORMAL && !is_area_plot)
+ if (go_finite (vals[i][j-1]))
+ if (j > 1 && path[i][j-1].code == ART_MOVETO_OPEN)
+ path[i][j].code = ART_MOVETO;
+ else
+ path[i][j].code = ART_LINETO;
+ else
+ path[i][j].code = ART_MOVETO_OPEN;
+ else
+ path[i][j].code = ART_LINETO;
+
+ sum += value;
+
+ if (gog_error_bar_is_visible (errors[i]))
+ error_data[i][j-1].x = j - 1;
+
+ switch (type) {
+ case GOG_1_5D_NORMAL :
+ path[i][j].y = gog_axis_map_to_canvas (y_map, value);
+ if (gog_error_bar_is_visible (errors[i])) {
+ error_data[i][j - 1].y = value;
+ error_data[i][j - 1].minus = minus;
+ error_data[i][j - 1].plus = plus;
+ }
+ break;
+
+ case GOG_1_5D_STACKED :
+ path[i][j].y = gog_axis_map_to_canvas (y_map, sum);
+ if (gog_error_bar_is_visible (errors[i])) {
+ error_data[i][j - 1].y = sum;
+ error_data[i][j - 1].minus = minus;
+ error_data[i][j - 1].plus = plus;
+ }
+ break;
+
+ case GOG_1_5D_AS_PERCENTAGE :
+ path[i][j].y = is_null ?
+ y_zero :
+ gog_axis_map_to_canvas (y_map, sum / abs_sum);
+ if (gog_error_bar_is_visible (errors[i])) {
+ error_data[i][j - 1].y = is_null ? 0. : sum / abs_sum;
+ error_data[i][j - 1].minus = is_null ? -1. : minus / abs_sum;
+ error_data[i][j - 1].plus = is_null ? -1. : plus / abs_sum;
+ }
+ break;
+ }
+ }
+ }
+
+ gog_renderer_clip_push (view->renderer, &view->allocation);
+
+ for (i = 0; i < num_series; i++) {
+
+ if (lengths[i] == 0)
+ continue;
+
+ gog_renderer_push_style (view->renderer, styles[i]);
+
+ path[i][0].x = path[i][1].x;
+ path[i][0].y = path[i][1].y;
+ path[i][0].code = ART_MOVETO;
+
+ if (!is_area_plot) {
+ path[i][lengths[i] +1].code = ART_END;
+
+ gog_renderer_draw_path (view->renderer,
+ path[i], NULL);
+ } else {
+ switch (type) {
+ case GOG_1_5D_NORMAL :
+ j = lengths[i] + 1;
+ path[i][j].x = path[i][j-1].x;
+ path[i][j].y = y_zero;
+ path[i][j].code = ART_LINETO;
+ j++;
+ path[i][j].x = path[i][0].x;
+ path[i][j].y = y_zero;
+ path[i][j].code = ART_LINETO;
+ j++;
+ path[i][j].x = path[i][0].x;
+ path[i][j].y = path[i][0].y;
+ path[i][j].code = ART_LINETO;
+ path[i][j+1].code = ART_END;
+ break;
+
+ case GOG_1_5D_STACKED :
+ case GOG_1_5D_AS_PERCENTAGE :
+ j = 2 * lengths[i] + 1;
+ path[i][j].x = path[i][0].x;
+ path[i][j].y = path[i][0].y;
+ path[i][j].code = ART_LINETO;
+ path[i][j+1].code = ART_END;
+ break;
+ }
+ gog_renderer_draw_polygon (view->renderer,
+ path[i], FALSE, NULL);
+ }
+
+ gog_renderer_pop_style (view->renderer);
+ }
+
+ /*Now draw error bars */
+ for (i = 0; i < num_series; i++)
+ if (gog_error_bar_is_visible (errors[i]))
+ for (j = 0; j < lengths[i]; j++)
+ gog_error_bar_render (errors[i], view->renderer, x_map, y_map,
+ error_data[i][j].x, error_data[i][j].y,
+ error_data[i][j].minus, error_data[i][j].plus,
+ FALSE);
+
+ gog_renderer_clip_pop (view->renderer);
+
+ /*Now draw markers*/
+ if (!is_area_plot) {
+ double x, y;
+ double x_margin_min, x_margin_max, y_margin_min, y_margin_max, margin;
+
+ margin = gog_renderer_line_size (view->renderer, 1.0);
+ x_margin_min = view->allocation.x - margin;
+ x_margin_max = view->allocation.x + view->allocation.w + margin;
+ y_margin_min = view->allocation.y - margin;
+ y_margin_max = view->allocation.y + view->allocation.h + margin;
+
+ for (i = 0; i < num_series; i++) {
+ if (lengths[i] == 0)
+ continue;
+
+ gog_renderer_push_style (view->renderer, styles[i]);
+
+ for (j = 0; j < lengths[i]; j++) {
+ x = path[i][j + 1].x;
+ y = path[i][j + 1].y;
+ if (x_margin_min <= x && x <= x_margin_max &&
+ y_margin_min <= y && y <= y_margin_max &&
+ path[i][j + 1].code != ART_MOVETO_OPEN)
+ gog_renderer_draw_marker (view->renderer, x, y);
+ }
+ gog_renderer_pop_style (view->renderer);
+ }
+ }
+
+ for (i = 0; i < num_series; i++) {
+ g_free (path[i]);
+ g_free (error_data[i]);
+ }
+
+ gog_axis_map_free (x_map);
+ gog_axis_map_free (y_map);
+}
+
+static gboolean
+gog_line_view_info_at_point (GogView *view, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name)
+{
+ if (obj != NULL)
+ *obj = view->model;
+ if (name != NULL)
+ *name = g_strdup (gog_object_get_name (GOG_OBJECT (view->model)));
+ return TRUE;
+}
+static void
+gog_line_view_class_init (GogViewClass *view_klass)
+{
+ view_klass->render = gog_line_view_render;
+ view_klass->info_at_point = gog_line_view_info_at_point;
+}
+
+static GSF_CLASS (GogLineView, gog_line_view,
+ gog_line_view_class_init, NULL,
+ GOG_PLOT_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/plugin.xml.in
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin id="GOffice_plot_barcol">
+ <information>
+ <_name>Charting : Bar/Col/Line/Area</_name>
+ <_description>Line, Area, Bar and Column plots</_description>
+ </information>
+ <loader type="Gnumeric_Builtin:module">
+ <attribute name="module_file" value="barcol"/>
+ </loader>
+ <services>
+ <service type="plot_engine" id="GogLinePlot">
+ <information>
+ <_description>Line plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_engine" id="GogAreaPlot">
+ <information>
+ <_description>Area plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_engine" id="GogBarColPlot">
+ <information>
+ <_description>Bar/Col plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_type" id="1.5d">
+ <file>plot-types.xml</file>
+ <information>
+ <_description>Default 1.5d plot types</_description>
+ </information>
+ </service>
+ </services>
+</plugin>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/plot-types.xml.in
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Types xmlns:graph="http://www.gnumeric.org/graph_v2.dtd">
+ <Family _name="Line" sample_image_file="linegraph.xpm"/>
+ <Family _name="Area" sample_image_file="area.xpm"/>
+ <Family _name="Bar" sample_image_file="bar.xpm"/>
+ <Family _name="Column" sample_image_file="column.xpm"/>
+
+ <Type _name="Unmarked Lines" row="1" col="1"
+ engine="GogLinePlot" family="Line"
+ _description="Line plot."
+ sample_image_file="chart_line_1_1.png">
+ <property name="type">normal</property>
+ <property name="default-style-has-markers">FALSE</property>
+ </Type>
+ <Type _name="Unmarked Stacked Lines" row="1" col="2"
+ engine="GogLinePlot" family="Line"
+ _description="Stacked line plot."
+ sample_image_file="chart_line_1_2.png">
+ <property name="type">stacked</property>
+ <property name="default-style-has-markers">FALSE</property>
+ </Type>
+ <Type _name="Unmarked Percentage Lines" row="1" col="3"
+ engine="GogLinePlot" family="Line"
+ _description="Percentage line plot."
+ sample_image_file="chart_line_1_3.png">
+ <property name="type">as_percentage</property>
+ <property name="default-style-has-markers">FALSE</property>
+ </Type>
+
+ <Type _name="Lines" row="2" col="1"
+ engine="GogLinePlot" family="Line"
+ _description="Line plot."
+ sample_image_file="chart_line_2_1.png">
+ <property name="type">normal</property>
+ </Type>
+ <Type _name="Stacked Lines" row="2" col="2"
+ engine="GogLinePlot" family="Line"
+ _description="Stacked line plot."
+ sample_image_file="chart_line_2_2.png">
+ <property name="type">stacked</property>
+ </Type>
+ <Type _name="Percentage Lines" row="2" col="3"
+ engine="GogLinePlot" family="Line"
+ _description="Percentage line plot."
+ sample_image_file="chart_line_2_3.png">
+ <property name="type">as_percentage</property>
+ </Type>
+
+ <Type _name="Areas" row="1" col="1"
+ engine="GogAreaPlot" family="Area"
+ _description="Area plot."
+ sample_image_file="chart_area_1_1.png">
+ <property name="type">normal</property>
+ </Type>
+ <Type _name="Stacked Areas" row="1" col="2"
+ engine="GogAreaPlot" family="Area"
+ _description="Stacked area plot."
+ sample_image_file="chart_area_1_2.png">
+ <property name="type">stacked</property>
+ </Type>
+
+ <Type _name="Percentage Areas" row="1" col="3"
+ engine="GogAreaPlot" family="Area"
+ _description="Percentage area plot."
+ sample_image_file="chart_area_1_3.png">
+ <property name="type">as_percentage</property>
+ </Type>
+ <Type _name="Adjacent Bars" row="1" col="1"
+ engine="GogBarColPlot" family="Bar"
+ _description="Adjacent horizontal bars grouped by major and minor categories."
+ sample_image_file="chart_bar_1_1.png">
+ <property name="horizontal">True</property>
+ <property name="type">normal</property>
+ </Type>
+
+ <Type _name="Stacked Bars" row="1" col="2"
+ engine="GogBarColPlot" family="Bar"
+ _description="Minor categories stacked in horizontal bars grouped by major category."
+ sample_image_file="chart_bar_1_2.png">
+ <property name="horizontal">True</property>
+ <property name="type">stacked</property>
+ <property name="overlap_percentage">100</property>
+ </Type>
+
+ <Type _name="Percentage Bars" row="1" col="3"
+ engine="GogBarColPlot" family="Bar"
+ _description="Minor categories stacked as percentages of the minor total, in horizontal bars, grouped by major category."
+ sample_image_file="chart_bar_1_3.png">
+ <property name="horizontal">True</property>
+ <property name="type">as_percentage</property>
+ <property name="overlap_percentage">100</property>
+ </Type>
+
+ <!-- No 3d yet
+ <Type _name="3D Adjacent Bars" row="2" col="1"
+ engine="GogBarColPlot" family="Bar"
+ _description="Adjacent horizontal 3D bars grouped by major and minor categories."
+ sample_image_file="chart_bar_2_1.png">
+ <property name="horizontal">True</property>
+ <property name="type">normal</property>
+ <property name="in_3d"/>
+ </Type>
+
+ <Type _name="3D Stacked Bars" row="2" col="2"
+ engine="GogBarColPlot" family="Bar"
+ _description="Minor categories stacked in horizontal 3D bars, grouped by major category."
+ sample_image_file="chart_bar_2_2.png">
+ <property name="horizontal">True</property>
+ <property name="type">stacked</property>
+ <property name="overlap_percentage">100</property>
+ <property name="in_3d"/>
+ </Type>
+
+ <Type _name="3D Percentage Bars" row="2" col="3"
+ engine="GogBarColPlot" family="Bar"
+ _description="Minor categories stacked as percentages of the minor total, in 3D horizontal bars, grouped by major category."
+ sample_image_file="chart_bar_2_3.png">
+ <property name="horizontal">True</property>
+ <property name="type">as_percentage</property>
+ <property name="overlap_percentage">100</property>
+ <property name="in_3d"/>
+ </Type>
+ -->
+
+ <Type _name="Adjacent Columns" row="1" col="1"
+ engine="GogBarColPlot" family="Column"
+ _description="Adjacent vertical columns grouped by major and minor categories."
+ sample_image_file="chart_column_1_1.png">
+ <property name="horizontal">False</property>
+ <property name="type">normal</property>
+ </Type>
+
+ <Type _name="Stacked Columns" row="1" col="2"
+ engine="GogBarColPlot" family="Column"
+ _description="Minor categories stacked in vertical columns grouped by major category."
+ sample_image_file="chart_column_1_2.png">
+ <property name="horizontal">False</property>
+ <property name="type">stacked</property>
+ <property name="overlap_percentage">100</property>
+ </Type>
+
+ <Type _name="Percentage Columns" row="1" col="3"
+ engine="GogBarColPlot" family="Column"
+ _description="Minor categories stacked as percentages of the minor total, in vertical columns, grouped by major category."
+ sample_image_file="chart_column_1_3.png">
+ <property name="horizontal">False</property>
+ <property name="type">as_percentage</property>
+ <property name="overlap_percentage">100</property>
+ </Type>
+
+ <!-- No 3d yet
+ <Type _name="3D Adjacent Columns" row="2" col="1"
+ engine="GogBarColPlot" family="Column"
+ _description="Adjacent vertical 3D columns grouped by major and minor categories."
+ sample_image_file="chart_column_2_1.png">
+ <property name="horizontal">False</property>
+ <property name="type">normal</property>
+ <property name="in_3d"/>
+ </Type>
+
+ <Type _name="3D Stacked Columns" row="2" col="2"
+ engine="GogBarColPlot" family="Column"
+ _description="Minor categories stacked in vertical 3D columns, grouped by major category."
+ sample_image_file="chart_column_2_2.png">
+ <property name="horizontal">False</property>
+ <property name="type">stacked</property>
+ <property name="overlap_percentage">100</property>
+ <property name="in_3d"/>
+ </Type>
+
+ <Type _name="3D Percentage Columns" row="2" col="3"
+ engine="GogBarColPlot" family="Column"
+ _description="Minor categories stacked as percentages of the minor total, in 3D vertical bars, grouped by major category."
+ sample_image_file="chart_column_2_3.png">
+ <property name="horizontal">False</property>
+ <property name="type">as_percentage</property>
+ <property name="overlap_percentage">100</property>
+ <property name="in_3d"/>
+ </Type>
+ -->
+</Types>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/gog-barcol-prefs.c
@@ -0,0 +1,76 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-barcol-prefs.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-barcol.h"
+//#include <src/plugin.h>
+//#include <src/gui-util.h>
+#include <plugin.h>
+#include <gui-util.h>
+
+#include <glade/glade-xml.h>
+#include <gtk/gtkspinbutton.h>
+
+GtkWidget *gog_barcol_plot_pref (GogBarColPlot *plot, GnmCmdContext *cc);
+
+static void
+cb_gap_changed (GtkAdjustment *adj, GObject *barcal)
+{
+ g_object_set (barcal, "gap_percentage", (int)adj->value, NULL);
+}
+
+static void
+cb_overlap_changed (GtkAdjustment *adj, GObject *barcol)
+{
+ g_object_set (barcol, "overlap_percentage", (int)adj->value, NULL);
+}
+
+GtkWidget *
+gog_barcol_plot_pref (GogBarColPlot *barcol, GnmCmdContext *cc)
+{
+ GtkWidget *w;
+ char const *dir = gnm_plugin_get_dir_name (
+ plugins_get_plugin_by_id ("GOffice_plot_barcol"));
+ char *path = g_build_filename (dir, "gog-barcol-prefs.glade", NULL);
+ GladeXML *gui = gnm_glade_xml_new (cc, path, "gog_barcol_prefs", NULL);
+
+ g_free (path);
+ if (gui == NULL)
+ return NULL;
+
+ w = glade_xml_get_widget (gui, "gap_spinner");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), barcol->gap_percentage);
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_gap_changed), barcol);
+
+ w = glade_xml_get_widget (gui, "overlap_spinner");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), barcol->overlap_percentage);
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_overlap_changed), barcol);
+
+ w = glade_xml_get_widget (gui, "gog_barcol_prefs");
+ g_object_set_data_full (G_OBJECT (w),
+ "state", gui, (GDestroyNotify)g_object_unref);
+
+ return w;
+}
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/Makefile.am
@@ -0,0 +1,30 @@
+goffice_graph_barcoldir = $(gnumeric_plugindir)/plot_barcol
+xmldir = $(goffice_graph_barcoldir)
+gladedir = $(goffice_graph_barcoldir)
+
+goffice_graph_barcol_LTLIBRARIES = barcol.la
+barcol_la_LDFLAGS = -module $(GOFFICE_PLUGIN_FLAGS)
+barcol_la_SOURCES = gog-1.5d.c \
+ gog-1.5d.h \
+ gog-line.c \
+ gog-line.h \
+ gog-barcol.c \
+ gog-barcol.h \
+ gog-barcol-prefs.c
+
+xml_in_files = plugin.xml.in plot-types.xml.in
+xml_DATA = $(xml_in_files:.xml.in=.xml) gog-barcol-prefs.glade
+
+ at INTLTOOL_XML_RULE@
+
+glade_DATA = gog-barcol-prefs.glade
+
+# do not use the intl-tool stuff to merge the text back
+# its simpler to just use gettext directly
+plot-types.xml : plot-types.xml.in
+ cp $< $@
+
+EXTRA_DIST = $(xml_in_files) $(glade_DATA)
+DISTCLEANFILES = $(xml_in_files:.xml.in=.xml)
+
+include $(srcdir)/../../../goffice-plugins.mk
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/gog-barcol.c
@@ -0,0 +1,463 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-barcol.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-barcol.h"
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-math.h>
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+enum {
+ BARCOL_PROP_0,
+ BARCOL_PROP_GAP_PERCENTAGE,
+ BARCOL_PROP_OVERLAP_PERCENTAGE,
+ BARCOL_PROP_HORIZONTAL
+};
+
+static GogObjectClass *gog_barcol_parent_klass;
+
+static GType gog_barcol_view_get_type (void);
+
+static void
+gog_barcol_plot_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogBarColPlot *barcol = GOG_BARCOL_PLOT (obj);
+
+ switch (param_id) {
+ case BARCOL_PROP_GAP_PERCENTAGE:
+ barcol->gap_percentage = g_value_get_int (value);
+ break;
+
+ case BARCOL_PROP_OVERLAP_PERCENTAGE:
+ barcol->overlap_percentage = g_value_get_int (value);
+ break;
+ case BARCOL_PROP_HORIZONTAL:
+ barcol->horizontal = g_value_get_boolean (value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+ gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
+}
+
+static void
+gog_barcol_plot_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogBarColPlot *barcol = GOG_BARCOL_PLOT (obj);
+
+ switch (param_id) {
+ case BARCOL_PROP_GAP_PERCENTAGE:
+ g_value_set_int (value, barcol->gap_percentage);
+ break;
+ case BARCOL_PROP_OVERLAP_PERCENTAGE:
+ g_value_set_int (value, barcol->overlap_percentage);
+ break;
+ case BARCOL_PROP_HORIZONTAL:
+ g_value_set_boolean (value, barcol->horizontal);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static char const *
+gog_barcol_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ /* xgettext : the base for how to name bar/col plot objects
+ * eg The 2nd bar/col plot in a chart will be called
+ * PlotBarCol2 */
+ return N_("PlotBarCol");
+}
+
+extern gpointer gog_barcol_plot_pref (GogBarColPlot *barcol, GnmCmdContext *cc);
+static gpointer
+gog_barcol_plot_editor (GogObject *item,
+ G_GNUC_UNUSED GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ return gog_barcol_plot_pref (GOG_BARCOL_PLOT (item), cc);
+}
+
+static gboolean
+gog_barcol_swap_x_and_y (GogPlot1_5d *model)
+{
+ return GOG_BARCOL_PLOT (model)->horizontal;
+}
+
+static void
+gog_barcol_update_stacked_and_percentage (GogPlot1_5d *model,
+ double **vals, GogErrorBar **errors, unsigned const *lengths)
+{
+ unsigned i, j;
+ double neg_sum, pos_sum, tmp, errplus, errminus, tmpmin, tmpmax;
+
+ for (i = model->num_elements ; i-- > 0 ; ) {
+ neg_sum = pos_sum = 0.;
+ tmpmin = DBL_MAX;
+ tmpmax = -DBL_MAX;
+ for (j = 0 ; j < model->num_series ; j++) {
+ if (i >= lengths[j])
+ continue;
+ tmp = vals[j][i];
+ if (!go_finite (tmp))
+ continue;
+ if (gog_error_bar_is_visible (errors[j])) {
+ gog_error_bar_get_bounds (errors[j], i, &errminus, &errplus);
+ errminus = errminus > 0. ? errminus : 0.;
+ errplus = errplus > 0. ? errplus : 0.;
+ } else
+ errplus = errminus = 0.;
+ if (tmp > 0.) {
+ pos_sum += tmp;
+ errminus = (pos_sum - errminus < neg_sum)? neg_sum - pos_sum + errminus: 0.;
+ } else {
+ neg_sum += tmp;
+ errplus = (neg_sum + errplus > pos_sum)? neg_sum - pos_sum + errplus: 0.;
+ }
+ if (tmpmin > neg_sum - errminus)
+ tmpmin = neg_sum - errminus;
+ if (tmpmax < pos_sum + errplus)
+ tmpmax = pos_sum + errplus;
+ }
+ if (GOG_1_5D_STACKED == model->type) {
+ if (model->minima > tmpmin)
+ model->minima = tmpmin;
+ if (model->maxima < tmpmax)
+ model->maxima = tmpmax;
+ } else {
+ if (model->minima > tmpmin / (pos_sum - neg_sum))
+ model->minima = tmpmin / (pos_sum - neg_sum);
+ if (model->maxima < tmpmax / (pos_sum - neg_sum))
+ model->maxima = tmpmax / (pos_sum - neg_sum);
+ }
+ }
+}
+
+static GOData *
+gog_barcol_axis_get_bounds (GogPlot *plot, GogAxisType axis,
+ GogPlotBoundInfo *bounds)
+{
+ GogPlot1_5d *model = GOG_PLOT1_5D (plot);
+ GogPlot1_5dClass *plot1_5d_klass = GOG_PLOT1_5D_CLASS (gog_barcol_parent_klass);
+ GOData *data;
+
+ data = (plot1_5d_klass->base.axis_get_bounds) (plot, axis, bounds);
+
+ if (axis == gog_axis_get_atype (gog_plot1_5d_get_index_axis (model))) {
+ bounds->val.minima -= .5;
+ bounds->val.maxima += .5;
+ bounds->logical.minima = -.5;
+ }
+
+ return data;
+}
+
+static void
+gog_barcol_plot_class_init (GogPlot1_5dClass *gog_plot_1_5d_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) gog_plot_1_5d_klass;
+ GogObjectClass *gog_object_klass = (GogObjectClass *) gog_plot_1_5d_klass;
+ GogPlotClass *plot_klass = (GogPlotClass *) gog_plot_1_5d_klass;
+
+ gog_barcol_parent_klass = g_type_class_peek_parent (gog_plot_1_5d_klass);
+ gobject_klass->set_property = gog_barcol_plot_set_property;
+ gobject_klass->get_property = gog_barcol_plot_get_property;
+
+ g_object_class_install_property (gobject_klass, BARCOL_PROP_GAP_PERCENTAGE,
+ g_param_spec_int ("gap_percentage", "gap percentage",
+ "The padding around each group as a percentage of their width",
+ 0, 500, 150, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, BARCOL_PROP_OVERLAP_PERCENTAGE,
+ g_param_spec_int ("overlap_percentage", "overlap percentage",
+ "The distance between series as a percentage of their width",
+ -100, 100, 0, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, BARCOL_PROP_HORIZONTAL,
+ g_param_spec_boolean ("horizontal", "horizontal",
+ "horizontal bars or vertical columns",
+ FALSE,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ gog_object_klass->type_name = gog_barcol_plot_type_name;
+ gog_object_klass->editor = gog_barcol_plot_editor;
+ gog_object_klass->view_type = gog_barcol_view_get_type ();
+
+ plot_klass->desc.series.style_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL;
+ plot_klass->axis_get_bounds = gog_barcol_axis_get_bounds;
+
+ gog_plot_1_5d_klass->swap_x_and_y = gog_barcol_swap_x_and_y;
+ gog_plot_1_5d_klass->update_stacked_and_percentage =
+ gog_barcol_update_stacked_and_percentage;
+}
+
+static void
+gog_barcol_plot_init (GogBarColPlot *model)
+{
+ model->gap_percentage = 150;
+}
+
+GSF_CLASS (GogBarColPlot, gog_barcol_plot,
+ gog_barcol_plot_class_init, gog_barcol_plot_init,
+ GOG_PLOT1_5D_TYPE)
+
+/*****************************************************************************/
+typedef GogPlotView GogBarColView;
+typedef GogPlotViewClass GogBarColViewClass;
+
+/**
+ * FIXME FIXME FIXME Wrong description
+ * barcol_draw_rect :
+ * @rend : #GogRenderer
+ * @flip :
+ * @base : #GogViewAllocation
+ * @rect : #GogViewAllocation
+ *
+ * A utility routine to build a vpath in @rect. @rect is assumed to be in
+ * coordinates relative to @base with 0,0 as the upper left. @flip transposes
+ * @rect and rotates it to put the origin in the bottom left. Play fast and
+ * loose with coordinates to keep widths >= 1. If we allow things to be less
+ * the background bleeds through.
+ **/
+static void
+barcol_draw_rect (GogRenderer *rend, gboolean flip,
+ GogAxisMap *x_map,
+ GogAxisMap *y_map,
+ GogViewAllocation const *rect)
+{
+ ArtVpath path[6];
+ double x0, x1, y0, y1;
+
+ if (flip) {
+ x0 = gog_axis_map_to_canvas (x_map, rect->y);
+ x1 = gog_axis_map_to_canvas (x_map, rect->y + rect->h);
+ y0 = gog_axis_map_to_canvas (y_map, rect->x);
+ y1 = gog_axis_map_to_canvas (y_map, rect->x + rect->w);
+ } else {
+ x0 = gog_axis_map_to_canvas (x_map, rect->x);
+ x1 = gog_axis_map_to_canvas (x_map, rect->x + rect->w);
+ y0 = gog_axis_map_to_canvas (y_map, rect->y);
+ y1 = gog_axis_map_to_canvas (y_map, rect->y + rect->h);
+ }
+
+ path[0].x = path[3].x = path[4].x = x0;
+ path[1].x = path[2].x = x1;
+ path[0].y = path[1].y = path[4].y = y0;
+ path[2].y = path[3].y = y1;
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+
+ gog_renderer_draw_sharp_polygon (rend, path,
+ fabs (x1 - x0) < 3. || fabs (y1 - y0) < 3.
+ , NULL);
+}
+
+typedef struct {
+ double plus;
+ double minus;
+ double x;
+ double y;
+} ErrorBarData;
+
+static void
+gog_barcol_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogBarColPlot const *model = GOG_BARCOL_PLOT (view->model);
+ GogPlot1_5d const *gog_1_5d_model = GOG_PLOT1_5D (view->model);
+ GogSeries1_5d const *series;
+ GogViewAllocation work;
+ GogRenderer *rend = view->renderer;
+ GogAxisMap *x_map, *y_map;
+ gboolean is_vertical = ! (model->horizontal);
+ double **vals, sum, neg_base, pos_base, tmp;
+ double x;
+ double col_step, group_step, offset, data_scale;
+ unsigned i, j;
+ unsigned num_elements = gog_1_5d_model->num_elements;
+ unsigned num_series = gog_1_5d_model->num_series;
+ GogPlot1_5dType const type = gog_1_5d_model->type;
+ GogStyle **styles;
+ ErrorBarData **error_data;
+ GogErrorBar **errors;
+ GSList *ptr;
+ unsigned *lengths;
+ double plus, minus;
+
+ if (num_elements <= 0 || num_series <= 0)
+ return;
+
+ x_map = gog_axis_map_new (GOG_PLOT (model)->axis[0],
+ view->allocation.x, view->allocation.w);
+ y_map = gog_axis_map_new (GOG_PLOT (model)->axis[1], view->allocation.y + view->allocation.h,
+ -view->allocation.h);
+
+ if (!(gog_axis_map_is_valid (x_map) &&
+ gog_axis_map_is_valid (y_map))) {
+ gog_axis_map_free (x_map);
+ gog_axis_map_free (y_map);
+ return;
+ }
+
+ vals = g_alloca (num_series * sizeof (double *));
+ lengths = g_alloca (num_series * sizeof (unsigned));
+ styles = g_alloca (num_series * sizeof (GogStyle *));
+ errors = g_alloca (num_series * sizeof (GogErrorBar *));
+ error_data = g_alloca (num_series * sizeof (ErrorBarData *));
+
+ i = 0;
+ for (ptr = gog_1_5d_model->base.series ; ptr != NULL ; ptr = ptr->next) {
+ series = ptr->data;
+ if (!gog_series_is_valid (GOG_SERIES (series)))
+ continue;
+ vals[i] = go_data_vector_get_values (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ lengths[i] = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ styles[i] = GOG_STYLED_OBJECT (series)->style;
+ errors[i] = series->errors;
+ if (gog_error_bar_is_visible (series->errors))
+ error_data[i] = g_malloc (sizeof (ErrorBarData) * lengths[i]);
+ else
+ error_data[i] = NULL;
+ i++;
+ }
+
+ /* work in coordinates drawing bars from the top */
+ col_step = 1. - model->overlap_percentage / 100.;
+ group_step = model->gap_percentage / 100.;
+ work.h = 1.0 / (1. + ((num_series - 1.0) * col_step) + group_step);
+ col_step *= work.h;
+ offset = (col_step * (num_series - 1.0) + work.h) / 2.0;
+ data_scale = 1.0;
+
+ for (i = 0 ; i < num_elements ; i++) {
+ if (type == GOG_1_5D_AS_PERCENTAGE) {
+ sum = 0.;
+ for (j = num_series ; j-- > 0 ; ) {
+ if (i >= lengths[j])
+ continue;
+ tmp = vals[j][i];
+ if (!go_finite (tmp))
+ continue;
+ if (tmp > 0.)
+ sum += tmp;
+ else
+ sum -= tmp;
+ }
+
+ data_scale = (fabs (go_sub_epsilon (sum)) > 0.0) ? 1.0 / sum : 1.0;
+ }
+
+ pos_base = neg_base = 0.0;
+ for (j = 0 ; j < num_series ; j++) {
+
+ work.y = (double) j * col_step + (double) i - offset;
+
+ if (i >= lengths[j])
+ continue;
+ tmp = vals[j][i];
+ if (!go_finite (tmp))
+ continue;
+ if (gog_error_bar_is_visible (errors[j])) {
+ gog_error_bar_get_bounds (errors[j], i, &minus, &plus);
+ }
+ tmp *= data_scale;
+ if (tmp >= 0.) {
+ work.x = pos_base;
+ work.w = tmp;
+ if (GOG_1_5D_NORMAL != type)
+ pos_base += tmp;
+ } else {
+ work.x = neg_base + tmp;
+ work.w = -tmp;
+ if (GOG_1_5D_NORMAL != type)
+ neg_base += tmp;
+ }
+
+ gog_renderer_push_style (view->renderer, styles[j]);
+ barcol_draw_rect (rend, is_vertical, x_map, y_map, &work);
+ gog_renderer_pop_style (view->renderer);
+
+ if (gog_error_bar_is_visible (errors[j])) {
+ x = tmp > 0 ? work.x + work.w: work.x;
+ error_data[j][i].plus = plus * data_scale;
+ error_data[j][i].minus =minus * data_scale;
+ if (is_vertical) {
+ error_data[j][i].x = work.y + work.h / 2.0;
+ error_data[j][i].y = x;
+ } else {
+ error_data[j][i].x = x;
+ error_data[j][i].y = work.y + work.h / 2.0;
+ }
+ }
+ }
+ }
+ /*Now draw error bars and clean*/
+ for (i = 0; i < num_series; i++)
+ if (gog_error_bar_is_visible (errors[i])) {
+ for (j = 0; j < lengths[i]; j++)
+ gog_error_bar_render (errors[i], view->renderer,
+ x_map, y_map,
+ error_data[i][j].x , error_data[i][j].y,
+ error_data[i][j].minus, error_data[i][j].plus,
+ model->horizontal);
+ g_free (error_data[i]);
+ }
+
+ gog_axis_map_free (x_map);
+ gog_axis_map_free (y_map);
+}
+
+static gboolean
+gog_barcol_view_info_at_point (GogView *view, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name)
+{
+ if (obj != NULL)
+ *obj = view->model;
+ if (name != NULL)
+ *name = g_strdup (gog_object_get_name (GOG_OBJECT (view->model)));
+ return TRUE;
+}
+
+static void
+gog_barcol_view_class_init (GogViewClass *view_klass)
+{
+ view_klass->render = gog_barcol_view_render;
+ view_klass->info_at_point = gog_barcol_view_info_at_point;
+ view_klass->clip = TRUE;
+}
+
+static GSF_CLASS (GogBarColView, gog_barcol_view,
+ gog_barcol_view_class_init, NULL,
+ GOG_PLOT_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/plugins/plot_barcol/gog-1.5d.h
@@ -0,0 +1,85 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-1.5d.h
+ *
+ * Copyright (C) 2003-2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_1_5D_H
+#define GOG_1_5D_H
+
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-series-impl.h>
+#include <goffice/graph/gog-error-bar.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GOG_1_5D_NORMAL,
+ GOG_1_5D_STACKED,
+ GOG_1_5D_AS_PERCENTAGE
+} GogPlot1_5dType;
+
+typedef struct {
+ GogPlot base;
+ GogPlot1_5dType type;
+ gboolean in_3d; /* placeholder */
+
+ /* cached content */
+ unsigned num_series, num_elements;
+ double maxima, minima; /* meaning varies depending on type */
+ gboolean implicit_index;
+ GOFormat *fmt;
+} GogPlot1_5d;
+typedef struct {
+ GogPlotClass base;
+
+ gboolean (*swap_x_and_y) (GogPlot1_5d *model);
+ void (*update_stacked_and_percentage) (GogPlot1_5d *model,
+ double **vals,
+ GogErrorBar **errors,
+ unsigned const *lengths);
+} GogPlot1_5dClass;
+
+#define GOG_PLOT1_5D_TYPE (gog_plot1_5d_get_type ())
+#define GOG_PLOT1_5D(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PLOT1_5D_TYPE, GogPlot1_5d))
+#define IS_GOG_PLOT1_5D(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PLOT1_5D_TYPE))
+#define GOG_PLOT1_5D_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_PLOT1_5D_TYPE, GogPlot1_5dClass))
+#define GOG_PLOT1_5D_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_PLOT1_5D_TYPE, GogPlot1_5dClass))
+
+GType gog_plot1_5d_get_type (void);
+
+GogAxis * gog_plot1_5d_get_index_axis (GogPlot1_5d *model);
+
+/***************************************************************************/
+
+typedef struct {
+ GogSeries base;
+ GogErrorBar *errors;
+ gboolean index_changed;
+} GogSeries1_5d;
+typedef GogSeriesClass GogSeries1_5dClass;
+
+#define GOG_SERIES1_5D_TYPE (gog_series1_5d_get_type ())
+#define GOG_SERIES1_5D(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_SERIES1_5D_TYPE, GogSeries1_5d))
+#define IS_GOG_SERIES1_5D(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_SERIES1_5D_TYPE))
+
+GType gog_series1_5d_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_1_5D_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_pie/gog-pie-prefs.glade
@@ -0,0 +1,191 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<widget class="GtkWindow" id="window1">
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+
+ <child>
+ <widget class="GtkTable" id="gog_pie_prefs">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">5</property>
+ <property name="column_spacing">5</property>
+
+ <child>
+ <widget class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Slices start _at:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">rotation_spinner</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label63">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">degrees</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="vary_style_by_element">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Vary colors by slice</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label64">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Slice Separation:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">separation_spinner</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label65">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">%</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="rotation_spinner">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Degrees counter clockwise from 3 O'Clock</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">10</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">True</property>
+ <property name="wrap">True</property>
+ <property name="adjustment">0 0 360 10 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="separation_spinner">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">The default amount each slice is separated from the center measured as a percentage of the radius of the pie</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">10</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">True</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 500 10 50 50</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_pie/gog-pie.h
@@ -0,0 +1,86 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-pie.h
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_PIE_PLOT_H
+#define GOG_PIE_PLOT_H
+
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-series-impl.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GogSeriesElement base;
+
+ double separation;
+} GogPieSeriesElement;
+
+#define GOG_PIE_SERIES_ELEMENT_TYPE (gog_pie_series_element_get_type ())
+#define GOG_PIE_SERIES_ELEMENT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PIE_SERIES_ELEMENT_TYPE, GogPieSeriesElement))
+#define IS_GOG_PIE_SERIES_ELEMENT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PIE_SERIES_ELEMENT_TYPE))
+
+GType gog_pie_series_element_get_type (void);
+
+typedef struct {
+ GogPlot base;
+
+ int initial_angle; /* degrees counterclockwise from 3 o'clock */
+ float default_separation; /* as a percentage of the radius */
+ gboolean in_3d;
+} GogPiePlot;
+
+#define GOG_PIE_PLOT_TYPE (gog_pie_plot_get_type ())
+#define GOG_PIE_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PIE_PLOT_TYPE, GogPiePlot))
+#define GOG_IS_PIE_PLOT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PIE_PLOT_TYPE))
+
+GType gog_pie_plot_get_type (void);
+
+typedef struct {
+ GogPiePlot base;
+
+ float center_size;
+} GogRingPlot;
+
+#define GOG_RING_PLOT_TYPE (gog_ring_plot_get_type ())
+#define GOG_RING_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RING_PLOT_TYPE, GogRingPlot))
+#define GOG_IS_RING_PLOT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RING_PLOT_TYPE))
+
+GType gog_ring_plot_get_type (void);
+
+typedef struct {
+ GogSeries base;
+
+ float initial_angle; /* degrees counterclockwise from 3 o'clock */
+ float separation; /* as a percentage of the radius */
+
+ double total;
+ float *extensions;
+} GogPieSeries;
+
+#define GOG_PIE_SERIES_TYPE (gog_pie_series_get_type ())
+#define GOG_PIE_SERIES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PIE_SERIES_TYPE, GogPieSeries))
+#define GOG_IS_PIE_SERIES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PIE_SERIES_TYPE))
+
+GType gog_pie_series_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_PIE_SERIES_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_pie/gog-pie.c
@@ -0,0 +1,729 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-pie.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-pie.h"
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-math.h>
+
+#include <module-plugin-defs.h>
+#include <numbers.h>
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+#include <math.h>
+
+static GObjectClass *ppe_parent_klass;
+
+typedef GogSeriesElementClass GogPieSeriesElementClass;
+enum {
+ ELEMENT_PROP_0,
+ ELEMENT_SEPARATION,
+};
+
+static void
+gog_pie_series_element_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogPieSeriesElement *pse = GOG_PIE_SERIES_ELEMENT (obj);
+
+ switch (param_id) {
+ case ELEMENT_SEPARATION :
+ pse->separation = g_value_get_float (value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_pie_series_element_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogPieSeriesElement *pse = GOG_PIE_SERIES_ELEMENT (obj);
+
+ switch (param_id) {
+ case ELEMENT_SEPARATION :
+ g_value_set_float (value, pse->separation);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+extern gpointer gog_pie_series_element_pref (GogPieSeriesElement *element, GnmCmdContext *cc);
+static gpointer
+gog_pie_series_element_editor (GogObject *gobj,
+ GnmCmdContext *cc)
+{
+ return gog_pie_series_element_pref (GOG_PIE_SERIES_ELEMENT (gobj), cc);
+}
+
+static void
+gog_pie_series_element_class_init (GogPieSeriesElementClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+
+ gobject_klass->set_property = gog_pie_series_element_set_property;
+ gobject_klass->get_property = gog_pie_series_element_get_property;
+
+ ppe_parent_klass = g_type_class_peek_parent (klass);
+ klass->gse_editor = gog_pie_series_element_editor;
+
+ g_object_class_install_property (gobject_klass, ELEMENT_SEPARATION,
+ g_param_spec_float ("separation", "separation",
+ "Amount a slice is extended as a percentage of the radius",
+ 0, G_MAXFLOAT, 0.,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+}
+
+GSF_CLASS (GogPieSeriesElement, gog_pie_series_element,
+ gog_pie_series_element_class_init, NULL /*gog_pie_series_element_init*/,
+ GOG_SERIES_ELEMENT_TYPE)
+
+/*****************************************************************************/
+
+typedef struct {
+ GogPlotClass base;
+} GogPiePlotClass;
+
+enum {
+ PLOT_PROP_0,
+ PLOT_PROP_INITIAL_ANGLE,
+ PLOT_PROP_DEFAULT_SEPARATION,
+ PLOT_PROP_IN_3D
+};
+
+GNUMERIC_MODULE_PLUGIN_INFO_DECL;
+
+static GObjectClass *pie_parent_klass;
+static GType gog_pie_view_get_type (void);
+
+static void
+gog_pie_plot_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogPiePlot *pie = GOG_PIE_PLOT (obj);
+ float f;
+
+ switch (param_id) {
+ case PLOT_PROP_INITIAL_ANGLE :
+ pie->initial_angle = g_value_get_float (value);
+ break;
+ case PLOT_PROP_DEFAULT_SEPARATION :
+ f = g_value_get_float (value);
+ pie->default_separation = MIN (f, 5.);
+ break;
+ case PLOT_PROP_IN_3D :
+ pie->in_3d = g_value_get_boolean (value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ /* none of the attributes triggers a size change yet.
+ * When we add data labels we'll need it */
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_pie_plot_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogPiePlot *pie = GOG_PIE_PLOT (obj);
+
+ switch (param_id) {
+ case PLOT_PROP_INITIAL_ANGLE :
+ g_value_set_float (value, pie->initial_angle);
+ break;
+ case PLOT_PROP_DEFAULT_SEPARATION :
+ g_value_set_float (value, pie->default_separation);
+ break;
+ case PLOT_PROP_IN_3D :
+ g_value_set_boolean (value, pie->in_3d);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static char const *
+gog_pie_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ return N_("PlotPie");
+}
+
+extern gpointer gog_pie_plot_pref (GogPiePlot *pie, GnmCmdContext *cc);
+static gpointer
+gog_pie_plot_editor (GogObject *item,
+ G_GNUC_UNUSED GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ return gog_pie_plot_pref (GOG_PIE_PLOT (item), cc);
+}
+
+static void
+gog_pie_plot_update (GogObject *obj)
+{
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_pie_plot_class_init (GogPlotClass *plot_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) plot_klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) plot_klass;
+
+ pie_parent_klass = g_type_class_peek_parent (plot_klass);
+ gobject_klass->set_property = gog_pie_plot_set_property;
+ gobject_klass->get_property = gog_pie_plot_get_property;
+
+ gog_klass->update = gog_pie_plot_update;
+ gog_klass->type_name = gog_pie_plot_type_name;
+ gog_klass->editor = gog_pie_plot_editor;
+ gog_klass->view_type = gog_pie_view_get_type ();
+
+ g_object_class_install_property (gobject_klass, PLOT_PROP_INITIAL_ANGLE,
+ g_param_spec_float ("initial_angle", "initial_angle",
+ "Degrees clockwise from 12 O'Clock.",
+ 0, G_MAXFLOAT, 0.,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, PLOT_PROP_DEFAULT_SEPARATION,
+ g_param_spec_float ("default_separation", "default_separation",
+ "Default amount a slice is extended as a percentage of the radius",
+ 0, G_MAXFLOAT, 0.,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, PLOT_PROP_IN_3D,
+ g_param_spec_boolean ("in_3d", "in_3d",
+ "Draw 3d wedges",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ {
+ static GogSeriesDimDesc dimensions[] = {
+ { N_("Labels"), GOG_SERIES_SUGGESTED, TRUE,
+ GOG_DIM_LABEL, GOG_MS_DIM_CATEGORIES },
+ { N_("Values"), GOG_SERIES_REQUIRED, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_VALUES }
+ };
+ plot_klass->desc.series.dim = dimensions;
+ plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
+ plot_klass->desc.series.style_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL;
+ }
+ plot_klass->desc.num_series_min = 1;
+ plot_klass->desc.num_series_max = 1;
+ plot_klass->series_type = gog_pie_series_get_type ();
+}
+
+static void
+gog_pie_plot_init (GogPiePlot *pie)
+{
+ pie->base.vary_style_by_element = TRUE;
+}
+
+GSF_CLASS (GogPiePlot, gog_pie_plot,
+ gog_pie_plot_class_init, gog_pie_plot_init,
+ GOG_PLOT_TYPE)
+
+/*****************************************************************************/
+
+enum {
+ RING_PLOT_PROP_0,
+ RING_PLOT_PROP_CENTER_SIZE,
+};
+
+typedef GogPiePlotClass GogRingPlotClass;
+static GObjectClass *ring_parent_klass;
+
+static void
+gog_ring_plot_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogRingPlot *ring = GOG_RING_PLOT (obj);
+
+ switch (param_id) {
+ case RING_PLOT_PROP_CENTER_SIZE :
+ ring->center_size = g_value_get_float (value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ /* none of the attributes triggers a size change yet.
+ * When we add data labels we'll need it */
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_ring_plot_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogRingPlot *ring = GOG_RING_PLOT (obj);
+
+ switch (param_id) {
+ case RING_PLOT_PROP_CENTER_SIZE :
+ g_value_set_float (value, ring->center_size);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static char const *
+gog_ring_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ return N_("PlotRing");
+}
+
+extern gpointer gog_ring_plot_pref (GogRingPlot *ring, GnmCmdContext *cc);
+static gpointer
+gog_ring_plot_editor (GogObject *item,
+ G_GNUC_UNUSED GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ return gog_ring_plot_pref (GOG_RING_PLOT (item), cc);
+}
+
+static void
+gog_ring_plot_class_init (GogPiePlotClass *pie_plot_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) pie_plot_klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) pie_plot_klass;
+ GogPlotClass *plot_klass = (GogPlotClass *) pie_plot_klass;
+
+ ring_parent_klass = g_type_class_peek_parent (pie_plot_klass);
+ gobject_klass->set_property = gog_ring_plot_set_property;
+ gobject_klass->get_property = gog_ring_plot_get_property;
+
+ gog_klass->type_name = gog_ring_plot_type_name;
+ gog_klass->editor = gog_ring_plot_editor;
+
+ g_object_class_install_property (gobject_klass, RING_PLOT_PROP_CENTER_SIZE,
+ g_param_spec_float ("center_size", "center_size",
+ "Size of the center hole as a percentage of the radius",
+ 0, 100., 0.,
+ G_PARAM_READWRITE));
+
+ plot_klass->desc.num_series_min = 1;
+ plot_klass->desc.num_series_max = G_MAXINT;
+ plot_klass->desc.series.style_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL;
+}
+
+static void
+gog_ring_plot_init (GogRingPlot *ring)
+{
+ ring->center_size = 0.5;
+}
+
+GSF_CLASS (GogRingPlot, gog_ring_plot,
+ gog_ring_plot_class_init, gog_ring_plot_init,
+ GOG_PIE_PLOT_TYPE)
+
+/*****************************************************************************/
+typedef GogPlotView GogPieView;
+typedef GogPlotViewClass GogPieViewClass;
+
+#define MAX_ARC_SEGMENTS 64
+
+static void
+gog_pie_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogPiePlot const *model = GOG_PIE_PLOT (view->model);
+ GogPieSeries const *series = NULL;
+ double separated_cx, separated_cy, cx, cy, r, dt, tmp, theta, len, *vals, scale;
+ double default_sep;
+ unsigned elem, j, n, k;
+ ArtVpath path [MAX_ARC_SEGMENTS*2 + 4];
+ GogTheme *theme = gog_object_get_theme (GOG_OBJECT (model));
+ GogStyle *style;
+ GSList *ptr;
+ unsigned num_series = 0;
+ unsigned index, mirror = 0; /* init mirror because gcc is silly */
+ double center_radius;
+ double center_size = 0.0;
+ double r_ext, r_int, r_tot;
+ double outline_width_max;
+ gboolean has_hole;
+ double separation_max, separation;
+ GogPieSeriesElement *gpse;
+ GList const *overrides;
+
+ /* compute number of valid series */
+ for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
+ series = ptr->data;
+ if (!gog_series_is_valid (GOG_SERIES (ptr->data)))
+ continue;
+ num_series++;
+ }
+
+ if (num_series <= 0)
+ return;
+
+ separation_max = .0;
+ outline_width_max = .0;
+ if ((style = gog_styled_object_get_style (GOG_STYLED_OBJECT (series))))
+ outline_width_max = gog_renderer_line_size (view->renderer, style->outline.width);
+ for (overrides = gog_series_get_overrides (GOG_SERIES (series));
+ overrides != NULL;
+ overrides = overrides->next) {
+ separation = GOG_PIE_SERIES_ELEMENT (overrides->data)->separation;
+ if (separation_max < separation)
+ separation_max = separation;
+ style = gog_styled_object_get_style (GOG_STYLED_OBJECT (overrides->data));
+ if (outline_width_max < style->outline.width)
+ outline_width_max = style->outline.width;
+ }
+ if (separation_max < -model->default_separation)
+ separation_max = -model->default_separation;
+
+ if (GOG_IS_RING_PLOT (model))
+ center_size = GOG_RING_PLOT(model)->center_size;
+ else if (num_series > 1)
+ num_series = 1;
+
+ /* centre things */
+ cx = view->allocation.x + view->allocation.w/2.;
+ cy = view->allocation.y + view->allocation.h/2.;
+
+ r_tot = view->allocation.h;
+ if (r_tot > view->allocation.w)
+ r_tot = view->allocation.w;
+ r_tot /= 2. * (1. + model->default_separation + separation_max);
+ default_sep = r_tot * model->default_separation;
+ center_radius = r_tot * center_size;
+ r = r_tot * (1. - center_size);
+
+ elem = model->base.index_num;
+ index = 1;
+ for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
+ series = ptr->data;
+
+ if (!gog_series_is_valid (GOG_SERIES (series)))
+ continue;
+ if (index > num_series) /* people snuck extra series into a pie */
+ break;
+
+ if (num_series == index)
+ r -= outline_width_max / 2.0;
+
+ has_hole = center_radius > 0. || index > 1;
+ r_int = center_radius + r * ((double)index - 1.0) / (double)num_series;
+ r_ext = center_radius + r * (double)index / (double)num_series;
+
+ theta = (model->initial_angle + series->initial_angle) * 2. * M_PI / 360. - M_PI / 2.;
+
+ scale = 2 * M_PI / series->total;
+ vals = go_data_vector_get_values (GO_DATA_VECTOR (series->base.values[1].data));
+
+ style = GOG_STYLED_OBJECT (series)->style;
+ if (model->base.vary_style_by_element)
+ style = gog_style_dup (style);
+ gog_renderer_push_style (view->renderer, style);
+
+ overrides = gog_series_get_overrides (GOG_SERIES (series));
+ for (k = 0 ; k < series->base.num_elements; k++) {
+ len = fabs (vals[k]) * scale;
+ if (!go_finite (len) || len < 1e-3)
+ continue;
+
+ gpse = NULL;
+ if ((overrides != NULL) &&
+ (GOG_SERIES_ELEMENT (overrides->data)->index == k)) {
+ gpse = GOG_PIE_SERIES_ELEMENT (overrides->data);
+ overrides = overrides->next;
+ gog_renderer_push_style (view->renderer,
+ gog_styled_object_get_style (
+ GOG_STYLED_OBJECT (gpse)));
+ } else if (model->base.vary_style_by_element)
+ gog_theme_fillin_style (theme, style, GOG_OBJECT (series),
+ model->base.index_num + k, FALSE);
+
+ /* only separate the outer ring */
+ separated_cx = cx;
+ separated_cy = cy;
+ if (num_series == index && (default_sep > 0. || gpse != NULL)) {
+
+ separation = default_sep;
+
+ if (gpse != NULL)
+ separation += gpse->separation * r_tot;
+
+ separated_cx += separation * cos (theta + len/2.);
+ separated_cy += separation * sin (theta + len/2.);
+ }
+ theta += len;
+
+ n = MAX_ARC_SEGMENTS * len / (2 * M_PI);
+ if (n < 6)
+ n = 6;
+ else if (n > MAX_ARC_SEGMENTS)
+ n = MAX_ARC_SEGMENTS;
+
+ dt = (double)-len / (double)n;
+ path[0].code = ART_MOVETO;
+ path[0].x = separated_cx;
+ path[0].y = separated_cy;
+ if (has_hole) {
+ path[0].x += r_int * cos (theta);
+ path[0].y += r_int * sin (theta);
+ mirror = 2*n + 3;
+ path[mirror].code = ART_END;
+ } else {
+ path[n+2].code = ART_LINETO;
+ path[n+2].x = separated_cx;
+ path[n+2].y = separated_cy;
+ path[n+3].code = ART_END;
+ }
+ for (tmp = theta, j = 0; j++ <= n ; tmp += dt) {
+ path[j].code = ART_LINETO;
+ path[j].x = separated_cx + r_ext * cos (tmp);
+ path[j].y = separated_cy + r_ext * sin (tmp);
+ if (has_hole) {
+ path[mirror - j].code = ART_LINETO;
+ path[mirror - j].x = separated_cx + r_int * cos (tmp);
+ path[mirror - j].y = separated_cy + r_int * sin (tmp);
+ }
+ }
+
+ gog_renderer_draw_polygon (view->renderer, path,
+ r * len < 5 /* drop outline for thin segments */, NULL);
+
+ if (gpse != NULL)
+ gog_renderer_pop_style (view->renderer);
+ }
+
+ gog_renderer_pop_style (view->renderer);
+ if (model->base.vary_style_by_element)
+ g_object_unref (style);
+
+ index ++;
+ }
+}
+
+static gboolean
+gog_pie_view_info_at_point (GogView *view, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name)
+{
+ GogPiePlot const *model = GOG_PIE_PLOT (view->model);
+ GogPieSeries const *series = NULL;
+ double *vals, scale, len = 0, theta, r = view->allocation.h;
+ GSList *ptr;
+ unsigned i;
+
+ if (r > view->allocation.w)
+ r = view->allocation.w;
+ r /= 2.;
+ x -= view->allocation.x + view->allocation.w/2.;
+ y -= view->allocation.y + view->allocation.h/2.;
+
+ if ((x*x + y*y) > (r*r))
+ return FALSE;
+
+ for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next)
+ if (gog_series_is_valid (GOG_SERIES (series = ptr->data)))
+ break;
+ if (ptr == NULL)
+ return FALSE;
+
+ /* TODO what follows does not work for ring plots, so exit here */
+ if (GOG_IS_RING_PLOT (view->model)) {
+ if (obj != NULL)
+ *obj = view->model;
+ if (name != NULL)
+ *name = NULL;
+ return TRUE;
+ }
+
+ theta = (atan2 (y, x) * 180 / M_PI - model->initial_angle + 90.) / 360.;
+ if (theta < 0)
+ theta += 1.;
+
+ vals = go_data_vector_get_values (GO_DATA_VECTOR (series->base.values[1].data));
+ scale = 1 / series->total;
+ for (i = 0 ; i < series->base.num_elements; i++) {
+ len = fabs (vals[i]) * scale;
+ if (go_finite (len) && len > 1e-3) {
+ theta -= len;
+ if (theta < 0)
+ break;
+ }
+ }
+
+ if (obj != NULL) {
+ if (cur_selection == view->model) {
+ *obj = GOG_OBJECT (gog_series_get_element (GOG_SERIES (series), i));
+ if (*obj == NULL) {
+ *obj = g_object_new (gog_pie_series_element_get_type (),
+ "index", i, NULL);
+ gog_object_add_by_name (GOG_OBJECT (series), "Point", *obj);
+ }
+ } else
+ *obj = view->model;
+ }
+ if (name != NULL)
+ *name = g_strdup_printf (_("%s point %d\nValue %g (%g)"),
+ gog_object_get_name (GOG_OBJECT (series)),
+ i+1, vals[i], len);
+
+ return TRUE;
+}
+
+static void
+gog_pie_view_class_init (GogViewClass *view_klass)
+{
+ view_klass->render = gog_pie_view_render;
+ view_klass->info_at_point = gog_pie_view_info_at_point;
+}
+
+static GSF_CLASS (GogPieView, gog_pie_view,
+ gog_pie_view_class_init, NULL,
+ GOG_PLOT_VIEW_TYPE)
+
+/*****************************************************************************/
+
+typedef GogSeriesClass GogPieSeriesClass;
+enum {
+ SERIES_PROP_0,
+ SERIES_PROP_INITIAL_ANGLE,
+ SERIES_PROP_SEPARATION,
+};
+
+static GogObjectClass *series_parent_klass;
+static void
+gog_pie_series_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogPieSeries *pie = GOG_PIE_SERIES (obj);
+
+ switch (param_id) {
+ case SERIES_PROP_INITIAL_ANGLE :
+ pie->initial_angle = g_value_get_float (value);
+ break;
+ case SERIES_PROP_SEPARATION :
+ pie->separation = g_value_get_float (value);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+ /* none of the attributes triggers a size change yet.
+ * When we add data labels we'll need it */
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_pie_series_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogPieSeries *pie = GOG_PIE_SERIES (obj);
+
+ switch (param_id) {
+ case SERIES_PROP_INITIAL_ANGLE :
+ g_value_set_float (value, pie->initial_angle);
+ break;
+ case SERIES_PROP_SEPARATION :
+ g_value_set_float (value, pie->separation);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_pie_series_update (GogObject *obj)
+{
+ double *vals = NULL, total;
+ int len = 0;
+ GogPieSeries *series = GOG_PIE_SERIES (obj);
+ unsigned old_num = series->base.num_elements;
+
+ if (series->base.values[1].data != NULL) {
+ vals = go_data_vector_get_values (GO_DATA_VECTOR (series->base.values[1].data));
+ len = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ }
+ series->base.num_elements = len;
+
+ for (total = 0. ; len-- > 0 ;)
+ if (go_finite (vals[len]))
+ total += fabs (vals[len]);
+ series->total = total;
+
+ /* queue plot for redraw */
+ gog_object_request_update (GOG_OBJECT (series->base.plot));
+ if (old_num != series->base.num_elements)
+ gog_plot_request_cardinality_update (series->base.plot);
+
+ if (series_parent_klass->update)
+ series_parent_klass->update (obj);
+}
+
+static void
+gog_pie_series_class_init (GObjectClass *gobject_klass)
+{
+ GogObjectClass *gog_klass = (GogObjectClass *)gobject_klass;
+ GogSeriesClass *series_klass = (GogSeriesClass *)gobject_klass;
+
+ series_parent_klass = g_type_class_peek_parent (gobject_klass);
+ gog_klass->update = gog_pie_series_update;
+ series_klass->series_element_type = GOG_PIE_SERIES_ELEMENT_TYPE;
+
+ gobject_klass->set_property = gog_pie_series_set_property;
+ gobject_klass->get_property = gog_pie_series_get_property;
+
+ g_object_class_install_property (gobject_klass, SERIES_PROP_INITIAL_ANGLE,
+ g_param_spec_float ("initial_angle", "initial_angle",
+ "Degrees clockwise from 12 O'Clock.",
+ 0, G_MAXFLOAT, 0.,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, SERIES_PROP_SEPARATION,
+ g_param_spec_float ("separation", "separation",
+ "Default amount a slice is extended as a percentage of the radius",
+ 0, G_MAXFLOAT, 0.,
+ G_PARAM_READWRITE));
+}
+
+GSF_CLASS (GogPieSeries, gog_pie_series,
+ gog_pie_series_class_init, NULL,
+ GOG_SERIES_TYPE)
+
+void
+plugin_init (void)
+{
+ gog_pie_series_element_get_type ();
+ gog_pie_plot_get_type ();
+ gog_ring_plot_get_type ();
+}
+
+void
+plugin_cleanup (void)
+{
+}
--- /dev/null
+++ lib/goffice/graph/plugins/plot_pie/gog-pie-prefs.c
@@ -0,0 +1,215 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-pie-prefs.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-pie.h"
+#include <plugin.h>
+#include <gui-util.h>
+
+#include <glade/glade-xml.h>
+#include <gtk/gtkspinbutton.h>
+#include <gtk/gtktogglebutton.h>
+
+GtkWidget *gog_pie_series_element_pref (GogPieSeriesElement *element, GnmCmdContext *cc);
+
+static void
+cb_element_separation_changed (GtkAdjustment *adj, GObject *element)
+{
+ g_object_set (element, "separation", adj->value / 100., NULL);
+}
+
+GtkWidget *
+gog_pie_series_element_pref (GogPieSeriesElement *element, GnmCmdContext *cc)
+{
+ GtkWidget *w;
+ char const *dir = gnm_plugin_get_dir_name (
+ plugins_get_plugin_by_id ("GOffice_plot_pie"));
+ char *path = g_build_filename (dir, "gog-pie-series.glade", NULL);
+ GladeXML *gui = gnm_glade_xml_new (cc, path, "gog_pie_series_element_prefs", NULL);
+
+ g_free (path);
+ if (gui == NULL)
+ return NULL;
+
+ w = glade_xml_get_widget (gui, "separation_spinner");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), element->separation * 100.);
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_element_separation_changed), element);
+
+ w = glade_xml_get_widget (gui, "gog_pie_series_element_prefs");
+ g_object_set_data_full (G_OBJECT (w),
+ "state", gui, (GDestroyNotify)g_object_unref);
+
+ return w;
+}
+
+/****************************************************************************/
+GtkWidget *gog_pie_plot_pref (GogPiePlot *plot, GnmCmdContext *cc);
+
+static void
+cb_default_separation_changed (GtkAdjustment *adj, GObject *pie)
+{
+ g_object_set (pie, "default_separation", adj->value / 100., NULL);
+}
+
+static void
+cb_rotation_changed (GtkAdjustment *adj, GObject *pie)
+{
+ g_object_set (pie, "initial_angle", adj->value, NULL);
+}
+
+
+static void
+cb_use_style_toggled (GtkToggleButton *button, GObject *series)
+{
+ g_object_set (series, "vary_style_by_element",
+ gtk_toggle_button_get_active (button), NULL);
+}
+
+static void
+gog_pie_plot_pref_signal_connect (GogPiePlot *pie, GladeXML *gui)
+{
+ GtkWidget *w;
+
+ w = glade_xml_get_widget (gui, "rotation_spinner");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), pie->initial_angle);
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_rotation_changed), pie);
+
+ w = glade_xml_get_widget (gui, "separation_spinner");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), pie->default_separation * 100.);
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_default_separation_changed), pie);
+
+ w = glade_xml_get_widget (gui, "vary_style_by_element");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), pie->base.vary_style_by_element);
+ g_signal_connect (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_use_style_toggled), pie);
+}
+
+GtkWidget *
+gog_pie_plot_pref (GogPiePlot *pie, GnmCmdContext *cc)
+{
+ GtkWidget *w;
+ char const *dir = gnm_plugin_get_dir_name (
+ plugins_get_plugin_by_id ("GOffice_plot_pie"));
+ char *path = g_build_filename (dir, "gog-pie-prefs.glade", NULL);
+ GladeXML *gui = gnm_glade_xml_new (cc, path, "gog_pie_prefs", NULL);
+
+ g_free (path);
+ if (gui == NULL)
+ return NULL;
+
+ gog_pie_plot_pref_signal_connect (pie, gui);
+
+ w = glade_xml_get_widget (gui, "gog_pie_prefs");
+ g_object_set_data_full (G_OBJECT (w),
+ "state", gui, (GDestroyNotify)g_object_unref);
+
+ return w;
+}
+
+/****************************************************************************/
+
+GtkWidget *gog_ring_plot_pref (GogRingPlot *ring, GnmCmdContext *cc);
+
+static void
+cb_center_size_changed (GtkAdjustment *adj, GObject *ring)
+{
+ g_object_set (ring, "center_size", adj->value/100., NULL);
+}
+
+
+GtkWidget *
+gog_ring_plot_pref (GogRingPlot *ring, GnmCmdContext *cc)
+{
+ GtkWidget *w;
+ char const *dir = gnm_plugin_get_dir_name (
+ plugins_get_plugin_by_id ("GOffice_plot_pie"));
+ char *path = g_build_filename (dir, "gog-ring-prefs.glade", NULL);
+ GladeXML *gui = gnm_glade_xml_new (cc, path, "gog_ring_prefs", NULL);
+
+ g_free (path);
+ if (gui == NULL)
+ return NULL;
+
+ gog_pie_plot_pref_signal_connect (GOG_PIE_PLOT (ring), gui);
+
+ w = glade_xml_get_widget (gui, "center_size_spinner");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), ring->center_size * 100);
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_center_size_changed), ring);
+
+ w = glade_xml_get_widget (gui, "gog_ring_prefs");
+ g_object_set_data_full (G_OBJECT (w),
+ "state", gui, (GDestroyNotify)g_object_unref);
+
+ return w;
+}
+
+/****************************************************************************/
+
+GtkWidget *gog_pie_series_pref (GogPieSeries *series, GnmCmdContext *cc);
+
+static void
+cb_separation_changed (GtkAdjustment *adj, GObject *pie)
+{
+ g_object_set (pie, "separation", adj->value, NULL);
+}
+
+GtkWidget *
+gog_pie_series_pref (GogPieSeries *pie, GnmCmdContext *cc)
+{
+ GtkWidget *w;
+ char const *dir = gnm_plugin_get_dir_name (
+ plugins_get_plugin_by_id ("GOffice_plot_pie"));
+ char *path = g_build_filename (dir, "gog-pie-prefs.glade", NULL);
+ GladeXML *gui = gnm_glade_xml_new (cc, path, "gog_pie_prefs", NULL);
+
+ g_free (path);
+ if (gui == NULL)
+ return NULL;
+
+ w = glade_xml_get_widget (gui, "rotation_spinner");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), pie->initial_angle);
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_rotation_changed), pie);
+
+ w = glade_xml_get_widget (gui, "separation_spinner");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), pie->separation);
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_separation_changed), pie);
+
+ gtk_widget_hide (glade_xml_get_widget (gui, "vary_style_by_element"));
+
+ w = glade_xml_get_widget (gui, "gog_pie_prefs");
+ g_object_set_data_full (G_OBJECT (w),
+ "state", gui, (GDestroyNotify)g_object_unref);
+
+ return w;
+}
--- /dev/null
+++ lib/goffice/graph/plugins/plot_pie/plugin.xml.in
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin id="GOffice_plot_pie">
+ <information>
+ <_name>Charting : Pie/Ring</_name>
+ <_description>Pie and Ring plots</_description>
+ </information>
+ <loader type="Gnumeric_Builtin:module">
+ <attribute name="module_file" value="pie"/>
+ </loader>
+ <services>
+ <service type="plot_engine" id="GogRingPlot">
+ <information>
+ <_description>Ring plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_engine" id="GogPiePlot">
+ <information>
+ <_description>Pie plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_type" id="pie">
+ <file>plot-types.xml</file>
+ <information>
+ <_description>Default pie types</_description>
+ </information>
+ </service>
+ </services>
+</plugin>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_pie/gog-ring-prefs.glade
@@ -0,0 +1,271 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+
+<widget class="GtkWindow" id="window1">
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkTable" id="gog_ring_prefs">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label61">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Slices start _at:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">rotation_spinner</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label63">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">degrees</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="vary_style_by_element">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Vary colors by slice</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label64">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Slice Separation:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">separation_spinner</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label65">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">%</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="rotation_spinner">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">Degrees counter clockwise from 3 O'Clock</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">10</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">True</property>
+ <property name="wrap">True</property>
+ <property name="adjustment">0 0 360 10 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="separation_spinner">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">The default amount each slice is separated from the center measured as a percentage of the radius of the pie</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">10</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">True</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 500 10 50 50</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label66">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Cen_ter size:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">center_size_spinner</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label67">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">%</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="center_size_spinner">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">The default amount each slice is separated from the center measured as a percentage of the radius of the pie</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">True</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 95 5 0.2 0.2</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_pie/plot-types.xml.in
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Types xmlns:graph="http://www.gnumeric.org/graph_v2.dtd">
+ <Family _name="Pie" sample_image_file="pie.xpm"/>
+ <Family _name="Ring" sample_image_file="doughnut.xpm"/>
+
+ <Type _name="Pie" row="1" col="1"
+ engine="GogPiePlot" family="Pie"
+ _description="Percentage of each contributor."
+ sample_image_file="chart_pie_1_1.png">
+ </Type>
+ <Type _name="Split Pie" row="1" col="2"
+ engine="GogPiePlot" family="Pie"
+ _description="Percentage of each contributor with wedges split apart."
+ sample_image_file="chart_pie_2_1.png">
+ <property name="default_separation">.20</property>
+ </Type>
+ <Type _name="Ring" row="1" col="1"
+ engine="GogRingPlot" family="Ring"
+ _description="Percentage of each contributor displayed in ring for each serie."
+ sample_image_file="chart_ring_1_1.png">
+ </Type>
+ <Type _name="Split Ring" row="1" col="2"
+ engine="GogRingPlot" family="Ring"
+ _description="Percentage of each contributor displayed in ring for each serie with wedges of the last ring split apart."
+ sample_image_file="chart_ring_1_2.png">
+ <property name="default_separation">.20</property>
+ </Type>
+ <!--
+ <Type _name="3D Pie" row="1" col="2"
+ engine="GogPiePlot" family="Pie"
+ _description="Percentage of each contributor in 3D pie."
+ sample_image_file="chart_pie_1_2.png">
+ <property name="in_3d"/>
+ </Type>
+ <Type _name="Multi-Pie" row="1" col="3"
+ engine="GogPiePlot" family="Pie"
+ _description="Major totals as percentages with each wedge subdivided into secondary pies."
+ sample_image_file="chart_pie_1_3.png">
+ <property name="in_3d"/>
+ </Type>
+ -->
+ <!--
+ <Type _name="3D Split Pie" row="2" col="2"
+ engine="GogPiePlot" family="Pie"
+ _description="Percentage of each contributor with 3D wedges split apart."
+ sample_image_file="chart_pie_2_2.png">
+ <property name="default_separation">.20</property>
+ <property name="in_3d"/>
+ </Type>
+ <Type _name="Multi-pie-bars" row="2" col="3"
+ engine="GogPiePlot" family="Pie"
+ _description="Percentage of each contributor with 3D wedges split apart."
+ _description="Major totals as percentages with each wedge subdivided into secondary stacked bars."
+ sample_image_file="chart_pie_2_3.png">
+ <property name="default_separation">.20</property>
+ <property name="in_3d"/>
+ </Type>
+ -->
+</Types>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_pie/Makefile.am
@@ -0,0 +1,30 @@
+goffice_graph_piedir = $(gnumeric_plugindir)/plot_pie
+xmldir = $(goffice_graph_piedir)
+gladedir = $(goffice_graph_piedir)
+
+goffice_graph_pie_LTLIBRARIES = pie.la
+pie_la_LDFLAGS = -module $(GOFFICE_PLUGIN_FLAGS)
+pie_la_SOURCES = \
+ gog-pie.c \
+ gog-pie.h \
+ gog-pie-prefs.c
+
+xml_in_files = plugin.xml.in plot-types.xml.in
+xml_DATA = $(xml_in_files:.xml.in=.xml) gog-pie-prefs.glade gog-ring-prefs.glade gog-pie-series.glade
+
+ at INTLTOOL_XML_RULE@
+
+glade_DATA = gog-pie-prefs.glade gog-ring-prefs.glade gog-pie-series.glade
+
+# do not use the intl-tool stuff to merge the text back
+# its simpler to just use gettext directly
+plot-types.xml : plot-types.xml.in
+ cp $< $@
+
+EXTRA_DIST = $(xml_in_files) $(glade_DATA)
+DISTCLEANFILES = $(xml_in_files:.xml.in=.xml)
+
+# AM_CFLAGS = ${GLIB_CFLAGS} ${XML_CFLAGS} ${GSF_CFLAGS} ${ART_CFLAGS} ${GNOME_CFLAGS} ${GDK_PIXBUF_CLFAGS} ${GLADE_CFLAGS}
+
+include $(srcdir)/../../../goffice-plugins.mk
+
--- /dev/null
+++ lib/goffice/graph/plugins/plot_pie/gog-pie-series.glade
@@ -0,0 +1,104 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+
+<widget class="GtkWindow" id="window1">
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkTable" id="gog_pie_series_element_prefs">
+ <property name="visible">True</property>
+ <property name="n_rows">1</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="label64">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Separation:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">separation_spinner</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label65">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">%</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="separation_spinner">
+ <property name="visible">True</property>
+ <property name="tooltip" translatable="yes">The amount this slice is separated from the center measured as a percentage of the radius of the pie</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">10</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">True</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 500 10 50 50</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
Index: configure.in
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/configure.in,v
retrieving revision 1.359.2.44
retrieving revision 1.359.2.45
diff -Lconfigure.in -Lconfigure.in -u -r1.359.2.44 -r1.359.2.45
--- configure.in
+++ configure.in
@@ -88,6 +88,13 @@
AC_CHECK_HEADERS(limits.h)
AC_CHECK_FUNCS(stpcpy memcpy timegm towupper setenv putenv)
+## +jsled
+# copied from gnumeric
+AC_CHECK_HEADERS(ieeefp.h ieee754.h)
+dnl Check for some functions
+AC_CHECK_FUNCS(random drand48 finite memmove mkdtemp uname times sysconf)
+## --jsled
+
STRUCT_TM_GMTOFF_CHECK
SCANF_LLD_CHECK
if test $am_cv_scanf_lld = "no"; then
@@ -354,6 +361,48 @@
### --------------------------------------------------------------------------
+### for GOG/goffice...
+###
+### GSF
+PKG_CHECK_MODULES(GSF, libgsf-1 >= 1.8.0
+ libgsf-gnome-1 >= 1.8.0)
+AC_SUBST(GSF_LIBS)
+AC_SUBST(GSF_CFLAGS)
+
+### libart
+PKG_CHECK_MODULES(ART, libart-2.0 >= 2.3.11)
+AC_SUBST(ART_LIBS)
+AC_SUBST(ART_CFLAGS)
+
+### silence convention-difference from cut-n-paste'd gnumeric Makefile.am's:
+AM_CONDITIONAL(WITH_GNOME, true)
+# for lib/goffice/graph/plugins/plot_barcol/gog-1.5d.c's use of
+# goffice/graph/gog-error-bar.h:gog_error_bar_prefs, which is bounded by
+# WITH_GTK for some reason... :/
+AM_CONDITIONAL(WITH_GTK, true)
+AC_DEFINE(WITH_GTK, 1, [Define if UI is built])
+
+### gnome-print [= gnome-font]
+PKG_CHECK_MODULES(PRINT, libgnomeprint-2.2 >= 2.5.2)
+AC_SUBST(PRINT_LIBS)
+AC_SUBST(PRINT_CFLAGS)
+
+AC_ARG_VAR(GLIB_GENMARSHAL, [Absolute path of the glib-genmarshal executable.])
+AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
+
+gog_plugindir='${GNC_LIBDIR}/plugins'
+# do this to minimize changes in the plugin makefiles...
+gnumeric_plugindir='${gog_plugindir}'
+AC_SUBST(gog_plugindir)
+AC_SUBST(gnumeric_plugindir)
+#AC_SUBST(pkglibdir) -- this breaks builds into /opt.
+gnumeric_icondir='${GNC_SHAREDIR}/pixmaps'
+AC_SUBST(gnumeric_icondir)
+
+
+# /GOG
+
+### --------------------------------------------------------------------------
### Variables
### Set up all the initial variable values...
@@ -876,7 +925,7 @@
AS_SCRUB_INCLUDE(GNOME_PRINT_CFLAGS)
AC_SUBST(GNOME_PRINT_CFLAGS)
AC_SUBST(GNOME_PRINT_LIBS)
-
+
# There is no libguppi. Just substitute empty stuff
LIBGUPPI_CFLAGS=
LIBGUPPI_LIBS=
@@ -891,7 +940,6 @@
AC_SUBST(GLADE_CFLAGS)
AC_SUBST(GLADE_LIBS)
-
# check for gtkhtml 3.x
gtkhtml=0
PKG_CHECK_MODULES(GTKHTML, libgtkhtml-3.6 , [gtkhtml=1] , [gtkhtml=0])
@@ -1039,16 +1087,19 @@
# us to keep it turned on, but also not break configure proceses.
# -- warlord, 02/02/2003
+
# Enable error-on-warning by default -- I'm tired of fixing other
# people's missing #includes, etc.
- AC_ARG_ENABLE(error-on-warning,
- [ --disable-error-on-warning disable treating compile warnings as errors],
- [case "${enableval}" in
- yes) warnFLAGS="${warnFLAGS} -Werror" ;;
- no) ;;
- *) AC_MSG_ERROR(bad value ${enableval} for --enable-error-on-warning) ;;
- esac],
- [ warnFLAGS="${warnFLAGS} -Werror" ])
+# Re-disabled for the moment since goffice/graph has a lot of specific/manual #warnings-as-TODOs.
+# 2004.12.30 -- jsled
+# AC_ARG_ENABLE(error-on-warning,
+# [ --disable-error-on-warning disable treating compile warnings as errors],
+# [case "${enableval}" in
+# yes) warnFLAGS="${warnFLAGS} -Werror" ;;
+# no) ;;
+# *) AC_MSG_ERROR(bad value ${enableval} for --enable-error-on-warning) ;;
+# esac],
+# [ warnFLAGS="${warnFLAGS} -Werror" ])
# For gcc >= 3.4.x, specifically enable the new warning switch
# -Wdeclaration-after-statement in order to preserve source code
@@ -1113,6 +1164,24 @@
lib/guile-www/Makefile
lib/srfi/Makefile
lib/libc/Makefile
+ lib/goffice/Makefile
+ lib/goffice/split/Makefile
+ lib/goffice/split/widgets/Makefile
+ lib/goffice/app/Makefile
+ lib/goffice/graph/Makefile
+ lib/goffice/graph/plugins/Makefile
+ lib/goffice/graph/plugins/plot_barcol/Makefile
+ lib/goffice/graph/plugins/plot_pie/Makefile
+ lib/goffice/graph/plugins/plot_radar/Makefile
+ lib/goffice/graph/plugins/plot_surface/Makefile
+ lib/goffice/graph/plugins/plot_xy/Makefile
+ lib/goffice/utils/Makefile
+ lib/goffice/gui-utils/Makefile
+ lib/goffice/drawing/Makefile
+ lib/goffice/pixmaps/Makefile
+ lib/goffice/cut-n-paste/Makefile
+ lib/goffice/cut-n-paste/pcre/Makefile
+ lib/goffice/cut-n-paste/egg-recent-files/Makefile
rpm/Makefile
src/Makefile
src/app-file/Makefile
Index: ChangeLog
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/ChangeLog,v
retrieving revision 1.1487.2.193
retrieving revision 1.1487.2.194
diff -LChangeLog -LChangeLog -u -r1.1487.2.193 -r1.1487.2.194
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,30 @@
+2005-04-23 Derek Atkins <derek at ihtfp.com>
+
+ * Merge gog-integ branch into g2 branch.
+
+ --- begin gog-integ Changelog ---
+
+2005-04-17 Derek Atkins <derek at ihtfp.com>
+
+ * configure.in: don't set pkglibdir
+ * lib/goffice/split.c: implement e_xml_get_child_by_name()
+ GOG branch now builds on FC3.
+
+2005-02-12 Joshua Sled <jsled at asynchronous.org>
+
+ * src/report/utility-reports/test-graphing.scm: Add to make
+ graphing development easier.
+
+ * src/gnome-utils/gnc-html-graph-gog.c (handle_barchart): fix;
+ handle stacked-columns.
+ (handle_scatter): add.
+
+2005-02-10 Joshua Sled <jsled at asynchronous.org>
+
+ * lib/goffice/ : Initial import of libgoffice "port".
+
+ -- end gog-integ Changelog --
+
2005-04-22 Joshua Sled <jsled at asynchronous.org>
* src/scm/main.scm (gnc:main): Expose and use functionality to
Index: HACKING
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/HACKING,v
retrieving revision 1.5.4.3
retrieving revision 1.5.4.4
diff -LHACKING -LHACKING -u -r1.5.4.3 -r1.5.4.4
--- HACKING
+++ HACKING
@@ -132,7 +132,7 @@
-- run ./src/bin/overrides/gnucash-valgrind
However, I did not find valgrind to be useful. It reported a bunch of
-guile bugs, some g_has_table bugs, and then the program exited prematurely
+guile bugs, some g_hash_table bugs, and then the program exited prematurely
for no appearenet reason. :-(
For the moment, use the supressions in lib/gnucash_valgrind.supp.
Index: GNOME2_STATUS
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/Attic/GNOME2_STATUS,v
retrieving revision 1.1.2.45
retrieving revision 1.1.2.46
diff -LGNOME2_STATUS -LGNOME2_STATUS -u -r1.1.2.45 -r1.1.2.46
--- GNOME2_STATUS
+++ GNOME2_STATUS
@@ -163,6 +163,7 @@
- Graphing still unsupported; [jsled] in periodic contact with Jody
Goldberg of Gnumeric regarding Gnome Office Graphing [GOG] to be factored
out of gnumeric into libgoffice.
+ GOG integrated but not completely working - warlord - 2005-04-23.
- Save/restore in unknown state; believed to crash on re-start.
@@ -258,3 +259,145 @@
- Rename "New Account Tree" to "Display Account Tree" as "New" gives the
impression that a creation of something will occur. PJK
+
+-----------------------------------------
+<#GOG> ----------------------------------
+[dc:created 2004-12-17]
+
+gog-guru.c
+ \- gog-guru.h
+ \- goffice-graph.h
+ \- <glib.h>
+ \- data-structures
+ \- command-context.h
+ \- <goffice/goffice-config.h>
+ \- <gnumeric-config.h> + "When the module splits we'll make this a real file"
+ \- <goffice/graph/gog-object.h>
+ \- <goffice/graph/goffice-graph.h>
+ \- <glib-object.h>
+ \- <command-context.h> /* for GnmCmdContext */
+ \- <libart_lgpl/art_rect.h>
+ \- <goffice/graph/gog-graph.h>
+ \- [nothing new]
+ \- <goffice/graph/gog-object.h>
+ \- [nothing new]
+ \- <goffice/graph/gog-chart.h>
+ \- <goffice/graph/gog-plot.h>
+ \- <goffice/utils/goffice-utils.h>
+ \- <glib.h>
+ \- decls
+ \- <goffice/graph/gog-view.h>
+ \- <goffice/graph/gog-plot-engine.h>
+ \- <goffice/graph/gog-data-allocator.h>
+ \- <goffice/graph/gog-control-foocanvas.h>
+ \- <goffice/graph/gog-renderer-pixbuf.h>
+ \- <gui-util.h>
+ \- "workbook-control-gui.h"
+ \- "error-info.h"
+ \- "command-context.h"
+ \- "gutils.h"
+
+
+<#goffice/graph> :depOn [ :module <#libart> ].
+
+<file:gog-plot-engine.c>
+ :depOn [ :module <#gsf>; :how "entirely [?] for GSF_CLASS macros" ]
+ ,[ :module <#gnumeric-plugin>; :how "plot-engine loading" ].
+
+<file:gog-graph.c>
+ :depOn [ :module <#gsf>; :how "entirely [?] for GSF_CLASS macros." ].
+
+<file:gog-view.c>
+ :depOn [ :module <#gsf> ].
+
+<file:gog-object.c> :depOn [ :module <#gsf> ].
+
+<file:gog-renderer.c>
+ :depOn [ :module <utils/go-math.h>; :how "basic math aliasing." ]
+ ,[ :module <utils/go-units.h>; :how "basic unit-typing/aliasing." ]
+ ,[ :module <utils/go-font.h>; :how "font aliasing; pango, mostly." ]
+
+<file:gog-renderer-impl.h>
+ :depOn [ :module <#go-line> ].
+
+<#go-line> :depOn [ :module <#gui-utils> ].
+
+<file:gog-renderer-pixbuf.c>
+ :depOn [ :module <#gsf>; :how "weakly" ]
+ ,[ :module <#pango> ]
+ ,[ :module <#libart> ]
+ ,[ :module <#gnumeric-app>; :how "for the `gnm_app_display_dpi_get(...)` call, apparently." ]
+.
+
+<file:gog-control-foocanvas.c>
+ :depOn [ :module <#gnm-libfoocanvas-copy>; :note "we can do without this." ].
+
+<file:gog-axis.c>
+ :depOn [ :module <#gnm-widget-format-selector>; :how "??" ]
+ ,[ :module <#gnm-gui-util>; :how "`number_format_selector_set_style_format`" ]
+ ,[ :module <#gnm-format.h>; :how "`style_format_is_general`" ]
+.
+
+
+------------------------------
+
+[jsled at phoenix:~/stuff/programming/projects/gnucash-budget/src-gnome2/gnucash/lib/goffice]$ nm -a .libs/libgoffice.a | ./symbols.py | egrep -v "(xml|gtk_|g_|gdk_|pango_|gsf_|art_|gnome_|glade_)" | sort
+# some manual cleanup of the results results in the list here:
+error_info_free
+error_info_print
+ - provided by error-info.[hc]
+font_selector_get_pango
+font_selector_get_type
+font_selector_new
+font_selector_set_from_pango
+ - used by gog-style.c:gog_style_editor.
+ - gog_styled_object_editor, however, is called from gog-{axis,label,legend,series}.
+format_value
+ - seems straightforward, but depends on format logic.
+gnm_app_display_dpi_get
+ - thin wrapper over configuration; should be easy to stub out.
+gnm_app_get_pixbuf
+ - called by go_action_combo_color_new, which isn't called in lib...
+gnm_font_find_closest_from_weight_slant
+ - depends on gnome print, but nicely seperable.
+gnm_measure_string
+ - nicely seperable; depends on pango.
+gnm_pixbuf_intelligent_scale
+ - nicely seperable; depends on GdkPixbuf.
+gnm_plugin_get_dir_name
+gnm_plugin_use_ref
+gnm_plugin_use_unref
+ - split/plugin.[hc]; seems to wrap plugin_* stuff...
+gnm_setup_label_atk
+ - seperable
+gnm_widget_disable_focus
+ - seperable
+gnumeric_button_new_with_stock_image
+ - looks seperable [being stolen from gtkedit]
+gnumeric_load_pixbuf
+ - depends weakly on gnumeric configuration path.
+go_nan
+ - text symbol
+gui_image_file_select
+ - used to select [fill] image file by gog-style ... do we need the UI
+ elements of gog-style here?
+number_format_selector_get_type
+number_format_selector_new
+number_format_selector_set_style_format
+plugin_service_define
+plugin_service_get_description
+plugin_service_get_plugin
+plugin_service_get_type
+plugin_service_gobject_loader_get_type
+plugin_service_load
+plugin_service_simple_get_type
+style_format_as_XL
+style_format_default_date
+style_format_default_money
+style_format_default_percentage
+style_format_default_time
+style_format_equal
+style_format_general
+style_format_new_XL
+style_format_ref
+style_format_unref
Index: gnc-main-window.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome/Attic/gnc-main-window.c,v
retrieving revision 1.1.2.37
retrieving revision 1.1.2.38
diff -Lsrc/gnome/gnc-main-window.c -Lsrc/gnome/gnc-main-window.c -u -r1.1.2.37 -r1.1.2.38
--- src/gnome/gnc-main-window.c
+++ src/gnome/gnc-main-window.c
@@ -1275,7 +1275,7 @@
{
GtkWindow *w = GTK_WINDOW(gtk_window_new( GTK_WINDOW_TOPLEVEL ));
gnc_html *gnchtml = gnc_html_new( w );
- gchar *html = "<html><head><title>testing</title></head><body><h1>testing</h1><h2>testing 2</h2> <p>Testing</p></body></html>";
+ gchar *html = "<html><head><title>testing</title></head><body><h1>testing</h1><h2>testing 2</h2> <p>Tes<br />ting<object classid=\"gnc-guppi-pie\" width=\"300\" height=\"200\">No pie for you!</object></p></body></html>";
gtk_container_add( GTK_CONTAINER(w), GTK_WIDGET(gnc_html_get_widget(gnchtml)) );
gnc_html_show_data( gnchtml, html, strlen( html ) );
Index: Makefile.am
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/lib/Makefile.am,v
retrieving revision 1.6.4.3
retrieving revision 1.6.4.4
diff -Llib/Makefile.am -Llib/Makefile.am -u -r1.6.4.3 -r1.6.4.4
--- lib/Makefile.am
+++ lib/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = libc guile-www srfi
+SUBDIRS = libc guile-www srfi goffice
EXTRA_DIST = README guppi-legend.patch
--- /dev/null
+++ lib/goffice/goffice-config.h
@@ -0,0 +1,4 @@
+/*
+ * When the module splits we'll make this a real file
+ */
+#include <config.h>
--- /dev/null
+++ lib/goffice/symbols.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+"""
+jsled, 2005 -- munge nm -a output to get a listing of actually undefined
+ symbols in libgoffice.
+
+USAGE:
+[jsled at phoenix:~/../gnucash/lib/goffice]$ nm -a .libs/libgoffice.a | ./symbols.py | egrep -v "(xml|gtk_|g_|gdk_|pango_|gsf_|art_|gnome_|glade_)" | sort
+
+"""
+
+import sys, re
+
+def main():
+ syms = {}
+ for line in sys.stdin:
+ line = line.strip()
+ match = re.match( r".*\b(U|T|D|c|t|R|B|C) ([^ ]+).*", line )
+ if not match:
+ continue
+ sym = match.group(2)
+ type = match.group(1)
+ if not syms.has_key( sym ):
+ syms[sym] = type
+ elif syms[sym] == "U":
+ syms[sym] = type
+ for sym,status in syms.iteritems():
+ if status == "U":
+ print sym
+
+if __name__ == "__main__":
+ main();
--- /dev/null
+++ lib/goffice/goffice-plugins.mk
@@ -0,0 +1,10 @@
+INCLUDES = \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/lib/goffice \
+ -I$(top_srcdir)/lib/goffice/split \
+ $(GNUCASH_CFLAGS)
+
+GNUCASH_PLUGIN_LDFLAGS =
+GOFFICE_PLUGIN_FLAGS = $(GNUCASH_PLUGIN_LDFLAGS)
+
+AM_CFLAGS = ${GLIB_CFLAGS} ${XML_CFLAGS} ${GSF_CFLAGS} ${ART_CFLAGS} ${GNOME_CFLAGS} ${GDK_PIXBUF_CLFAGS} ${GLADE_CFLAGS}
--- /dev/null
+++ lib/goffice/split.h
@@ -0,0 +1,22 @@
+#ifndef SPLIT_H
+#define SPLIT_H
+
+#include "gnumeric.h"
+
+struct _Workbook
+{
+};
+
+GnmDateConventions const * workbook_date_conv( Workbook const *wb );
+extern GnmExprConventions *gnm_expr_conventions_default;
+
+struct _GnmExprConventions
+{
+ gboolean output_translated;
+ /* If non-null, used to separate elements in lists. */
+ char const *output_argument_sep;
+ /* If non-null, used to separate array columns. */
+ char const *output_array_col_sep;
+};
+
+#endif /*SPLIT_H*/
--- /dev/null
+++ lib/goffice/ChangeLog
@@ -0,0 +1,3451 @@
+2004-12-15 Morten Welinder <terra at gnome.org>
+
+ * gui-utils/go-action-combo-stack.c (get_key_at_path): Don't crash
+ if we don't get an iter.
+ (cb_motion_notify_event): Handle empty tree case.
+
+2004-12-09 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.4.1
+
+2004-12-09 Jody Goldberg <jody at gnome.org>
+
+ * utils/go-file.c (go_url_show) : patch the problems morten pointed
+ out and finish the '%1' subsitution.
+
+2004-12-08 Jody Goldberg <jody at gnome.org>
+
+ * utils/go-file.c (go_url_show) : new with a win32 and non-gnome wrapper
+
+2004-12-01 Jody Goldberg <jody at gnome.org>
+
+ * goffice.mk (INCLUDES) : remove GNUMERIC_ICON_DIR
+
+2004-11-28 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.4.0
+
+2004-11-19 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-style.c (gog_style_gradient_load): call
+ gog_style_set_fill_brightness in order to correctly initialize start
+ and end colors.
+
+2004-11-18 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (create_invalid_axis_ticks): add a flag to note
+ create "##" labels.
+ * graph/gog-guru.c (cb_attr_tree_selection_change): scroll to selected
+ row.
+
+2004-11-14 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-renderer-pixbuf.c (line_size): width <= 0. is hairline.
+ * graph/gog-renderer.c (gog_render_line_size): ditto.
+ * graph/gog-style.c (cb_outline_size_changed): round to 2 significant
+ digits since sometimes adj->value returns 1E-17 instead of zero.
+ (cb_line_size_changed): ditto.
+ (gog_style_apply_theme): handle dash_type property.
+ (gog_style_gradient_sax_save): fix brightness test to be consistent
+ with dom_save.
+ (gog_style_gradient_load): start-color and end-coilor were inverted.
+ (gog_style_is_outline_visible): line is visible for all width values.
+ (gog_style_is_line_visible): ditto.
+ (gog_style_force_auto): handle auto_dash.
+ * graph/plugins/plot_xyt/gog-xy.c (gog_xy_series_init_style): handle
+ dash_type property.
+
+2004-11-14 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=152514
+ * graph/gog-guru.c (graph_guru_init_ok_button): new
+ (graph_guru_init): call graph_guru_init_ok_button
+ (gog_guru): initialize state->editing
+
+2004-11-07 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.3.93
+
+2004-10-31 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.3.92
+
+2004-11-01 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=157048
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render): fix
+ leak.
+
+2004-10-30 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=152765
+ * graph/gog-guru.c (graph_guru_init_format_page): delete_button is
+ really a button now, so connect "clicked" signal instead of
+ "activate".
+ * graph/gog-guru.glade: replace "Delete" menuitem by a button as a
+ workaround of gtk+ bug #155336.
+
+2004-10-29 Morten Welinder <terra at gnome.org>
+
+ * cut-n-paste/pcre/pcre.c (ord2utf8): Fix ABR. (Fixed in PCRE
+ 5.0, but I don't want to upgrade right now.)
+
+2004-10-27 Morten Welinder <terra at gnome.org>
+
+ * utils/go-line.c (go_line_clip_vpath): Make sure "reject" is
+ initialized.
+ (GOLineDashDesc): Do not rely on C99 features.
+
+2004-10-27 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-style.c (gog_style_gradient_load): fix brightness case.
+
+2004-10-26 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render): fix
+ marker for missing data.
+
+2004-10-26 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=152585
+ * grah/gog-axis.c (gog_axis_update): allways emit changed signal for
+ discrete axis, since labels may have changed.
+ * plugins/plot_barcol/gog-1.5d.c (gog_plot1_5d_update): call
+ gog_axis_bound_changed if GogSeries1_5D->index_changed == TRUE.
+ (gog_series1_5d_dim_changed): new. Used to set index_changed to TRUE
+ when vector dim_i == 0 changed.
+
+2004-10-25 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (draw_axis_from_a_to_b): use
+ gog_style_is_line_visible instead of checking line width.
+ (gog_axis_view_render): ditto.
+ * graph/gog-label.c (gog_label_view_render): draw sharp rectangle
+ around label.
+ * graph/gog-legend.c (cb_render_elements): draw sharp sample line and
+ sharp rectangle around elements.
+ (gog_legend_class_init): set clip to TRUE in order to avoid sample
+ lines to extend beyond legend area.
+ * graph/gog-outlined-object.c (gog_outlined_view_size_request): use
+ gog_style_is_line_visible instead of checking for positive line width.
+ (gog_outlined_view_size_allocate): ditto.
+ (gog_outlined_view_render): draw sharp rectangle.
+ * graph/gog-renderer-gnome-print.c (set_dash): new.
+ (gog_renderer_gnome_print_draw_path): honor dash setting. Clip dashed path
+ in order to avoid huge memory allocation and crash for very long
+ lines.
+ (gog_renderer_gnome_print_draw_polygon): ditto.
+ * graph/gog-renderer-impl.h: add GogRenderer::line_dash and
+ GogRenderer::outline_dash members.
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_clip_push): fix
+ clip area rounding.
+ (line_size): new. Same as gog_renderer_line_size, but don't round
+ width < 1.0 .
+ (gog_renderer_pixbuf_line_size): use line_size.
+ (gog_renderer_pixbuf_sharp_path): fix rounding.
+ (gog_renderer_pixbuf_draw_path): honor dash setting.
+ (gog_renderer_pixbuf_draw_polygon): ditto.
+ * graph/gog-renderer-svg.c (gog_renderer_svg_clip_push): use C locale
+ for double to string conversion.
+ (stroke_dasharray): new.
+ (gog_renderer_svg_draw_path): honor dash setting.
+ (gog_renderer_svg_draw_polygon): ditto.
+ * graph/gog-renderer.c: define a hair line width and set it to 0.24pt.
+ (gog_renderer_finalize): free line_dash and outline_dash.
+ (update_dash): new.
+ (gog_renderer_push_style): call update_dash.
+ (gog_renderer_pop_style): ditto.
+ (gog_renderer_clip_push): keep a pointer to the current clip.
+ (gog_renderer_clip_pop): ditto.
+ (draw_rectangle): new.
+ (gog_renderer_draw_sharp_rectangle): new.
+ (gog_renderer_line_size): return GOG_RENDERER_HAIR_LINE_WIDTH instead
+ of 1.0 for style->line.width == 0.0 .
+ * graph/gog-style-prefs.glade: add dash type combos.
+ * graph/gog-style.c (cb_outline_dash_type_changed): new.
+ (cb_line_dash_type_changed): new.
+ (outline_init): add dash selector.
+ (line_init): ditto.
+ (gog_style_line_load): load dash settings.
+ (gog_style_line_dom_save): save dash settings.
+ (gog_style_line_sax_save): ditto.
+ (gog_style_is_different_size): check dash_type.
+ (gog_style_is_outline_visible): new.
+ (gog_style_is_line_visible): check dash_type.
+ * graph/gog-theme.c (gog_themes_init): use dash_type.
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render): don't
+ draw sharp path.
+ * utils/go-line.[ch]: new.
+
+2004-10-13 Jean Brefort <jean.brefort at normalesup.org>
+
+ * graph/gog-axis.c: add center_on_ticks boolean to force centering labels
+ on ticks for discree mapped axis.
+ * graph/gog-plot.h: ditto.
+ * graph/gog-plot.c: ditto.
+
+2004-10-19 Morten Welinder <terra at gnome.org>
+
+ * utils/go-file.c (go_dirname_from_uri): New function.
+
+2004-10-13 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (gog_axis_dim_changed): call gog_axis_update in
+ order to refresh labels and ticks.
+
+2004-10-13 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_get_entry): Remove band-aid no longer
+ needed. (Although files created in the past few days will have
+ the vector type in the file.)
+
+2004-10-13 Jean Brefort <jean.brefort at normalesup.org>
+
+ * graph/gog-axis.c: (make_dim_editor): replace TRUE by GO_DATA_SCALAR in the
+ call to gog_data_allocator_editor.
+ * graph/gog-label.c: (gog_label_editor): ditto.
+ * graph/gog-series.c: (gog_series_editor): ditto.
+
+2004-10-13 Jean Brefort <jean.brefort at normalesup.org>
+
+ * graph/gog-plot-impl.h: added a foreach_elem member in GogPlotClass
+ * graph/gog-plot.c: (gog_plot_foreach_elem): calls Klass->foreach_elem
+ when not NULL to override default legends management.
+ * graph/gog-renderer-gnome-print.c: (draw_path): add ART_MOVETO_OPEN case,
+ (gog_renderer_gnome_print_measure_text): fix a bad sign.
+ * graph/gog-renderer-svg.c: (draw_path): add ART_MOVETO_OPEN case.
+
+2004-10-12 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-axis.h (GogAxisTickTypes): Fix namespace.
+
+ * graph/gog-axis.c (GogAxisElemType): Name this type.
+ (gog_axis_get_entry): Use GogAxisElemType, not unsigned int, for
+ the index argument.
+
+2004-10-12 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-axis.c (map_linear_auto_bound): Fix gnm_float/double
+ confusion.
+ (gog_axis_get_entry): Temporary fix for user-defined axis limits.
+
+2004-10-11 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-series.c (gog_series_finalize): Plug leak.
+
+2004-10-11 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/Makefile.am : drop the dock code we're not going to be
+ using it before 1.4
+
+2004-10-02 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=154664
+ * graph/gog-guru.glade: set width and height request for sample area
+ and object list.
+
+2004-10-09 Jean Brefort <jean.brefort at normalesup.org>
+
+ * graph/go-data.c: (go_data_matrix_emit_changed),
+ (go_data_matrix_class_init), (go_data_matrix_get_size),
+ (go_data_matrix_get_values), (go_data_matrix_get_value),
+ (go_data_matrix_get_str), (go_data_matrix_get_minmax): new GOMatix class.
+ * graph/go-data.h: ditto.
+ * graph/go-data-impl.h: ditto.
+ * graph/goffice-graph.h: ditto. Added GODataType enumerated type.
+ * graph/gog-data-allocator.c: (gog_data_allocator_editor): Replaced
+ "gboolean prefers_scalar" by "GODataType data_type".
+ * graph/gog-data-allocator.h: ditto.
+ * graph/gog-error-bar.c: (cb_type_changed), (gog_error_bar_prefs):
+ * graph/gog-series.c: (gog_series_editor): ditto.
+ * graph/plugins/plot_surface/Makefile.am: new contour plots.
+ * graph/plugins/plot_surface/gog-contour-prefs.c: ditto.
+ * graph/plugins/plot_surface/gog-contour-prefs.glade: ditto.
+ * graph/plugins/plot_surface/gog-surface.c: ditto.
+ * graph/plugins/plot_surface/gog-surface.h: ditto.
+ * graph/plugins/plot_surface/plot-types.xml.in: ditto.
+ * graph/plugins/plot_surface/plugin.xml.in: ditto.
+
+2004-10-08 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-object.c (gog_object_dup) : copy the position.
+
+2004-10-06 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-control-foocanvas.c (gog_control_foocanvas_update) : Fix
+ redraw. There were two bugs.
+ 1) foocanvas_group_update was wiping tbe bounds (things always look
+ like they move)
+ 2) We were not requesting a redraw for the old position before
+ moving.
+
+2004-10-05 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.3.91
+
+2004-10-03 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-control-foocanvas.c : Make the class definition public to
+ make it easier to graft on an additional interface in the app. We
+ may not need this later if SheetObject moves down into goffice
+
+2004-10-02 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=152672
+ * graph/gog-theme.c (gog_themes_init): set pattern background to
+ white, even when automatic pattern style is NONE.
+ * graph/gog-label.c (gog_label_editor): fix layout.
+ (gog_label_view_render): add padding
+ when outline > 0. or pattern != NONE.
+ * graph/gog-outlined-object.c (gog_outlined_view_size_request): ditto.
+ (gog_outlined_view_size_allocate): ditto.
+ * graph/gog-style.c (gog_style_is_different_size): changing pattern
+ type can change object size.
+
+2004-09-29 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-guru-type-selector.glade: new.
+ * graph/Makefile.am: add gog-guru-type-selector.glade.
+ * graph/gog-axis-prefs.glade: fix layout.
+ * graph/gog-axis.c (make_dim_editor): ditto.
+ (gog_axis_editor): ditto.
+ * graph/gog-guru.c (cb_attr_tree_selection_change): allways use a
+ notebook, but hide tabs when there's only one page.
+ (graph_guru_type_selector_new): load layout from glade file.
+ * graph/gog-guru-glade: remove scrollbar around property notebook.
+ Make object menu unshrinkable. Fix layout.
+ * graph/gog-series.c (gog_series_element_editor): fix layout.
+ * graph/gog-style.c (font_int): ditto.
+ * graph/plugins/plot_barcol/gog-barcol-prefs.glade: ditto.
+ * graph/plugins/plot_pie/gog-pie-series.glade: ditto.
+ * graph/plugins/plot_pie/gog-ring-prefs.glade: ditto.
+ * graph/plugins/plot_xye/gog-xy-prefs.glade: ditto.
+
+2004-09-29 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=153402
+ * graph/plugins/gog-radar-prefs.c: removed.
+ * graph/plugins/gog-radar-prefs.glade: removed.
+ * graph/plugins/gog-radar.c: removed reference to plot editor.
+ * graph/plugins/Makefile.am: removed reference to
+ gog-radar-prefs.[c,glade].
+
+2004-09-29 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_finalize):
+ Conditionalise the use of pango_context_get_font_map.
+
+2004-09-28 Morten Welinder <terra at gnome.org>
+
+ * utils/go-font.c (go_pango_fc_font_map_cache_clear): New
+ function, wrapping pango_fc_font_map_cache_clear (which isn't
+ public).
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_finalize): Call
+ go_pango_fc_font_map_cache_clear.
+
+ * utils/go-color.c (go_color_to_pango): Add is_fore argument and
+ export.
+
+2004-09-28 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_view_info_at_point):
+ disable automatic point creation for ring plot, since it
+ doesn't work. For pie plot, don't create new point when it already
+ exists, but select it.
+
+2004-09-28 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=153401
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_view_render): fix special
+ inner arc handling test (has_hole :) ).
+
+2004-09-27 Morten Welinder <terra at gnome.org>
+
+ * utils/go-math.c (go_fake_ceil): New function.
+
+ * utils/go-file.c (go_url_encode): Make this half-way decent.
+ (go_url_decode): Make this O(n) too.
+
+ * libpresent/ppt-parsing-helper.h: Add header guard.
+
+2004-09-27 Jon K Hellan <hellan at acm.org>
+
+ * graph/gog-renderer-gnome-print.c
+ (gog_graph_print_to_gnome_print): Conditionalize call to
+ gnome_print_pango_create_layout.
+
+2004-09-26 Jody Goldberg <jody at gnome.org>
+
+ From : Yukihiro Nakai <nakai at gnome.gr.jp>
+ http://bugzilla.gnome.org/show_bug.cgi?id=148550
+ * utils/go-file.c (go_url_decode) : new
+ (go_url_encode) : new. I'm not sure these belong in go vs gsf
+ but lets keep them here for now until they get fleshed out.
+
+2004-09-25 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-error-bar-prefs.glade: 12 px between label and control.
+ * graph/gog-style-prefs.glade: 12 px padding. Use GtkAlignment for
+ group layout.
+ * graph/gog-style.c: use new layout.
+
+2004-09-25 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-error-bar-prefs.glade: layout rework.
+ * graph/gog-error-bar.c (cb_type_changed): use new layout. Hide styles
+ when category is none.
+ (gog_error_bar_prefs): ditto.
+
+2004-09-23 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (gog_axis_view_render): draw_minor and draw_major
+ only if line_width > 0.
+ * grapg/gog-theme.c (gog_themes_init): initialize GogGrid outline.
+
+2004-09-23 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (gog_axis_view_render): Calculate major_out even if
+ line_width <= 0, because it's needed for label positionning.
+
+2004-09-23 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c: make GogGridLine as child of GogAxis.
+ (role_grid_line_can_add): new.
+ (role_grid_line_major_can_add): new.
+ (role_grid_line_minor_can_add): new.
+ (role_grid_line_major_post_add): new.
+ (role_grid_line_minor_post_add): new.
+ (gog_axis_view_class_init): add grid line roles.
+ (gog_axis_view_render_children): new. Don't render grid lines here.
+ (gog_axis_view_render): call gog_axis_view_render_children. Free
+ axis_list.
+ * graph/gog-chart.c
+ (gog_chart_view_class_init): set call_parent_render to FALSE.
+ (grid_line_render): new. Render minor grid, then major.
+ (gog_chart_view_render): new. Kludge for grid lines rendering before
+ axis.
+ * graph/gog-grid-line.c: add new is_minor property and remove type
+ property.
+ (gog_grid_line_is_minor): new.
+ (gog_grid_line_view_render): implement radar grid.
+ * graph/gog-grid.c: Remove grid line roles.
+ (role_grid_line_can_add): removed.
+ (role_grid_x_major_can_add): removed.
+ (role_grid_x_minor_can_add): removed.
+ (role_grid_y_major_can_add): removed.
+ (role_grid_y_minor_can_add): removed.
+ (role_grid_x_major_post_add): removed.
+ (role_grid_x_minor_post_add): removed.
+ (role_grid_y_major_post_add): removed.
+ (role_grid_y_minor_post_add): removed.
+ (gog_grid_view_render): sharpen polygon.
+ (gog_grid_init_style): add OUTLINE property.
+ * graph/gog-outlined-object.c: add a call_parent_render class
+ property. Deafult to TRUE.
+ * graph/gog-theme.c (gog_themes_init): add MajorGrid and MinorGrid
+ themes. Remove X-MajorGrid, Y-MajorGrid, X-MinorGrid, Y-MinorGrid
+ themes.
+
+2004-09-23 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-renderer-gnome-print.c
+ (gog_renderer_gnome_print_draw_text): Get the right kind of size.
+ (gog_renderer_gnome_print_measure_text): Use layout method here
+ too.
+
+2004-09-22 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-renderer-gnome-print.c
+ (gog_renderer_gnome_print_draw_text): Print using pango layouts.
+ Adapted from code by Yaacov Zamir <kzamir at walla.co.il>.
+
+2004-09-21 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=153289
+ * graph/gog-axis.c (gog_axis_editor): hide minor tick properties when
+ editing discrete axis properties.
+ * grah/gog-axis-prefs.glade: name minor tick frame.
+
+2004-09-20 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * pixmaps: make color order consistent and rerender png from svg with
+ inkscape instead of rsvg.
+
+2004-09-20 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * goffice.c: new GogGridLine type.
+ * graph/gog-axis.c (map_discrete_calc_ticks): allways calc major and
+ minor ticks.
+ (map_linear_calc_ticks): ditto.
+ (map_log_calc_ticks): ditto.
+ (gog_axis_get_ticks): return tick list and tick number now.
+ (gog_axis_view_render): since all ticks are in cache, only draw them
+ if they are visibles.
+ * graph/gog-grid-line.[ch]: new.
+ * graph/gog-grid.c (gog_grid_init_style): use only fill property.
+ (role_grid_line_can_add): new.
+ (role_grid_x_major_can_add): new.
+ (role_grid_x_minor_can_add): new.
+ (role_grid_y_major_can_add): new.
+ (role_grid_y_minor_can_add): new.
+ (role_grid_x_major_post_add): new.
+ (role_grid_x_minor_post_add): new.
+ (role_grid_y_major_post_add): new.
+ (role_grid_y_minor_post_add): new.
+ (gog_grid_class_init): register new grid line roles.
+ * graph/gog-theme.c: register theme for new grid line objects.
+
+2004-09-20 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=153146
+ * graph/plugins/plot_radar/gog-radar.c (gog_radar_view_render): Check
+ if series is valid before trying to render it.
+
+2004-09-19 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=152615
+ * pixmaps: new consistent charting icon set (based on a Jimmac work).
+
+2004-09-16 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-error-bar.c (gog_error_bar_get_min_max): get values from
+ outside of loop.
+ (gog_error_bar_get_bounds): add assertions. Use
+ go_data_vector_get_value.
+
+2004-09-15 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=152499
+ * graph/gog-error-bar.c (gog_error_bar_get_minmax): check wether
+ associated GogSeries is valid.
+
+2004-09-15 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-error-bar.c (gog_error_bar_get_minmax): add assertions.
+
+2004-09-14 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (gog_axis_map_is_valid): add assertion.
+
+2004-09-09 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-series.c (gog_series_element_set_index) : new to handle
+ theming properly.
+ (gog_series_element_set_property) : use it here.
+ (gog_series_element_init_style) : and here.
+ (role_series_element_allocate) : and here.
+
+2004-09-10 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_draw_text): remove
+ remaining unref.
+
+2004-09-10 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=152201
+ * graph/gog-axis.c (map_discrete_auto_bound): Set automatic maximum
+ number of major and lables to 100.
+ (gog_axis_view_render): don't draw axis + ticks in one path.
+ * graph/gog-renderer-pixbuf.c: cache pango layout.
+ (gog_renderer_pixbuf_get_pango_layout): new.
+ (gog_renderer_pixbuf_get_pango_context): new.
+ (gog_renderer_pixbuf_push_style): new. unref pango_layout.
+ (gog_renderer_pixbuf_push_style): ditto.
+
+2004-09-08 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.3.90
+
+2004-09-07 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=151530
+ tick label truncated by chart outline
+ http://bugzilla.gnome.org/show_bug.cgi?id=151527
+ pb when exporting to svg a chart without outline
+ http://bugzilla.gnome.org/show_bug.cgi?id=127203
+ X axis labels centre on chart, not on axis
+
+ * graph/goffice-graph.h: new GogViewPadding type.
+ * graph/gog-axis.c (map_log_to_canvas): DBL_MIN is not -DBL_MAX.
+ (gog_axis_view_padding_request): new.
+ (gog_axis_view_size_request): just call gog_view_size_child_request.
+ (gog_axis_view_size_allocate): remove.
+ (gog_axis_view_render): stop libart path to third element in case of
+ line_width <= 0. Fix label dropping. Use Chart->plot_area for axis
+ drawing.
+ * graph/gog-chart.c (gog_chart_view_get_plot_area): new.
+ (child_request): new.
+ (gog_chart_view_size_allocate): axis now request space around
+ residual. Store residual in Chart->plot_area.
+ * graph/gog-label.c (gog_label_view_render): draw rectangle here,
+ since in gog_outlined_object, rectangle around label is clipped to
+ view->allocation.
+ * graph/gog-renderer-pixbuf.c (draw_text): don't clip text, to be
+ consistent with svg and gnome-print renderer.
+
+2004-09-06 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-color-palette.c (go_color_palette_setup) : warning
+ suppression
+
+2004-09-05 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=151628
+ * graph/gog-guru.c (cb_typesel_sample_plot_resize) : hard code the
+ zoom when recalculating the size. The sample graph is only zoomed
+ when visible.
+
+2004-09-05 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-guru.c (graph_guru_type_selector_new) : Add a light bulb
+ to the 'show sample' button to make it stand out a bit more
+
+ * graph/gog-guru.c (graph_guru_type_selector_new) : Make the sample
+ canvas the main resizeable element in the type selector.
+
+2004-09-01 Jody Goldberg <jody at gnome.org>
+
+ * utils/go-gradient.c : conditionalize selector on WITH_GTK
+ * utils/go-pattern.c : ditto
+ * utils/go-marker.c : conditionalize selector and the use of GdkPixbuf
+
+ * utils/go-color.c : Remove unnecessary include of color-combo
+ and conditionalize the gdk support routines in case we do not have
+ gtk.
+
+2004-09-03 Morten Welinder <terra at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-1.5d.c
+ (gog_series1_5d_populate_editor): Terminate g_object_get call by
+ NULL, not 0.
+
+2004-09-02 Morten Welinder <terra at gnome.org>
+
+ * graph/plugins/plot_radar/gog-radar-prefs.glade: Someone
+ accidentally cut the guts of this. Add vary-style button that the
+ code appears to want.
+
+2004-08-31 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=151529
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_render): set series to to
+ right value for marker drawing.
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render): use
+ right index when walking through path for marker drawing.
+
+2004-08-30 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-plot-engine.c (gog_plot_new_by_name): Ick. We need to
+ keep a GObject ref in addition to the GnmPlugin ref.
+
+2004-08-29 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.3.2
+
+2004-08-26 Morten Welinder <terra at gnome.org>
+
+ * goffice.c (libgoffice_shutdown): Showdown plugin services.
+
+ * graph/gog-plot-engine.c (gog_plot_new_by_name): Handle inactive
+ plugins. Mark plugins as used.
+ (gog_plugin_services_shutdown): New function.
+
+2004-08-25 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_view_render) : don't leak an axis map for
+ discrete axes
+
+2004-08-24 Morten Welinder <terra at gnome.org>
+
+ * utils/go-file.c (go_file_split_uris): New function for
+ text/uri-list parsing.
+
+2004-08-23 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-plot-engine.c (create_plot_families): New function.
+ (gog_plot_family_by_name, gog_plot_families): Ensure we have
+ families.
+
+2004-08-20 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-error-bar.c (gog_error_bar_prefs): Arrange for gui to
+ be unref'd.
+
+2004-08-18 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (map_linear_calc_ticks): fix tick number
+ calculation.
+ (overlap): use already defined MAX / MIN.
+
+2004-08-17 Morten Welinder <terra at gnome.org>
+
+ * goffice.c (libgoffice_init): Call gsf_init.
+
+2004-08-17 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render):
+ don't clip markers to plot area.
+
+2004-08-17 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (create_invalid_axis_ticks): new.
+ (map_[log,discrete,linear]_init): when axis is not valid, init data
+ for special invalid axis rendering.
+ (map_[log,discrete,linear]_calc_ticks): when axis is not valid, return
+ a special set of ticks/labels.
+ (gog_axis_map_is_valid): new.
+ (gog_axis_map_new): set the map->is_valid flag. Default to FALSE if
+ there's no init function.
+ * graph/plugins/plot_barcol/gog-barcol.c (gog_barcol_view_render): use
+ gog_axis_map_is_valid.
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render): idem.
+ * graph/plugins/plot_radar/gog-radar.c (gog_radar_view_render): idem.
+ Add support for mapping.
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_render): don't clip
+ markers to plot area, but to chart area.
+
+2004-08-17 Jon K Hellan <hellan at acm.org>
+
+ * graph/gog-stisyle.c (cb_image_file_select): is_save parameter to
+ gui_image_file_select no longer needed.
+
+2004-08-13 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (gog_axis_editor): don't show preferences for
+ circular axis.
+ (overlap): new.
+ (draw_axis_from_a_to_b): use gog_axis_map and draw labels.
+ (gog_axis_view_render): at least width of the '0' char between two
+ labels for GOG_AXIS_X.
+ * graph/gog-view.h: remove call_parent_render and add a clip flag.
+ * graph/gog-chart.c (gog_chart_view_render): removed.
+ (gog_chart_view_class_init): set clip to TRUE.
+ * graph/gog-outlined-object.c: allways call parent render.
+ * graph/gog-renderer-gnome-print.c: change start/stop_clipping to
+ clip_push/pop.
+ * graph/gog-renderer-svg.c: change start/stop_clipping to
+ clip_push/pop.
+ (gog_renderer_draw_text): add xml child in current node instead of
+ first doc child.
+ * graph/gog-renderer-pixbuf: change start/stop_clipping to
+ clip_push/pop.
+ (gog_renderer_pixbuf_clip_push): add multilevel clipping capability.
+ (gog_renderer_pixbuf_print_clip_pop): idem.
+ * graph/gog-renderer.c: change start/stop_clipping to clip_push/pop
+ and add a clip stack to handle mutilevel clipping.
+ * graph/gog-view.c (gog_view_class_init): set default clip flag to
+ FALSE.
+ (gog_view_render): if klass->clip is TRUE, call to clip_push/pop
+ functions.
+ * graph/plugins/plot_barcol/gog-barcol.c: set klass->clip to TRUE.
+ * graph/plugins/plot_barcol/gog-line.c: idem.
+ * graph/plugins/plot_xy/gog-xy.c: idem.
+ * graph/plugins/plot_radar/gog-radar.c: idem.
+
+2004-08-09 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (map_[log,linear,discrete]_init) : add arguments
+ for direct mapping to canvas coordinates.
+ (gog_axis_map_new) : idem.
+ (map_[log,linear,discrete]_to_canvas : new.
+ (gog_axis_map_to_canvas) : new.
+ (map_discrete) : handle barcol / area,line correctly.
+ (map_log_auto_bounds): be smarter in case of min <= 0.
+ (gog_axis_render) : draw axis/ticks in one call. Drop labels when they
+ overlap.
+ * graph/gog-error-bar.c (gog_error_bar_get_bounds) : return relative
+ values of errors instead of absolute values.
+ (gog_error_bar_get_min_max) : adapt to new gog_error_bar_get_bounds.
+ (gog_error_bar_render) : use gog_axis_map functions.
+ * graph/gog-grid.c (gog_grid_render) : remove kludge.
+ * grapg/gog-renderer-gnome-print.c (gog_graph_print_to_gnome_print) :
+ add a workaround for a bug in libgnomeprint.
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_start_clipping) :
+ rendering fix.
+ (gog_renderer_pixbuf_line_size) : new.
+ (gog_renderer_pixbuf_sharp_path) : new. Tweak a path for sharp
+ rendering with libart.
+ (gog_renderer_pixbuf_draw_marker) : zoom support for markers. Fix
+ marker position.
+ * graph/gog-renderer.c (gog_renderer_draw_sharp_path) : new.
+ (gog_renderer_draw_sharp_polygon) : new.
+ (gog_renderer_line_size) : remove kludge.
+ * graph/plugins/plot_barcol/gog-1.5d.c (gog_plot1_5d_axis_get_bounds)
+ : maxima = num_elements - 1.
+ * graph/plugins/plot_barcol/gog-barcol.c
+ (gog_barcol_update_stacked_and_percentage) : adapt
+ to new gog_error_bar_get_bounds.
+ (gog_barcol_axis_get_bounds) : new.
+ (barcol_draw_rest) : remove kludge.
+ (gog_barcol_view_render) : use gog_axis_map functions.
+ * graph/plugins/plot_barcol/gog-line.c
+ (gog_line_update_stacked_and_percentage) : adapt
+ to new gog_error_bar_get_bounds.
+ (gog_line_view_render) : use gog_axis_map functions.
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_plot_render) : use
+ gog_axis_map_to_canvas and new gog_error_bar_get_bounds.
+ * utils/go-marker.c (go_marker_get_pixbuf) : add a scale argument.
+
+2004-07-29 Morten Welinder <terra at gnome.org>
+
+ * gui-utils/go-action-combo-pixmaps.c
+ (go_action_combo_pixmaps_create_menu_item): Let's initialize item
+ before we use it.
+
+2004-07-28 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis-prefs.glade: HIGification.
+ * graph/gog-axis.c (get_adjusted_tick_array): new.
+ (map_[discrete,linear,log]_init): new. Init for map functions.
+ (map_[discrete,linear,log]): new. Map data to visible plot area.
+ (map_[discrete,linear,log]_auto_bound): new. Calculate auto bounds and
+ tick spacing.
+ (map_[discrete,linear,log]_calc_ticks): new. Calculate position of
+ major ticks,minor ticks and labels.
+ (gog_axis_map_new),
+ (gog_axis_map),
+ (gog_axis_map_free): new. For use in plot view rendering functions.
+ (gog_axis_set_ticks): new. Put tick and label positions in axis->ticks.
+ (gog_axis_auto_bound),
+ (gog_axis_calc_ticks): new.
+ (gog_axis_set_property): check if update or calc_ticks is needed.
+ (gog_axis_update): makes use of gog_axis_set_ticks and
+ gog_axis_auto_bound.
+ (gog_axis_editor): hide map combobox if axis is discrete.
+ (gog_axis_view_render): use axis->ticks for rendering.
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_render): use new map
+ functions.
+
+2004-07-22 Christopher James Lahey <clahey at ximian.com>
+
+ * libpresent/Makefile.am (libgoffice_libpresent_la_SOURCES): Added
+ ppt-parsing-helper.c and ppt-parsing-helper.h.
+
+ * libpresent/god-drawing-ms-client-handler-ppt.c: Handle
+ TextBytesAtom and StyleTextPropAtom.
+
+ * libpresent/god-drawing-ms-client-handler-ppt.h: Added parameter
+ fonts.
+
+ * libpresent/load-ppt.c: Moved StyleTextPropAtom parsing to
+ ppt-parsing-helper.c.
+
+ * libpresent/ppt-parsing-helper.c,
+ libpresent/ppt-parsing-helper.h: Moved StyleTextPropAtom parsing
+ here. Improved StyleTextPropAtom parsing.
+
+ * test/dump-ppt-records.c: Improved StyleTextPropAtom parsing.
+
+2004-07-20 Christopher James Lahey <clahey at ximian.com>
+
+ * libpresent/load-ppt.c (handle_atom), test/dump-ppt-records.c
+ (handle_atom): Improved StyleTextPropAtom parsing.
+
+2004-07-20 Christopher James Lahey <clahey at ximian.com>
+
+ * libpresent/load-ppt.c (handle_atom): Missing NULL.
+
+2004-07-20 Christopher James Lahey <clahey at ximian.com>
+
+ * drawing/god-drawing-renderer-gdk.c (draw_text),
+ drawing/god-paragraph-attributes.c,
+ drawing/god-paragraph-attributes.h, libpresent/load-ppt.c: Support
+ default paragraph attributes and support bullets and alignment.
+
+ * libpresent/present-view.c: Added space and backspace bindings
+ (these don't work since the widget doesn't get focus.)
+
+ * test/dump-ppt-records.c (handle_atom): Handle TxMasterStyleAtom
+ better.
+
+ * test/test-view-ppt.c: Added q and escape bindings.
+
+2004-07-19 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.3.1
+
+2004-07-19 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-action-combo-pixmaps.c : switch from inline pixbufs to
+ stock ids.
+
+ * gui-utils/go-action-combo-color.c
+ (go_action_combo_color_set_color): implement
+
+2004-07-13 Christopher James Lahey <clahey at ximian.com>
+
+ * libpresent/load-ppt.c (handle_atom): Parse fonts.
+
+ * test/dump-ppt-records.c (handle_atom): Print a bunch of info
+ about default attributes.
+
+2004-07-12 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_view_info_at_point) :
+ expand this to offer name and override creation.
+
+ * graph/gog-view.c (gog_view_info_at_point) : expand this interface a
+ bit. I'm still not happy with it.
+
+ * graph/gog-series.c : keep a sorted list of overrides in place
+
+ * graph/gog-plot.c (gog_plot_foreach_elem) : handle point overrides
+
+ * graph/gog-object.c (gog_object_emit_changed) : handle updates for
+ objects not yet connected to parents.
+
+2004-07-11 Christopher James Lahey <clahey at ximian.com>
+
+ * drawing/Makefile.am (libgoffice_drawing_la_SOURCES): Added
+ god-default-attributes.c and god-default-attributes.h.
+
+ * drawing/god-default-attributes.c,
+ drawing/god-default-attributes.h: Default pango and paragraph
+ attributes per indent level.
+
+ * drawing/god-drawing-renderer-gdk.c: Handle default attributes.
+ Rework character attributes.
+
+ * drawing/god-text-model.c, drawing/god-text-model.h: Added indent
+ and default attributes concepts.
+
+ * libpresent/load-ppt.c (handle_atom): Parse TxMasterStyleAtom.
+
+ * libpresent/present-presentation.c,
+ libpresent/present-presentation.h: Added a default default
+ attributes object per text type.
+
+ * libpresent/present-text.c: Added a property to store a pointer
+ to the main presentation object.
+
+ * test/dump-ppt-records.c: Parse TxMasterStyleAtom.
+
+2004-07-08 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-guru.c (cb_canvas_select_item) : handle the zoom
+
+ * graph/gog-theme.c (gog_themes_init) : Add a global alias for
+ GogSeriesElement -> GogSeries
+ (gog_theme_find_element) : Look up theme local and global aliases for
+ classes.
+ (gog_theme_add_alias) : new.
+
+2004-07-08 Jody Goldberg <jody at gnome.org>
+
+ * goffice.c (libgoffice_init) : Add SeriesElement to be pedantic
+
+2004-07-06 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-graph.c (gog_graph_init) : apply theme style for graphs
+ to do a full init.
+
+2004-07-04 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-styled-object.c (gog_styled_object_parent_changed) :
+ initialize the style completely using the theme when we have a
+ theme.
+
+2004-07-04 Christopher James Lahey <clahey at ximian.com>
+
+ * drawing/god-drawing-renderer-gdk.c (draw_text): Handle
+ char_attributes.
+
+ * drawing/god-text-model.c, drawing/god-text-model.h
+ (real_god_text_model_set_paragraph_attributes): Fixed up this
+ function a bit.
+ (real_god_text_model_set_pango_attributes): Implemented this
+ function. Changed it to take GList instead of PangoAttrList.
+
+ * libpresent/load-ppt.c (handle_atom): Implemented parsing
+ character attributes.
+
+2004-07-02 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-theme.c (map_area_series_solid_default) : simplify now
+ that we're just filling in the color no need to twiddle types
+
+ * graph/gog-style.c : rework GogStyle::fill share pattern:fore/back
+ and gradient:start/end.
+ merge all the different auto flags.
+ initialize the gradient brightness to -1 to avoid starting with black
+ Change fill from union to struct to simplify changing fill types
+ Allow themed gradient fills
+
+2004-06-30 Christopher James Lahey <clahey at ximian.com>
+
+ * drawing/god-text-model.c
+ (real_god_text_model_set_paragraph_attributes): Oh yeah, the
+ character count should increase as you iterate through the
+ paragraph.
+
+ * libpresent/load-ppt.c (handle_atom): Pass in the position in the
+ text instead of the position in the file. That might be a good
+ idea.
+
+2004-06-29 Christopher James Lahey <clahey at ximian.com>
+
+ * drawing/Makefile.am (libgoffice_drawing_la_SOURCES): Added
+ god-paragraph-attributes.c and god-paragraph-attributes.h.
+
+ * drawing/god-drawing-renderer-gdk.c (draw_text): Draw each
+ paragraph separately with some paragraph formatting.
+
+ * drawing/god-paragraph-attributes.c,
+ drawing/god-paragraph-attributes.h: New file to handle attributes
+ of a paragraph.
+
+ * drawing/god-text-model.c, drawing/god-text-model.h: Store as a
+ list of paragraphs. Store some formatting information.
+
+ * libpresent/load-ppt.c: Load some of the paragraph formatting
+ from ppt.
+
+ * test/dump-ppt-records: Attempted to parse BaseTextPropAtom.
+
+ * test/test-view-ppt.c (main): Fullscreen the window.
+
+2004-06-23 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * Makefile.am: Added cut-n-paste subdir.
+ * cut-n-paste/Makefile.am: New file.
+ * cut-n-paste/pcre/Makefile.am: pcre tree moved from gnumeric.
+ * cut-n-paste/pcre/get.c: (pcre_get_stringnumber),
+ (pcre_copy_substring), (pcre_copy_named_substring),
+ (pcre_get_substring_list), (pcre_free_substring_list),
+ (pcre_get_substring), (pcre_get_named_substring),
+ (pcre_free_substring): ditto, changed gnumeric_* to go_*
+ * cut-n-paste/pcre/internal.h: ditto.
+ * cut-n-paste/pcre/maketables.c: (pcre_maketables): ditto.
+ * cut-n-paste/pcre/pcre.c: (make_pcre_default_tables), (ord2utf8),
+ (pcre_version), (pcre_info), (pcre_fullinfo), (pcre_config),
+ (pchars), (check_escape), (is_counted_repeat),
+ (read_repeat_counts), (first_significant_code), (find_fixedlength),
+ (find_bracket), (find_recurse), (could_be_empty_branch),
+ (could_be_empty), (check_posix_syntax), (check_posix_name),
+ (adjust_recurse), (compile_branch), (compile_regex), (is_anchored),
+ (is_startline), (find_firstassertedchar), (valid_utf8),
+ (pcre_compile), (match_ref), (match_xclass), (match), (pcre_exec):
+ * cut-n-paste/pcre/pcre.h: ditto.
+ * cut-n-paste/pcre/pcreposix.c: (pcre_posix_error_code),
+ (go_regerror), (go_regfree), (go_regcomp), (go_regexec):
+ * cut-n-paste/pcre/pcreposix.h: ditto.
+ * cut-n-paste/pcre/printint.c: (print_char), (print_internals): ditto.
+ * cut-n-paste/pcre/study.c: (set_bit), (set_start_bits),
+ (pcre_study): ditto.
+ * goffice.c: (libgoffice_init): Added call to go_math_init.
+ * graph/go-data-simple.c: (go_data_scalar_str_get_value),
+ (go_data_vector_str_get_value),
+ (go_data_vector_str_set_translation_domain): use new go_nan and functions in utils/go-math.*
+ * graph/go-data.c: (go_data_vector_get_value): ditto.
+ * graph/gog-axis.c: (gog_axis_get_entry), (gog_axis_update),
+ (cb_enable_dim), (cb_axis_bound_changed), (gog_axis_get_bounds),
+ (gog_axis_num_markers), (gog_axis_view_render): ditto.
+ * graph/gog-error-bar.c: (gog_error_bar_get_bounds): ditto.
+ * graph/gog-plot.c: (gog_plot_get_axis_bounds): ditto.
+ * graph/plugins/plot_barcol/gog-1.5d.c:
+ (gog_plot1_5d_axis_get_bounds): ditto.
+ * graph/plugins/plot_barcol/gog-barcol.c:
+ (gog_barcol_update_stacked_and_percentage),
+ (gog_barcol_view_render): ditto.
+ * graph/plugins/plot_barcol/gog-line.c:
+ (gog_line_update_stacked_and_percentage), (gog_line_view_render): ditto.
+ * graph/plugins/plot_pie/gog-pie.c: (gog_pie_view_render),
+ (gog_pie_series_update): ditto.
+ * graph/plugins/plot_radar/gog-radar.c:
+ (gog_radar_plot_axis_get_bounds), (gog_radar_view_render):
+ * graph/plugins/plot_xy/gog-xy.c: (gog_2d_plot_update),
+ (gog_2d_plot_axis_get_bounds), (gog_xy_view_render): ditto.
+ * utils/go-marker.c: ditto.
+ * utils/Makefile.am: Added go_math.[c,h]
+ * utils/go-math.c: (go_math_init), (go_add_epsilon),
+ (go_sub_epsilon), (go_fake_floor), (go_fake_trunc): imported from gnumeric.
+ * utils/go-math.h: ditto.
+
+2004-06-20 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_view_render) : Ensure that the line
+ extends out far enough for the first and last tick.
+
+ * graph/gog-theme.c (map_area_series_solid_default) : honour the new
+ disable_theming flag.
+ * graph/gog-style.c (gog_style_is_completely_auto) : new.
+
+2004-06-16 Morten Welinder <terra at gnome.org>
+
+ * utils/go-file.c (go_filename_to_uri): Handle "//" and "/./"
+ parts in filenames.
+
+ * graph/gog-style.c (cb_image_file_select): Handle change in
+ gui_image_file_select's signature. (Barely.)
+
+ * utils/go-file.c (go_file_create): New function.
+
+2004-06-15 Morten Welinder <terra at gnome.org>
+
+ * utils/go-file.c (go_basename_from_uri): Better WITH_GNOME
+ implementation that handles fragments and methods.
+
+2004-06-13 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-1.5d.c : tack on a placeholder 'in_3d'
+ attribute to simplify roundtripping for xls.
+
+2004-06-11 Morten Welinder <terra at gnome.org>
+
+ * utils/go-file.c: New file.
+
+2004-06-11 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis-prefs.glade : add a combobox for map selection.
+ * graph/gog-axis.c : add a map description structure. remove log_scale
+ boolean property of GogAxis, and add a new named map property.
+ (map_init_linear),
+ (map_linear),
+ (map_init_log),
+ (map_log),
+ (gog_axis_map_set_by_num),
+ (gog_axis_map_populate_combo),
+ (gog_axis_map_set),
+ (gog_axis_map_init),
+ (gog_axis_map),
+ (gog_axis_map_destroy) : new.
+ (gog_axis_editor) : handle map combobox.
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_render) : use map
+ functions.
+
+2004-06-05 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_get_entry) : add some protection now that
+ this is public.
+ (gog_axis_is_discrete) : ditto.
+
+ * graph/gog-style.c : add sax exporters
+ * graph/gog-error-bar.c : ditto
+
+ * graph/gog-object-xml.h : s/GogPersistDOM/GogPersist
+ * graph/gog-object-xml.c : add sax exporters
+ (go_xml_out_add_color) : new.
+
+ * utils/go-font.c : Added some conditional leak debug tools
+
+2004-06-01 Morten Welinder <terra at gnome.org>
+
+ * gui-utils/go-combo-text.c (go_combo_text_set_text): Plug leak.
+
+2004-05-31 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-view.c (gog_view_find_child_view) : new
+
+2004-05-31 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-series.c (gog_series_get_plot) : new.
+
+ * graph/gog-axis.c (gog_axis_get_entry) : rename from axis_get_entry
+ and make public.
+
+2004-05-24 Jody Goldberg <jody at gnome.org>
+
+ * graph/go-data-simple.c (go_data_vector_str_get_str) : enable the
+ translation mechanism
+
+2004-05-26 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * gui-utils/Makefile.am: added go-combo-text.h
+ * gui-utils/go-action-combo-text.c: (cb_entry_changed),
+ (go_action_combo_create_tool_item),
+ (go_action_combo_text_set_entry):
+ * gui-utils/go-combo-text.h: was gnumeric-combo-text.h
+ * gui-utils/go-combo-text.c: was gnumeric-combo-text.c,
+ replaced GtkList by GtkTreeView
+ * gui-utils/go-marshalers.list: added BOOLEAN:POINTER
+
+2004-05-23 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-chart.c (gog_chart_view_class_init) : set call_parent_render
+ to FALSE.
+ * graph/gog-outlined-object.c (gog_outlined_view_render) : call parent
+ render method only if call_parent_render is TRUE;
+ (gog_outlined_view_class_init) : set call_parent_render to TRUE;
+ * graph/gog-outlined-object.h : add a call_parent_render boolean.
+
+2004-05-20 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=142212
+ * graph/plugins/plot-barcol/gog-line.c (gog_line_view_render) : don't
+ use '0' if Y value is missing.
+
+2004-05-13 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer-svg.c (gog_renderer_svg_measure_text) : use
+ logical rather than ink extents.
+ (gog_renderer_pixbuf_draw_text) : ditto.
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_text) : ditto.
+ (gog_renderer_pixbuf_draw_text) : ditto.
+
+ * graph/gog-axis.c (gog_axis_view_size_request) : tick labels for
+ discrete axes are below the line not the major tick marks.
+ (gog_axis_view_render) : ditto
+
+ * graph/gog-renderer-svg.c (make_layout) : cache the context.
+ (gog_renderer_pixbuf_finalize) : unref cached contexts.
+ (gog_renderer_pixbuf_class_init) : connect the new finalize.
+
+ * graph/gog-style.c (gog_style_is_different_size) : line width change
+ for axis changes size.
+
+ * graph/gog-axis.c (gog_axis_view_render) : don't allocate size for
+ ticks if the lines are invisible.
+
+ * graph/gog-plot.c (gog_plot_get_axis_bounds) : add some safety.
+ This will be called for things like a pie when adding one while
+ something with an still exists.
+
+ * graph/gog-guru.c (cb_attr_tree_selection_change) : be more forgiving
+ about when a user can go back to select a plot. If there is only
+ one chart, or one plot things are unambiguous.
+
+ * drawing/god-property-table.h : sync with the extensions in the xls
+ importer to handle gradients and richtext.
+
+ * drawing/god-property-table.c (god_property_table_get_markup) : new.
+ (god_property_table_set_markup) : ditto.
+
+2004-05-11 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/gog-renderer-pixbuf.c: (make_layout),
+ (gog_renderer_pixbuf_update): replaced deprecated pango_ft2_get_context.
+ * graph/gog-renderer-svg.c: (make_layout): ditto.
+ * graph/gog-style-prefs.glade: replaced GtkOptionMenu by GtkComboBox.
+ * graph/gog-style.c: (cb_gradient_style_changed),
+ (fill_gradient_init), (cb_image_style_changed), (fill_image_init),
+ (cb_fill_type_changed), (fill_init): ditto.
+
+2004-05-10 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-plot.c (gog_plot_foreach_elem) : pass the index of the
+ iteration, not the absolute for has-legend situations
+
+ * graph/gog-series.c (gog_series_set_property) : update cardinality if
+ has-legend changes.
+ (gog_series_editor) : Add a quick 'show in legend' toggle
+
+2004-05-07 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-theme.c (gog_themes_init) : sync the default theme with
+ XL's notion of things so that we do not theme away auto settings on
+ import. Specificly Give chart's the background and make graph
+ empty.
+
+ * */Makefile.am : use goffice.mk for include paths
+
+2004-05-03 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=136363
+ * graph/gog-style.c (gog_style_apply_theme) : Do not theme the fonts
+ it over rides the user selection because there is no 'auto' flag.
+
+2004-05-06 Morten Welinder <terra at gnome.org>
+
+ * utils/go-gradient.c (go_gradient_dir_from_str,
+ go_gradient_dir_as_str): Use G_N_ELEMENTS.
+ (grad_dir_names[]): Constify.
+ (go_gradient_selector): Eliminate elements table.
+
+2004-04-29 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=141405
+ * drawing/god-drawing-renderer-gdk.c
+ (god_drawing_renderer_gdk_render_shape) : explicitly set alignment for
+ now. Set layout bounds so that we wrap.
+
+2004-04-19 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=127411
+ * graph/gog-chart.c (gog_chart_view_render) : render the style.
+
+2004-04-17 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_xy/gog-xy.c (gog_bubble_plot_class_init) : Do not
+ replicate ms dim types. That breaks xls import.
+ (gog_xy_plot_class_init) : ditto.
+ * graph/plugins/plot_barcol/gog-1.5d.c (gog_plot1_5d_class_init) : ditto
+
+2004-04-15 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-error-bar.c (gog_error_bar_prefs): Look for pixmaps in
+ the right directory.
+
+2004-04-08 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/plugins/plot_barcol/gog-1.5d.c: (gog_plot1_5d_update),
+ (gog_plot1_5d_axis_get_bounds), (gog_plot1_5d_class_init),
+ (gog_series1_5d_set_property), (gog_series1_5d_get_property),
+ (gog_series1_5d_populate_editor), (gog_series1_5d_class_init),
+ (gog_series1_5d_init): Add error bars support.
+ * graph/plugins/plot_barcol/gog-1.5d.h: ditto.
+ * graph/plugins/plot_barcol/gog-barcol.c: ditto.
+ (gog_barcol_update_stacked_and_percentage),
+ (gog_barcol_view_render): ditto.
+ * graph/plugins/plot_barcol/gog-line.c: ditto.
+ (gog_line_update_stacked_and_percentage), (gog_line_view_render): ditto.
+ * graph/plugins/plot_xy/gog-xy.c: (gog_2d_plot_update): use gog_error_bar_is_visible instead
+ of detailed tests.
+
+2004-04-05 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=139205
+ * gui-utils/go-action-combo-stack.c
+ (go_action_combo_stack_create_menu_item) : There's no reason to set a
+ default label not only was it ugly, but it disabled the nice utility
+ code in GtkAction::connect_proxy that would set 'use_underline' for us
+
+2004-03-30 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=138532
+ * graph/plugins/plot_radar/gog-radar.c (gog_radar_plot_update) : don't
+ use an invalid series for anything.
+
+2004-03-28 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.3.0
+
+2004-03-26 Christopher James Lahey <clahey at ximian.com>
+
+ * drawing/Makefile.am (libgoffice_drawing_la_SOURCES): Added
+ god-drawing-renderer-gdk.c, god-drawing-renderer-gdk.h,
+ god-drawing-view.c, and god-drawing-view.h.
+
+ * drawing/god-drawing-renderer-gdk.c,
+ drawing/god-drawing-renderer-gdk.h: New class. Renders a drawing
+ to a gdk drawable.
+
+ * drawing/god-drawing-view.c, god-drawing-view.h: New class.
+ Widget to display a drawing.
+
+ * drawing/god-drawing.c, drawing/god-drawing.h: Added a background
+ shape.
+
+ * drawing/god-image.c: Call gdk_pixbuf_loader_close.
+
+ * drawing/god-property-table.c, drawing/god-property-table.h:
+ Added length type and fixed up flag type. Added a bunch of types.
+
+ * drawing/god-shape.c: Allow a NULL anchor.
+
+ * libpresent/Makefile.am (libgoffice_libpresent_la_SOURCES): Added
+ present-view.c and present-view.h.
+
+ * libpresent/load-ppt.c: Handle the DocumentAtom.
+
+ * libpresent/present-presentation.c,
+ libpresent/present-presentation.h: Added extents and notes_extents
+ fields.
+
+ * libpresent/present-view.c, libpresent/present-view.h: New class.
+ Widget to display a presentation.
+
+ * ms-compat/god-image-ms.c: Added a bunch of EscherOPT fields.
+ Handle the patriarch and background shapes more cleanly.
+
+ * test/.cvsignore: Added test-view-ppt.
+
+ * test/Makefile.am (test_view_ppt_SOURCES): Added test-view-ppt.c.
+
+ * test/test-ppt.c: Handle the separate patriarch and background
+ shapes.
+
+ * test/test-view-ppt.c: New Class. Tests present-view.c.
+
+ * utils/go-units.h: Added new unit EMU. (360000 EMUs per
+ centimeter, 914400 EMUs per inch, 12700 EMUs per point.)
+
+2004-03-22 Christopher James Lahey <clahey at ximian.com>
+
+ * drawing/Makefile.am (libgoffice_drawing_la_SOURCES): Added
+ god-image.c, god-image.h, god-image-store.c, and
+ god-image-store.h.
+
+ * drawing/god-drawing-group.c, drawing/god-drawing-group.h: Added
+ an image store to the drawing group. Implemented
+ god_drawing_group_new.
+
+ * drawing/god-drawing.c, drawing/god-drawing.h: Gave each drawing
+ a link to its drawing group.
+
+ * drawing/god-image-store.c, drawing/god-image-store.h: New class.
+ An array of images.
+
+ * drawing/god-image.c, drawing/god-image.h: An image or a
+ placeholder for an image. Includes the original data as well as a
+ loaded pixbuf.
+
+ * libpresent/load-ppt.c: Actually store the drawings in the slides
+ and the drawing groups in the presentations. Load the pictures
+ into the drawing group.
+
+ * libpresent/present-presentation.c,
+ libpresent/present-presentation.h: Added a drawing group here.
+
+ * libpresent/present-slide.c, libpresent/present-slide.h: Added a
+ drawing here.
+
+ * ms-compat/Makefile.am (libgoffice_ms_compat_la_SOURCES): Added
+ god-image-ms.c and god-image-ms.h.
+
+ * ms-compat/god-drawing-ms.c, ms-compat/god-drawing-ms.h: Actually
+ do drawing group loads and load up the image store as we go.
+ Added a function to handle parsing of images not in the main
+ Escher stream.
+
+ * ms-compat/god-image-ms.c, ms-compat/god-image-ms.h: New class.
+ Just a god_image with a hash that's used at load time.
+
+ * test/test-ppt.c: Moved dumping the Drawings here since they're
+ included in the slides now so it makes more sense to dump them
+ here than in load-ppt.c.
+
+2004-03-22 Morten Welinder <terra at gnome.org>
+
+ * libpresent/load-ppt.c (handle_atom): The usual fix.
+
+2004-03-21 Christopher James Lahey <clahey at ximian.com>
+
+ * ms-compat/god-drawing-ms.c (handle_atom),
+ test/dump-ppt-records.c (handle_atom): Moved some of the debugging
+ stuff from god-drawing-ms to dump-ppt-records.
+
+2004-03-20 Christopher James Lahey <clahey at ximian.com>
+
+ * drawing/god-anchor.c (god_anchor_finalize),
+ ms-compat/god-drawing-ms-client-handler.c
+ (god_drawing_ms_client_handler_finalize): Chain the finalize
+ function.
+
+ * libpresent/Makefile.am (libgoffice_libpresent_la_SOURCES): Added
+ present-presentation.c, present-presentation.h, present-slide.c,
+ present-slide.h, present-text.c, present-text.h.
+
+ * libpresent/god-drawing-ms-client-handler-ppt.c,
+ libpresent/god-drawing-ms-client-handler-ppt.h: Add a PresentSlide
+ so that we can parse OutlineTextRefAtom and link it up to the
+ outline.
+
+ * libpresent/load-ppt.c, libpresent/load-ppt.h: Changed it to use
+ PresentPresentation. Pass a PresentSlide into the client_handler
+ if the PPDrawing is a child of a Slide.
+
+ * libpresent/present-presentation.c,
+ libpresent/present-presentation.h, libpresent/present-slide.c,
+ libpresent/present-slide.h, libpresent/present-text.c,
+ libpresent/present-text.h: New classes. A presentation, a slide,
+ and a piece of text on a slide.
+
+ * test/test-ppt.c: Changed this to use PresentPresentation.
+
+2004-03-19 Christopher James Lahey <clahey at ximian.com>
+
+ * libpresent/god-drawing-ms-client-handler-ppt.c
+ (god_drawing_ms_client_handler_ppt_handle_client_text): Handles
+ simple cases with text inside of a shape in Escher.
+
+ * libpresent/load-ppt.c (dump_shape): Print text if there is any.
+
+ * ms-compat/go-ms-parser.c (go_ms_parser_read): Only read the data
+ if there's a handler.
+
+ * ms-compat/god-drawing-ms-client-handler.c,
+ ms-compat/god-drawing-ms-client-handler.h: Make it so subclasses
+ can specify whether they want the super class to read the data
+ from the input stream.
+
+ * ms-compat/god-drawing-ms.c (handle_atom): Handle
+ EscherClientTextbox by calling the client handler.
+
+2004-03-18 Christopher James Lahey <clahey at ximian.com>
+
+ * ms-compat/go-ms-parser.c, ms-compat/go-ms-parser.h
+ (go_ms_parser_read): Included the record type as a field in the
+ record structure.
+
+ * test/.cvsignore, test/Makefile.am: Added dump-ppt-records.
+
+ * test/dump-ppt-records.c: New test app to list all the records in
+ a ppt file.
+
+2004-03-18 Christopher James Lahey <clahey at ximian.com>
+
+ * drawing/god-shape.c, drawing/god-shape.h: Each shape has an
+ anchor. Added functions to get the children of a shape.
+
+ * graph/gog-legend.c (gog_legend_init),
+ graph/gog-outlined-object.c (gog_outlined_object_init),
+ graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_update),
+ graph/gog-renderer-svg.c (make_layout), graph/gog-renderer.c
+ (gog_renderer_init): Cast to double before calling
+ GO_.._TO_.. unit conversion functions.
+
+ * libpresent/Makefile.am (libgoffice_libpresent_la_SOURCES): Added
+ god-drawing-ms-client-handler-ppt.c and
+ god-drawing-ms-client-handler-ppt.h.
+
+ * libpresent/god-drawing-ms-client-handler-ppt.c,
+ libpresent/god-drawing-ms-client-handler-ppt.h: New class.
+ Handles client functions for PPT.
+
+ * libpresent/load-ppt.c: Added #include <gnumeric-config.h>.
+ Created a GodDrawingMsClientHandlerPpt for use in parsing. Added
+ code to dump the Escher drawing for testing purposes.
+
+ * ms-compat/Makefile.am (libgoffice_ms_compat_la_SOURCES): Added
+ god-drawing-ms-client-handler.c and
+ god-drawing-ms-client-handler.h.
+
+ * ms-compat/god-drawing-ms-client-handler.c,
+ ms-compat/god-drawing-ms-client-handler.h: New class. Allows
+ Escher parser to let the host application handle Client records.
+
+ * ms-compat/god-drawing-ms.c, ms-compat/god-drawing-ms.h: Use
+ GodDrawingMsClientHandler to parse EscherClientAnchor records.
+
+ * test/.cvsignore: Ignore test-ppt.
+
+ * test/test-ppt.c: Fixed include here. Added #include
+ <gnumeric-config.h>
+
+ * utils/go-unit.h: Changed these to use the math in whatever type
+ the input is. If you pass a double, it uses double math. If you
+ pass an integer type, it uses integer math.
+
+2004-03-18 Morten Welinder <terra at gnome.org>
+
+ * libpresent/*.h: Add header guards.
+
+2004-03-16 Christopher James Lahey <clahey at ximian.com>
+
+ * Makefile.am (SUBDIRS): Added libpresent and test.
+
+ * drawing/Makefile.am (libgoffice_drawing_la_SOURCES): Added
+ god-anchor.c and god-anchor.h.
+
+ * drawing/Makefile.am, drawing/god-drawing-group.c,
+ drawing/god-drawing.c, drawing/god-drawing.h,
+ drawing/god-property-table.c, drawing/god-shape.c,
+ drawing/god-shape.h, drawing/god-text-model.c,
+ ms-compat/Makefile.am, ms-compat/go-ms-parser.c,
+ ms-compat/god-drawing-ms.c, ms-compat/god-drawing-ms.h:
+ Standardized #include lines.
+
+ * utils/go-units.h: Added an underlying unit and added a type for
+ storing lengths. Added point and rect types.
+
+2004-03-16 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-error-bar-prefs.glade: Don't start visible.
+
+2004-03-16 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-error-bar.c (gog_error_bar_persist_dom_save): Constify
+ and avoid errors.
+
+ * graph/gog-graph.c (gog_graph_validate_chart_layout): Initialize
+ graph.
+
+ * drawing/god-property-table.c (god_property_table_finalize): Chain up.
+ * drawing/god-shape.c (god_shape_dispose): Chain up.
+ * drawing/god-text-model.c (god_text_model_finalize): Chain up.
+ * drawing/god-drawing-group.c (god_drawing_group_finalize): Chain
+ up.
+
+ * gui-utils/go-action-combo-text.c
+ (go_tool_combo_text_finalize): Add dummy chain.
+
+ * graph/plugins/plot_xy/gog-xy.c (gog_2d_plot_update): Initialize
+ series.
+ (gog_xy_series_finalize, gog_2d_finalize): Skip pointless tests.
+
+2004-03-16 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * Makefile.am: restore data subdir
+ * data/Makefile.am: add error bars icons
+ *data/bar-*.png: new icons for error bars editor.
+ * goffice.c: (libgoffice_init): add GOD_ERROR_BAR_TYPE
+ * graph/Makefile.am: add gog-error-bar.*
+ * graph/goffice-graph.h: add GogErrorBar struct
+ * graph/gog-object-xml.c: (gog_object_write_property):
+ set success to FLASE when the object does not exist.
+ * graph/gog-series-impl.h: add populate_editor in GogSeriesClass
+ * graph/gog-series.c: (gog_series_editor): add pages for error bars.
+ * graph/gog-error-bar-prefs.glade:
+ * graph/gog-error-bar.[c,h]: new files.
+ * graph/plugins/plot_xy/gog-xy.c: (gog_2d_plot_update),
+ (gog_xy_plot_class_init), (gog_bubble_plot_class_init),
+ (gog_xy_view_render), (gog_xy_series_init),
+ (gog_xy_series_finalize), (gog_xy_series_set_property),
+ (gog_xy_series_get_property), (gog_xy_series_populate_editor),
+ (gog_xy_series_class_init): add support for error bars.
+
+2004-03-15 Morten Welinder <terra at gnome.org>
+
+ * gui-utils/go-combo-box.c (go_combo_box_destroy): Plug leaks.
+
+2004-03-15 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=135621
+ * graph/gog-axis.c (gog_axis_render) : fix rounding issue.
+
+2004-03-07 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_radar/gog-radar.c (gog_radar_plot_type_name) :
+ remove the starting angle pref. That is better handled by the axis.
+
+2004-03-13 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/plugins/plot_xy/gog-xy.c: (gog_xy_view_render),
+ (gog_xy_series_update), (gog_xy_series_init_style): fixed tests about bubble plots.
+
+2004-03-13 Christopher James Lahey <clahey at ximian.com>
+
+ * Makefile.am (SUBDIRS): Added drawing and ms-compat.
+ (libgoffice_la_LIBADD): Added drawing/libgoffice-drawing.la and
+ ms-compat/libgoffice/ms-compat.la.
+
+2004-03-12 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-series.c (gog_series_element_class_init): Apply the
+ usual fix.
+
+2004-03-11 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/plugins/plot_xy/gog-xy.c : draw markers when they are in plot
+ area and a margin half marker size wide (partly fix 135621).
+
+2004-03-09 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-object.c : make use_parent_as_proxy a class property
+ instead of an instance one.
+ * graph/gog-style.c : defines a new GogStyledObject derived
+ GogSeriesElement object for storing of single element style overrides.
+ (gog_series_class_init) : declares a new GogObjectRole for use of
+ GogSeriesElement objects. Set use_parent_as_proxy as true.
+ (gog_series_get_elements) : returns the GList of GogSeriesElement
+ children of GogSeries.
+ (gog_series_get_valid_element_index) : returns the next or previous
+ index which is not already used by a GogSeriesElement children of
+ GogSeries.
+ * graph/gog-style.c : remove unused implementation of
+ GogStyleExtension and old gog_series_element_style code.
+ (gog_style_editor) : remove code related to GogStyleExtension.
+ (gog_style_assing) : idem.
+ (gog_style_persist_dom_load) : idem.
+ (gog_style_persist_dom_save) : idem.
+ * graph/gog-syled-object.c (gog_styled_object_set_property) : use of
+ gog_styled_object_set_style.
+ (gog_styled_object_set_style) : new.
+ * graph/plugins/plot_pie/gog-pie-series-element-prefs.glade : new.
+ * graph/plugins/plot_pie/gog-pie-prefs.c (gog_pie_series_element_pref)
+ : new.
+ * graph/plugins/plot_pie/gog-pie.c : define a new GogSeriesElement
+ derived GogPieSeriesElement object for storage of separation and style
+ of single elements.
+ (gog_pie_view_render) : handle single element style overrides.
+ * graph/plugins/plot_pie/gog-ring-prefs.glade : fix climb_rate, digits
+ and adjustment properties of the separation spin_button.
+
+2004-03-03 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=136088
+ * graph/gog-object-xml.c (gog_object_write_property) : Add a
+ GOG_PARAM_FORCE_SAVE to save a parameter even if the value is the
+ same as the default.
+ * graph/gog-plot.c (gog_plot_class_init) : Use it here to always store
+ the vary_style_by_element property.
+
+2004-02-27 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-theme.c (map_area_series_solid_default) : don't set colors
+ for image fills.
+ (map_area_series_solid_guppi) : ditto.
+
+2004-02-23 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_radar/gog-radar.c : use
+ 'default-style-has-markers' rather than 'markers' to be consistent.
+ (gog_radar_plot_update) : store the minima and maxima from all the
+ series.
+ (gog_radar_plot_axis_get_bounds) : renamed from gog_radar_plot_axis_bounds
+ for consistency. Set the bounds for a radial axis more generally.
+ We want the outbound to be hard (unscaled) and the inner rounded.
+
+2004-02-22 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-chart.c (gog_chart_view_size_allocate) : only position th
+ axis, not all the children.
+
+ * graph/plugins/plot_radar/gog-radar.c
+ (gog_radar_view_render_series) : Just alloca the max numbr of points
+ aka model::num_elements, and don't free alloca-ed memory.
+
+2004-02-22 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/plugins/plot_radar/gog-radar.c
+ (gog_plot_radar_render_series) : use g_new instead of g_alloca for
+ allocation of path.
+
+2004-02-21 Jody Goldberg <jody at gnome.org>
+
+ * utils/go-locale.c : Some cut-n-paste for the without_gnome case
+
+ * gui-utils/go-action-combo-text.c : some initial implementation
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_plot_foreach_elem) :
+ handle mismatched numbers of elements and labels.
+ (gog_pie_view_render) : take the outline into account when sizing.
+
+ * graph/gog-style.h : add weak notion of centered image. This needs
+ to be stronger to specify alignement.
+
+ * graph/gog-style.c (gog_style_set_fill_image_filename) : new.
+ (cb_image_file_select) : Use it.
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_polygon) :
+ supported centered images.
+
+ * graph/gog-renderer-gnome-print.c
+ (gog_renderer_gnome_print_draw_polygon) : support centered images.
+
+ * graph/gog-object.c (gog_object_get_children) : add a filter parm.
+ (gog_object_get_child_by_role) : new utility routine.
+
+ * graph/gog-legend.c (gog_legend_update) : set up the editor properly
+ for fonts.
+
+ * graph/gog-graph.c (gog_graph_view_size_allocate) : typo. Only
+ effected manually created plots
+
+ * graph/gog-control-foocanvas.c (gog_control_foocanvas_draw) : make
+ more resistent to out of memory situations.
+
+ * graph/go-data-simple.c : Add some quick and dirty constant vectors
+ for double [], and char const *[]. These could certainly be more
+ robust.
+
+ From Michael Devine <mdevine at cs.stanford.edu> :
+ * graph/goffice-graph.h : Add a first pass at 'radar' plots
+ * graph/gog-axis.c : ditto.
+ * goffice/graph/gog-chart.c : ditto
+
+2004-02-13 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-style.c (gog_style_extension_editor): Fix declaration
+ and check order.
+
+2004-02-13 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-style.[ch] : new GogStyleExtension object.
+ (gog_style_assign) : handle extension.
+ (gog_style_editor) : shows extension editor if it exists.
+ (gog_style_get_extension) : new.
+ (gog_style_set_extension) : new.
+ * graph/gog-series : add a new style_extension_type field in
+ GogSeriesDesc, and use it in gog_series_init_style.
+
+2004-02-09 Morten Welinder <terra at gnome.org>
+
+ * graph/plugins/plot_xy/gog-bubble-prefs.glade: Do not start
+ visible.
+
+2004-02-09 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/plugins/plot_xy/gog-xy.c: (gog_xy_view_render): replaced
+ test series.num_dim == 3 by GOG_IS_BUBBLE_PLOT
+ * graph/plugins/plot_xy/gog-xy.h: fixed GOG_IS_BUBBLE_PLOT
+
+2004-02-04 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-color-palette.c (cb_menu_custom_activate) : don't show
+ until after the signal in case a handler is marking the dialog as a
+ transient.
+ (cb_combo_custom_clicked) : ditto.
+
+2004-02-02 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/plugins/plot_xy/gog-xy.c: fixed several warnings
+
+2004-02-02 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-combo-color.c (cb_preview_clicked) : return the real
+ is_custom state.
+
+ * gui-utils/go-color-palette.c (handle_color_sel) : simplify and just
+ return the color caller can emit the signal, and store the new
+ colour.
+ (cb_combo_custom_response) : store results.
+ (cb_menu_default_activate) : ditto.
+ (cb_menu_color_activate) : ditto.
+ (cb_menu_custom_response) : ditto.
+ (set_color) : store when something is a custom of default.
+ (go_color_palette_get_current_color) : return a flag indicating if
+ this is custom.
+
+2004-02-01 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-color-palette.c (handle_color_sel) : destroy before we
+ emit in case the custom dialog handler played with the wrapper
+ actions (eg desensitized them)
+
+ * gui-utils/go-combo-color.c (cb_proxy_custom_dialog) : pop the combo
+ down when the custom dialog goes up.
+
+2004-01-29 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.c (cb_gradient_type_changed) : store the last
+ selected type.
+ (populate_gradient_combo) : default to that when changing fill type to
+ gradient.
+
+ * graph/gog-style-prefs.glade : fix mnemonics to not clash.
+
+ * gui-utils/go-action-combo-stack.c
+ (go_action_combo_stack_create_tool_item) : patch leak
+
+ * utils/go-marker.c (go_marker_selector) :
+ go_combo_pixmaps_add_element absorbs a ref to the pixbuf, don't lose
+ control of it. Handle shape=none nicely too.
+
+2004-01-29 Morten Welinder <terra at gnome.org>
+
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_render): Attempt
+ fix.
+
+2004-01-28 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/plugins/plot_xy/gog-xy.c: fix 2 syntax errors to make it
+ compile
+
+2004-01-28 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/plugins/plot_xy/Makefile.am:
+ * graph/plugins/plot_xy/gog-bubble-prefs.glade:
+ * graph/plugins/plot_xy/gog-bubble-prefs.c:
+ * graph/plugins/plot_xy/gog-xy.c:
+ * graph/plugins/plot_xy/gog-xy.h:
+ added support for bubble plots options
+
+2004-01-24 Jon K Hellan <hellan at acm.org>
+
+ * gui-utils/go-dock.c (go_dock_class_init): Initialize parent_class.
+
+ * gui-utils/go-dock-item.c (go_dock_item_class_init): Ditto.
+
+ * gui-utils/go-dock-band.c (go_dock_band_class_init): Ditto.
+
+2004-01-24 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_xy/gog-xy.c : suppress warning
+
+ * gui-utils/go-action-combo-text.c (go_action_combo_text_set_entry) :
+ implement.
+
+ * gui-utils/go-action-combo-stack.c
+ (go_action_combo_stack_create_tool_item) : set the relief here too.
+ We're close to having a GOActionCombo base for this.
+
+2004-01-23 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-action-combo-stack.c : tweak to make it sorta work.
+ I do not like the kludgy approach required by our not supporting
+ arguments to GtkAction::activate
+
+2004-01-22 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-action-combo-pixmaps.c : rewrite.
+
+2004-01-17 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-action-combo-color.c (cb_color_changed) : Use the
+ supplied color directly.
+ (go_action_combo_color_create_menu_item) : connect up the activate signal.
+
+2004-01-22 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+ * graph/gog-style.c (gog_style_editor) : edit a GogStyle, not
+ GogStyledObject.
+ (gog_styled_object_editor) : new.
+ * graph/gog-axis.c : use gog_styled_object_editor instead of
+ gog_style_editor.
+ * graph/gog-label.c : ditto.
+ * graph/gog-series.c : ditto.
+ * graph/gog-styled-object.c : ditto. rename gog_styled_object_editor
+ to styled_object_editor.
+
+2004-01-17 Jean Brefort <jean.brefort at ac-dijon.fr>
+ * graph/plugins/plot_xy/* : add initial support for bubble plots
+
+2004-01-16 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-action-combo-stack.c (go_action_combo_stack_push) :
+ operate on the combo, not the toolitem wrapper.
+ (go_action_combo_stack_pop) : ditto.
+ (go_action_combo_stack_truncate) : ditto.
+
+2004-01-16 Jon K Hellan <hellan at acm.org>
+
+ * gui-utils/go-action-combo-stack.c (go_action_combo_stack_push)
+ (go_action_combo_stack_pop): Fix typos.
+
+2004-01-14 Jody Goldberg <jody at gnome.org>
+
+ * gui-utils/go-action-combo-color.c
+ (go_action_combo_color_create_menu_item) : some initial work on
+ producing a menu. Its not bad, but the combo needs alot of cleanup
+ before it can migrate.
+
+2003-12-23 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.2.3
+
+2003-12-22 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_editor) : set the current format.
+
+2003-12-19 Jody Goldberg <jody at gnome.org>
+
+ * utils/go-format.c (go_format_eq) : new.
+ (go_format_as_XL) : new
+ (go_format_new_from_XL) : new
+
+ * graph/gog-axis.c (gog_axis_editor) : add a format selection page for
+ non-discrete axis.
+
+2003-12-18 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=129445
+ * graph/gog-style.c (gog_style_assign) : transfer the
+ needs_obj_defaults field.
+ (gog_style_init) : init needs_obj_defaults field to TRUE.
+ (gog_style_persist_dom_load) : persisted styles do not need object
+ defaults.
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_series_init_style) :
+ respect the needs_obj_defaults.
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_series_init_style) : ditto.
+
+ * graph/gog-axis.c (gog_axis_get_marker) : Use go_format to handle the
+ markers.
+
+ * utils/go-format.c : A quick cheesy wrapper to GnmFormat in
+ preparation for sucking it down here.
+
+2003-12-14 Jody Goldberg <jody at gnome.org>
+
+ * utils/go-pattern.c (go_pattern_selector) : fix leak.
+ * utils/go-gradient.c (go_gradient_selector) : fix leak.
+ * utils/go-marker.c (go_marker_selector) : fix leak.
+
+2003-12-14 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=128874
+ * graph/gog-axis.c (gog_axis_finalize) : unref labels.
+ (gog_axis_update) : ref them here.
+
+2003-11-26 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.2.2
+
+2003-11-18 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-styled-object.c (gog_styled_object_set_property) : dup the
+ style to avoid themes stomping on shared styles. refcounting is not
+ an ideal semantic for GogStyle.
+
+2003-11-17 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-object-xml.c (gog_object_write_property): Plug leak.
+
+2003-11-15 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_view_render) : support minor ticks and
+ tune placement to work around anti-aliasing blur.
+
+ * graph/gog-grid.c (gog_grid_view_render) : tune the cheat to work
+ around anti-aliasing blur.
+
+2003-11-14 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=126775
+ * graph/gog-axis.c (gog_axis_update) : be smarter when min == max.
+
+2003-11-13 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-guru.c (cb_obj_children_reordered) : implement.
+ (cb_reordered_find) : new.
+
+ * graph/gog-object.c (gog_object_can_reorder) : implement.
+
+2003-11-13 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-barcol.c (gog_barcol_view_render) :
+ avoid the potential for trouble for a series with only zeros.
+
+2003-11-12 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-guru.c (graph_guru_init_format_page) : store the
+ precedence widgets.
+ (cb_attr_tree_selection_change) : adjust sensitivity of the precedence
+ elements.
+
+2003-11-11 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.c (marker_init) : fix leak.
+
+ * graph/gog-guru.c (cb_attr_tree_selection_change) : work around a
+ probable bug in GtkViewport by adding an extra GtkFrame inside the
+ viewport and using that.
+
+2003-11-11 Morten Welinder <terra at gnome.org>
+
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_render): Use finite
+ consistently.
+
+2003-11-11 Jody Goldberg <jody at gnome.org>
+
+ * utils/go-pattern.c (go_pattern_selector) : add auto support.
+
+ * utils/go-marker.c : move the default handling where it belongs.
+
+ * utils/go-color.c (go_color_to_gdk) : new.
+
+ * graph/plugins/plot_barcol/plot-types.xml.in : include non-marker
+ variants.
+ * graph/plugins/plot_xy/plot-types.xml.in : include non-marker, and
+ barcol non-line variants.
+
+ * graph/gog-theme.c : respect the auto flags here.
+
+ * graph/gog-styled-object.c (gog_styled_object_apply_theme) : new.
+ (gog_styled_object_set_property) : Use it here.
+ (gog_styled_object_parent_changed) : and here.
+ (gog_styled_object_editor) : provide a default impl.
+ (gog_styled_object_get_auto_style) : new.
+
+ * graph/gog-style.c :
+ - Add support for auto* flags for the marker here, not GoMarker.
+ - Support restoring auto for shapes and colours
+
+ * graph/gog-series.c (gog_series_init_style) : new. Gives us finer
+ control of what is themable.
+
+ * graph/gog-guru.c : Simplify our lives and only have 1 prop sheet at
+ any given time. No need to worry about updating when something
+ changes externally (styles or dimensions)
+
+ * graph/gog-chart.c : Use the default editor and init_style
+ * graph/gog-graph.c : ditto
+
+ * graph/gog-axis.c : convert from ::interesting_fields -> ::init_style
+ * graph/gog-grid.c : ditto.
+ * graph/gog-label.c : ditto.
+ * graph/gog-legend.c : ditto.
+
+2003-11-05 Jody Goldberg <jody at gnome.org>
+
+ * utils/go-marker.h : I don't want GtkWidget in these headers
+
+2003-11-04 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=125986
+ * graph/gog-style.c (gog_style_merge) : A cheesy solution to the
+ current lack of clarity on the theming. We only have a
+ colour.is_auto flag, so only assign the colour. This means that
+ lines and outlines can only theme the colour, not the width or the
+ pattern. Which seems reasonably until we have a plan for defining
+ 'autoness' for those attributes.
+
+2003-11-03 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=126056
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_render) : differentiate
+ between missing and bad strings. X axis strings are indicies, Y
+ axis strings are 0.
+
+2003-11-02 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.c (gog_style_editor) : Add a weakref so that we can
+ disconnect the signal if the object is destroyed.
+
+ * graph/gog-axis.c (make_dim_editor) : Use closures to avoid having a
+ callback after a widget has been destroyed.
+
+2003-11-02 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=125419
+ * graph/gog-renderer.[ch] (gog_renderer_start_clipping),
+ (gog_renderer_stop_clipping) : new. Implement clipping of drawing.
+ Only one level of clipping is allowed.
+ * graph/gog-renderer[svg, pixbuf, gnome-print]
+ (gog_renderer_.._start_clipping),
+ (gog_renderer_.._stop_clipping) : new.
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_path),
+ (gog_renderer_pixbuf_draw_polygon),
+ (gog_renderer_pixbuf_draw_text) : handle offset for drawing when
+ clipping.
+ * graph/gog-rendere-gnome-print.c (make_rectangle_path) : new;
+ * graph/gog-chart.c (gog_chart_render) : new. Use the same clipping
+ region for all plots.
+
+2003-11-01 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/gog-renderer-svg.c: added scale parameter to gog_graph_export_to_svg.
+ * graph/gog-renderer-svg.c: implement gog_renderer_svg_measure_text and
+ gog_renderer_svg_draw_text.
+
+2003-11-01 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-barcol.c
+ (gog_barcol_plot_class_init) : set the default to match the real
+ default so that things persist properly.
+
+2003-10-30 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-plot.c (gog_plot_get_cardinality) : Set the index even if
+ the series is invald. This makes like easier when adding a series.
+
+ * graph/gog-style.c (gog_object_set_style) : block style change
+ signals.
+ (gog_style_pref_state_free) : disconnect the style change handler.
+ (gog_style_editor) : monitor style changed signals.
+ (cb_style_changed) : new.
+
+ * graph/gog-series.c (gog_series_set_index) : signal when the style
+ changes.
+
+ * graph/gog-object.c (gog_object_get_editor) : force an update before
+ creating an editor to avoid flicker later.
+ * graph/gog-graph.c (gog_graph_force_update) : new.
+
+2003-10-29 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (cb_axis_bound_changed) : update all the auto
+ bounds when anything changes.
+
+2003-10-28 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_render) : be lazy and
+ do the clipping in the renderer rather than here. It will make
+ life easier when we add splines.
+
+2003-10-27 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=125619
+ * graph/gog-axis.c (gog_axis_editor) : init the high/low button
+
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render) : clip
+ markers and lines.
+
+ * graph/gog-renderer-pixbuf.c (clip_path) : new.
+ (gog_renderer_pixbuf_draw_path) : support the new clipping argument.
+ (gog_renderer_pixbuf_draw_polygon) : ditto.
+
+2003-10-23 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=125045
+ * graph/gog-axis.c (gog_axis_view_render) : clip correctly and space
+ the ticks based on the actual tick values, not just their index.
+ (gog_axis_num_markers) : return a step fraction too.
+
+2003-10-21 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer.c (gog_renderer_init) : init zoom to 1.
+ * graph/gog-renderer-gnome-print.c (gog_graph_print_to_gnome_print) :
+ ditto.
+
+2003-10-14 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/gog-axis.c (axis_get_entry) : Add a user_defined parameter in
+ order to know if the returned value is defined by user or computed.
+ (gog_axis_update) : Use user defined bound for the tick spacing
+ calculation.
+ (gog_axis_num_markers) : round to nearest value instead of the
+ automatic double to int cast that removes the fractionnal part.
+
+2003-10-18 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-chart-impl.h : Inherit from GogOutlinedObject and use the
+ padding there.
+ * graph/gog-graph-impl.h : ditto.
+ * graph/gog-label.c : ditto.
+ * graph/gog-legend.c : ditto.
+
+ * graph/gog-chart.c (gog_chart_get_property) : padding is in outlined
+ object now.
+ (gog_chart_set_property) : delete.
+ (gog_chart_class_init) : delete set_prop method and padding_pts prop
+ (gog_chart_init) : delete padding_pts.
+ (gog_chart_view_size_allocate) : use outlined view.
+ (gog_chart_view_render) : ditto.
+ (gog_chart_view_class_init) : just use render in outlined view.
+
+ * graph/gog-graph.c (gog_graph_set_property) : remove PADDING
+ (gog_graph_get_property) : ditto.
+ (gog_graph_class_init) : ditto.
+ (gog_graph_init) : we don't inherit directly from StyledObject anymore
+ (gog_graph_view_size_allocate) : use OutlinedView.
+ (gog_graph_view_render) : ditto.
+ (gog_graph_view_class_init) : ditto.
+
+ * graph/Makefile.am : Add gog-outlined-object.[ch]
+
+2003-10-13 Jody Goldberg <jody at gnome.org>
+
+ http://bugzilla.gnome.org/show_bug.cgi?id=122546
+ * graph/plugins/plot_barcol/gog-1.5d.c (gog_plot1_5d_update) : Update
+ the index axis if our labels change. This problem does not apply to
+ XY plots because their x bounds will change when a dim is set or
+ cleared.
+
+2003-10-08 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.2.1
+
+2003-10-06 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/gog-renderer-svg.c (gog_renderer_svg_draw_polygon) : use go_pattern_is_solid.
+ (gog_renderer_svg_draw_marker) : implemented.
+
+2003-10-08 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (role_label_post_add) : put y axis labels to the
+ left or right not at the top.
+ (gog_axis_class_init) : enable y axis labels
+
+2003-10-07 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_dataset_dims) : enable setting the cross
+ point.
+
+2003-10-07 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_update): Fake floor, not fake trunc.
+
+2003-10-06 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-plot.c (gog_plot_get_axis_bounds): Return NULL, not
+ FALSE.
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_point): Ditto.
+
+2003-10-05 Jon K Hellan <hellan at acm.org>
+
+ * utils/go-pattern.c (go_pattern_selector): gtk_combo_box renamed
+ to gnm_combo_box.
+
+ * utils/go-marker.c (go_marker_selector): Ditto.
+
+ * utils/go-gradient.c (go_gradient_selector): Ditto.
+
+ * graph/gog-style.c (create_color_combo): Ditto.
+
+2003-10-05 J.H.M. Dassen (Ray) <jdassen at debian.org>
+
+ * graph/gog-guru.c: #include gtkliststore.h to fix build.
+
+2003-09-30 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render) : we
+ already winnowed out the invalid series, no need to flag them.
+ * graph/plugins/plot_barcol/gog-barcol.c (gog_barcol_view_render) :
+ ditto.
+
+2003-09-28 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.c (gog_style_is_marker_visible) : Use the
+ interesting field too.
+ (gog_style_assign) : Assign the interesting flags too.
+
+ * graph/gog-legend.c (cb_size_elements) : see if anything has a marker
+ (gog_legend_view_size_request) : if any of the elements has a marker
+ rather than a swatch we need 3x swatch width.
+ (cb_render_elements) : If entry has a marker draw the line and marker
+ (gog_legend_view_render) : Prep for drawing markers if any exist.
+
+2003-09-29 Morten Welinder <terra at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render):
+ Survive getting a NULL vals, even though that might be a symptom
+ of something else.
+
+ * graph/gog-object.c (gog_object_dup): Plug leak.
+
+ * graph/gog-object-xml.c (gog_object_new_from_xml): Plug leaks.
+
+2003-09-23 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.h : delete unused enum GogAxisTickLevel.
+ Publicize the data elements.
+ (gog_axis_update) : Manually handle the epsilon shifts so that we can
+ ignore sign.
+
+2003-09-22 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-view.c (cb_remove_child) : doh!
+
+2003-09-21 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer-pixbuf.c (make_layout) : scale the font size
+ manually to work around.
+ http://bugzilla.gnome.org/show_bug.cgi?id=121543
+
+2003-09-15 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/gog-renderer-svg.c : added gradient support.
+
+ * utils/go-gradient.c (go_gradient_setup): changed vector for oblique gradients.
+
+2003-09-20 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-1.5d.c (gog_plot1_5d_axis_bounds) :
+ only valid series can contribute to an index dimension.
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_plot_axis_bounds) : ditto.
+
+ * graph/gog-renderer-gnome-print.c (get_font) : respect zoom.
+
+ * graph/gog-style.c (font_init) : set the font _before_ connecting the
+ signal. doh!
+
+2003-09-16 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-theme.c (gog_themes_init) : change the default theme for
+ Adrian.
+
+2003-09-16 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-renderer-pixbuf.c (make_layout): Change to way font
+ description is set so that it will work both before and after the
+ fix to http://bugzilla.gnome.org/show_bug.cgi?id=121543.
+
+2003-09-15 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-label.c (gog_label_view_size_request) : empty labels are
+ of size 0,0 not 1,1
+
+ * graph/gog-view.c (gog_view_size_allocate_real) : don't pad if the
+ child is 0 sized.
+ (gog_view_size_child_request) : ditto.
+
+ * graph/gog-axis.c (gog_axis_num_markers) : we can't use the max_val
+ as a fallback if there is no data defining it.
+
+2003-09-15 Morten Welinder <terra at gnome.org>
+
+ * utils/go-marker.c (go_marker_selector): Terminate the array as
+ it is shorter than we tell the pixbuf combo.
+
+2003-09-15 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.2.0
+
+2003-09-15 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer.c (gog_renderer_measure_text) : pango does not
+ like measuring ""
+ (gog_renderer_draw_text) : ditto.
+
+2003-09-15 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_view_render) : clipping the label text in
+ the iterating dimension is a bad idea, we want to clip whole labels,
+ not the individual lines.
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_view_render) : only draw
+ the first series for a pie. Things can sneak in.
+
+ * utils/go-font.c (go_font_init) : make default font smaller
+
+ * graph/gog-object.c (gog_object_get_pos) : new.
+ (gog_object_set_pos) : new.
+
+2003-09-14 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_editor) : We ignore the bounds for a
+ discrete axis.
+ (gog_axis_update) : store information pertaining to axes with discrete
+ enumerations.
+ (gog_axis_editor) : discrete enumerations do not allow for manual boun
+ changes.
+ (gog_axis_num_markers) : handle discrete enumerations.
+ (gog_axis_get_marker) : ditto.
+ (gog_axis_view_size_request) : ditto.
+ (gog_axis_view_render) : rework to support in and out ticks.
+ Support discrete enumerations.
+ (gog_axis_is_discrete) : new.
+ (gog_axis_get_ticks) : new.
+
+ * graph/gog-renderer.c (gog_renderer_draw_text) : revamp the interface
+ yet again to make parameters clearer.
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_text) : adjsut
+ to the new interface and imrpve clipping support.
+ * graph/gog-renderer-gnome-print.c (gog_renderer_gnome_print_draw_text) :
+ adjust to the new interface, add support for anchors.
+
+2003-09-14 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * gog-style-prefs.glade: `colour' should be `color'
+
+2003-09-14 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-renderer-gnome-print.c: replace calls of
+ gnome_font_find_closest_from_weight_slant with
+ gnm_font_find_closest_from_weight_slant
+
+2003-09-14 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (axis_get_entry) : new utility.
+ (gog_axis_get_bounds) : use it here.
+ (gog_axis_num_markers) : here.
+ (gog_axis_get_marker) : and here.
+ (role_label_can_add) : new.
+ (gog_axis_class_init) : use it here to disable labels on the Y axis
+ until we can get a smarter layout engine.
+
+2003-09-14 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_update) : yet another layer of heuristic
+ to handle -1 .. 1 better.
+ (gog_axis_get_marker) : force to 0.
+
+ * graph/plugins/plot_barcol/gog-barcol.c (gog_barcol_view_render) :
+ Get the bounds from the value axis. (fixes bar plots)
+
+2003-09-13 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (cb_enable_dim) : get smarter and handle
+ transitions to from auto state.
+
+2003-09-13 Jon K Hellan <hellan at acm.org>
+
+ * graph/gog-style.c (cb_fg_color_changed, cb_bg_color_changed,
+ cb_fill_gradient_start_color, cb_fill_gradient_end_color): Update
+ is_auto and pattern_fore_auto, pattern_back_auto,
+ gradient_start_auto, gradient_end_auto flags.
+ (gog_style_merge): Only merge color attributes for fill.
+ (gog_style_init): Initialize attern_fore_auto, pattern_back_auto,
+ gradient_start_auto, gradient_end_auto flags
+
+ * graph/gog-style.h: Add non-persistent auto flags for each color
+ element in fill.
+
+2003-09-13 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (role_label_post_add) : be smarter about
+ positioning.
+
+ * graph/gog-view.c (gog_view_size_child_request) : hard code some
+ inter-child padding. We can be more elegant in the future.
+ (gog_view_size_allocate_real) : ditto.
+
+ * graph/gog-axis.c (gog_axis_view_size_request) : request space for
+ children too.
+
+2003-09-13 Jon K Hellan <hellan at acm.org>
+
+ * graph/gog-legend.c (cb_render_elements): Allow for floating
+ point inaccuracies when deciding if there is room for an element.
+
+2003-09-13 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_view_render) : support index
+ axis for X.
+ (gog_xy_series_update) : ditto.
+
+ * graph/gog-axis.c (gog_axis_editor) : we want font too.
+ Align the Auto header nicely.
+ (make_dim_editor) : some initial work to give the Auto button's some
+ feedback.
+
+ * graph/gog-style.c (cb_line_color_changed) : set auto_color.
+ (cb_outline_color_changed) : ditto.
+
+2003-09-13 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-chart.c (gog_chart_axis_set_assign) : clear_parent clears
+ ::grid, store the pointer for a moment so that we can free it.
+
+ * utils/go-color.c (color_combo_get_gocolor) : cheap hack to support
+ alpha channel despite interface limitations. It only works for
+ custom colours, but thats the most important case.
+ (color_combo_set_gocolor) : init the custom picker too to ensure that
+ alpha gets set correctly.
+
+ * utils/go-gradient.c (go_gradient_selector) : no need to free images,
+ have the combo absorb them.
+ * utils/go-pattern.c (go_pattern_selector) : ditto.
+ fix cut-n-paste-o the first
+ 'Thin Diagonal Crosshatch' was actually
+ 'Thin Diagonal Stripe'
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_text) : leak.
+
+2003-09-12 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_set_property) : fix typos.
+ (gog_axis_view_render) : actually support some of the tick options.
+ (gog_axis_editor) : hook up some of the controls.
+
+ * graph/gog-chart.c (gog_chart_axis_set_assign) : add or remove a grid
+ depending on the axis set.
+
+2003-09-11 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-object-xml.c (gog_object_write_property) : don't bother
+ saving parameters with default values.
+
+ * graph/plugins/plot_barcol/gog-line.c (gog_line_view_render) :
+ respect axis bounds.
+
+ * graph/gog-axis.c (gog_axis_view_render) : invert alignment of text
+ and support tick marks.
+ (gog_axis_view_size_request) : Make axis labels optional, and allocate
+ size for major and minor ticks.
+ (gog_axis_class_init) : Lots of new options.
+ (gog_axis_get_property) : ditto, not all of them are supported yet.
+ (gog_axis_set_property) : ditto, not all of them are supported yet.
+ (gog_axis_update) : Add pull to 0, and step doubling heuristics.
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_text) : Clip
+ text trying to draw out of the physical bounds.
+
+ * graph/plugins/plot_barcol/gog-1.5d.c (gog_plot1_5d_axis_bounds) :
+ fix cut-n-paste-o.
+
+2003-09-10 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.1.90
+
+2003-09-09 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-styled-object.c (gog_styled_object_parent_changed) : set
+ the interesting fields when all the parents are in place.
+
+ * graph/gog-renderer.h : Add an anchor parm to draw_text and clean up
+ the semantics of size.
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_text) : handle
+ the various anchor types.
+
+ * graph/gog-axis.c : Some initial work at generating bounds, and
+ drawing axis values.
+
+ * graph/gog-view.c (gog_view_render) : filter objects with invalid
+ size. eg a user makes the plot too small to be useful.
+
+2003-09-09 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-1.5d.c (gog_plot1_5d_update) :
+ percentage charts logicly limit -1 .. 1
+
+ * graph/plugins/plot_xy/gog-xy.c (gog_xy_plot_update) : no logical min
+ or max.
+
+2003-09-08 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/plot-types.xml.in : fix icons to match
+ reality. All lines currently have markers by default.
+
+ * graph/plugins/plot_barcol/gog-line.c
+ (gog_line_update_stacked_and_percentage) : Generate the correct bounds.
+
+ * graph/plugins/plot_barcol/gog-barcol.c
+ (gog_barcol_update_stacked_and_percentage) : Why special case 0..1
+ (gog_barcol_view_render) : handle bound clipping.
+
+2003-09-08 Jon K Hellan <hellan at acm.org>
+
+ * graph/gog-renderer-svg.c (gog_renderer_svg_draw_marker): New
+ dummy implementation.
+ (gog_renderer_svg_class_init): Use it.
+
+2003-09-08 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.c (cb_fill_gradient_end_color) : throw the update
+ into a timer to decrease flicker.
+ Works around a bug in older libarts.
+ (gog_style_set_fill_brightness) : new.
+ (cb_gradient_brightness_value_changed) : actually hide the brightness.
+ (cb_gradient_style_changed) : ditto.
+ (fill_gradient_init) : ditto.
+
+ * graph/gog-renderer-svg.c (gog_graph_export_to_svg) : convert
+ interface to use libgsf.
+
+2003-09-07 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * utils/go-gradient.c (go_gradient_setup): added a small value to gradient->c for
+ vertical gradients to avoid a line whith the bad color at the top of the area.
+ * graph/gog-style.c: handle brightness in gradients.
+ * graph/gog-renderer-svg.[c,h]: new files to handle svg export (not
+ fully implemented).
+ * graph/Makefile.am: added gog-renderer-svg.[c,h].
+
+2003-09-07 Jody Goldberg <jody at gnome.org>
+
+ * utils/go-marker.c (go_marker_selector) : Add marker names.
+
+2003-09-07 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * utils/go-marker.c : removed unused property and signal stuff.
+ (go_marker_selector) : new.
+ * graph/gog-style.c (populate_marker_combo) : new.
+ (marker_init) : use the marker combo.
+ * graph/gog-style-prefs.glade : removed marker option menu.
+ * graph/gog-renderer-gnome-print.c
+ (gog_renderer_gnome_print_draw_marker): returns when one of the path
+ is NULL (fix a crash).
+
+2003-09-07 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-styled-object.c (gog_styled_object_set_property) : set the
+ interesting field for a style.
+ (gog_styled_object_init) : ditto.
+ (gog_styled_object_interesting_fields) : default to outline & fill.
+
+ * graph/gog-style.c (gog_style_persist_dom_save) : only save
+ interesting fields.
+
+ * graph/gog-series.c (gog_series_interesting_fields) : new.
+ (gog_series_class_init) : hook it up.
+ * graph/gog-legend.c (gog_legend_interesting_fields) : new.
+ (gog_legend_class_init) : hook it up.
+ * graph/gog-label.c (gog_label_interesting_fields) : new.
+ (gog_label_class_init) : hook it up.
+ * graph/gog-axis.c (gog_axis_interesting_fields) : new.
+ (gog_axis_class_init) : hook it up.
+
+2003-09-07 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.c (gog_style_line_load) : support auto_color flags.
+ (gog_style_line_save) : ditto.
+ (gog_style_fill_load) : handle is_auto.
+ (gog_style_fill_save) : ditto.
+
+ * graph/gog-legend.c (gog_legend_view_render) : tune the padding.
+ (cb_render_elements) : ditto.
+
+2003-09-06 Jon K Hellan <hellan at acm.org>
+
+ * graph/gog-theme.c (gog_theme_init_style): Merge styles instead
+ of assigning style from theme.
+ (map_area_series_solid_default): Only change elements which hold
+ auto values.
+
+ * utils/go-marker.[ch] (go_marker_shape_from_str,
+ go_marker_shape_as_str): Move from gog-style.c and rename.
+ (go_marker_is_auto): New. Tests if marker is different from
+ default. Should really test if user has chosen the marker
+ explicitly.
+
+ * graph/gog-style.c (gog_style_merge): Implement.
+ (str_as_marker_shape, marker_shape_as_str):Move to utils/go-marker
+ and rename.
+ (gog_style_marker_load, gog_style_marker_save): Use renamed
+ version of above functions.
+ (gog_style_persist_dom_load): Mark style elements read from file
+ as non-auto (for now).
+
+2003-09-06 Jon K Hellan <hellan at acm.org>
+
+ * utils/go-pattern.[ch] (go_pattern_as_str, go_pattern_as_str): Move
+ from gog-style.c and rename.
+
+ * utils/go-gradient.[ch] (go_gradient_dir_from_str)
+ (go_gradient_dir_as_str): Ditto.
+
+ * graph/gog-style.c (str_as_pattern, pattern_as_str,
+ str_as_grad_dir, grad_dir_as_str): Move to utils/go-pattern /
+ go-gradient and rename.
+ (gog_style_gradient_load, gog_style_gradient_save,
+ gog_style_fill_load, gog_style_fill_save): Use renamed version of
+ above functions.
+
+2003-09-05 Jon K Hellan <hellan at acm.org>
+
+ * utils/go-color.[ch] (go_color_from_str, go_color_as_str):
+ New. Convert color to/from string.
+
+ * graph/gog-style.c (str_as_fill_style, fill_style_as_str):
+ New. Convert fill style to/from string.
+ (str_as_pattern, pattern_as_str): New. Convert pattern to/from
+ string.
+ (str_as_grad_dir, grad_dir_as_str): New. Convert gradient
+ direction to/from string.
+ (str_as_marker_shape, marker_shape_as_str): New. Convert marker
+ shape to/from string.
+ (gog_style_line_load, gog_style_line_save): New. Load/save a
+ line/outline style.
+ (gog_style_gradient_load, gog_style_gradient_save): New. Load/save
+ a gradient.
+ (gog_style_fill_load, gog_style_fill_save): New. Load/save a fill.
+ (gog_style_marker_load, gog_style_marker_save): New. Load/save a
+ marker.
+ (gog_style_font_load, gog_style_font_save): New. Load/save a font.
+ (gog_style_persist_dom_load, gog_style_persist_dom_save):
+ Implement.
+
+ * graph/gog-object-xml.c (gog_object_set_arg_full) : Don't expect
+ GObject type name as entity content.
+ (gog_object_write_property): Don't save GObject type name as
+ entity content.
+
+2003-09-04 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-1.5d.c (gog_plot1_5d_update) : don't
+ force 0. That will be handled at the axis level.
+
+ * graph/gog-style.c : move the marker editor back into the main style
+ editor rather than being distinct.
+
+2003-09-03 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * utils/go-marker.[ch] : new.
+ * graph/gog-pixbuf-renderer.c : add draw_marker method.
+ * graph/gog-gnome-print-renderer.c : idem.
+ * graph/gog-renderer.c : idem.
+ * graph/gog-style-prefs.glade : update marker editor
+ * graph/gog-style.c (cb_marker_changed) : new.
+ (marker_init) : use editor in gog-marker.c.
+ (gog_style_pref_state_free) : free marker pointer.
+ (gog_style_assign) : handle marker pointer.
+ (gog_style_finalize) : idem.
+ (gog_style_init) : init marker pointer.
+ * graph/gog-theme.c (map_area_series_solid_default)
+ (map_area_series_solid_guppi) : initialize marker properties.
+ * graph/plugins/plot_barcol/gog-line.c
+ (gog_line_view_render) : draw markers.
+
+2003-09-03 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-legend.c (cb_render_elements) : clip before things
+ overflow.
+
+2003-09-03 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.h : add GogStyle::font::color
+ * graph/gog-style.c (gog_style_init) : init font::color
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_text) :
+ composite manually cause libart was dog slow when using the approach
+ in librsvg.
+
+2003-09-03 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-label.c (gog_label_view_size_request) : take outline
+ into account.
+
+ * graph/gog-legend.c (gog_legend_view_render) : don't over draw space
+ allocated to children, and actually measure the text.
+ (gog_legend_editor) : add.
+
+ * graph/gog-view.c (gog_view_size_child_request) : new utility to
+ build up a size requestfor the children around the request for the
+ parent.
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_plot_foreach_elem) :
+ handle case with no series.
+
+2003-09-03 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-object-xml.c (gog_object_set_arg_full): Plug leak.
+
+2003-09-01 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer-pixbuf.c (make_layout) : kludge a patch for font
+ scaling.
+
+2003-09-01 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_xy/gog-xy.c : An initial skeleton, because I
+ accidentally deleted the last one. Useless.
+
+ * graph/gog-style.c (gog_object_get_style) : fix leak.
+ (fill_init) : do not assign the style as part of the initialization.
+
+ * graph/gog-renderer-pixbuf.c (make_layout) : another failing attempt
+ to get font sizes to change.
+
+ * graph/gog-renderer-gnome-print.c : An initial pass at font support.
+ Untested, and unlikely to work out of the box.
+
+2003-08-29 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-axis.c (gog_axis_class_init): Plug leak.
+
+ * utils/go-pattern.c (go_pattern_selector): Free the pixel data.
+
+ * graph/gog-guru.c (graph_typeselect_minor): Handle ->plot
+ changing underneath us.
+
+ * utils/go-font.c (go_font_init): Since the key is a pango font
+ description, not a go-font, use proper hash and equal functions.
+
+2003-08-29 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_view_render) : go
+ clockwise to avoid having ArtRender think the figure is inside out.
+
+2003-08-28 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer-impl.h : Add font_removed.
+
+2003-08-28 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_plot_class_init) : pie
+ series need outlines and fills.
+
+ * graph/gog-theme.c (gog_theme_element_free) : new.
+ (gog_theme_element_hash) : ditto.
+ (gog_theme_element_eq) : ditto.
+ (gog_themes_shutdown) : new to avoid leaking.
+ (gog_theme_finalize) : handle lookup by role.
+ (gog_theme_init) : ditto.
+ (gog_theme_add_element) : ditto.
+ (gog_theme_init_style) : rework to clarify precedence.
+ 1) <parent_type>::<role>
+ 2) ::<role>
+ 3) object_type
+
+ * graph/gog-style.c (gog_style_assign) : Use GOFont.
+ (gog_style_finalize) : ditto.
+ (gog_style_init) : ditto.
+ (gog_style_set_font) : new,
+
+ * goffice.c (libgoffice_init) : init fonts.
+ (libgoffice_shutdown) : shutdown fonts and themes.
+
+ * utils/go-font.c : new utility class to ref count fonts.
+
+2003-08-28 Morten Welinder <terra at gnome.org>
+
+ * utils/go-gradient.c (go_gradient_selector): Clean a bit.
+
+ * graph/gog-style.c (gog_style_pref_state_free): Don't unref NULL
+ images.
+ (gog_style_set_image_preview): Handle setting the same picture,
+ just in case.
+
+2003-08-27 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-style.c (gog_style_set_image_preview): add
+ argument to gnm_pixbuf_intelligent_scale call
+ (cb_image_file_select) use preview_file_selection_set_filename
+ rather than gtk_file_selection_set_filename
+ (fill_image_init): set minimum preview size
+
+2003-08-27 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-style.c (gog_style_set_image_preview): new
+ (cb_image_file_select): use gtk_image_set_from_pixbuf
+ (fill_image_init): initialize state->fill.image.image
+ (cb_fill_type_changed): store original size image
+ (gog_style_pref_state_free): free state->fill.image.image
+ * graph/gog-style-prefs.glade: add size label to image-fill
+ preview
+
+2003-08-26 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-style.c (cb_image_file_select): use
+ preview_file_selection_new and not
+ gnumeric_dialog_image_file_selection
+
+2003-08-26 Morten Welinder <terra at gnome.org>
+
+ * utils/go-pattern.c (go_pattern_selector): Get the args to
+ gdk_pixbuf_new right. Plug leak.
+
+ * utils/go-gradient.c (go_gradient_setup): New function.
+ (go_gradient_selector): Use go_gradient_setup. Make return type
+ sane.
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_polygon):
+ Use go_gradient_setup.
+
+ * graph/gog-renderer-gnome-print.c
+ (gog_renderer_gnome_print_draw_polygon): Use go_gradient_setup.
+
+2003-08-25 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-style.c (cb_image_file_select): use
+ gnumeric_dialog_image_file_selection
+
+2003-08-25 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer-gnome-print.c
+ (gog_renderer_gnome_print_draw_polygon) : remove double
+ gonme_print_grestore
+
+2003-08-24 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-guru.c (cb_graph_guru_clicked) :clear the tmp value that
+ was refing the graph.
+
+2003-08-24 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-guru.c (gog_guru): store and ref gclosure
+ (graph_guru_state_destroy): unref closure
+ (cb_graph_guru_clicked): invoke gclosure
+ * graph/gog-guru.h (gog_guru): use gclosure
+
+2003-08-23 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-1.5d.c (gog_plot1_5d_update) : doh!
+
+2003-08-22 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-object.c (gog_role_cmp) : use the new priority field to
+ be smarter.
+ (gog_object_dup) : Use the new go_data_dup.
+ (gog_object_set_parent) : use gog_role_cmp instead of just using the
+ position.
+
+2003-08-21 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-guru.c (gog_guru) : use gog_graph_dup now that styles
+ work.
+
+ * graph/gog-object.c (gog_object_dup) : doh! dst = src works better
+ than src = src.
+
+2003-08-21 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.1.20
+
+2003-08-21 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-object-xml.c (gog_object_new_from_xml) : don't mark newly
+ reconstituted objects as explititly typed unless they really were.
+ (gog_dataset_save) : patch leak.
+
+ * graph/gog-object.c (gog_object_dup) : new.
+
+2003-08-21 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-axis-prefs.glade : Remove the ticks menu. I'll handle it
+ via roles later.
+
+ * graph/gog-axis.c (gog_axis_editor) : we're only interested in the
+ line characteristics.
+
+ * utils/go-units.h : fix arg names
+
+ * graph/gog-chart.c (gog_chart_class_init) : add padding_pts.
+ * graph/gog-graph.c (gog_graph_class_init) : typo.
+
+2003-08-19 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer.c (gog_renderer_draw_rectangle) : handle outlines
+ properly.
+
+2003-08-14 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-style-prefs.glade: shuffle fill-image widgets again
+
+2003-08-14 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-style.c (cb_fill_type_changed): Don't unref the old
+ image if it is NULL.
+
+2003-08-14 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-chart.c (role_plot_post_add) : don't set the axis twice
+ for the first plot. Add a post condition to keep us honest.
+
+2003-08-12 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.c (fill_image_init) : store the filename
+ (cb_image_file_select) : ditto.
+ (cb_fill_type_changed) : use it here to support restoring the image
+ filaname even though all we have is the image.
+
+2003-08-13 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-style-prefs.glade: shuffle fill-image widgets
+ * graph/gog-guru.glade: increase default size and increase
+ default style portion
+
+2003-08-13 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-style-prefs.glade: align fill-image widgets
+
+2003-08-12 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-style-prefs.glade: improve spacing, add scoll window
+
+2003-08-12 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-style-prefs.glade: add some missed label names
+
+2003-08-10 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.c : re-enable image handling and cache the pixbuf.
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_polygon) : The
+ style stores the pixbuf now, no need to reload the damn thing from
+ disk every time we use it.
+
+ * graph/gog-renderer-gnome-print.c
+ (gog_renderer_gnome_print_draw_polygon) : Use a convenience routine
+ (print_image) : new convenience routine.
+
+2003-08-11 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/plugins/plot_pie/gog-pie-prefs.c (cb_center_size_changed):
+ scale between display and storage
+ (gog_ring_plot_pref): ditto
+ * graph/plugins/plot_pie/gog-ring-prefs.glade: center_size spin
+ button should range from 0 to 95 in steps of 5
+
+2003-08-09 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/gog-guru.c: add correct helpfile address
+
+2003-08-01 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_finalize): Plug
+ leak.
+
+ * graph/gog-style.c (gog_style_class_init): Plug leaks.
+
+2003-08-01 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_path) : use
+ the line characteristics, not outline.
+ * graph/gog-renderer-gnome-print.c
+ (gog_renderer_gnome_print_draw_path) : ditto.
+
+ * graph/gog-theme.c (gog_themes_init) : set the line width.
+ (map_area_series_solid_default) : line colour seems to start at an
+ offset to area colours.
+ (map_area_series_solid_guppi) : set the line colour too.
+
+2003-07-31 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-series.c (gog_series_set_index) : always init the style,
+ don't be cheap. When loading index is initialized to 0, so the
+ first series would not get styled because it did not look like
+ anything changed.
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_view_render) : fix
+ interpretation of the center_size parameter. Thanks tino.
+
+ * graph/gog-plot.c (gog_plot_init) : copy the plot descriptor from the
+ class to the plot here for use cases that use g_object_new.
+ * graph/gog-plot-engine.c (gog_plot_new_by_name) : rather than here
+ where it gets missed.
+
+ * graph/gog-legend.c (gog_legend_parent_changed) : catch object name
+ changes in the chart and trigger an update.
+
+ * graph/gog-graph.c (cb_graph_idle) : clear the handler before doing
+ the update so that a handler can queue an update for another object.
+
+2003-07-30 Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+
+ * graph/plugins/plot_barcol/gog-line.[ch]: new. Line and Area support.
+ * graph/plugins/plot_barcol/gog-1.d.[ch]: new. Defines base class for
+ gog-line and gog-barcol.
+ * graph/plugins/plot_barcol/plot-types.xml.in: add support for are and
+ line plots.
+ * graph/plugins/plot_barcol/plugin.xml.in: add are and line engines.
+
+2003-07-19 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-chart.c (gog_chart_get_axis) : new.
+
+2003-07-19 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/plot-types.xml.in : for stacked and
+ percentage set the overlap to 100.
+
+2003-07-17 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_barcol/gog-barcol.c : Request XY axis set.
+
+ * graph/gog-data-set.c (gog_dataset_set_dim_internal) : always fire an
+ update.
+
+2003-07-09 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-object-xml.h : fix guards
+
+2003-07-07 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-legend.c (gog_legend_parent_changed) : chain upwards.
+ * graph/gog-series.c (gog_series_parent_changed) : ditto.
+
+2003-07-06 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/plugins/plot_pie/gog-ring-prefs.glade: colour -> color
+
+2003-07-06 Andreas J. Guelzow <aguelzow at taliesin.ca>
+
+ * graph/plugins/plot_pie/gog-pie-prefs.glade: colour -> color
+
+2003-07-03 Jon K Hellan <hellan at acm.org>
+
+ * graph/gog-style.c (init_solid_page, init_gradient_page)
+ (init_gradient_page, gog_style_editor): Turn off color combo
+ tearoff behaviour in dialogs.
+
+2003-07-02 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer.c (gog_renderer_init) : tweak the default size to
+ produce a better proportion.
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_view_render) : some minor
+ cleanup.
+
+ * graph/plugins/plot_pie/gog-pie-prefs.glade : fix units of separation
+ spinner.
+
+2003-06-29 Jody Goldberg <jody at gnome.org>
+
+ For Emmanuel Pacaud <emmanuel.pacaud at univ-poitiers.fr>
+ * graph/plugins/plot_pie/gog-pie.c : Handle rings
+
+2003-06-27 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-plot.c (gog_plot_set_property) : don't allow setting
+ vary_style_by_element if the plot does not permit it in the current
+ state.
+ (gog_plot_get_property) : be anal.
+
+ * graph/plugins/plot_barcol/gog-barcol.c
+ (gog_barcol_supports_vary_style_by_element) : new.
+
+2003-06-27 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-plot-impl.h : Add GogPlotClass::supports_vary_by_element
+
+2003-06-26 Morten Welinder <terra at gnome.org>
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_polygon):
+ Fix gradients.
+
+2003-06-25 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-plot-engine.c (gog_plot_type_service_finalize) : still
+ incomplete, but the lists definitely need to be freed.
+
+2003-06-25 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-data-allocator.c (gog_dataset_get_type) : fix
+ cut-n-paste-o.
+
+2003-06-25 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/gog-renderer-gnome-print.c: use alpha channel when
+ printing gradients
+
+2003-06-20 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/gog-renderer-gnome-print.c (gog_renderer_gnome_print_draw_polygon):
+ unref the pixmaps
+
+2003-06-22 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_plot_set_property) : vary
+ style by element handled in plot now.
+ (gog_pie_plot_get_property) : ditto.
+ (gog_pie_plot_cardinality) : deleted.
+ (gog_pie_plot_foreach_elem) : doh! USe the label we just calculated.
+ (gog_pie_view_render) : implement general extracted slices.
+
+ * graph/gog-theme.c (gog_theme_get_name) : new util.
+
+ * graph/gog-style.c : Implement the hooks for serialization but have
+ not actually written them yet.
+
+ * graph/gog-series.c (gog_series_editor) : add a notebook wrapper to
+ allow changing style and data.
+ (gog_series_init) : set GogObject::use_parent_as_proxy flag.
+ (gog_series_dataset_dims) new.
+
+ * graph/gog-plot.c (gog_plot_set_property) : handle the
+ 'vary_style_by_element' at this level.
+ (gog_plot_get_cardinality) : ditto.
+
+ * graph/gog-object.c (gog_object_emit_changed) : Add
+ 'use_parent_as_proxy' utility to make life easier for things like
+ series that will not have individual views. This will fire a
+ changed signal from their plots.
+
+ * graph/gog-legend.c (gog_legend_parent_changed) : new. ensure we get
+ updated when chart cardinality changes.
+ (gog_legend_update) : new.
+
+ * graph/gog-label.c (gog_label_editor) : fix.
+ (gog_label_class_init) : fix.
+ (gog_label_dims) : added to handle the extension to dataset interface.
+
+ * graph/gog-guru.glade : remove frame wrapping the prop notebook.
+
+ * graph/gog-guru.c (prop_notebook_set_current_page) : new util.
+ to cleanup the handling of prop pages. Only show the border if the
+ prop page is not a notebook. This keeps the layout visually
+ similar in both cases.
+ (cb_select_prop_page) : use it here.
+ (cb_attr_tree_selection_change) : and here.
+ (graph_guru_type_selector_new) : remove the useless notebook wrapping
+ the type selector. It gives us more space and forces an initial
+ selection.
+
+ * graph/gog-graph.c (gog_graph_set_property) : add a 'theme-name'
+ property to facilitate serialization.
+
+ * graph/gog-data-allocator.c (gog_dataset_dims) : extension to the
+ dataset interface to facilitate serialization.
+
+ * graph/gog-chart.c (gog_chart_get_property) : new. Needed a way to
+ signal that the cardinality had changed, and a read only property
+ with a notify handler seemed cleaner than a stand alone signal.
+ Looking back at that decision, it seems ugly. Might revisit this
+ later.
+
+ * graph/gog-axis.c : hook up the dataset interface to prepare for
+ serializing all the flags.
+
+ * graph/go-data-impl.h (GOData) : extend interface to require a
+ 'from_str' operation to allow serialization to xml.
+ * graph/go-data.c (go_data_from_str) : wrapper.
+
+ * graph/Makefile.am : Add gog-object-xml.[ch]
+
+2003-06-14 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/gog-style.c (gog_style_editor): move a bad placed g_signal_connect
+ * graph/gog-style.c (gog_style_copy): duplicate filename if useful
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_polygon):
+ added missing white spaces (purely cosmetic)
+ * graph/gog-renderer-pixbuf.c (go_color_to_artpix): removed static
+ * graph/gog-renderer-gnome-print.c (gog_renderer_gnome_print_draw_polygon):
+ added image and gradient support
+
+2003-06-13 Jon K Hellan <hellan at acm.org>
+
+ * graph/gog-style.c (gog_style_editor) : Use the new
+ color_combo_set_instant_apply flag.
+
+2003-06-12 Jody Goldberg <jody at gnome.org>
+
+ * graph/goffice-graph.h : remove POSITION_FILL
+ It does no good to have random thought experiments floating around.
+
+2003-06-12 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-guru.c (graph_guru_type_selector_new) : add an accelerator
+ for the plot family selector.
+
+2003-06-10 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-view.c (gog_view_size_allocate) : Use the new debug macro.
+ (cb_model_changed) : ditto.
+ * graph/gog-object.c (gog_object_update) : and here.
+
+ * graph/goffice-graph.h : Add trivial d() debugging macro so that
+ normal folk don't get deluged with debug spew.
+ * graph/lib.c : store goffice_graph_debug_level here.
+
+ * graph/gog-renderer-gnome-print.c * (gog_renderer_gnome_print_draw_text) :
+ implement trivial version. Need to decide how to handle fonts.
+
+2003-06-10 Jody Goldberg <jody at gnome.org>
+
+ * graph/plugins/plot_pie/gog-pie.c (gog_pie_series_update) :
+ handle series before they are valid.
+ * graph/plugins/plot_barcol/gog-barcol.c (gog_barcol_series_update) : ditto.
+
+ * graph/gog-object.c (gog_object_set_parent) : call the role's
+ post_add routine then objects parent_changed before signaling its
+ addition.
+ (gog_object_add_by_role) : set_parent calls the role functions, not us.
+
+2003-06-07 Jean Brefort <jean.brefort at ac-dijon.fr>
+
+ * graph/gog-style.c (gog_style_editor) :
+ * graph/gog-style.h (struct _GogStyle) :
+ * graph/gog-style-prefs.glade :
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_draw_polygon) : Start work on image
+
+2003-06-07 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.1.19
+
+2003-06-07 Jody Goldberg <jody at gnome.org>
+
+ * Release 1.1.18
+
+2003-06-07 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-view.c (gog_view_queue_resize) : invalidate the allocation
+ too if there is no parent.
+
+2003-06-06 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-chart.c (gog_chart_set_position) : simplify.
+ * graph/gog-graph.c (gog_graph_validate_chart_layout) : new.
+
+ * graph/gog-guru.c (cb_sample_pressed) : set the zoom here.
+ (cb_sample_released) : and unset it here so that the icons don't get
+ shrunk.
+
+2003-06-04 J.H.M. Dassen (Ray) <jdassen at debian.org>
+
+ * graph/gog-style.h: Fixed include of command-context.h to make it work
+ for builds outside the source dir.
+
+2003-06-04 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-object.c (gog_object_generate_name) : return NULL, not FALSE
+
+2003-06-03 J.H.M. Dassen (Ray) <jdassen at debian.org>
+
+ * graph/gog-guru.h: Fixed include of command-context.h to make it work
+ for builds outside the source dir.
+
+2003-06-02 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-legend.c (cb_render_elements) : quick and dirty draw the
+ text. Still lots of work needed to measure things properly and do a
+ more dynamic layout.
+
+ * graph/gog-guru.c (cb_typesel_sample_plot_resize) : use the canvas zoom
+ (cb_sample_plot_resize) : ditto.
+ (graph_guru_init_format_page) : ditto.
+ (graph_guru_type_selector_new) : ditto.
+
+ * graph/gog-renderer-pixbuf.c (make_layout) : new.
+ (gog_renderer_pixbuf_draw_text) : new. simple handler no rotation yet.
+ (gog_renderer_pixbuf_measure_text) : new.
+ (gog_art_renderer_new) : new utility.
+ (gog_renderer_pixbuf_draw_polygon) : used here.
+
+ * graph/gog-control-foocanvas.c (gog_control_foocanvas_draw) : another
+ speed up by using regions rather tha nthe bounding rect of the expose.
+ Thanks to AlexL for pointing out that this will help us when
+ multiple exposes are compressed into 1 event and we do better to
+ clip against the distinct sub regions, than the bounding box of all
+ of them.
+ (gog_control_foocanvas_update) : make life easier and pass the zoom to
+ the renderer.
+
+2003-06-02 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-style.c : minor warning suppression and format style
+ tidying.
+
+2003-06-01 Jean Brefort <jean.brefort at ac-dijon.fr>
+ * graph/gog-style.h : new gradient styles,
+ fill type accessible only if GOG_STYLE_FILL is used
+ * graph/gog-style.c : Enhanced gradient selector
+ * graph/gog-renderer-pixbuf.c : Support for new gradient types
+ * graph/gog-themes.c : Top to bottom gradient in Guppi theme
+
+2003-05-31 Jody Goldberg <jody at gnome.org>
+
+ For Jean Brefort <jean.brefort at ac-dijon.fr>
+ * graph/gog-style.c (gog_style_editor) : Start work on gradient
+ selector.
+
+2003-05-31 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_update) : fix
+ canvas warnings about tryingto queue an update from an update
+ handler.
+
+2003-05-30 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-theme.c (gog_themes_init) : make default them fill legend
+ with white.
+
+ * graph/plugins/plot_barcol/gog-barcol.c (barcol_draw_rect) : smooth
+ the anti-aliasing fuzziness in the libart backend for hairline
+ outlines.
+
+ * graph/gog-chart.c (gog_chart_view_size_allocate) : support
+ positions of NE, NW, SE, SW so that we can handle XL's 'corner'
+ legends.
+
+ * graph/gog-control-foocanvas.c (gog_control_foocanvas_draw) : big
+ speed win. Only draw the piece of the chart that was exposed.
+
+ * graph/gog-plot.c (gog_plot_request_cardinality_update) : we may add
+ a series before assigning to a chart.
+
+ * graph/gog-theme.c (gog_themes_init) : default and guppi themes were
+ inverted for legends.
+
+ * graph/gog-chart.c (gog_chart_class_init) : default to centering
+ legends.
+
+2003-05-30 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-chart.c (gog_chart_view_render) : re-enable.
+
+ * graph/gog-renderer-pixbuf.c (gog_renderer_pixbuf_update) :
+ When the scale changes size requests are invalidated.
+
+ * graph/gog-renderer.c (gog_renderer_invalidate_size_requests) : new.
+ * graph/gog-view.c (gog_view_invalidate_sizes) : new.
+
+ * graph/gog-legend.c (gog_legend_view_render) : typo. pad is in Y
+ coordinates.
+
+ * graph/gog-plot.c (role_series_post_add) : adding a series changes
+ the cardinality.
+ (role_series_pre_remove) : ditto.
+ (gog_plot_update) : delete. Already handled at the chart level.
+ (gog_plot_init) : Make sure cardinailty is initially valid so that we
+ queue an an update request as necessary when it really
+ does change.
+
+ * graph/gog-chart.c (gog_chart_view_size_allocate) : support the
+ alignment flags and enforce clipping.
+ (role_plot_post_add) : adding a plot changes the cardinality.
+ (role_plot_pre_remove) : ditto.
+ (gog_chart_init) : Make sure cardinailty is initially valid so that we
+ queue an an update request as necessary when it really
+ does change.
+
+2003-05-29 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-renderer.c (gog_renderer_outline_size) : keep widths >= 1
+
+ * graph/plugins/plot_barcol/gog-barcol.c (barcol_draw_rect) : clip to
+ integer sizes to decrease amount of bleed through due to antialiasing.
+
+2003-05-27 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-legend.c : make the swatch sizes properties.
+
+ * graph/gog-guru.c (cb_typesel_sample_plot_resize) : fix sizing.
+ (cb_sample_plot_resize) : ditto.
+
+ * graph/gog-renderer.c (gog_renderer_set_property) : doh!
+ set the values if they are _different_ not the same.
+
+2003-05-26 Jody Goldberg <jody at gnome.org>
+
+ * graph/gog-guru.c (graph_guru_set_page) : we can't run the
+ typeselector without a chart selected.
+ (cb_graph_guru_add_plot) : the child_added handler already populates
+ things.
+ (cb_find_child_added) : It may happen that something else will add an
+ item while we're editing, and the change of focus may not be
+ welcome, However, it is far more likely that we just added it.
+
+ * graph/gog-object.c (gog_object_is_deletable) : don't let the guru
+ delete the top level graph.
+
+ * graph/gog-guru.glade : Add border to the menu bar to align it with
+ the scrolled window.
+ Add a 'Precedence' menu.
+
+2003-05-25 Jody Goldberg <jody at gnome.org>
+
+ The code is in CVS time to start keeping a changelog.
--- /dev/null
+++ lib/goffice/goffice.mk
@@ -0,0 +1,8 @@
+# prune this when the code moves
+INCLUDES = -I$(top_srcdir)/lib/goffice \
+ -I$(top_srcdir)/lib/goffice/split \
+ -I$(top_srcdir)/lib \
+ $(GNUCASH_CFLAGS)
+
+GOFFICE_PLUGIN_FLAGS =
+# GOFFICE_PLUGIN_FLAGS = $(GNUCASH_PLUGIN_LDFLAGS)
--- /dev/null
+++ lib/goffice/paths.h.in
@@ -0,0 +1,6 @@
+#ifndef PATHS_H
+#define PATHS_H
+
+#define DATA_DIR "@-GNC_LIBDIR-@"
+
+#endif // PATHS_H
--- /dev/null
+++ lib/goffice/Makefile.am
@@ -0,0 +1,37 @@
+SUBDIRS = app graph utils gui-utils drawing pixmaps split cut-n-paste
+
+AM_CFLAGS = $(GLIB_CFLAGS) $(ART_CFLAGS) $(GNOME_CFLAGS) $(GSF_CFLAGS)
+
+noinst_LTLIBRARIES = libgoffice.la
+
+BUILT_SOURCES = \
+ paths.h
+
+libgoffice_la_LIBADD = \
+ utils/libgoffice-utils.la \
+ app/libgoffice-app.la \
+ gui-utils/libgoffice-gui-utils.la \
+ graph/libgoffice-graph.la \
+ drawing/libgoffice-drawing.la \
+ split/libgoffice-split.la \
+ split/widgets/libgoffice-split-widgets.la \
+ cut-n-paste/pcre/libpcre.la \
+ ${GLIB_LIBS}
+
+libgoffice_la_SOURCES = \
+ $(BUILT_SOURCES) \
+ goffice.c \
+ goffice.h \
+ goffice-config.h \
+ split.h \
+ split.c
+
+EXTRA_DIST = goffice.mk goffice-plugins.mk
+
+paths.h: paths.h.in ${top_builddir}/config.status
+ rm -f $@.tmp
+ sed < $< > $@.tmp \
+ -e 's:@-GNC_LIBDIR-@:${GNC_LIBDIR}:g'
+ mv $@.tmp $@
+
+include $(srcdir)/goffice.mk
--- /dev/null
+++ lib/goffice/split.c
@@ -0,0 +1,308 @@
+#include "split.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkwidget.h>
+
+#include <goffice/utils/go-locale.h>
+#include <libxml/tree.h>
+#include "xml-io.h"
+
+#include "application.h"
+#include "datetime.h"
+#include "gnumeric.h"
+
+#include "value.h"
+
+#define CC2XML(s) ((const xmlChar *)(s))
+#define CXML2C(s) ((const char *)(s))
+
+// 1904 = false
+GnmDateConventions gdc_singleton = { 0 };
+
+GnmExprConventions stack_gnm_expr_conventions_default = {
+ FALSE,
+ NULL,
+ NULL
+};
+
+GnmExprConventions *gnm_expr_conventions_default = &stack_gnm_expr_conventions_default;
+
+GnmDateConventions const *
+workbook_date_conv( Workbook const *wb )
+{
+ return &gdc_singleton;
+}
+
+GnmValue const *
+value_area_fetch_x_y (GnmValue const *v, int x, int y, GnmEvalPos const *ep)
+{
+ GnmValue const * const res = value_area_get_x_y (v, x, y, ep);
+ if (res && res->type != VALUE_EMPTY)
+ return res;
+
+ return value_zero;
+}
+
+/*
+ * An internal routine to get a cell from an array or range. If any
+ * problems occur a NULL is returned.
+ */
+GnmValue const *
+value_area_get_x_y (GnmValue const *v, int x, int y, GnmEvalPos const *ep)
+{
+ g_return_val_if_fail (v, NULL);
+
+ if (v->type == VALUE_ARRAY){
+ g_return_val_if_fail (x < v->v_array.x &&
+ y < v->v_array.y,
+ NULL);
+ return v->v_array.vals [x][y];
+ } else if (v->type == VALUE_CELLRANGE) {
+ // jsled: throw a frickin' exception.
+ printf( "failure. cellrange.\n" );
+ } else
+ return v;
+
+ return NULL;
+}
+
+char const *
+range_name(GnmRange const *src)
+{
+ return "undefined";
+}
+
+char *
+global_range_name(Sheet *sheet, GnmRange const *r)
+{
+ return "unimplemented";
+}
+
+double
+gnm_app_display_dpi_get (gboolean horizontal)
+{
+ // jsled: Taken as default value from gnumeric/src/gnumeric-gconf.c
+ return 96.;
+}
+
+struct _GnmApp {
+ GObject base;
+
+ /* Clipboard */
+ SheetView *clipboard_sheet_view;
+ GnmCellRegion *clipboard_copied_contents;
+ GnmRange *clipboard_cut_range;
+
+ /* History for file menu */
+ GSList *history_list;
+
+ /* Others */
+ GtkWidget *pref_dialog;
+
+ GList *workbook_list;
+
+ GHashTable *named_pixbufs;
+};
+
+/* Signals */
+enum {
+ WORKBOOK_ADDED,
+ WORKBOOK_REMOVED,
+ WINDOW_LIST_CHANGED,
+ CUSTOM_UI_ADDED,
+ CUSTOM_UI_REMOVED,
+ CLIPBOARD_MODIFIED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+static GnmApp *app;
+
+GnmAction *
+gnm_action_new (char const *id, char const *label,
+ char const *icon_name, gboolean always_available,
+ GnmActionHandler handler)
+{
+ GnmAction *res = g_new0 (GnmAction, 1);
+ res->id = g_strdup (id);
+ res->label = g_strdup (label);
+ res->icon_name = g_strdup (icon_name);
+ res->always_available = always_available;
+ res->handler = handler;
+ return res;
+}
+
+void
+gnm_action_free (GnmAction *action)
+{
+ if (NULL != action) {
+ g_free (action->id);
+ g_free (action->label);
+ g_free (action->icon_name);
+ g_free (action);
+ }
+}
+
+static GSList *extra_uis = NULL;
+
+GnmAppExtraUI *
+gnm_app_add_extra_ui (GSList *actions, char *layout,
+ char const *domain,
+ gpointer user_data)
+{
+ GnmAppExtraUI *extra_ui = g_new0 (GnmAppExtraUI, 1);
+ extra_uis = g_slist_prepend (extra_uis, extra_ui);
+ extra_ui->actions = actions;
+ extra_ui->layout = layout;
+ extra_ui->user_data = user_data;
+ g_signal_emit (G_OBJECT (app), signals [CUSTOM_UI_ADDED], 0, extra_ui);
+ return extra_ui;
+}
+
+void
+gnm_app_remove_extra_ui (GnmAppExtraUI *extra_ui)
+{
+ g_signal_emit (G_OBJECT (app), signals [CUSTOM_UI_REMOVED], 0, extra_ui);
+}
+
+/*
+ * Get a named pixbuf.
+ */
+GdkPixbuf *
+gnm_app_get_pixbuf (const char *name)
+{
+ g_return_val_if_fail (app != NULL, NULL);
+ return g_hash_table_lookup (app->named_pixbufs, name);
+}
+
+gboolean
+xml_node_get_bool (xmlNodePtr node, char const *name, gboolean *val)
+{
+ xmlChar *buf = xml_node_get_cstr (node, name);
+ if (buf == NULL)
+ return FALSE;
+
+ *val = (!strcmp (buf, "1")
+ || 0 == g_ascii_strcasecmp (buf, "true"));
+ g_free (buf);
+ return TRUE;
+}
+
+xmlChar *
+xml_node_get_cstr (xmlNodePtr node, char const *name)
+{
+ if (name != NULL)
+ return xmlGetProp (node, CC2XML (name));
+ /* in libxml1 <foo/> would return NULL
+ * in libxml2 <foo/> would return ""
+ */
+ if (node->xmlChildrenNode != NULL)
+ return xmlNodeGetContent (node);
+ return NULL;
+}
+
+gboolean
+xml_node_get_int (xmlNodePtr node, char const *name, int *val)
+{
+ xmlChar *buf;
+ char *end;
+
+ buf = xml_node_get_cstr (node, name);
+ if (buf == NULL)
+ return FALSE;
+
+ errno = 0; /* strto(ld) sets errno, but does not clear it. */
+ *val = strtol (CXML2C (buf), &end, 10);
+ xmlFree (buf);
+
+ /* FIXME: it is, strictly speaking, not valid to use buf here. */
+ return (CXML2C (buf) != end) && (errno != ERANGE);
+}
+
+xmlNode *
+e_xml_get_child_by_name (xmlNode const *parent, char const *child_name)
+{
+ xmlNode *child;
+
+ g_return_val_if_fail (parent != NULL, NULL);
+ g_return_val_if_fail (child_name != NULL, NULL);
+
+ for (child = parent->xmlChildrenNode; child != NULL; child = child->next) {
+ if (xmlStrcmp (child->name, child_name) == 0) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+xmlNode *
+e_xml_get_child_by_name_no_lang (xmlNode const *parent, char const *name)
+{
+ xmlNodePtr node;
+
+ g_return_val_if_fail (parent != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ for (node = parent->xmlChildrenNode; node != NULL; node = node->next) {
+ xmlChar *lang;
+
+ if (node->name == NULL || strcmp (node->name, name) != 0) {
+ continue;
+ }
+ lang = xmlGetProp (node, "xml:lang");
+ if (lang == NULL) {
+ return node;
+ }
+ xmlFree (lang);
+ }
+
+ return NULL;
+}
+
+xmlNode *
+e_xml_get_child_by_name_by_lang (const xmlNode *parent, const gchar *name)
+{
+ xmlNodePtr best_node = NULL, node;
+ gint best_lang_score = INT_MAX;
+ GList const *lang_list = go_locale_languages ();
+
+ g_return_val_if_fail (parent != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ for (node = parent->xmlChildrenNode; node != NULL; node = node->next) {
+ xmlChar *lang;
+
+ if (node->name == NULL || strcmp (node->name, name) != 0)
+ continue;
+
+ lang = xmlGetProp (node, "xml:lang");
+ if (lang != NULL) {
+ const GList *l;
+ gint i;
+
+ for (l = lang_list, i = 0;
+ l != NULL && i < best_lang_score;
+ l = l->next, i++) {
+ if (strcmp ((gchar *) l->data, lang) == 0) {
+ best_node = node;
+ best_lang_score = i;
+ }
+ }
+ } else if (best_node == NULL)
+ best_node = node;
+
+ xmlFree (lang);
+ if (best_lang_score == 0)
+ return best_node;
+ }
+
+ return best_node;
+}
+
--- /dev/null
+++ lib/goffice/goffice.c
@@ -0,0 +1,90 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * goffice.c : a bogus little init file to pull all the parts together
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/goffice.h>
+#include "plugin-service.h"
+#include "command-context.h"
+#include "command-context-stderr.h"
+#include <goffice/graph/gog-series.h>
+#include <goffice/graph/gog-plot.h>
+#include <goffice/graph/gog-plot-engine.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-legend.h>
+#include <goffice/graph/gog-label.h>
+#include <goffice/graph/gog-grid.h>
+#include <goffice/graph/gog-grid-line.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-error-bar.h>
+#include <goffice/graph/go-data-simple.h>
+#include <goffice/utils/go-font.h>
+#include <goffice/utils/go-math.h>
+#include <gsf/gsf-utils.h>
+
+int goffice_graph_debug_level = 1;
+
+void
+libgoffice_init (void)
+{
+ GnmCmdContext *ctx;
+
+ plugin_services_init();
+
+ // effective: +libgoffice_init...
+ go_font_init ();
+ go_math_init ();
+ gsf_init ();
+
+ /* keep trigger happy linkers from leaving things out */
+ gog_plugin_services_init ();
+ (void) GOG_GRAPH_TYPE;
+ (void) GOG_CHART_TYPE;
+ (void) GOG_PLOT_TYPE;
+ (void) GOG_SERIES_TYPE;
+ (void) GOG_SERIES_ELEMENT_TYPE;
+ (void) GOG_LEGEND_TYPE;
+ (void) GOG_AXIS_TYPE;
+ (void) GOG_LABEL_TYPE;
+ (void) GOG_GRID_TYPE;
+ (void) GOG_GRID_LINE_TYPE;
+ (void) GOG_ERROR_BAR_TYPE;
+ (void) GO_DATA_SCALAR_VAL_TYPE;
+ (void) GO_DATA_SCALAR_STR_TYPE;
+ gog_themes_init ();
+ // -libgoffice_init
+
+ gnm_string_init();
+ mstyle_init();
+ gnm_conf_init( FALSE );
+
+ ctx = cmd_context_stderr_new();
+ plugins_init( ctx );
+}
+
+void
+libgoffice_shutdown (void)
+{
+ gog_themes_shutdown ();
+ go_font_shutdown ();
+ gog_plugin_services_shutdown ();
+}
--- /dev/null
+++ lib/goffice/goffice.h
@@ -0,0 +1,33 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * lib.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOFFICE_H
+#define GOFFICE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void libgoffice_init (void);
+void libgoffice_shutdown (void);
+
+G_END_DECLS
+
+#endif /* GOFFICE_H */
--- /dev/null
+++ lib/goffice/cut-n-paste/Makefile.am
@@ -0,0 +1,8 @@
+if WITH_GNOME
+ EGG_RECENT = egg-recent-files
+else
+ EGG_RECENT =
+endif
+
+SUBDIRS = pcre
+#$(EGG_RECENT)
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-view.c
@@ -0,0 +1,71 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include <goffice/goffice-config.h>
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * James Willcox <jwillcox at cs.indiana.edu>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+/* #include <config.h> */
+#endif
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include "egg-recent-view.h"
+
+
+GtkType
+egg_recent_view_get_type (void)
+{
+ static GtkType view_type = 0;
+
+ if (!view_type)
+ {
+ static const GTypeInfo view_info =
+ {
+ sizeof (EggRecentViewClass), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ view_type = g_type_register_static (G_TYPE_INTERFACE,
+ "EggRecentView",
+ &view_info, 0);
+ }
+
+ return view_type;
+}
+
+EggRecentModel *
+egg_recent_view_get_model (EggRecentView *view)
+{
+ g_return_val_if_fail (view, NULL);
+
+ return EGG_RECENT_VIEW_GET_CLASS (view)->do_get_model (view);
+}
+
+void
+egg_recent_view_set_model (EggRecentView *view, EggRecentModel *model)
+{
+ g_return_if_fail (view);
+ g_return_if_fail (model);
+
+ EGG_RECENT_VIEW_GET_CLASS (view)->do_set_model (view, model);
+}
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-view.h
@@ -0,0 +1,45 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#ifndef __EGG_RECENT_VIEW_H__
+#define __EGG_RECENT_VIEW_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtkwidget.h>
+#include "egg-recent-model.h"
+#include "egg-recent-item.h"
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_RECENT_VIEW (egg_recent_view_get_type ())
+#define EGG_RECENT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_RECENT_VIEW, EggRecentView))
+#define EGG_RECENT_VIEW_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), EGG_TYPE_RECENT_VIEW, EggRecentViewClass))
+#define EGG_IS_RECENT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_RECENT_VIEW))
+#define EGG_IS_RECENT_VIEW_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), EGG_TYPE_RECENT_VIEW))
+#define EGG_RECENT_VIEW_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EGG_TYPE_RECENT_VIEW, EggRecentViewClass))
+
+
+typedef struct _EggRecentView EggRecentView;
+typedef struct _EggRecentViewClass EggRecentViewClass;
+
+struct _EggRecentViewClass
+{
+ GTypeInterface base_iface;
+
+ /* vtable, not signals */
+ void (* do_set_model) (EggRecentView *view,
+ EggRecentModel *model);
+ EggRecentModel * (* do_get_model) (EggRecentView *view);
+};
+
+GtkType egg_recent_view_get_type (void) G_GNUC_CONST;
+void egg_recent_view_set_list (EggRecentView *view,
+ GSList *list);
+void egg_recent_view_clear (EggRecentView *view);
+EggRecentModel *egg_recent_view_get_model (EggRecentView *view);
+void egg_recent_view_set_model (EggRecentView *view,
+ EggRecentModel *model);
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_VIEW_H__ */
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-model.h
@@ -0,0 +1,82 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef __EGG_RECENT_MODEL_H__
+#define __EGG_RECENT_MODEL_H__
+
+#include "egg-recent-item.h"
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_RECENT_MODEL (egg_recent_model_get_type ())
+#define EGG_RECENT_MODEL(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, EGG_TYPE_RECENT_MODEL, EggRecentModel)
+#define EGG_RECENT_MODEL_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, EGG_TYPE_RECENT_MODEL, EggRecentModelClass)
+#define EGG_IS_RECENT_MODEL(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_model_get_type ())
+
+typedef struct _EggRecentModel EggRecentModel;
+typedef struct _EggRecentModelPrivate EggRecentModelPrivate;
+typedef struct _EggRecentModelClass EggRecentModelClass;
+
+struct _EggRecentModel {
+ GObject parent_instance;
+
+ EggRecentModelPrivate *priv;
+};
+
+struct _EggRecentModelClass {
+ GObjectClass parent_class;
+
+ void (*changed) (EggRecentModel *model, GList *list);
+};
+
+typedef enum {
+ EGG_RECENT_MODEL_SORT_MRU,
+ EGG_RECENT_MODEL_SORT_LRU,
+ EGG_RECENT_MODEL_SORT_NONE
+} EggRecentModelSort;
+
+
+/* Standard group names */
+#define EGG_RECENT_GROUP_LAUNCHERS "Launchers"
+
+
+GType egg_recent_model_get_type (void);
+
+/* constructors */
+EggRecentModel * egg_recent_model_new (EggRecentModelSort sort);
+
+/* public methods */
+void egg_recent_model_set_filter_mime_types (EggRecentModel *model,
+ ...);
+
+void egg_recent_model_set_filter_groups (EggRecentModel *model, ...);
+
+void egg_recent_model_set_filter_uri_schemes (EggRecentModel *model,
+ ...);
+
+void egg_recent_model_set_sort (EggRecentModel *model,
+ EggRecentModelSort sort);
+
+gboolean egg_recent_model_add_full (EggRecentModel *model,
+ EggRecentItem *item);
+
+gboolean egg_recent_model_add (EggRecentModel *model,
+ const gchar *uri);
+
+gboolean egg_recent_model_delete (EggRecentModel *model,
+ const gchar *uri);
+
+void egg_recent_model_clear (EggRecentModel *model);
+
+GList * egg_recent_model_get_list (EggRecentModel *model);
+
+void egg_recent_model_changed (EggRecentModel *model);
+
+void egg_recent_model_set_limit (EggRecentModel *model, int limit);
+int egg_recent_model_get_limit (EggRecentModel *model);
+
+void egg_recent_model_remove_expired (EggRecentModel *model);
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_MODEL_H__ */
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-item.c
@@ -0,0 +1,443 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include <goffice/goffice-config.h>
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * James Willcox <jwillcox at cs.indiana.edu>
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include "egg-recent-item.h"
+
+
+
+EggRecentItem *
+egg_recent_item_new (void)
+{
+ EggRecentItem *item;
+
+ item = g_new (EggRecentItem, 1);
+
+ item->groups = NULL;
+ item->private_data = FALSE;
+ item->uri = NULL;
+ item->mime_type = NULL;
+
+ item->refcount = 1;
+
+ return item;
+}
+
+static void
+egg_recent_item_free (EggRecentItem *item)
+{
+ if (item->uri)
+ g_free (item->uri);
+
+ if (item->mime_type)
+ g_free (item->mime_type);
+
+ if (item->groups) {
+ g_list_foreach (item->groups, (GFunc)g_free, NULL);
+ g_list_free (item->groups);
+ item->groups = NULL;
+ }
+
+ g_free (item);
+}
+
+EggRecentItem *
+egg_recent_item_ref (EggRecentItem *item)
+{
+ item->refcount++;
+ return item;
+}
+
+EggRecentItem *
+egg_recent_item_unref (EggRecentItem *item)
+{
+ item->refcount--;
+
+ if (item->refcount == 0) {
+ egg_recent_item_free (item);
+ }
+
+ return item;
+}
+
+
+EggRecentItem *
+egg_recent_item_new_from_uri (const gchar *uri)
+{
+ EggRecentItem *item;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ item = egg_recent_item_new ();
+
+ if (!egg_recent_item_set_uri (item ,uri)) {
+ egg_recent_item_free (item);
+ return NULL;
+ }
+
+ item->mime_type = gnome_vfs_get_mime_type (item->uri);
+
+ if (!item->mime_type)
+ item->mime_type = g_strdup (GNOME_VFS_MIME_TYPE_UNKNOWN);
+
+ return item;
+}
+
+/*
+static GList *
+egg_recent_item_copy_groups (const GList *list)
+{
+ GList *newlist = NULL;
+
+ while (list) {
+ gchar *group = (gchar *)list->data;
+
+ newlist = g_list_prepend (newlist, g_strdup (group));
+
+ list = list->next;
+ }
+
+ return newlist;
+}
+
+
+EggRecentItem *
+egg_recent_item_copy (const EggRecentItem *item)
+{
+ EggRecentItem *newitem;
+
+ newitem = egg_recent_item_new ();
+ newitem->uri = g_strdup (item->uri);
+ if (item->mime_type)
+ newitem->mime_type = g_strdup (item->mime_type);
+ newitem->timestamp = item->timestamp;
+ newitem->private_data = item->private_data;
+ newitem->groups = egg_recent_item_copy_groups (item->groups);
+
+ return newitem;
+}
+*/
+
+/*
+EggRecentItem *
+egg_recent_item_new_valist (const gchar *uri, va_list args)
+{
+ EggRecentItem *item;
+ EggRecentArg arg;
+ gchar *str1;
+ gchar *str2;
+ gboolean priv;
+
+ item = egg_recent_item_new ();
+
+ arg = va_arg (args, EggRecentArg);
+
+ while (arg != EGG_RECENT_ARG_NONE) {
+ switch (arg) {
+ case EGG_RECENT_ARG_MIME_TYPE:
+ str1 = va_arg (args, gchar*);
+
+ egg_recent_item_set_mime_type (item, str1);
+ break;
+ case EGG_RECENT_ARG_GROUP:
+ str1 = va_arg (args, gchar*);
+
+ egg_recent_item_add_group (item, str1);
+ break;
+ case EGG_RECENT_ARG_PRIVATE:
+ priv = va_arg (args, gboolean);
+
+ egg_recent_item_set_private (item, priv);
+ break;
+ default:
+ break;
+ }
+
+ arg = va_arg (args, EggRecentArg);
+ }
+
+ return item;
+}
+*/
+
+gboolean
+egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri)
+{
+ gchar *utf8_uri;
+
+ /* if G_BROKEN_FILENAMES is not set, this should succede */
+ if (g_utf8_validate (uri, -1, NULL)) {
+ item->uri = gnome_vfs_make_uri_from_input (uri);
+ } else {
+ utf8_uri = g_filename_to_utf8 (uri, -1, NULL, NULL, NULL);
+
+ if (utf8_uri == NULL) {
+ g_warning ("Couldn't convert URI to UTF-8");
+ return FALSE;
+ }
+
+ if (g_utf8_validate (utf8_uri, -1, NULL)) {
+ item->uri = gnome_vfs_make_uri_from_input (utf8_uri);
+ } else {
+ g_free (utf8_uri);
+ return FALSE;
+ }
+
+ g_free (utf8_uri);
+ }
+
+ return TRUE;
+}
+
+gchar *
+egg_recent_item_get_uri (const EggRecentItem *item)
+{
+ return g_strdup (item->uri);
+}
+
+G_CONST_RETURN gchar *
+egg_recent_item_peek_uri (const EggRecentItem *item)
+{
+ return item->uri;
+}
+
+gchar *
+egg_recent_item_get_uri_utf8 (const EggRecentItem *item)
+{
+ /* this could fail, but it's not likely, since we've already done it
+ * once in set_uri()
+ */
+ return g_filename_to_utf8 (item->uri, -1, NULL, NULL, NULL);
+}
+
+gchar *
+egg_recent_item_get_uri_for_display (const EggRecentItem *item)
+{
+ return gnome_vfs_format_uri_for_display (item->uri);
+}
+
+/* Stolen from gnome_vfs_make_valid_utf8() */
+static char *
+make_valid_utf8 (const char *name)
+{
+ GString *string;
+ const char *remainder, *invalid;
+ int remaining_bytes, valid_bytes;
+
+ string = NULL;
+ remainder = name;
+ remaining_bytes = strlen (name);
+
+ while (remaining_bytes != 0) {
+ if (g_utf8_validate (remainder, remaining_bytes, &invalid))
+ break;
+
+ valid_bytes = invalid - remainder;
+
+ if (string == NULL)
+ string = g_string_sized_new (remaining_bytes);
+
+ g_string_append_len (string, remainder, valid_bytes);
+ g_string_append_c (string, '?');
+
+ remaining_bytes -= valid_bytes + 1;
+ remainder = invalid + 1;
+ }
+
+ if (string == NULL)
+ return g_strdup (name);
+
+ g_string_append (string, remainder);
+/* g_string_append (string, _(" (invalid file name)")); */
+ g_assert (g_utf8_validate (string->str, -1, NULL));
+
+ return g_string_free (string, FALSE);
+}
+
+/**
+ * egg_recent_item_get_short_name:
+ * @item: an #EggRecentItem
+ *
+ * Computes a valid UTF-8 string that can be used as the name of the item in a
+ * menu or list. For example, calling this function on an item that refers to
+ * "file:///foo/bar.txt" will yield "bar.txt".
+ *
+ * Return value: A newly-allocated string in UTF-8 encoding; free it with
+ * g_free().
+ **/
+gchar *
+egg_recent_item_get_short_name (const EggRecentItem *item)
+{
+ GnomeVFSURI *uri;
+ char *short_name;
+ gboolean valid;
+
+ g_return_val_if_fail (item != NULL, NULL);
+
+ if (item->uri == NULL)
+ return NULL;
+
+ uri = gnome_vfs_uri_new (item->uri);
+ if (uri == NULL)
+ return NULL;
+
+ short_name = gnome_vfs_uri_extract_short_name (uri);
+ valid = FALSE;
+
+ if (strcmp (gnome_vfs_uri_get_scheme (uri), "file") == 0) {
+ char *tmp;
+
+ tmp = g_filename_to_utf8 (short_name, -1, NULL, NULL, NULL);
+ if (tmp) {
+ g_free (short_name);
+ short_name = tmp;
+ valid = TRUE;
+ }
+ }
+
+ if (!valid) {
+ char *tmp;
+
+ tmp = make_valid_utf8 (short_name);
+ g_assert (tmp != NULL);
+ g_free (short_name);
+ short_name = tmp;
+ }
+
+ gnome_vfs_uri_unref (uri);
+
+ return short_name;
+}
+
+void
+egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime)
+{
+ item->mime_type = g_strdup (mime);
+}
+
+gchar *
+egg_recent_item_get_mime_type (const EggRecentItem *item)
+{
+ return g_strdup (item->mime_type);
+}
+
+void
+egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp)
+{
+ if (timestamp == (time_t) -1)
+ time (×tamp);
+
+ item->timestamp = timestamp;
+}
+
+time_t
+egg_recent_item_get_timestamp (const EggRecentItem *item)
+{
+ return item->timestamp;
+}
+
+G_CONST_RETURN GList *
+egg_recent_item_get_groups (const EggRecentItem *item)
+{
+ return item->groups;
+}
+
+gboolean
+egg_recent_item_in_group (const EggRecentItem *item, const gchar *group_name)
+{
+ GList *tmp;
+
+ tmp = item->groups;
+ while (tmp != NULL) {
+ gchar *val = (gchar *)tmp->data;
+
+ if (strcmp (group_name, val) == 0)
+ return TRUE;
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
+
+void
+egg_recent_item_add_group (EggRecentItem *item, const gchar *group_name)
+{
+ g_return_if_fail (group_name != NULL);
+
+ if (!egg_recent_item_in_group (item, group_name))
+ item->groups = g_list_append (item->groups, g_strdup (group_name));
+}
+
+void
+egg_recent_item_remove_group (EggRecentItem *item, const gchar *group_name)
+{
+ GList *tmp;
+
+ g_return_if_fail (group_name != NULL);
+
+ tmp = item->groups;
+ while (tmp != NULL) {
+ gchar *val = (gchar *)tmp->data;
+
+ if (strcmp (group_name, val) == 0) {
+ item->groups = g_list_remove (item->groups,
+ val);
+ g_free (val);
+ break;
+ }
+
+ tmp = tmp->next;
+ }
+}
+
+void
+egg_recent_item_set_private (EggRecentItem *item, gboolean priv)
+{
+ item->private_data = priv;
+}
+
+gboolean
+egg_recent_item_get_private (const EggRecentItem *item)
+{
+ return item->private_data;
+}
+
+GType
+egg_recent_item_get_type (void)
+{
+ static GType boxed_type = 0;
+
+ if (!boxed_type) {
+ boxed_type = g_boxed_type_register_static ("EggRecentItem",
+ (GBoxedCopyFunc)egg_recent_item_ref,
+ (GBoxedFreeFunc)egg_recent_item_unref);
+ }
+
+ return boxed_type;
+}
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-model.c
@@ -0,0 +1,1784 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include <goffice/goffice-config.h>
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * James Willcox <jwillcox at cs.indiana.edu>
+ */
+
+#ifdef HAVE_CONFIG_H
+/* #include <config.h> */
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <gtk/gtk.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include <gconf/gconf-client.h>
+#include "egg-recent-model.h"
+#include "egg-recent-item.h"
+
+#define EGG_RECENT_MODEL_FILE_PATH "/.recently-used"
+#define EGG_RECENT_MODEL_BUFFER_SIZE 8192
+
+#define EGG_RECENT_MODEL_MAX_ITEMS 500
+#define EGG_RECENT_MODEL_DEFAULT_LIMIT 10
+#define EGG_RECENT_MODEL_TIMEOUT_LENGTH 200
+
+#define EGG_RECENT_MODEL_KEY_DIR "/desktop/gnome/recent_files"
+#define EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY EGG_RECENT_MODEL_KEY_DIR "/default_limit"
+#define EGG_RECENT_MODEL_EXPIRE_KEY EGG_RECENT_MODEL_KEY_DIR "/expire"
+
+struct _EggRecentModelPrivate {
+ GSList *mime_filter_values; /* list of mime types we allow */
+ GSList *group_filter_values; /* list of groups we allow */
+ GSList *scheme_filter_values; /* list of URI schemes we allow */
+
+ EggRecentModelSort sort_type; /* type of sorting to be done */
+
+ int limit; /* soft limit for length of the list */
+ int expire_days; /* number of days to hold an item */
+
+ char *path; /* path to the file we store stuff in */
+
+ GHashTable *monitors;
+
+ GnomeVFSMonitorHandle *monitor;
+
+ GConfClient *client;
+ gboolean use_default_limit;
+
+ guint limit_change_notify_id;
+ guint expiration_change_notify_id;
+
+ guint changed_timeout;
+};
+
+/* signals */
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static GType model_signals[LAST_SIGNAL] = { 0 };
+
+/* properties */
+enum {
+ PROP_BOGUS,
+ PROP_MIME_FILTERS,
+ PROP_GROUP_FILTERS,
+ PROP_SCHEME_FILTERS,
+ PROP_SORT_TYPE,
+ PROP_LIMIT
+};
+
+typedef struct {
+ GSList *states;
+ GList *items;
+ EggRecentItem *current_item;
+}ParseInfo;
+
+typedef enum {
+ STATE_START,
+ STATE_RECENT_FILES,
+ STATE_RECENT_ITEM,
+ STATE_URI,
+ STATE_MIME_TYPE,
+ STATE_TIMESTAMP,
+ STATE_PRIVATE,
+ STATE_GROUPS,
+ STATE_GROUP
+} ParseState;
+
+typedef struct _ChangedData {
+ EggRecentModel *model;
+ GList *list;
+}ChangedData;
+
+#define TAG_RECENT_FILES "RecentFiles"
+#define TAG_RECENT_ITEM "RecentItem"
+#define TAG_URI "URI"
+#define TAG_MIME_TYPE "Mime-Type"
+#define TAG_TIMESTAMP "Timestamp"
+#define TAG_PRIVATE "Private"
+#define TAG_GROUPS "Groups"
+#define TAG_GROUP "Group"
+
+static void start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error);
+
+static void end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error);
+
+static void text_handler (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error);
+
+static void error_handler (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data);
+
+static GMarkupParser parser = {start_element_handler, end_element_handler,
+ text_handler,
+ NULL,
+ error_handler};
+
+static gboolean
+egg_recent_model_string_match (const GSList *list, const gchar *str)
+{
+ const GSList *tmp;
+
+ if (list == NULL || str == NULL)
+ return TRUE;
+
+ tmp = list;
+
+ while (tmp) {
+ if (g_pattern_match_string (tmp->data, str))
+ return TRUE;
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+egg_recent_model_write_raw (EggRecentModel *model, FILE *file,
+ const gchar *content)
+{
+ int len;
+ int fd;
+ struct stat sbuf;
+
+ rewind (file);
+
+ len = strlen (content);
+ fd = fileno (file);
+
+ if (fstat (fd, &sbuf) < 0)
+ g_warning ("Couldn't stat XML document.");
+
+ if ((off_t)len < sbuf.st_size) {
+ ftruncate (fd, len);
+ }
+
+ if (fputs (content, file) == EOF)
+ return FALSE;
+
+ fsync (fd);
+ rewind (file);
+
+ return TRUE;
+}
+
+static GList *
+egg_recent_model_delete_from_list (GList *list,
+ const gchar *uri)
+{
+ GList *tmp;
+
+ if (!uri)
+ return list;
+
+ tmp = list;
+
+ while (tmp) {
+ EggRecentItem *item = tmp->data;
+ GList *next;
+
+ next = tmp->next;
+
+ if (!strcmp (egg_recent_item_peek_uri (item), uri)) {
+ egg_recent_item_unref (item);
+
+ list = g_list_remove_link (list, tmp);
+ g_list_free_1 (tmp);
+ }
+
+ tmp = next;
+ }
+
+ return list;
+}
+
+static void
+egg_recent_model_add_new_groups (EggRecentItem *item,
+ EggRecentItem *upd_item)
+{
+ const GList *tmp;
+
+ tmp = egg_recent_item_get_groups (upd_item);
+
+ while (tmp) {
+ char *group = tmp->data;
+
+ if (!egg_recent_item_in_group (item, group))
+ egg_recent_item_add_group (item, group);
+
+ tmp = tmp->next;
+ }
+}
+
+static gboolean
+egg_recent_model_update_item (GList *items, EggRecentItem *upd_item)
+{
+ GList *tmp;
+ const char *uri;
+
+ uri = egg_recent_item_peek_uri (upd_item);
+
+ tmp = items;
+
+ while (tmp) {
+ EggRecentItem *item = tmp->data;
+
+ if (gnome_vfs_uris_match (egg_recent_item_peek_uri (item), uri)) {
+ egg_recent_item_set_timestamp (item, (time_t) -1);
+
+ egg_recent_model_add_new_groups (item, upd_item);
+
+ return TRUE;
+ }
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+egg_recent_model_read_raw (EggRecentModel *model, FILE *file)
+{
+ GString *string;
+ char buf[EGG_RECENT_MODEL_BUFFER_SIZE];
+
+ rewind (file);
+
+ string = g_string_new (NULL);
+ while (fgets (buf, EGG_RECENT_MODEL_BUFFER_SIZE, file)) {
+ string = g_string_append (string, buf);
+ }
+
+ rewind (file);
+
+ return g_string_free (string, FALSE);
+}
+
+
+
+static void
+parse_info_init (ParseInfo *info)
+{
+ info->states = g_slist_prepend (NULL, STATE_START);
+ info->items = NULL;
+}
+
+static void
+parse_info_free (ParseInfo *info)
+{
+ g_slist_free (info->states);
+}
+
+static void
+push_state (ParseInfo *info,
+ ParseState state)
+{
+ info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
+}
+
+static void
+pop_state (ParseInfo *info)
+{
+ g_return_if_fail (info->states != NULL);
+
+ info->states = g_slist_remove (info->states, info->states->data);
+}
+
+static ParseState
+peek_state (ParseInfo *info)
+{
+ g_return_val_if_fail (info->states != NULL, STATE_START);
+
+ return GPOINTER_TO_INT (info->states->data);
+}
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+static void
+start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = (ParseInfo *)user_data;
+
+ if (ELEMENT_IS (TAG_RECENT_FILES))
+ push_state (info, STATE_RECENT_FILES);
+ else if (ELEMENT_IS (TAG_RECENT_ITEM)) {
+ info->current_item = egg_recent_item_new ();
+ push_state (info, STATE_RECENT_ITEM);
+ } else if (ELEMENT_IS (TAG_URI))
+ push_state (info, STATE_URI);
+ else if (ELEMENT_IS (TAG_MIME_TYPE))
+ push_state (info, STATE_MIME_TYPE);
+ else if (ELEMENT_IS (TAG_TIMESTAMP))
+ push_state (info, STATE_TIMESTAMP);
+ else if (ELEMENT_IS (TAG_PRIVATE)) {
+ push_state (info, STATE_PRIVATE);
+ egg_recent_item_set_private (info->current_item, TRUE);
+ } else if (ELEMENT_IS (TAG_GROUPS))
+ push_state (info, STATE_GROUPS);
+ else if (ELEMENT_IS (TAG_GROUP))
+ push_state (info, STATE_GROUP);
+}
+
+static gint
+list_compare_func_mru (gpointer a, gpointer b)
+{
+ EggRecentItem *item_a = (EggRecentItem *)a;
+ EggRecentItem *item_b = (EggRecentItem *)b;
+
+ return item_a->timestamp < item_b->timestamp;
+}
+
+static gint
+list_compare_func_lru (gpointer a, gpointer b)
+{
+ EggRecentItem *item_a = (EggRecentItem *)a;
+ EggRecentItem *item_b = (EggRecentItem *)b;
+
+ return item_a->timestamp > item_b->timestamp;
+}
+
+
+
+static void
+end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = (ParseInfo *)user_data;
+
+ switch (peek_state (info)) {
+ case STATE_RECENT_ITEM:
+ info->items = g_list_append (info->items,
+ info->current_item);
+ if (info->current_item->uri == NULL ||
+ strlen (info->current_item->uri) == 0)
+ g_warning ("URI NOT LOADED");
+ break;
+ default:
+ break;
+ }
+
+ pop_state (info);
+}
+
+static void
+text_handler (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = (ParseInfo *)user_data;
+
+ switch (peek_state (info)) {
+ case STATE_START:
+ case STATE_RECENT_FILES:
+ case STATE_RECENT_ITEM:
+ case STATE_PRIVATE:
+ case STATE_GROUPS:
+ break;
+ case STATE_URI:
+ egg_recent_item_set_uri (info->current_item, text);
+ break;
+ case STATE_MIME_TYPE:
+ egg_recent_item_set_mime_type (info->current_item,
+ text);
+ break;
+ case STATE_TIMESTAMP:
+ egg_recent_item_set_timestamp (info->current_item,
+ (time_t)atoi (text));
+ break;
+ case STATE_GROUP:
+ egg_recent_item_add_group (info->current_item,
+ text);
+ break;
+ }
+
+}
+
+static void
+error_handler (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data)
+{
+ g_warning ("Error in parse: %s", error->message);
+}
+
+static void
+egg_recent_model_enforce_limit (GList *list, int limit)
+{
+ int len;
+ GList *end;
+
+ /* limit < 0 means unlimited */
+ if (limit <= 0)
+ return;
+
+ len = g_list_length (list);
+
+ if (len > limit) {
+ GList *next;
+
+ end = g_list_nth (list, limit-1);
+ next = end->next;
+
+ end->next = NULL;
+
+ EGG_RECENT_ITEM_LIST_UNREF (next);
+ }
+}
+
+static GList *
+egg_recent_model_sort (EggRecentModel *model, GList *list)
+{
+ switch (model->priv->sort_type) {
+ case EGG_RECENT_MODEL_SORT_MRU:
+ list = g_list_sort (list,
+ (GCompareFunc)list_compare_func_mru);
+ break;
+ case EGG_RECENT_MODEL_SORT_LRU:
+ list = g_list_sort (list,
+ (GCompareFunc)list_compare_func_lru);
+ break;
+ case EGG_RECENT_MODEL_SORT_NONE:
+ break;
+ }
+
+ return list;
+}
+
+static gboolean
+egg_recent_model_group_match (EggRecentItem *item, GSList *groups)
+{
+ GSList *tmp;
+
+ tmp = groups;
+
+ while (tmp != NULL) {
+ const gchar * group = (const gchar *)tmp->data;
+
+ if (egg_recent_item_in_group (item, group))
+ return TRUE;
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
+
+static GList *
+egg_recent_model_filter (EggRecentModel *model,
+ GList *list)
+{
+ EggRecentItem *item;
+ GList *newlist = NULL;
+ gchar *mime_type;
+ gchar *uri;
+
+ g_return_val_if_fail (list != NULL, NULL);
+
+ while (list) {
+ gboolean pass_mime_test = FALSE;
+ gboolean pass_group_test = FALSE;
+ gboolean pass_scheme_test = FALSE;
+ item = (EggRecentItem *)list->data;
+ list = list->next;
+
+ uri = egg_recent_item_get_uri (item);
+
+ /* filter by mime type */
+ if (model->priv->mime_filter_values != NULL) {
+ mime_type = egg_recent_item_get_mime_type (item);
+
+ if (egg_recent_model_string_match
+ (model->priv->mime_filter_values,
+ mime_type))
+ pass_mime_test = TRUE;
+
+ g_free (mime_type);
+ } else
+ pass_mime_test = TRUE;
+
+ /* filter by group */
+ if (pass_mime_test && model->priv->group_filter_values != NULL) {
+ if (egg_recent_model_group_match
+ (item, model->priv->group_filter_values))
+ pass_group_test = TRUE;
+ } else if (egg_recent_item_get_private (item)) {
+ pass_group_test = FALSE;
+ } else
+ pass_group_test = TRUE;
+
+ /* filter by URI scheme */
+ if (pass_mime_test && pass_group_test &&
+ model->priv->scheme_filter_values != NULL) {
+ gchar *scheme;
+
+ scheme = gnome_vfs_get_uri_scheme (uri);
+
+ if (egg_recent_model_string_match
+ (model->priv->scheme_filter_values, scheme))
+ pass_scheme_test = TRUE;
+
+ g_free (scheme);
+ } else
+ pass_scheme_test = TRUE;
+
+ if (pass_mime_test && pass_group_test && pass_scheme_test)
+ newlist = g_list_prepend (newlist, item);
+
+ g_free (uri);
+ }
+
+ if (newlist) {
+ newlist = g_list_reverse (newlist);
+ g_list_free (list);
+ }
+
+
+ return newlist;
+}
+
+
+
+#if 0
+static void
+egg_recent_model_monitor_list_cb (GnomeVFSMonitorHandle *handle,
+ const gchar *monitor_uri,
+ const gchar *info_uri,
+ GnomeVFSMonitorEventType event_type,
+ gpointer user_data)
+{
+ EggRecentModel *model;
+
+ model = EGG_RECENT_MODEL (user_data);
+
+ if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
+ egg_recent_model_delete (model, monitor_uri);
+ g_hash_table_remove (model->priv->monitors, monitor_uri);
+ }
+}
+
+
+
+static void
+egg_recent_model_monitor_list (EggRecentModel *model, GList *list)
+{
+ GList *tmp;
+
+ tmp = list;
+ while (tmp) {
+ EggRecentItem *item = (EggRecentItem *)tmp->data;
+ GnomeVFSMonitorHandle *handle;
+ GnomeVFSResult res;
+ gchar *uri;
+
+ tmp = tmp->next;
+
+ uri = egg_recent_item_get_uri (item);
+ if (g_hash_table_lookup (model->priv->monitors, uri)) {
+ /* already monitoring this one */
+ g_free (uri);
+ continue;
+ }
+
+ res = gnome_vfs_monitor_add (&handle, uri,
+ GNOME_VFS_MONITOR_FILE,
+ egg_recent_model_monitor_list_cb,
+ model);
+
+ if (res == GNOME_VFS_OK)
+ g_hash_table_insert (model->priv->monitors, uri, handle);
+ else
+ g_free (uri);
+ }
+}
+#endif
+
+
+static gboolean
+egg_recent_model_changed_timeout (EggRecentModel *model)
+{
+ egg_recent_model_changed (model);
+
+ return FALSE;
+}
+
+static void
+egg_recent_model_monitor_cb (GnomeVFSMonitorHandle *handle,
+ const gchar *monitor_uri,
+ const gchar *info_uri,
+ GnomeVFSMonitorEventType event_type,
+ gpointer user_data)
+{
+ EggRecentModel *model;
+
+ g_return_if_fail (user_data != NULL);
+ g_return_if_fail (EGG_IS_RECENT_MODEL (user_data));
+ model = EGG_RECENT_MODEL (user_data);
+
+ if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) {
+ if (model->priv->changed_timeout > 0) {
+ g_source_remove (model->priv->changed_timeout);
+ }
+
+ model->priv->changed_timeout = g_timeout_add (
+ EGG_RECENT_MODEL_TIMEOUT_LENGTH,
+ (GSourceFunc)egg_recent_model_changed_timeout,
+ model);
+ }
+}
+
+static void
+egg_recent_model_monitor (EggRecentModel *model, gboolean should_monitor)
+{
+ if (should_monitor && model->priv->monitor == NULL) {
+ char *uri;
+
+ uri = gnome_vfs_get_uri_from_local_path (model->priv->path);
+
+ gnome_vfs_monitor_add (&model->priv->monitor,
+ uri,
+ GNOME_VFS_MONITOR_FILE,
+ egg_recent_model_monitor_cb,
+ model);
+
+ g_free (uri);
+
+ /* if the above fails, don't worry about it.
+ * local notifications will still happen
+ */
+
+ } else if (!should_monitor && model->priv->monitor != NULL) {
+ gnome_vfs_monitor_cancel (model->priv->monitor);
+ model->priv->monitor = NULL;
+ }
+}
+
+static void
+egg_recent_model_set_limit_internal (EggRecentModel *model, int limit)
+{
+ model->priv->limit = limit;
+
+ if (limit <= 0)
+ egg_recent_model_monitor (model, FALSE);
+ else {
+ egg_recent_model_monitor (model, TRUE);
+ egg_recent_model_changed (model);
+ }
+}
+
+static GList *
+egg_recent_model_read (EggRecentModel *model, FILE *file)
+{
+ GList *list=NULL;
+ gchar *content;
+ GMarkupParseContext *ctx;
+ ParseInfo info;
+ GError *error;
+
+ content = egg_recent_model_read_raw (model, file);
+
+ if (strlen (content) <= 0) {
+ g_free (content);
+ return NULL;
+ }
+
+ parse_info_init (&info);
+
+ ctx = g_markup_parse_context_new (&parser, 0, &info, NULL);
+
+ error = NULL;
+ if (!g_markup_parse_context_parse (ctx, content, strlen (content),
+ &error)) {
+ g_warning (error->message);
+ g_error_free (error);
+ error = NULL;
+ goto out;
+ }
+
+ error = NULL;
+ if (!g_markup_parse_context_end_parse (ctx, &error))
+ goto out;
+
+ g_markup_parse_context_free (ctx);
+out:
+ list = info.items;
+
+ parse_info_free (&info);
+
+ g_free (content);
+
+ /*
+ g_print ("Total items: %d\n", g_list_length (list));
+ */
+
+ return list;
+}
+
+
+static gboolean
+egg_recent_model_write (EggRecentModel *model, FILE *file, GList *list)
+{
+ GString *string;
+ gchar *data;
+ EggRecentItem *item;
+ const GList *groups;
+ int i;
+ int ret;
+
+ string = g_string_new ("<?xml version=\"1.0\"?>\n");
+ string = g_string_append (string, "<" TAG_RECENT_FILES ">\n");
+
+ i=0;
+ while (list) {
+ gchar *uri;
+ gchar *mime_type;
+ gchar *escaped_uri;
+ time_t timestamp;
+ item = (EggRecentItem *)list->data;
+
+
+ uri = egg_recent_item_get_uri_utf8 (item);
+ escaped_uri = g_markup_escape_text (uri,
+ strlen (uri));
+ g_free (uri);
+
+ mime_type = egg_recent_item_get_mime_type (item);
+ timestamp = egg_recent_item_get_timestamp (item);
+
+ string = g_string_append (string, " <" TAG_RECENT_ITEM ">\n");
+
+ g_string_append_printf (string,
+ " <" TAG_URI ">%s</" TAG_URI ">\n", escaped_uri);
+
+ if (mime_type)
+ g_string_append_printf (string,
+ " <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n", mime_type);
+ else
+ g_string_append_printf (string,
+ " <" TAG_MIME_TYPE "></" TAG_MIME_TYPE ">\n");
+
+
+ g_string_append_printf (string,
+ " <" TAG_TIMESTAMP ">%d</" TAG_TIMESTAMP ">\n", (int)timestamp);
+
+ if (egg_recent_item_get_private (item))
+ string = g_string_append (string,
+ " <" TAG_PRIVATE "/>\n");
+
+ /* write the groups */
+ string = g_string_append (string,
+ " <" TAG_GROUPS ">\n");
+ groups = egg_recent_item_get_groups (item);
+
+ if (groups == NULL && egg_recent_item_get_private (item))
+ g_warning ("Item with URI \"%s\" marked as private, but"
+ " does not belong to any groups.\n", uri);
+
+ while (groups) {
+ const gchar *group = (const gchar *)groups->data;
+ gchar *escaped_group;
+
+ escaped_group = g_markup_escape_text (group, strlen(group));
+
+ g_string_append_printf (string,
+ " <" TAG_GROUP ">%s</" TAG_GROUP ">\n",
+ escaped_group);
+
+ g_free (escaped_group);
+
+ groups = groups->next;
+ }
+
+ string = g_string_append (string, " </" TAG_GROUPS ">\n");
+
+ string = g_string_append (string,
+ " </" TAG_RECENT_ITEM ">\n");
+
+ g_free (mime_type);
+ g_free (escaped_uri);
+
+ list = list->next;
+ i++;
+ }
+
+ string = g_string_append (string, "</" TAG_RECENT_FILES ">");
+
+ data = g_string_free (string, FALSE);
+
+ ret = egg_recent_model_write_raw (model, file, data);
+
+ g_free (data);
+
+ return ret;
+}
+
+static FILE *
+egg_recent_model_open_file (EggRecentModel *model)
+{
+ FILE *file;
+ mode_t prev_umask;
+
+ file = fopen (model->priv->path, "r+");
+ if (file == NULL) {
+ /* be paranoid */
+ prev_umask = umask (077);
+
+ file = fopen (model->priv->path, "w+");
+
+ umask (prev_umask);
+
+ g_return_val_if_fail (file != NULL, NULL);
+ }
+
+ return file;
+}
+
+static gboolean
+egg_recent_model_lock_file (FILE *file)
+{
+ int fd;
+ gint try = 5;
+
+ rewind (file);
+ fd = fileno (file);
+
+ /* Attempt to lock the file 5 times,
+ * waiting a random interval (< 1 second)
+ * in between attempts.
+ * We should really be doing asynchronous
+ * locking, but requires substantially larger
+ * changes.
+ */
+
+ while (try > 0)
+ {
+ int rand_interval;
+
+ if (lockf (fd, F_TLOCK, 0) == 0)
+ return TRUE;
+
+ rand_interval = 1 + (int) (10.0 * rand()/(RAND_MAX + 1.0));
+
+ g_usleep (100000 * rand_interval);
+
+ --try;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+egg_recent_model_unlock_file (FILE *file)
+{
+ int fd;
+
+ rewind (file);
+ fd = fileno (file);
+
+ return (lockf (fd, F_ULOCK, 0) == 0) ? TRUE : FALSE;
+}
+
+static void
+egg_recent_model_finalize (GObject *object)
+{
+ EggRecentModel *model = EGG_RECENT_MODEL (object);
+
+ egg_recent_model_monitor (model, FALSE);
+
+
+ g_slist_foreach (model->priv->mime_filter_values,
+ (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free (model->priv->mime_filter_values);
+ model->priv->mime_filter_values = NULL;
+
+ g_slist_foreach (model->priv->scheme_filter_values,
+ (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free (model->priv->scheme_filter_values);
+ model->priv->scheme_filter_values = NULL;
+
+ g_slist_foreach (model->priv->group_filter_values,
+ (GFunc) g_free, NULL);
+ g_slist_free (model->priv->group_filter_values);
+ model->priv->group_filter_values = NULL;
+
+
+ if (model->priv->limit_change_notify_id)
+ gconf_client_notify_remove (model->priv->client,
+ model->priv->limit_change_notify_id);
+ model->priv->expiration_change_notify_id = 0;
+
+ if (model->priv->expiration_change_notify_id)
+ gconf_client_notify_remove (model->priv->client,
+ model->priv->expiration_change_notify_id);
+ model->priv->expiration_change_notify_id = 0;
+
+ g_object_unref (model->priv->client);
+ model->priv->client = NULL;
+
+
+ g_free (model->priv->path);
+ model->priv->path = NULL;
+
+ g_hash_table_destroy (model->priv->monitors);
+ model->priv->monitors = NULL;
+
+
+ g_free (model->priv);
+}
+
+static void
+egg_recent_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EggRecentModel *model = EGG_RECENT_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_MIME_FILTERS:
+ model->priv->mime_filter_values =
+ (GSList *)g_value_get_pointer (value);
+ break;
+
+ case PROP_GROUP_FILTERS:
+ model->priv->group_filter_values =
+ (GSList *)g_value_get_pointer (value);
+ break;
+
+ case PROP_SCHEME_FILTERS:
+ model->priv->scheme_filter_values =
+ (GSList *)g_value_get_pointer (value);
+ break;
+
+ case PROP_SORT_TYPE:
+ model->priv->sort_type = g_value_get_int (value);
+ break;
+
+ case PROP_LIMIT:
+ egg_recent_model_set_limit (model,
+ g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_recent_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EggRecentModel *model = EGG_RECENT_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_MIME_FILTERS:
+ g_value_set_pointer (value, model->priv->mime_filter_values);
+ break;
+
+ case PROP_GROUP_FILTERS:
+ g_value_set_pointer (value, model->priv->group_filter_values);
+ break;
+
+ case PROP_SCHEME_FILTERS:
+ g_value_set_pointer (value, model->priv->scheme_filter_values);
+ break;
+
+ case PROP_SORT_TYPE:
+ g_value_set_int (value, model->priv->sort_type);
+ break;
+
+ case PROP_LIMIT:
+ g_value_set_int (value, model->priv->limit);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_recent_model_class_init (EggRecentModelClass * klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = egg_recent_model_set_property;
+ object_class->get_property = egg_recent_model_get_property;
+ object_class->finalize = egg_recent_model_finalize;
+
+ model_signals[CHANGED] = g_signal_new ("changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EggRecentModelClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+
+
+ g_object_class_install_property (object_class,
+ PROP_MIME_FILTERS,
+ g_param_spec_pointer ("mime-filters",
+ "Mime Filters",
+ "List of mime types to be allowed.",
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_GROUP_FILTERS,
+ g_param_spec_pointer ("group-filters",
+ "Group Filters",
+ "List of groups to be allowed.",
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_SCHEME_FILTERS,
+ g_param_spec_pointer ("scheme-filters",
+ "Scheme Filters",
+ "List of URI schemes to be allowed.",
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_SORT_TYPE,
+ g_param_spec_int ("sort-type",
+ "Sort Type",
+ "Type of sorting to be done.",
+ 0, EGG_RECENT_MODEL_SORT_NONE,
+ EGG_RECENT_MODEL_SORT_MRU,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_LIMIT,
+ g_param_spec_int ("limit",
+ "Limit",
+ "Max number of items allowed.",
+ -1, EGG_RECENT_MODEL_MAX_ITEMS,
+ EGG_RECENT_MODEL_DEFAULT_LIMIT,
+ G_PARAM_READWRITE));
+
+ klass->changed = NULL;
+}
+
+
+
+static void
+egg_recent_model_limit_changed (GConfClient *client, guint cnxn_id,
+ GConfEntry *entry, gpointer user_data)
+{
+ EggRecentModel *model;
+ GConfValue *value;
+
+ model = EGG_RECENT_MODEL (user_data);
+
+ g_return_if_fail (model != NULL);
+
+ if (model->priv->use_default_limit == FALSE)
+ return; /* ignore this key */
+
+ /* the key was unset, and the schema has apparently failed */
+ if (entry == NULL)
+ return;
+
+ value = gconf_entry_get_value (entry);
+
+ if (value->type != GCONF_VALUE_INT) {
+ g_warning ("Expected GConfValue of type integer, "
+ "got something else");
+ }
+
+
+ egg_recent_model_set_limit_internal (model, gconf_value_get_int (value));
+}
+
+static void
+egg_recent_model_expiration_changed (GConfClient *client, guint cnxn_id,
+ GConfEntry *entry, gpointer user_data)
+{
+
+}
+
+static void
+egg_recent_model_init (EggRecentModel * model)
+{
+ if (!gnome_vfs_init ()) {
+ g_warning ("gnome-vfs initialization failed.");
+ return;
+ }
+
+
+ model->priv = g_new0 (EggRecentModelPrivate, 1);
+
+ model->priv->path = g_strdup_printf ("%s" EGG_RECENT_MODEL_FILE_PATH,
+ g_get_home_dir ());
+
+ model->priv->mime_filter_values = NULL;
+ model->priv->group_filter_values = NULL;
+ model->priv->scheme_filter_values = NULL;
+
+ model->priv->client = gconf_client_get_default ();
+ gconf_client_add_dir (model->priv->client, EGG_RECENT_MODEL_KEY_DIR,
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+ model->priv->limit_change_notify_id =
+ gconf_client_notify_add (model->priv->client,
+ EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY,
+ egg_recent_model_limit_changed,
+ model, NULL, NULL);
+
+ model->priv->expiration_change_notify_id =
+ gconf_client_notify_add (model->priv->client,
+ EGG_RECENT_MODEL_EXPIRE_KEY,
+ egg_recent_model_expiration_changed,
+ model, NULL, NULL);
+
+ model->priv->expire_days = gconf_client_get_int (
+ model->priv->client,
+ EGG_RECENT_MODEL_EXPIRE_KEY,
+ NULL);
+
+#if 0
+ /* keep this out, for now */
+ model->priv->limit = gconf_client_get_int (
+ model->priv->client,
+ EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY, NULL);
+ model->priv->use_default_limit = TRUE;
+#endif
+ model->priv->limit = EGG_RECENT_MODEL_DEFAULT_LIMIT;
+ model->priv->use_default_limit = FALSE;
+
+ model->priv->monitors = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) gnome_vfs_monitor_cancel);
+
+ model->priv->monitor = NULL;
+ egg_recent_model_monitor (model, TRUE);
+}
+
+
+/**
+ * egg_recent_model_new:
+ * @sort: the type of sorting to use
+ * @limit: maximum number of items in the list
+ *
+ * This creates a new EggRecentModel object.
+ *
+ * Returns: a EggRecentModel object
+ */
+EggRecentModel *
+egg_recent_model_new (EggRecentModelSort sort)
+{
+ EggRecentModel *model;
+
+ model = EGG_RECENT_MODEL (g_object_new (egg_recent_model_get_type (),
+ "sort-type", sort, NULL));
+
+ g_return_val_if_fail (model, NULL);
+
+ return model;
+}
+
+/**
+ * egg_recent_model_add_full:
+ * @model: A EggRecentModel object.
+ * @item: A EggRecentItem
+ *
+ * This function adds an item to the list of recently used URIs.
+ *
+ * Returns: gboolean
+ */
+gboolean
+egg_recent_model_add_full (EggRecentModel * model, EggRecentItem *item)
+{
+ FILE *file;
+ GList *list = NULL;
+ gboolean ret = FALSE;
+ gboolean updated = FALSE;
+ char *uri;
+ time_t t;
+
+ g_return_val_if_fail (model != NULL, FALSE);
+ g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
+
+ uri = egg_recent_item_get_uri (item);
+ if (strncmp (uri, "recent-files://", strlen ("recent-files://")) == 0) {
+ g_free (uri);
+ return FALSE;
+ } else {
+ g_free (uri);
+ }
+
+ file = egg_recent_model_open_file (model);
+ g_return_val_if_fail (file != NULL, FALSE);
+
+ time (&t);
+ egg_recent_item_set_timestamp (item, t);
+
+ if (egg_recent_model_lock_file (file)) {
+
+ /* read existing stuff */
+ list = egg_recent_model_read (model, file);
+
+ /* if it's already there, we just update it */
+ updated = egg_recent_model_update_item (list, item);
+
+ if (!updated) {
+ list = g_list_prepend (list, item);
+
+ egg_recent_model_enforce_limit (list,
+ EGG_RECENT_MODEL_MAX_ITEMS);
+ }
+
+ /* write new stuff */
+ if (!egg_recent_model_write (model, file, list))
+ g_warning ("Write failed: %s", strerror (errno));
+
+ if (!updated)
+ list = g_list_remove (list, item);
+
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+ ret = TRUE;
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ fclose (file);
+ return FALSE;
+ }
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ fclose (file);
+
+ if (model->priv->monitor == NULL) {
+ /* since monitoring isn't working, at least give a
+ * local notification
+ */
+ egg_recent_model_changed (model);
+ }
+
+ return ret;
+}
+
+/**
+ * egg_recent_model_add:
+ * @model: A EggRecentModel object.
+ * @uri: A string URI
+ *
+ * This function adds an item to the list of recently used URIs.
+ *
+ * Returns: gboolean
+ */
+gboolean
+egg_recent_model_add (EggRecentModel *model, const gchar *uri)
+{
+ EggRecentItem *item;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (model != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ item = egg_recent_item_new_from_uri (uri);
+
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ ret = egg_recent_model_add_full (model, item);
+
+ egg_recent_item_unref (item);
+
+ return ret;
+}
+
+
+
+/**
+ * egg_recent_model_delete:
+ * @model: A EggRecentModel object.
+ * @uri: The URI you want to delete.
+ *
+ * This function deletes a URI from the file of recently used URIs.
+ *
+ * Returns: gboolean
+ */
+gboolean
+egg_recent_model_delete (EggRecentModel * model, const gchar * uri)
+{
+ FILE *file;
+ GList *list;
+ unsigned int length;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (model != NULL, FALSE);
+ g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ file = egg_recent_model_open_file (model);
+ g_return_val_if_fail (file != NULL, FALSE);
+
+ if (egg_recent_model_lock_file (file)) {
+ list = egg_recent_model_read (model, file);
+
+ if (list == NULL)
+ goto out;
+
+ length = g_list_length (list);
+
+ list = egg_recent_model_delete_from_list (list, uri);
+
+ if (length == g_list_length (list)) {
+ /* nothing was deleted */
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+ } else {
+ egg_recent_model_write (model, file, list);
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+ ret = TRUE;
+
+ }
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ return FALSE;
+ }
+
+out:
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ fclose (file);
+
+ g_hash_table_remove (model->priv->monitors, uri);
+
+ if (model->priv->monitor == NULL && ret) {
+ /* since monitoring isn't working, at least give a
+ * local notification
+ */
+ egg_recent_model_changed (model);
+ }
+
+ return ret;
+}
+
+
+/**
+ * egg_recent_model_get_list:
+ * @model: A EggRecentModel object.
+ *
+ * This function gets the current contents of the file
+ *
+ * Returns: a GList
+ */
+GList *
+egg_recent_model_get_list (EggRecentModel *model)
+{
+ FILE *file;
+ GList *list=NULL;
+
+ file = egg_recent_model_open_file (model);
+ g_return_val_if_fail (file != NULL, NULL);
+
+ if (egg_recent_model_lock_file (file)) {
+ list = egg_recent_model_read (model, file);
+
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ fclose (file);
+ return NULL;
+ }
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ if (list != NULL) {
+ list = egg_recent_model_filter (model, list);
+ list = egg_recent_model_sort (model, list);
+
+ egg_recent_model_enforce_limit (list, model->priv->limit);
+ }
+
+ fclose (file);
+
+ return list;
+}
+
+
+
+/**
+ * egg_recent_model_set_limit:
+ * @model: A EggRecentModel object.
+ * @limit: The maximum length of the list
+ *
+ * This function sets the maximum length of the list. Note: This only affects
+ * the length of the list emitted in the "changed" signal, not the list stored
+ * on disk.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_limit (EggRecentModel *model, int limit)
+{
+ model->priv->use_default_limit = FALSE;
+
+ egg_recent_model_set_limit_internal (model, limit);
+}
+
+/**
+ * egg_recent_model_get_limit:
+ * @model: A EggRecentModel object.
+ *
+ * This function gets the maximum length of the list.
+ *
+ * Returns: int
+ */
+int
+egg_recent_model_get_limit (EggRecentModel *model)
+{
+ return model->priv->limit;
+}
+
+
+/**
+ * egg_recent_model_clear:
+ * @model: A EggRecentModel object.
+ *
+ * This function clears the contents of the file
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_clear (EggRecentModel *model)
+{
+ FILE *file;
+ int fd;
+
+ file = egg_recent_model_open_file (model);
+ g_return_if_fail (file != NULL);
+
+ fd = fileno (file);
+
+ if (egg_recent_model_lock_file (file)) {
+ ftruncate (fd, 0);
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ return;
+ }
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ fclose (file);
+}
+
+
+/**
+ * egg_recent_model_set_filter_mime_types:
+ * @model: A EggRecentModel object.
+ *
+ * Sets which mime types are allowed in the list.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_filter_mime_types (EggRecentModel *model,
+ ...)
+{
+ va_list valist;
+ GSList *list = NULL;
+ gchar *str;
+
+ g_return_if_fail (model != NULL);
+
+ if (model->priv->mime_filter_values != NULL) {
+ g_slist_foreach (model->priv->mime_filter_values,
+ (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free (model->priv->mime_filter_values);
+ model->priv->mime_filter_values = NULL;
+ }
+
+ va_start (valist, model);
+
+ str = va_arg (valist, gchar*);
+
+ while (str != NULL) {
+ list = g_slist_prepend (list, g_pattern_spec_new (str));
+
+ str = va_arg (valist, gchar*);
+ }
+
+ va_end (valist);
+
+ model->priv->mime_filter_values = list;
+}
+
+/**
+ * egg_recent_model_set_filter_groups:
+ * @model: A EggRecentModel object.
+ *
+ * Sets which groups are allowed in the list.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_filter_groups (EggRecentModel *model,
+ ...)
+{
+ va_list valist;
+ GSList *list = NULL;
+ gchar *str;
+
+ g_return_if_fail (model != NULL);
+
+ if (model->priv->group_filter_values != NULL) {
+ g_slist_foreach (model->priv->group_filter_values, (GFunc)g_free, NULL);
+ g_slist_free (model->priv->group_filter_values);
+ model->priv->group_filter_values = NULL;
+ }
+
+ va_start (valist, model);
+
+ str = va_arg (valist, gchar*);
+
+ while (str != NULL) {
+ list = g_slist_prepend (list, g_strdup (str));
+
+ str = va_arg (valist, gchar*);
+ }
+
+ va_end (valist);
+
+ model->priv->group_filter_values = list;
+}
+
+/**
+ * egg_recent_model_set_filter_uri_schemes:
+ * @model: A EggRecentModel object.
+ *
+ * Sets which URI schemes (file, http, ftp, etc) are allowed in the list.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_filter_uri_schemes (EggRecentModel *model, ...)
+{
+ va_list valist;
+ GSList *list = NULL;
+ gchar *str;
+
+ g_return_if_fail (model != NULL);
+
+ if (model->priv->scheme_filter_values != NULL) {
+ g_slist_foreach (model->priv->scheme_filter_values,
+ (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free (model->priv->scheme_filter_values);
+ model->priv->scheme_filter_values = NULL;
+ }
+
+ va_start (valist, model);
+
+ str = va_arg (valist, gchar*);
+
+ while (str != NULL) {
+ list = g_slist_prepend (list, g_pattern_spec_new (str));
+
+ str = va_arg (valist, gchar*);
+ }
+
+ va_end (valist);
+
+ model->priv->scheme_filter_values = list;
+}
+
+/**
+ * egg_recent_model_set_sort:
+ * @model: A EggRecentModel object.
+ * @sort: A EggRecentModelSort type
+ *
+ * Sets the type of sorting to be used.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_sort (EggRecentModel *model,
+ EggRecentModelSort sort)
+{
+ g_return_if_fail (model != NULL);
+
+ model->priv->sort_type = sort;
+}
+
+/**
+ * egg_recent_model_changed:
+ * @model: A EggRecentModel object.
+ *
+ * This function causes a "changed" signal to be emitted.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_changed (EggRecentModel *model)
+{
+ GList *list = NULL;
+
+ if (model->priv->limit > 0) {
+ list = egg_recent_model_get_list (model);
+ /* egg_recent_model_monitor_list (model, list); */
+
+ g_signal_emit (G_OBJECT (model), model_signals[CHANGED], 0,
+ list);
+ }
+
+ if (list)
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+}
+
+static void
+egg_recent_model_remove_expired_list (EggRecentModel *model, GList *list)
+{
+ time_t current_time;
+ time_t day_seconds;
+
+ time (¤t_time);
+ day_seconds = model->priv->expire_days*24*60*60;
+
+ while (list != NULL) {
+ EggRecentItem *item = list->data;
+ time_t timestamp;
+
+ timestamp = egg_recent_item_get_timestamp (item);
+
+ if ((timestamp+day_seconds) < current_time) {
+ gchar *uri = egg_recent_item_get_uri (item);
+ egg_recent_model_delete (model, uri);
+
+ g_strdup (uri);
+ }
+
+ list = list->next;
+ }
+}
+
+
+/**
+ * egg_recent_model_remove_expired:
+ * @model: A EggRecentModel object.
+ *
+ * Goes through the entire list, and removes any items that are older than
+ * the user-specified expiration period.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_remove_expired (EggRecentModel *model)
+{
+ FILE *file;
+ GList *list=NULL;
+
+ g_return_if_fail (model != NULL);
+
+ file = egg_recent_model_open_file (model);
+ g_return_if_fail (file != NULL);
+
+ if (egg_recent_model_lock_file (file)) {
+ list = egg_recent_model_read (model, file);
+
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ return;
+ }
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ if (list != NULL) {
+ egg_recent_model_remove_expired_list (model, list);
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+ }
+
+ fclose (file);
+}
+
+/**
+ * egg_recent_model_get_type:
+ *
+ * This returns a GType representing a EggRecentModel object.
+ *
+ * Returns: a GType
+ */
+GType
+egg_recent_model_get_type (void)
+{
+ static GType egg_recent_model_type = 0;
+
+ if(!egg_recent_model_type) {
+ static const GTypeInfo egg_recent_model_info = {
+ sizeof (EggRecentModelClass),
+ NULL, /* base init */
+ NULL, /* base finalize */
+ (GClassInitFunc)egg_recent_model_class_init, /* class init */
+ NULL, /* class finalize */
+ NULL, /* class data */
+ sizeof (EggRecentModel),
+ 0,
+ (GInstanceInitFunc) egg_recent_model_init
+ };
+
+ egg_recent_model_type = g_type_register_static (G_TYPE_OBJECT,
+ "EggRecentModel",
+ &egg_recent_model_info, 0);
+ }
+
+ return egg_recent_model_type;
+}
+
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-item.h
@@ -0,0 +1,80 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+
+#ifndef __EGG_RECENT_ITEM_H__
+#define __EGG_RECENT_ITEM_H__
+
+#include <time.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_RECENT_ITEM (egg_recent_item_get_type ())
+
+#define EGG_RECENT_ITEM_LIST_UNREF(list) \
+ g_list_foreach (list, (GFunc)egg_recent_item_unref, NULL); \
+ g_list_free (list);
+
+typedef struct _EggRecentItem EggRecentItem;
+
+struct _EggRecentItem {
+ /* do not access any of these directly */
+ gchar *uri;
+ gchar *mime_type;
+ time_t timestamp;
+
+ gboolean private_data;
+
+ GList *groups;
+
+ int refcount;
+};
+
+GType egg_recent_item_get_type (void) G_GNUC_CONST;
+
+/* constructors */
+EggRecentItem * egg_recent_item_new (void);
+
+EggRecentItem * egg_recent_item_ref (EggRecentItem *item);
+EggRecentItem * egg_recent_item_unref (EggRecentItem *item);
+
+/* automatically fetches the mime type, etc */
+EggRecentItem * egg_recent_item_new_from_uri (const gchar *uri);
+
+gboolean egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri);
+gchar * egg_recent_item_get_uri (const EggRecentItem *item);
+gchar * egg_recent_item_get_uri_utf8 (const EggRecentItem *item);
+gchar * egg_recent_item_get_uri_for_display (const EggRecentItem *item);
+gchar * egg_recent_item_get_short_name (const EggRecentItem *item);
+
+void egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime);
+gchar * egg_recent_item_get_mime_type (const EggRecentItem *item);
+
+void egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp);
+time_t egg_recent_item_get_timestamp (const EggRecentItem *item);
+
+G_CONST_RETURN gchar *egg_recent_item_peek_uri (const EggRecentItem *item);
+
+
+/* groups */
+G_CONST_RETURN GList * egg_recent_item_get_groups (const EggRecentItem *item);
+
+gboolean egg_recent_item_in_group (const EggRecentItem *item,
+ const gchar *group_name);
+
+void egg_recent_item_add_group (EggRecentItem *item,
+ const gchar *group_name);
+
+void egg_recent_item_remove_group (EggRecentItem *item,
+ const gchar *group_name);
+
+void egg_recent_item_set_private (EggRecentItem *item,
+ gboolean priv);
+
+gboolean egg_recent_item_get_private (const EggRecentItem *item);
+
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_ITEM_H__ */
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent.h
@@ -0,0 +1,7 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include "egg-recent-item.h"
+#include "egg-recent-model.h"
+#include "egg-recent-view.h"
+#include "egg-recent-view-bonobo.h"
+#include "egg-recent-view-gtk.h"
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-util.c
@@ -0,0 +1,141 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include <goffice/goffice-config.h>
+/* #include <config.h> */
+#include <stdio.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#include <libgnomeui/gnome-icon-lookup.h>
+#endif
+#include <math.h>
+#include "egg-recent-util.h"
+
+#define EGG_RECENT_UTIL_HOSTNAME_SIZE 512
+
+/* ripped out of gedit2 */
+gchar*
+egg_recent_util_escape_underlines (const gchar* text)
+{
+ GString *str;
+ gint length;
+ const gchar *p;
+ const gchar *end;
+
+ g_return_val_if_fail (text != NULL, NULL);
+
+ length = strlen (text);
+
+ str = g_string_new ("");
+
+ p = text;
+ end = text + length;
+
+ while (p != end)
+ {
+ const gchar *next;
+ next = g_utf8_next_char (p);
+
+ switch (*p)
+ {
+ case '_':
+ g_string_append (str, "__");
+ break;
+ default:
+ g_string_append_len (str, p, next - p);
+ break;
+ }
+
+ p = next;
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+#ifndef USE_STABLE_LIBGNOMEUI
+static GdkPixbuf *
+load_icon_file (char *filename,
+ guint nominal_size)
+{
+ GdkPixbuf *pixbuf, *scaled_pixbuf;
+ guint width, height;
+
+ pixbuf = gdk_pixbuf_new_from_file_at_size (filename, nominal_size, nominal_size, NULL);
+
+ if (pixbuf == NULL) {
+ return NULL;
+ }
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ /* if the icon is larger than the nominal size, scale down */
+ if (MAX (width, height) > nominal_size) {
+ if (width > height) {
+ height = height * nominal_size / width;
+ width = nominal_size;
+ } else {
+ width = width * nominal_size / height;
+ height = nominal_size;
+ }
+ scaled_pixbuf = gdk_pixbuf_scale_simple
+ (pixbuf, width, height, GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+ pixbuf = scaled_pixbuf;
+ }
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+egg_recent_util_get_icon (GnomeIconTheme *theme, const gchar *uri,
+ const gchar *mime_type, int size)
+{
+ gchar *icon;
+ gchar *filename;
+ const GnomeIconData *icon_data;
+ GdkPixbuf *pixbuf;
+
+ icon = gnome_icon_lookup (theme, NULL, uri, NULL, NULL,
+ mime_type, 0, NULL);
+
+
+ g_return_val_if_fail (icon != NULL, NULL);
+
+ filename = gnome_icon_theme_lookup_icon (theme, icon,
+ size,
+ &icon_data,
+ NULL);
+ g_free (icon);
+
+ if (filename == NULL) {
+ return NULL;
+ }
+
+ pixbuf = load_icon_file (filename, size);
+ g_free (filename);
+
+
+ return pixbuf;
+}
+#endif /* !USE_STABLE_LIBGNOMEUI */
+
+gchar *
+egg_recent_util_get_unique_id (void)
+{
+ char hostname[EGG_RECENT_UTIL_HOSTNAME_SIZE];
+ time_t the_time;
+ guint32 rand;
+ int pid;
+
+ gethostname (hostname, EGG_RECENT_UTIL_HOSTNAME_SIZE);
+
+ time (&the_time);
+ rand = g_random_int ();
+ pid = getpid ();
+
+ return g_strdup_printf ("%s-%d-%d-%d", hostname, (int)time, (int)rand, (int)pid);
+}
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-util.h
@@ -0,0 +1,25 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+
+#ifndef __EGG_RECENT_UTIL__
+#define __EGG_RECENT_UTIL__
+
+#include <gtk/gtk.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#endif
+
+G_BEGIN_DECLS
+
+gchar * egg_recent_util_escape_underlines (const gchar *uri);
+gchar * egg_recent_util_get_unique_id (void);
+#ifndef USE_STABLE_LIBGNOMEUI
+GdkPixbuf * egg_recent_util_get_icon (GnomeIconTheme *theme,
+ const gchar *uri,
+ const gchar *mime_type,
+ int size);
+#endif
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_UTIL__ */
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-view-gtk.c
@@ -0,0 +1,820 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include <goffice/goffice-config.h>
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * James Willcox <jwillcox at cs.indiana.edu>
+ */
+
+#ifdef HAVE_CONFIG_H
+/* #include <config.h> */
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <libgnomevfs/gnome-vfs.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#endif
+#include <gconf/gconf-client.h>
+#include "egg-recent-model.h"
+#include "egg-recent-view.h"
+#include "egg-recent-view-gtk.h"
+#include "egg-recent-util.h"
+#include "egg-recent-item.h"
+
+struct _EggRecentViewGtk {
+ GObject parent_instance; /* We emit signals */
+
+ GtkWidget *menu;
+ GtkWidget *start_menu_item;
+
+ gboolean leading_sep;
+ gboolean trailing_sep;
+
+ gulong changed_cb_id;
+
+ gchar *uid;
+
+ gboolean show_icons;
+ gboolean show_numbers;
+#ifndef USE_STABLE_LIBGNOMEUI
+ GnomeIconTheme *theme;
+#endif
+
+ GtkTooltips *tooltips;
+ EggRecentViewGtkTooltipFunc tooltip_func;
+ gpointer tooltip_func_data;
+
+ EggRecentModel *model;
+ GConfClient *client;
+ GtkIconSize icon_size;
+};
+
+
+
+struct _EggRecentViewGtkMenuData {
+ EggRecentViewGtk *view;
+ EggRecentItem *item;
+};
+
+typedef struct _EggRecentViewGtkMenuData EggRecentViewGtkMenuData;
+
+enum {
+ ACTIVATE,
+ LAST_SIGNAL
+};
+
+/* GObject properties */
+enum {
+ PROP_BOGUS,
+ PROP_MENU,
+ PROP_START_MENU_ITEM,
+ PROP_SHOW_ICONS,
+ PROP_SHOW_NUMBERS
+};
+
+static guint view_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+egg_recent_view_gtk_clear (EggRecentViewGtk *view)
+{
+ GList *menu_children;
+ GList *p;
+ GObject *menu_item;
+ gint *menu_data=NULL;
+
+ g_return_if_fail (view->menu != NULL);
+
+ menu_children = gtk_container_get_children (GTK_CONTAINER (view->menu));
+
+ p = menu_children;
+ while (p != NULL) {
+ menu_item = (GObject *)p->data;
+
+ menu_data = (gint *)g_object_get_data (menu_item,
+ view->uid);
+
+ if (menu_data) {
+ gtk_container_remove (GTK_CONTAINER (view->menu),
+ GTK_WIDGET (menu_item));
+
+ }
+
+ p = p->next;
+ }
+}
+
+
+static gint
+egg_recent_view_gtk_find_menu_offset (EggRecentViewGtk *view)
+{
+ gint i;
+ GList *menu_children;
+ GList *p;
+ GtkWidget *menu_item;
+ gint menu_loc=-1;
+
+ g_return_val_if_fail (view, 0);
+
+ menu_children = GTK_MENU_SHELL (view->menu)->children;
+
+ i = 0;
+ p = menu_children;
+ while (p != NULL) {
+ menu_item = (GtkWidget *)p->data;
+
+ if (menu_item == view->start_menu_item) {
+ menu_loc = i;
+ break;
+ }
+
+ p = p->next;
+ i++;
+ }
+
+ return menu_loc;
+}
+
+static void
+egg_recent_view_gtk_menu_cb (GtkWidget *menu, gpointer data)
+{
+ EggRecentViewGtkMenuData *md = (EggRecentViewGtkMenuData *) data;
+ EggRecentItem *item;
+
+ g_return_if_fail (md);
+ g_return_if_fail (md->item);
+ g_return_if_fail (md->view);
+ g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (md->view));
+
+ item = md->item;
+
+ egg_recent_item_ref (item);
+
+ g_signal_emit (G_OBJECT(md->view), view_signals[ACTIVATE], 0,
+ item);
+
+ egg_recent_item_unref (item);
+}
+
+static void
+egg_recent_view_gtk_destroy_cb (gpointer data, GClosure *closure)
+{
+ EggRecentViewGtkMenuData *md = data;
+
+ egg_recent_item_unref (md->item);
+ g_free (md);
+}
+
+static GtkWidget *
+egg_recent_view_gtk_new_separator (EggRecentViewGtk *view)
+{
+ GtkWidget *retval;
+
+ g_return_val_if_fail (view, NULL);
+
+ retval = gtk_separator_menu_item_new ();
+
+ /**
+ * this is a tag so we can distinguish our menu items
+ * from others that may be in the menu.
+ */
+ g_object_set_data (G_OBJECT (retval),
+ view->uid,
+ GINT_TO_POINTER (1));
+
+
+ gtk_widget_show (retval);
+
+ return retval;
+}
+
+static GtkWidget *
+egg_recent_view_gtk_new_menu_item (EggRecentViewGtk *view,
+ EggRecentItem *item,
+ gint index)
+{
+ GtkWidget *menu_item;
+ EggRecentViewGtkMenuData *md;
+ gchar *mime_type;
+ GtkWidget *image;
+ GdkPixbuf *pixbuf;
+ gchar *text;
+ gchar *short_name;
+ gchar *escaped;
+
+ g_return_val_if_fail (view, NULL);
+ g_return_val_if_fail (item, NULL);
+
+ short_name = egg_recent_item_get_short_name (item);
+ if (!short_name)
+ return NULL;
+
+ escaped = egg_recent_util_escape_underlines (short_name);
+ g_free (short_name);
+
+ if (view->show_numbers) {
+ /* avoid having conflicting mnemonics */
+ if (index >= 10)
+ text = g_strdup_printf ("%d. %s", index,
+ escaped);
+ else
+ text = g_strdup_printf ("_%d. %s", index,
+ escaped);
+ g_free (escaped);
+ } else {
+ text = escaped;
+ }
+
+ mime_type = egg_recent_item_get_mime_type (item);
+#ifndef USE_STABLE_LIBGNOMEUI
+ {
+ int width, height;
+ gchar *uri;
+
+ gtk_icon_size_lookup_for_settings
+ (gtk_widget_get_settings (view->menu),
+ view->icon_size,
+ &width, &height);
+
+ uri = egg_recent_item_get_uri (item);
+ pixbuf = egg_recent_util_get_icon (view->theme, uri,
+ mime_type,
+ height);
+ g_free (uri);
+ }
+#else
+ pixbuf = NULL;
+#endif
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ if (pixbuf)
+ g_object_unref (pixbuf);
+
+ if (view->show_icons)
+ gtk_widget_show (image);
+
+ menu_item = gtk_image_menu_item_new_with_mnemonic (text);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
+ image);
+
+ md = g_new0 (EggRecentViewGtkMenuData, 1);
+ md->view = view;
+ md->item = egg_recent_item_ref (item);
+
+ g_signal_connect_data (G_OBJECT (menu_item), "activate",
+ G_CALLBACK (egg_recent_view_gtk_menu_cb),
+ md,
+ (GClosureNotify)egg_recent_view_gtk_destroy_cb,
+ 0);
+
+ g_free (mime_type);
+ g_free (text);
+
+ /**
+ * this is a tag so we can distinguish our menu items
+ * from others that may be in the menu.
+ */
+ g_object_set_data (G_OBJECT (menu_item),
+ view->uid,
+ GINT_TO_POINTER (1));
+
+
+ gtk_widget_show (menu_item);
+
+ return menu_item;
+}
+
+static void
+egg_recent_view_gtk_add_to_menu (EggRecentViewGtk *view,
+ EggRecentItem *item,
+ gint display,
+ gint index)
+{
+ GtkWidget *menu_item;
+ gint menu_offset;
+
+ g_return_if_fail (view);
+ g_return_if_fail (view->menu);
+
+ menu_offset = egg_recent_view_gtk_find_menu_offset (view);
+
+ if (item != NULL)
+ menu_item = egg_recent_view_gtk_new_menu_item (view, item, display);
+ else
+ menu_item = egg_recent_view_gtk_new_separator (view);
+
+ if (view->tooltip_func != NULL && menu_item != NULL) {
+ view->tooltip_func (view->tooltips, menu_item,
+ item, view->tooltip_func_data);
+ }
+
+ if (menu_item)
+ gtk_menu_shell_insert (GTK_MENU_SHELL (view->menu), menu_item,
+ menu_offset+index);
+}
+
+static void
+egg_recent_view_gtk_set_list (EggRecentViewGtk *view, GList *list)
+{
+ EggRecentItem *item;
+ GList *p;
+ gint display=1;
+ gint index=1;
+
+ g_return_if_fail (view);
+
+ egg_recent_view_gtk_clear (view);
+
+ if (view->leading_sep) {
+ egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
+ index++;
+ }
+
+ p = list;
+ while (p != NULL) {
+ item = (EggRecentItem *)p->data;
+
+ egg_recent_view_gtk_add_to_menu (view, item, display, index);
+
+ p = p->next;
+ display++;
+ index++;
+ }
+
+ if (view->trailing_sep)
+ egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
+}
+
+static void
+model_changed_cb (EggRecentModel *model, GList *list, EggRecentViewGtk *view)
+{
+ if (list != NULL)
+ egg_recent_view_gtk_set_list (view, list);
+ else
+ egg_recent_view_gtk_clear (view);
+}
+
+static EggRecentModel *
+egg_recent_view_gtk_get_model (EggRecentView *view_parent)
+{
+ EggRecentViewGtk *view;
+
+ g_return_val_if_fail (view_parent != NULL, NULL);
+ view = EGG_RECENT_VIEW_GTK (view_parent);
+ return view->model;
+}
+
+static void
+egg_recent_view_gtk_set_model (EggRecentView *view_parent,
+ EggRecentModel *model)
+{
+ EggRecentViewGtk *view;
+
+ g_return_if_fail (view_parent != NULL);
+ view = EGG_RECENT_VIEW_GTK (view_parent);
+
+ if (view->model != NULL) {
+ g_object_unref (view->model);
+ g_signal_handler_disconnect (G_OBJECT (model),
+ view->changed_cb_id);
+ }
+
+ view->model = model;
+ g_object_ref (view->model);
+
+ view->changed_cb_id = g_signal_connect_object (G_OBJECT (model),
+ "changed",
+ G_CALLBACK (model_changed_cb),
+ view, 0);
+
+ egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_set_leading_sep (EggRecentViewGtk *view, gboolean val)
+{
+ view->leading_sep = val;
+
+ egg_recent_view_gtk_clear (view);
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_set_trailing_sep (EggRecentViewGtk *view, gboolean val)
+{
+ view->trailing_sep = val;
+
+ egg_recent_view_gtk_clear (view);
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+static void
+egg_recent_view_gtk_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
+
+ switch (prop_id)
+ {
+ case PROP_MENU:
+ egg_recent_view_gtk_set_menu (view,
+ GTK_WIDGET (g_value_get_object (value)));
+ break;
+ case PROP_START_MENU_ITEM:
+ egg_recent_view_gtk_set_start_menu_item (view,
+ g_value_get_object (value));
+ break;
+ case PROP_SHOW_ICONS:
+ egg_recent_view_gtk_show_icons (view,
+ g_value_get_boolean (value));
+ break;
+ case PROP_SHOW_NUMBERS:
+ egg_recent_view_gtk_show_numbers (view,
+ g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_recent_view_gtk_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
+
+ switch (prop_id)
+ {
+ case PROP_MENU:
+ g_value_set_object (value, view->menu);
+ break;
+ case PROP_START_MENU_ITEM:
+ g_value_set_object (value, view->start_menu_item);
+ break;
+ case PROP_SHOW_ICONS:
+ g_value_set_boolean (value, view->show_icons);
+ break;
+ case PROP_SHOW_NUMBERS:
+ g_value_set_boolean (value, view->show_numbers);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_recent_view_gtk_finalize (GObject *object)
+{
+ EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
+
+ g_signal_handler_disconnect (G_OBJECT (view->model),
+ view->changed_cb_id);
+
+ g_free (view->uid);
+
+ g_object_unref (view->menu);
+ g_object_unref (view->model);
+#ifndef USE_STABLE_LIBGNOMEUI
+ g_object_unref (view->theme);
+#endif
+ g_object_unref (view->client);
+
+ g_object_unref (view->tooltips);
+}
+
+static void
+egg_recent_view_gtk_class_init (EggRecentViewGtkClass * klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = egg_recent_view_gtk_set_property;
+ object_class->get_property = egg_recent_view_gtk_get_property;
+ object_class->finalize = egg_recent_view_gtk_finalize;
+
+ view_signals[ACTIVATE] = g_signal_new ("activate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EggRecentViewGtkClass, activate),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ EGG_TYPE_RECENT_ITEM);
+
+ g_object_class_install_property (object_class,
+ PROP_MENU,
+ g_param_spec_object ("menu",
+ "Menu",
+ "The GtkMenuShell this object will update.",
+ gtk_menu_get_type(),
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_START_MENU_ITEM,
+ g_param_spec_object ("start-menu-item",
+ "Start Menu Item",
+ "The menu item that precedes where are menu items will go",
+ gtk_menu_item_get_type (),
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_SHOW_ICONS,
+ g_param_spec_boolean ("show-icons",
+ "Show Icons",
+ "Whether or not to show icons",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_SHOW_NUMBERS,
+ g_param_spec_boolean ("show-numbers",
+ "Show Numbers",
+ "Whether or not to show numbers",
+ TRUE,
+ G_PARAM_READWRITE));
+
+ klass->activate = NULL;
+}
+
+static void
+egg_recent_view_init (EggRecentViewClass *iface)
+{
+ iface->do_get_model = egg_recent_view_gtk_get_model;
+ iface->do_set_model = egg_recent_view_gtk_set_model;
+}
+
+static void
+show_menus_changed_cb (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ EggRecentViewGtk *view)
+{
+ GConfValue *value;
+
+ value = gconf_entry_get_value (entry);
+
+ g_return_if_fail (value->type == GCONF_VALUE_BOOL);
+
+ egg_recent_view_gtk_show_icons (view,
+ gconf_value_get_bool (value));
+
+}
+
+#ifndef USE_STABLE_LIBGNOMEUI
+static void
+theme_changed_cb (GnomeIconTheme *theme, EggRecentViewGtk *view)
+{
+ if (view->model != NULL)
+ egg_recent_model_changed (view->model);
+}
+#endif
+
+static void
+egg_recent_view_gtk_init (EggRecentViewGtk * view)
+{
+ view->client = gconf_client_get_default ();
+
+ view->show_icons =
+ gconf_client_get_bool (view->client,
+ "/desktop/gnome/interface/menus_have_icons",
+ NULL);
+
+ gconf_client_add_dir (view->client, "/desktop/gnome/interface",
+ GCONF_CLIENT_PRELOAD_NONE,
+ NULL);
+ gconf_client_notify_add (view->client,
+ "/desktop/gnome/interface/menus_have_icons",
+ (GConfClientNotifyFunc)show_menus_changed_cb,
+ view, NULL, NULL);
+
+
+ view->leading_sep = FALSE;
+ view->trailing_sep = FALSE;
+
+ view->uid = egg_recent_util_get_unique_id ();
+#ifndef USE_STABLE_LIBGNOMEUI
+ view->theme = gnome_icon_theme_new ();
+ gnome_icon_theme_set_allow_svg (view->theme, TRUE);
+ g_signal_connect_object (view->theme, "changed",
+ G_CALLBACK (theme_changed_cb), view, 0);
+#endif
+ view->tooltips = gtk_tooltips_new ();
+ g_object_ref (view->tooltips);
+ gtk_object_sink (GTK_OBJECT (view->tooltips));
+ view->tooltip_func = NULL;
+ view->tooltip_func_data = NULL;
+
+ view->icon_size = GTK_ICON_SIZE_MENU;
+}
+
+void
+egg_recent_view_gtk_set_icon_size (EggRecentViewGtk *view,
+ GtkIconSize icon_size)
+{
+ if (view->icon_size != icon_size) {
+ view->icon_size = icon_size;
+ egg_recent_model_changed (view->model);
+ } else {
+ view->icon_size = icon_size;
+ }
+}
+
+GtkIconSize
+egg_recent_view_gtk_get_icon_size (EggRecentViewGtk *view)
+{
+ return view->icon_size;
+}
+
+void
+egg_recent_view_gtk_show_icons (EggRecentViewGtk *view, gboolean show)
+{
+ view->show_icons = show;
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_show_numbers (EggRecentViewGtk *view, gboolean show)
+{
+ view->show_numbers = show;
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_set_tooltip_func (EggRecentViewGtk *view,
+ EggRecentViewGtkTooltipFunc func,
+ gpointer user_data)
+{
+ view->tooltip_func = func;
+ view->tooltip_func_data = user_data;
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+/**
+ * egg_recent_view_gtk_set_menu:
+ * @view: A EggRecentViewGtk object.
+ * @menu: The GtkMenuShell to put the menu items in.
+ *
+ * Use this function to change the GtkMenuShell that the recent
+ * documents appear in.
+ *
+ */
+void
+egg_recent_view_gtk_set_menu (EggRecentViewGtk *view,
+ GtkWidget *menu)
+{
+ g_return_if_fail (view);
+ g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
+ g_return_if_fail (menu);
+
+ if (view->menu != NULL)
+ g_object_unref (view->menu);
+
+ view->menu = menu;
+ g_object_ref (view->menu);
+}
+
+/**
+ * egg_recent_view_gtk_set_start_menu_item:
+ * @view: A EggRecentViewGtk object.
+ * @start_menu_item: The menu item that appears just before where our menu
+ * items should appear
+ *
+ */
+void
+egg_recent_view_gtk_set_start_menu_item (EggRecentViewGtk *view,
+ GtkWidget *menu_item)
+{
+ g_return_if_fail (view);
+ g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
+
+ view->start_menu_item = menu_item;
+}
+
+/**
+ * egg_recent_view_gtk_get_menu:
+ * @view: A EggRecentViewGtk object.
+ *
+ */
+GtkWidget *
+egg_recent_view_gtk_get_menu (EggRecentViewGtk *view)
+{
+ return view->menu;
+}
+
+/**
+ * egg_recent_view_gtk_get_start_menu_item
+ * @view: A EggRecentViewGtk object.
+ *
+ */
+GtkWidget *
+egg_recent_view_gtk_get_start_menu_item (EggRecentViewGtk *view)
+{
+ return view->start_menu_item;
+}
+
+
+/**
+ * egg_recent_view_gtk_new:
+ * @appname: The name of your application.
+ * @limit: The maximum number of items allowed.
+ *
+ * This creates a new EggRecentViewGtk object.
+ *
+ * Returns: a EggRecentViewGtk object
+ */
+EggRecentViewGtk *
+egg_recent_view_gtk_new (GtkWidget *menu, GtkWidget *start_menu_item)
+{
+ EggRecentViewGtk *view;
+
+ g_return_val_if_fail (menu, NULL);
+
+ view = EGG_RECENT_VIEW_GTK (g_object_new (egg_recent_view_gtk_get_type (),
+ "start-menu-item",
+ start_menu_item,
+ "menu", menu,
+ "show-numbers", TRUE, NULL));
+
+ g_return_val_if_fail (view, NULL);
+
+ return view;
+}
+
+/**
+ * egg_recent_view_gtk_get_type:
+ * @:
+ *
+ * This returns a GType representing a EggRecentViewGtk object.
+ *
+ * Returns: a GType
+ */
+GType
+egg_recent_view_gtk_get_type (void)
+{
+ static GType egg_recent_view_gtk_type = 0;
+
+ if(!egg_recent_view_gtk_type) {
+ static const GTypeInfo egg_recent_view_gtk_info = {
+ sizeof (EggRecentViewGtkClass),
+ NULL, /* base init */
+ NULL, /* base finalize */
+ (GClassInitFunc)egg_recent_view_gtk_class_init, /* class init */
+ NULL, /* class finalize */
+ NULL, /* class data */
+ sizeof (EggRecentViewGtk),
+ 0,
+ (GInstanceInitFunc) egg_recent_view_gtk_init
+ };
+
+ static const GInterfaceInfo view_info =
+ {
+ (GInterfaceInitFunc) egg_recent_view_init,
+ NULL,
+ NULL
+ };
+
+ egg_recent_view_gtk_type = g_type_register_static (G_TYPE_OBJECT,
+ "EggRecentViewGtk",
+ &egg_recent_view_gtk_info, 0);
+ g_type_add_interface_static (egg_recent_view_gtk_type,
+ EGG_TYPE_RECENT_VIEW,
+ &view_info);
+ }
+
+ return egg_recent_view_gtk_type;
+}
+
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/Makefile.am
@@ -0,0 +1,33 @@
+INCLUDES = \
+ $(GNUMERIC_CFLAGS) -I$(top_srcdir)/src -DUSE_STABLE_LIBGNOMEUI \
+ \
+ \
+ -DEGG_COMPILATION
+
+noinst_LIBRARIES = libeggrecent.a
+
+vfsdir = $(libdir)/gnome-vfs-2.0/modules
+
+vfsconfdir = $(sysconfdir)/gnome-vfs-2.0/modules
+
+libeggrecent_a_LIBADD = $(EGG_RECENT_LIBS)
+
+libeggrecent_a_SOURCES = \
+ egg-recent.h \
+ egg-recent-item.h \
+ egg-recent-item.c \
+ egg-recent-model.c \
+ egg-recent-model.h \
+ egg-recent-view.c \
+ egg-recent-view.h \
+ egg-recent-view-gtk.c \
+ egg-recent-view-gtk.h \
+ \
+ \
+ egg-recent-util.c \
+ egg-recent-util.h
+
+
+
+
+
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-view-gtk.h
@@ -0,0 +1,66 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef __EGG_RECENT_VIEW_GTK_H__
+#define __EGG_RECENT_VIEW_GTK_H__
+
+G_BEGIN_DECLS
+
+#include <gtk/gtk.h>
+#include "egg-recent-item.h"
+
+#define EGG_RECENT_VIEW_GTK(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, egg_recent_view_gtk_get_type (), EggRecentViewGtk)
+#define EGG_RECENT_VIEW_GTK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, egg_recent_view_gtk_get_type (), EggRecentViewGtkClass)
+#define EGG_IS_RECENT_VIEW_GTK(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_view_gtk_get_type ())
+
+typedef void (*EggRecentViewGtkTooltipFunc) (GtkTooltips *tooltips,
+ GtkWidget *menu,
+ EggRecentItem *item,
+ gpointer user_data);
+
+typedef struct _EggRecentViewGtk EggRecentViewGtk;
+
+typedef struct _EggRecentViewGtkClass EggRecentViewGtkClass;
+
+struct _EggRecentViewGtkClass {
+ GObjectClass parent_class;
+
+ void (*activate) (EggRecentViewGtk *view, EggRecentItem *item);
+};
+
+GType egg_recent_view_gtk_get_type (void);
+
+EggRecentViewGtk * egg_recent_view_gtk_new (GtkWidget *menu,
+ GtkWidget *start_menu_item);
+
+void egg_recent_view_gtk_set_menu (EggRecentViewGtk *view,
+ GtkWidget *menu);
+GtkWidget * egg_recent_view_gtk_get_menu (EggRecentViewGtk *view);
+
+
+void egg_recent_view_gtk_set_start_menu_item (EggRecentViewGtk *view,
+ GtkWidget *menu_item);
+GtkWidget *egg_recent_view_gtk_get_start_menu_item (EggRecentViewGtk *view);
+
+void egg_recent_view_gtk_set_leading_sep (EggRecentViewGtk *view,
+ gboolean val);
+
+void egg_recent_view_gtk_set_trailing_sep (EggRecentViewGtk *view,
+ gboolean val);
+
+void egg_recent_view_gtk_show_icons (EggRecentViewGtk *view,
+ gboolean show);
+void egg_recent_view_gtk_show_numbers (EggRecentViewGtk *view,
+ gboolean show);
+
+void egg_recent_view_gtk_set_tooltip_func (EggRecentViewGtk *view,
+ EggRecentViewGtkTooltipFunc func,
+ gpointer user_data);
+
+void egg_recent_view_gtk_set_icon_size (EggRecentViewGtk *view,
+ GtkIconSize icon_size);
+GtkIconSize egg_recent_view_gtk_get_icon_size (EggRecentViewGtk *view);
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_VIEW_GTK_H__ */
--- /dev/null
+++ lib/goffice/drawing/god-property-table.h
@@ -0,0 +1,174 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-property-table.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#ifndef GOD_PROPERTY_TABLE_H
+#define GOD_PROPERTY_TABLE_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <utils/go-units.h>
+#include <pango/pango-attributes.h>
+
+G_BEGIN_DECLS
+
+#define GOD_PROPERTY_TABLE_TYPE (god_property_table_get_type ())
+#define GOD_PROPERTY_TABLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_PROPERTY_TABLE_TYPE, GodPropertyTable))
+#define GOD_PROPERTY_TABLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_PROPERTY_TABLE_TYPE, GodClassPropertyTable))
+#define IS_GOD_PROPERTY_TABLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_PROPERTY_TABLE_TYPE))
+#define IS_GOD_PROPERTY_TABLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_PROPERTY_TABLE_TYPE))
+
+typedef struct GodPropertyTablePrivate_ GodPropertyTablePrivate;
+
+typedef struct {
+ GObject parent;
+ GodPropertyTablePrivate *priv;
+} GodPropertyTable;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodPropertyTableClass;
+
+/* Flags */
+#define GOD_PROPERTY_FLIP_H "flip_h"
+#define GOD_PROPERTY_FLIP_V "flip_v"
+#define GOD_PROPERTY_FILLED "filled"
+#define GOD_PROPERTY_BACKGROUND "background"
+
+/* will be enums when we support multiple "arrow shapes */
+#define GOD_PROPERTY_ARROW_START "arrow_start"
+#define GOD_PROPERTY_ARROW_END "arrow_end"
+
+/* Integers & Enums */
+#define GOD_PROPERTY_BLIP_ID "blip_id"
+#define GOD_PROPERTY_FONT_COLOR "font_color"
+#define GOD_PROPERTY_FILL_TYPE "fill_type"
+#define GOD_PROPERTY_FILL_SHADE_TYPE "fill_shade_type"
+#define GOD_PROPERTY_FILL_ANGLE "fill_angle"
+#define GOD_PROPERTY_FILL_FOCUS "fill_focus"
+#define GOD_PROPERTY_FILL_COLOR "fill_color"
+#define GOD_PROPERTY_FILL_ALPHA "fill_alpha"
+#define GOD_PROPERTY_FILL_PRESET "fill_preset"
+#define GOD_PROPERTY_FILL_BACKGROUND "fill_background"
+#define GOD_PROPERTY_FILL_BACKGROUND_ALPHA "fill_background_alpha"
+#define GOD_PROPERTY_OUTLINE_COLOR "outline_color"
+#define GOD_PROPERTY_OUTLINE_WIDTH "outline_width"
+#define GOD_PROPERTY_OUTLINE_STYLE "outline_style"
+#define GOD_PROPERTY_SCROLLBAR_VALUE "scrollbar_value"
+#define GOD_PROPERTY_SCROLLBAR_MIN "scrollbar_min"
+#define GOD_PROPERTY_SCROLLBAR_MAX "scrollbar_max"
+#define GOD_PROPERTY_SCROLLBAR_INC "scrollbar_inc"
+#define GOD_PROPERTY_SCROLLBAR_PAGE "scrollbar_page"
+#define GOD_PROPERTY_BLIP_CROP_TOP "blip_crop_top"
+#define GOD_PROPERTY_BLIP_CROP_BOTTOM "blip_crop_bottom"
+#define GOD_PROPERTY_BLIP_CROP_LEFT "blip_crop_left"
+#define GOD_PROPERTY_BLIP_CROP_RIGHT "blip_crop_right"
+
+#define GOD_PROPERTY_LTXID "ltxid"
+#define GOD_PROPERTY_DX_TEXT_LEFT "dx_text_left"
+#define GOD_PROPERTY_DX_TEXT_TOP "dx_text_top"
+#define GOD_PROPERTY_DX_TEXT_RIGHT "dx_text_right"
+#define GOD_PROPERTY_DX_TEXT_BOTTOM "dx_text_bottom"
+#define GOD_PROPERTY_FILL_RECT_LEFT "fill_rect_left"
+#define GOD_PROPERTY_FILL_RECT_TOP "fill_rect_top"
+#define GOD_PROPERTY_FILL_RECT_RIGHT "fill_rect_right"
+#define GOD_PROPERTY_FILL_RECT_BOTTOM "fill_rect_bottom"
+
+/* Ptrs */
+#define GOD_PROPERTY_ANCHOR "anchor"
+#define GOD_PROPERTY_TEXT "text"
+
+/* GArrays */
+#define GOD_PROPERTY_POLYGON_COORDS "polygon_coords"
+
+/* Expressions */
+#define GOD_PROPERTY_CHECKBOX_LINK "checkbox_link"
+#define GOD_PROPERTY_SCROLLBAR_LINK "scrollbar_link"
+
+/* PangoAttrList */
+#define GOD_PROPERTY_MARKUP "markup"
+
+typedef enum {
+ GOD_FILL_TYPE_SOLID,
+ GOD_FILL_TYPE_PATTERN,
+ GOD_FILL_TYPE_TEXTURE,
+ GOD_FILL_TYPE_PICTURE,
+ GOD_FILL_TYPE_SHADE,
+ GOD_FILL_TYPE_SHADE_CENTER,
+ GOD_FILL_TYPE_SHADE_SHAPE,
+ GOD_FILL_TYPE_SHADE_SCALE,
+ GOD_FILL_TYPE_SHADE_TITLE,
+ GOD_FILL_TYPE_SHADE_BACKGROUND
+} GodFillType;
+
+typedef const char *GodPropertyID;
+
+GType god_property_table_get_type (void);
+
+/* Base methods */
+void god_property_table_set (GodPropertyTable *attrs,
+ GodPropertyID id,
+ GValue *attr);
+GValue *god_property_table_get (GodPropertyTable *table,
+ GodPropertyID id);
+
+/* Set methods */
+void god_property_table_set_flag (GodPropertyTable *table,
+ GodPropertyID id,
+ gboolean val);
+void god_property_table_set_uint (GodPropertyTable *table,
+ GodPropertyID id,
+ guint32 val);
+void god_property_table_set_int (GodPropertyTable *table,
+ GodPropertyID id,
+ gint32 val);
+void god_property_table_set_length (GodPropertyTable *table,
+ GodPropertyID id,
+ go_unit_t val);
+void god_property_table_set_pointer (GodPropertyTable *table,
+ GodPropertyID id,
+ gpointer val);
+void god_property_table_set_array (GodPropertyTable *table,
+ GodPropertyID id,
+ GArray *array);
+void god_property_table_set_markup (GodPropertyTable *table,
+ GodPropertyID id,
+ PangoAttrList *list);
+
+/* Get methods */
+gboolean god_property_table_get_flag (GodPropertyTable *table,
+ GodPropertyID id,
+ gboolean default_value);
+guint32 god_property_table_get_uint (GodPropertyTable *table,
+ GodPropertyID id,
+ guint32 default_value);
+gint32 god_property_table_get_int (GodPropertyTable *table,
+ GodPropertyID id,
+ gint32 default_value);
+go_unit_t god_property_table_get_length (GodPropertyTable *table,
+ GodPropertyID id,
+ go_unit_t default_value);
+gpointer god_property_table_get_pointer (GodPropertyTable *table,
+ GodPropertyID id,
+ gpointer default_value);
+GArray *god_property_table_get_array (GodPropertyTable *table,
+ GodPropertyID id,
+ GArray *default_value);
+PangoAttrList *god_property_table_get_markup (GodPropertyTable *table,
+ GodPropertyID id,
+ PangoAttrList *default_value);
+
+/* Allocation */
+GodPropertyTable *god_property_table_new (void);
+
+G_END_DECLS
+
+#endif /* GOD_PROPERTY_TABLE_H */
--- /dev/null
+++ lib/goffice/drawing/god-image.c
@@ -0,0 +1,140 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-image.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-image.h"
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodImagePrivate_ {
+ char *format;
+ guint8 *data;
+ guint32 length;
+ GdkPixbuf *pixbuf;
+};
+
+static void
+ensure_pixbuf (GodImage *image)
+{
+ GdkPixbufLoader *loader;
+
+ if (image->priv->pixbuf)
+ return;
+
+ if (image->priv->format)
+ loader = gdk_pixbuf_loader_new_with_type (image->priv->format, NULL);
+ else
+ loader = gdk_pixbuf_loader_new ();
+ if (loader) {
+ if (gdk_pixbuf_loader_write (loader, image->priv->data, image->priv->length, NULL)) {
+ image->priv->pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+ if (image->priv->pixbuf) {
+ g_object_ref (image->priv->pixbuf);
+ }
+ }
+ gdk_pixbuf_loader_close (loader, NULL);
+ g_object_unref (loader);
+ }
+}
+
+GodImage *
+god_image_new (void)
+{
+ GodImage *image;
+
+ image = g_object_new (GOD_IMAGE_TYPE, NULL);
+
+ return image;
+}
+
+GdkPixbuf *
+god_image_get_pixbuf (GodImage *image)
+{
+ ensure_pixbuf (image);
+
+ if (image->priv->pixbuf)
+ g_object_ref (image->priv->pixbuf);
+ return image->priv->pixbuf;
+}
+
+void
+god_image_set_image_data (GodImage *image,
+ const char *format,
+ const guint8 *data,
+ guint32 length)
+{
+ g_free (image->priv->data);
+ g_free (image->priv->format);
+ image->priv->format = g_strdup (format);
+ image->priv->length = length;
+ image->priv->data = g_memdup (data, length);
+
+ if (image->priv->pixbuf)
+ g_object_unref (image->priv->pixbuf);
+ image->priv->pixbuf = NULL;
+}
+
+static void
+god_image_init (GObject *object)
+{
+ GodImage *image = GOD_IMAGE (object);
+ image->priv = g_new0 (GodImagePrivate, 1);
+}
+
+static void
+god_image_dispose (GObject *object)
+{
+ GodImage *image = GOD_IMAGE (object);
+
+ if (image->priv == NULL)
+ return;
+
+ if (image->priv->pixbuf)
+ g_object_unref (image->priv->pixbuf);
+ g_free (image->priv->data);
+ g_free (image->priv->format);
+ g_free (image->priv);
+ image->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_image_class_init (GodImageClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_image_dispose;
+}
+
+GSF_CLASS (GodImage, god_image,
+ god_image_class_init, god_image_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-image-store.c
@@ -0,0 +1,165 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-image-store.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-image-store.h"
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodImageStorePrivate_ {
+ GPtrArray *images; /* Of type GodImage. */
+};
+
+static GPtrArray*
+g_ptr_array_insert_val (GPtrArray *array,
+ guint index,
+ gpointer data)
+{
+ g_ptr_array_add (array, data);
+ memmove (array->pdata + index + 1,
+ array->pdata + index,
+ array->len - index - 1);
+ g_ptr_array_index (array, index) = data;
+ return array;
+}
+
+GodImageStore *
+god_image_store_new (void)
+{
+ GodImageStore *shape;
+
+ shape = g_object_new (GOD_IMAGE_STORE_TYPE, NULL);
+
+ return shape;
+}
+
+void
+god_image_store_append_image (GodImageStore *store,
+ GodImage *image)
+{
+ god_image_store_insert_image (store, image, -1);
+}
+
+/* pos can be -1 to represent the end. */
+void
+god_image_store_insert_image (GodImageStore *store,
+ GodImage *image,
+ int pos)
+{
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (image != NULL);
+
+ if (pos == -1)
+ pos = store->priv->images->len;
+
+ g_ptr_array_insert_val (store->priv->images, pos, image);
+ g_object_ref (image);
+}
+
+/* pos must be an actual position */
+void
+god_image_store_delete_image (GodImageStore *store,
+ int pos)
+{
+ GodImage *image = g_ptr_array_remove_index (store->priv->images, pos);
+ g_object_unref (image);
+}
+
+/* old_pos must be an actual position. new_pos can be -1 to represent the end. */
+void
+god_image_store_reorder_image (GodImageStore *store,
+ int old_pos,
+ int new_pos)
+{
+ GodImage *image = g_ptr_array_remove_index (store->priv->images, old_pos);
+ god_image_store_insert_image (store, image, new_pos);
+ g_object_unref (image);
+}
+
+int
+god_image_store_get_image_count (GodImageStore *store)
+{
+ return store->priv->images->len;
+}
+
+GodImage *
+god_image_store_get_image (GodImageStore *store,
+ int pos)
+{
+ GodImage *image;
+
+ g_return_val_if_fail (pos < god_image_store_get_image_count (store), NULL);
+
+ image = g_ptr_array_index (store->priv->images, pos);
+
+ g_return_val_if_fail (image, NULL);
+
+ g_object_ref (image);
+ return image;
+}
+
+static void
+god_image_store_init (GObject *object)
+{
+ GodImageStore *shape = GOD_IMAGE_STORE (object);
+ shape->priv = g_new0 (GodImageStorePrivate, 1);
+ shape->priv->images = g_ptr_array_new ();
+}
+
+static void
+god_image_store_dispose (GObject *object)
+{
+ GodImageStore *shape = GOD_IMAGE_STORE (object);
+ guint i;
+
+ if (shape->priv == NULL)
+ return;
+
+ for (i = 0; i < shape->priv->images->len; i++)
+ g_object_unref (g_ptr_array_index (shape->priv->images, i));
+ g_ptr_array_free (shape->priv->images, TRUE);
+ g_free (shape->priv);
+ shape->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_image_store_class_init (GodImageStoreClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_image_store_dispose;
+}
+
+GSF_CLASS (GodImageStore, god_image_store,
+ god_image_store_class_init, god_image_store_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-drawing.c
@@ -0,0 +1,147 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-drawing.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-drawing.h"
+#include <gsf/gsf-impl-utils.h>
+
+static GObjectClass *parent_class;
+
+struct GodDrawingPrivate_ {
+ GodShape *root_shape;
+ GodShape *background;
+ GodDrawingGroup *drawing_group;
+};
+
+GodDrawing *
+god_drawing_new (void)
+{
+ GodDrawing *drawing;
+
+ drawing = g_object_new (GOD_DRAWING_TYPE, NULL);
+
+ return drawing;
+}
+
+GodShape *
+god_drawing_get_root_shape (GodDrawing *drawing)
+{
+ if (drawing->priv->root_shape)
+ g_object_ref (drawing->priv->root_shape);
+ return drawing->priv->root_shape;
+}
+
+void
+god_drawing_set_root_shape (GodDrawing *drawing,
+ GodShape *root_shape)
+{
+ if (drawing->priv->root_shape)
+ g_object_unref (drawing->priv->root_shape);
+ drawing->priv->root_shape = root_shape;
+ if (drawing->priv->root_shape)
+ g_object_ref (drawing->priv->root_shape);
+}
+
+GodShape *
+god_drawing_get_background (GodDrawing *drawing)
+{
+ if (drawing->priv->background)
+ g_object_ref (drawing->priv->background);
+ return drawing->priv->background;
+}
+
+void
+god_drawing_set_background (GodDrawing *drawing,
+ GodShape *background)
+{
+ if (drawing->priv->background)
+ g_object_unref (drawing->priv->background);
+ drawing->priv->background = background;
+ if (drawing->priv->background)
+ g_object_ref (drawing->priv->background);
+}
+
+GodDrawingGroup *
+god_drawing_get_drawing_group (GodDrawing *drawing)
+{
+ if (drawing->priv->drawing_group)
+ g_object_ref (drawing->priv->drawing_group);
+ return drawing->priv->drawing_group;
+}
+
+void
+god_drawing_set_drawing_group (GodDrawing *drawing,
+ GodDrawingGroup *drawing_group)
+{
+ if (drawing->priv->drawing_group)
+ g_object_unref (drawing->priv->drawing_group);
+ drawing->priv->drawing_group = drawing_group;
+ if (drawing->priv->drawing_group)
+ g_object_ref (drawing->priv->drawing_group);
+}
+
+static void
+god_drawing_init (GObject *object)
+{
+ GodDrawing *drawing = GOD_DRAWING (object);
+
+ drawing->priv = g_new0 (GodDrawingPrivate, 1);
+}
+
+static void
+god_drawing_dispose (GObject *object)
+{
+ GodDrawing *drawing = GOD_DRAWING (object);
+
+ if (drawing->priv == NULL)
+ return;
+
+ if (drawing->priv->root_shape)
+ g_object_unref (drawing->priv->root_shape);
+ if (drawing->priv->background)
+ g_object_unref (drawing->priv->background);
+ if (drawing->priv->drawing_group)
+ g_object_unref (drawing->priv->drawing_group);
+ g_free (drawing->priv);
+ drawing->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_drawing_class_init (GodDrawingClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_drawing_dispose;
+}
+
+GSF_CLASS (GodDrawing, god_drawing,
+ god_drawing_class_init, god_drawing_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-text-model.c
@@ -0,0 +1,330 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-text-model.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-text-model.h"
+#include <drawing/god-default-attributes.h>
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+#define PARAGRAPH(i) (text && text->priv && text->priv->paragraphs ? ((GodTextModelParagraph *)(text->priv->paragraphs->data)) + (i) : (GodTextModelParagraph *) NULL)
+
+static GObjectClass *parent_class;
+
+struct GodTextModelPrivate {
+ GArray *paragraphs; /* Of type GodTextModelParagraph */
+ char *text_cache;
+};
+
+GodTextModel *
+god_text_model_new (void)
+{
+ GodTextModel *text;
+
+ text = g_object_new (GOD_TEXT_MODEL_TYPE, NULL);
+
+ return text;
+}
+
+const char *
+god_text_model_get_text (GodTextModel *text)
+{
+ if (GOD_TEXT_MODEL_GET_CLASS (text)->get_text)
+ return GOD_TEXT_MODEL_GET_CLASS (text)->get_text (text);
+ else
+ return NULL;
+}
+
+void
+god_text_model_set_text (GodTextModel *text,
+ const char *text_value)
+{
+ if (GOD_TEXT_MODEL_GET_CLASS (text)->set_text)
+ GOD_TEXT_MODEL_GET_CLASS (text)->set_text (text, text_value);
+}
+
+void
+god_text_model_set_paragraph_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GodParagraphAttributes *attributes)
+{
+ if (GOD_TEXT_MODEL_GET_CLASS (text)->set_paragraph_attributes)
+ GOD_TEXT_MODEL_GET_CLASS (text)->set_paragraph_attributes (text, start, end, attributes);
+}
+
+void
+god_text_model_set_indent (GodTextModel *text,
+ int start,
+ int end,
+ int indent)
+{
+ if (GOD_TEXT_MODEL_GET_CLASS (text)->set_indent)
+ GOD_TEXT_MODEL_GET_CLASS (text)->set_indent (text, start, end, indent);
+}
+
+void
+god_text_model_set_pango_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GList *attributes)
+{
+ if (GOD_TEXT_MODEL_GET_CLASS (text)->set_pango_attributes)
+ GOD_TEXT_MODEL_GET_CLASS (text)->set_pango_attributes (text, start, end, attributes);
+}
+
+const GodDefaultAttributes *
+god_text_model_get_default_attributes (GodTextModel *text)
+{
+ if (GOD_TEXT_MODEL_GET_CLASS (text)->get_default_attributes)
+ return GOD_TEXT_MODEL_GET_CLASS (text)->get_default_attributes (text);
+ else
+ return NULL;
+}
+
+void
+god_text_model_paragraph_foreach (GodTextModel *text,
+ GodTextModelParagraphForeachCallback callback,
+ gpointer user_data)
+{
+ if (GOD_TEXT_MODEL_GET_CLASS (text)->paragraph_foreach)
+ GOD_TEXT_MODEL_GET_CLASS (text)->paragraph_foreach (text, callback, user_data);
+}
+
+static void
+god_text_model_init (GObject *object)
+{
+ GodTextModel *text = GOD_TEXT_MODEL (object);
+ text->priv = g_new0 (GodTextModelPrivate, 1);
+}
+
+static void
+god_text_model_finalize (GObject *object)
+{
+ GodTextModel *text = GOD_TEXT_MODEL (object);
+
+ g_free (text->priv->text_cache);
+ g_free (text->priv);
+ text->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static const char *
+real_god_text_model_get_text (GodTextModel *text)
+{
+ guint i;
+ if (text->priv->text_cache == NULL && text->priv->paragraphs) {
+ GString *string;
+ string = g_string_new ("");
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ g_string_append (string, PARAGRAPH(i)->text);
+ g_string_append_c (string, '\n');
+ }
+ if (string->len > 0)
+ g_string_truncate (string, string->len - 1);
+ text->priv->text_cache = string->str;
+ g_string_free (string, FALSE);
+ }
+ return text->priv->text_cache;
+}
+
+int
+god_text_model_get_length (GodTextModel *text)
+{
+ guint i;
+ if (text->priv->text_cache != NULL)
+ return strlen (text->priv->text_cache);
+ if (text->priv->paragraphs) {
+ int length = 0;
+ for (i = 0; i < text->priv->paragraphs->len; i++)
+ length += strlen (PARAGRAPH(i)->text) + 1;
+ if (length > 0)
+ length --;
+ return length;
+ }
+ return 0;
+}
+
+static void
+real_god_text_model_set_text (GodTextModel *text,
+ const char *text_value)
+{
+ gchar **paras;
+ guint i;
+
+ g_free (text->priv->text_cache);
+ text->priv->text_cache = NULL;
+
+ if (text->priv->paragraphs) {
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ g_free (PARAGRAPH(i)->text);
+ if (PARAGRAPH(i)->char_attributes)
+ g_object_unref (PARAGRAPH(i)->char_attributes);
+ if (PARAGRAPH(i)->para_attributes)
+ g_object_unref (PARAGRAPH(i)->para_attributes);
+ }
+ g_array_free (text->priv->paragraphs, TRUE);
+ }
+
+ text->priv->paragraphs = g_array_new (TRUE, TRUE, sizeof (GodTextModelParagraph));
+
+ paras = g_strsplit (text_value, "\r", 0);
+ for (i = 0; paras[i]; i++) {
+ GodTextModelParagraph paragraph;
+ paragraph.text = paras[i];
+ paragraph.char_attributes = NULL;
+ paragraph.para_attributes = NULL;
+ g_array_append_vals (text->priv->paragraphs, ¶graph, 1);
+ }
+ g_free (paras);
+}
+
+static void
+real_god_text_model_set_paragraph_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GodParagraphAttributes *attributes)
+{
+ guint i;
+ int count = 0;
+ if (text->priv->paragraphs) {
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ int length = strlen (PARAGRAPH(i)->text);
+ if (count >= end)
+ return;
+ if (count + length + 1 > start) {
+ if (PARAGRAPH(i)->para_attributes)
+ g_object_unref (PARAGRAPH(i)->para_attributes);
+ PARAGRAPH(i)->para_attributes = attributes;
+ if (PARAGRAPH(i)->para_attributes)
+ g_object_ref (PARAGRAPH(i)->para_attributes);
+ }
+ count += length + 1;
+ }
+ }
+}
+
+static void
+real_god_text_model_set_pango_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GList *attributes)
+{
+ guint i;
+ int count = 0;
+ if (start == end)
+ return;
+ if (text->priv->paragraphs) {
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ int length = strlen (PARAGRAPH(i)->text);
+ if (length == 0)
+ continue;
+ if (count >= end)
+ return;
+ if (count + length >= start) {
+ int thisstart = MAX (start, count) - count;
+ int thisend = MIN (end, count + length) - count;
+ GList *iterator;
+
+ if (thisstart == thisend)
+ continue;
+
+ if (!PARAGRAPH(i)->char_attributes)
+ PARAGRAPH(i)->char_attributes = pango_attr_list_new ();
+
+ for (iterator = attributes; iterator; iterator = iterator->next) {
+ PangoAttribute *new_attr = pango_attribute_copy (iterator->data);
+ new_attr->start_index = thisstart;
+ new_attr->end_index = thisend;
+ pango_attr_list_insert (PARAGRAPH(i)->char_attributes, new_attr);
+ }
+ }
+ count += length + 1;
+ }
+ }
+}
+
+static void
+real_god_text_model_set_indent (GodTextModel *text,
+ int start,
+ int end,
+ int indent)
+{
+ guint i;
+ int count = 0;
+ if (text->priv->paragraphs) {
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ int length = strlen (PARAGRAPH(i)->text);
+ if (count >= end)
+ return;
+ if (count + length + 1 > start) {
+ PARAGRAPH(i)->indent = indent;
+ }
+ count += length + 1;
+ }
+ }
+}
+
+static void
+real_god_text_model_paragraph_foreach (GodTextModel *text,
+ GodTextModelParagraphForeachCallback callback,
+ gpointer user_data)
+{
+ guint i;
+
+ if (callback == NULL)
+ return;
+ if (text->priv->paragraphs) {
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ callback (text, PARAGRAPH(i), user_data);
+ }
+ }
+}
+
+static void
+god_text_model_class_init (GodTextModelClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_text_model_finalize;
+
+ class->get_text = real_god_text_model_get_text;
+ class->set_text = real_god_text_model_set_text;
+
+ class->set_paragraph_attributes = real_god_text_model_set_paragraph_attributes;
+ class->set_pango_attributes = real_god_text_model_set_pango_attributes;
+ class->set_indent = real_god_text_model_set_indent;
+ class->get_default_attributes = NULL;
+ class->paragraph_foreach = real_god_text_model_paragraph_foreach;
+}
+
+GSF_CLASS (GodTextModel, god_text_model,
+ god_text_model_class_init, god_text_model_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-default-attributes.h
@@ -0,0 +1,63 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-default-attributes.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_DEFAULT_ATTRIBUTES_H
+#define GOD_DEFAULT_ATTRIBUTES_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-paragraph-attributes.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DEFAULT_ATTRIBUTES_TYPE (god_default_attributes_get_type ())
+#define GOD_DEFAULT_ATTRIBUTES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DEFAULT_ATTRIBUTES_TYPE, GodDefaultAttributes))
+#define GOD_DEFAULT_ATTRIBUTES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DEFAULT_ATTRIBUTES_TYPE, GodDefaultAttributesClass))
+#define GOD_DEFAULT_ATTRIBUTES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_DEFAULT_ATTRIBUTES_TYPE, GodDefaultAttributesClass))
+#define IS_GOD_DEFAULT_ATTRIBUTES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DEFAULT_ATTRIBUTES_TYPE))
+#define IS_GOD_DEFAULT_ATTRIBUTES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DEFAULT_ATTRIBUTES_TYPE))
+
+typedef struct GodDefaultAttributesPrivate_ GodDefaultAttributesPrivate;
+
+typedef struct {
+ GObject parent;
+ GodDefaultAttributesPrivate *priv;
+} GodDefaultAttributes;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodDefaultAttributesClass;
+
+GType god_default_attributes_get_type (void);
+GodDefaultAttributes *god_default_attributes_new (void);
+
+void god_default_attributes_set_paragraph_attributes (GodDefaultAttributes *attributes,
+ GodParagraphAttributes *paragraph_attributes);
+void god_default_attributes_set_paragraph_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent,
+ GodParagraphAttributes *paragraph_attributes);
+void god_default_attributes_set_pango_attributes (GodDefaultAttributes *attributes,
+ GList *pango_attributes);
+void god_default_attributes_set_pango_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent,
+ GList *pango_attributes);
+
+const GodParagraphAttributes *god_default_attributes_get_paragraph_attributes (GodDefaultAttributes *attributes);
+const GodParagraphAttributes *god_default_attributes_get_paragraph_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent);
+const GList *god_default_attributes_get_pango_attributes (GodDefaultAttributes *attributes);
+const GList *god_default_attributes_get_pango_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent);
+
+
+G_END_DECLS
+
+#endif /* GOD_DEFAULT_ATTRIBUTES_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-renderer-gdk.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *//* vim: set sw=8: */
+#ifndef GOD_DRAWING_RENDERER_GDK_H
+#define GOD_DRAWING_RENDERER_GDK_H
+
+/**
+ * god-drawing-renderer-gdk.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-drawing.h>
+#include <drawing/god-anchor.h>
+#include <gdk/gdkdrawable.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DRAWING_RENDERER_GDK_TYPE (god_drawing_renderer_gdk_get_type ())
+#define GOD_DRAWING_RENDERER_GDK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DRAWING_RENDERER_GDK_TYPE, GodDrawingRendererGdk))
+#define GOD_DRAWING_RENDERER_GDK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DRAWING_RENDERER_GDK_TYPE, GodDrawingRendererGdkClass))
+#define IS_GOD_DRAWING_RENDERER_GDK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DRAWING_RENDERER_GDK_TYPE))
+#define IS_GOD_DRAWING_RENDERER_GDK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DRAWING_RENDERER_GDK_TYPE))
+
+typedef struct GodDrawingRendererGdkPrivate_ GodDrawingRendererGdkPrivate;
+
+typedef struct {
+ GObject parent;
+ GodDrawingRendererGdkPrivate *priv;
+} GodDrawingRendererGdk;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodDrawingRendererGdkClass;
+
+GType god_drawing_renderer_gdk_get_type (void);
+GodDrawingRendererGdk *god_drawing_renderer_gdk_new (void);
+
+/* Return value is reffed. */
+GodDrawing *god_drawing_renderer_gdk_get_drawing (GodDrawingRendererGdk *renderer);
+void god_drawing_renderer_gdk_set_drawing (GodDrawingRendererGdk *renderer,
+ GodDrawing *drawing);
+
+/* Return value is reffed. */
+GdkDrawable *god_drawing_renderer_gdk_get_drawable (GodDrawingRendererGdk *renderer);
+void god_drawing_renderer_gdk_set_drawable (GodDrawingRendererGdk *renderer,
+ GdkDrawable *drawable);
+
+/* Return value is reffed. */
+GdkGC *god_drawing_renderer_gdk_get_gc (GodDrawingRendererGdk *renderer);
+void god_drawing_renderer_gdk_set_gc (GodDrawingRendererGdk *renderer,
+ GdkGC *gc);
+
+/* Return value is reffed. */
+GodAnchor *god_drawing_renderer_gdk_get_extents (GodDrawingRendererGdk *renderer);
+void god_drawing_renderer_gdk_set_extents (GodDrawingRendererGdk *renderer,
+ GodAnchor *extents);
+void god_drawing_renderer_gdk_render (GodDrawingRendererGdk *renderer,
+ GdkRectangle *area);
+
+G_END_DECLS
+
+#endif /* GOD_DRAWING_RENDERER_GDK_H */
--- /dev/null
+++ lib/goffice/drawing/god-default-attributes.c
@@ -0,0 +1,201 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-default-attributes.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-default-attributes.h"
+
+#include <pango/pango-attributes.h>
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+static GObjectClass *parent_class;
+
+struct GodDefaultAttributesPrivate_ {
+ GPtrArray *paragraph_attributes;
+ GPtrArray *pango_attributes;
+};
+
+GodDefaultAttributes *
+god_default_attributes_new (void)
+{
+ GodDefaultAttributes *default_attributes;
+
+ default_attributes = g_object_new (GOD_DEFAULT_ATTRIBUTES_TYPE, NULL);
+
+ return default_attributes;
+}
+
+void
+god_default_attributes_set_paragraph_attributes (GodDefaultAttributes *attributes,
+ GodParagraphAttributes *paragraph_attributes)
+{
+ god_default_attributes_set_paragraph_attributes_for_indent (attributes,
+ 0,
+ paragraph_attributes);
+}
+
+void
+god_default_attributes_set_paragraph_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent,
+ GodParagraphAttributes *paragraph_attributes)
+{
+ if (attributes->priv->paragraph_attributes == NULL)
+ attributes->priv->paragraph_attributes = g_ptr_array_new();
+ if (attributes->priv->paragraph_attributes->len <= indent)
+ g_ptr_array_set_size (attributes->priv->paragraph_attributes, indent + 1);
+ if (g_ptr_array_index (attributes->priv->paragraph_attributes, indent))
+ g_object_unref (g_ptr_array_index (attributes->priv->paragraph_attributes, indent));
+ g_ptr_array_index (attributes->priv->paragraph_attributes, indent) = paragraph_attributes;
+ if (paragraph_attributes)
+ g_object_ref (paragraph_attributes);
+}
+
+void
+god_default_attributes_set_pango_attributes (GodDefaultAttributes *attributes,
+ GList *pango_attributes)
+{
+ god_default_attributes_set_pango_attributes_for_indent (attributes,
+ 0,
+ pango_attributes);
+}
+
+static void
+add_attributes (PangoAttribute *attribute, GList **new_attributes)
+{
+ PangoAttribute *new_attribute = pango_attribute_copy (attribute);
+ new_attribute->start_index = 0;
+ new_attribute->end_index = (guint) -1;
+ *new_attributes = g_list_prepend (*new_attributes, pango_attribute_copy (attribute));
+}
+
+void
+god_default_attributes_set_pango_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent,
+ GList *pango_attributes)
+{
+ GList **pango_attributes_location;
+ if (attributes->priv->pango_attributes == NULL)
+ attributes->priv->pango_attributes = g_ptr_array_new();
+ if (attributes->priv->pango_attributes->len <= indent)
+ g_ptr_array_set_size (attributes->priv->pango_attributes, indent + 1);
+
+ pango_attributes_location = (GList **) &g_ptr_array_index (attributes->priv->pango_attributes, indent);
+
+ g_list_foreach (*pango_attributes_location, (GFunc) pango_attribute_destroy, NULL);
+ g_list_free (*pango_attributes_location);
+ *pango_attributes_location = NULL;
+ g_list_foreach (pango_attributes, (GFunc) add_attributes, pango_attributes_location);
+ *pango_attributes_location = g_list_reverse (*pango_attributes_location);
+}
+
+const GodParagraphAttributes *
+god_default_attributes_get_paragraph_attributes (GodDefaultAttributes *attributes)
+{
+ return god_default_attributes_get_paragraph_attributes_for_indent (attributes,
+ 0);
+}
+
+const GodParagraphAttributes *
+god_default_attributes_get_paragraph_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent)
+{
+ if (attributes->priv->paragraph_attributes == NULL)
+ return NULL;
+ if (attributes->priv->paragraph_attributes->len <= indent)
+ return NULL;
+ return g_ptr_array_index (attributes->priv->paragraph_attributes, indent);
+}
+
+const GList *
+god_default_attributes_get_pango_attributes (GodDefaultAttributes *attributes)
+{
+ return god_default_attributes_get_pango_attributes_for_indent (attributes,
+ 0);
+}
+
+const GList *
+god_default_attributes_get_pango_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent)
+{
+ if (attributes->priv->pango_attributes == NULL)
+ return NULL;
+ if (attributes->priv->pango_attributes->len <= indent)
+ return NULL;
+ return g_ptr_array_index (attributes->priv->pango_attributes, indent);
+}
+
+static void
+god_default_attributes_init (GObject *object)
+{
+ GodDefaultAttributes *default_attributes = GOD_DEFAULT_ATTRIBUTES (object);
+ default_attributes->priv = g_new0 (GodDefaultAttributesPrivate, 1);
+ default_attributes->priv->paragraph_attributes = NULL;
+ default_attributes->priv->pango_attributes = NULL;
+}
+
+static void
+maybe_unref (gpointer data, gpointer user_data)
+{
+ if (data)
+ g_object_unref (data);
+}
+
+static void
+free_list (gpointer data, gpointer user_data)
+{
+ g_list_foreach (data, (GFunc) pango_attribute_destroy, NULL);
+}
+
+static void
+god_default_attributes_finalize (GObject *object)
+{
+ GodDefaultAttributes *default_attributes = GOD_DEFAULT_ATTRIBUTES (object);
+
+ g_ptr_array_foreach (default_attributes->priv->paragraph_attributes, maybe_unref, NULL);
+ g_ptr_array_foreach (default_attributes->priv->pango_attributes, free_list, NULL);
+ g_ptr_array_free (default_attributes->priv->paragraph_attributes, TRUE);
+ g_ptr_array_free (default_attributes->priv->pango_attributes, TRUE);
+ g_free (default_attributes->priv);
+ default_attributes->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+god_default_attributes_class_init (GodDefaultAttributesClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_default_attributes_finalize;
+}
+
+GSF_CLASS (GodDefaultAttributes, god_default_attributes,
+ god_default_attributes_class_init, god_default_attributes_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-drawing-view.c
@@ -0,0 +1,142 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-drawing-view.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-drawing-view.h"
+#include <gsf/gsf-impl-utils.h>
+#include <drawing/god-drawing-renderer-gdk.h>
+
+static GObjectClass *parent_class;
+
+struct GodDrawingViewPrivate_ {
+ GodDrawingRendererGdk *renderer;
+};
+
+GodDrawingView *
+god_drawing_view_new (void)
+{
+ GodDrawingView *view;
+
+ view = g_object_new (GOD_DRAWING_VIEW_TYPE, NULL);
+
+ return view;
+}
+
+GodDrawing *
+god_drawing_view_get_drawing (GodDrawingView *view)
+{
+ return god_drawing_renderer_gdk_get_drawing (view->priv->renderer);
+}
+
+void
+god_drawing_view_set_drawing (GodDrawingView *view,
+ GodDrawing *drawing)
+{
+ god_drawing_renderer_gdk_set_drawing (view->priv->renderer, drawing);
+ gtk_widget_queue_draw (GTK_WIDGET (view));
+}
+
+GodAnchor *
+god_drawing_view_get_extents (GodDrawingView *view)
+{
+ return god_drawing_renderer_gdk_get_extents (view->priv->renderer);
+}
+
+void
+god_drawing_view_set_extents (GodDrawingView *view,
+ GodAnchor *extents)
+{
+ god_drawing_renderer_gdk_set_extents (view->priv->renderer, extents);
+ gtk_widget_queue_draw (GTK_WIDGET (view));
+}
+
+static void
+god_drawing_view_init (GObject *object)
+{
+ GodDrawingView *view = GOD_DRAWING_VIEW (object);
+
+ view->priv = g_new0 (GodDrawingViewPrivate, 1);
+ view->priv->renderer = god_drawing_renderer_gdk_new ();
+}
+
+static void
+god_drawing_view_realize (GtkWidget *widget)
+{
+ GodDrawingView *view = GOD_DRAWING_VIEW (widget);
+
+ GTK_WIDGET_CLASS(parent_class)->realize (widget);
+
+ god_drawing_renderer_gdk_set_drawable (view->priv->renderer,
+ widget->window);
+ god_drawing_renderer_gdk_set_gc (view->priv->renderer,
+ widget->style->fg_gc[0]);
+}
+
+static gboolean
+god_drawing_view_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GodDrawingView *view = GOD_DRAWING_VIEW (widget);
+ god_drawing_renderer_gdk_render (view->priv->renderer,
+ &event->area);
+ return TRUE;
+}
+
+static void
+god_drawing_view_dispose (GObject *object)
+{
+ GodDrawingView *view = GOD_DRAWING_VIEW (object);
+
+ if (view->priv == NULL)
+ return;
+
+ if (view->priv->renderer)
+ g_object_unref (view->priv->renderer);
+ g_free (view->priv);
+ view->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_drawing_view_class_init (GodDrawingViewClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_drawing_view_dispose;
+
+ widget_class->realize = god_drawing_view_realize;
+ widget_class->expose_event = god_drawing_view_expose_event;
+}
+
+GSF_CLASS (GodDrawingView, god_drawing_view,
+ god_drawing_view_class_init, god_drawing_view_init,
+ GTK_TYPE_DRAWING_AREA)
--- /dev/null
+++ lib/goffice/drawing/god-text-model.h
@@ -0,0 +1,98 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-text-model.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#ifndef GOD_TEXT_MODEL_H
+#define GOD_TEXT_MODEL_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-paragraph-attributes.h>
+#include <drawing/god-default-attributes.h>
+#include <pango/pango-attributes.h>
+
+G_BEGIN_DECLS
+
+#define GOD_TEXT_MODEL_TYPE (god_text_model_get_type ())
+#define GOD_TEXT_MODEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_TEXT_MODEL_TYPE, GodTextModel))
+#define GOD_TEXT_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_TEXT_MODEL_TYPE, GodTextModelClass))
+#define GOD_TEXT_MODEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_TEXT_MODEL_TYPE, GodTextModelClass))
+#define IS_GOD_TEXT_MODEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_TEXT_MODEL_TYPE))
+#define IS_GOD_TEXT_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_TEXT_MODEL_TYPE))
+
+typedef struct GodTextModelPrivate GodTextModelPrivate;
+typedef struct GodTextModel GodTextModel;
+typedef struct GodTextModelClass GodTextModelClass;
+
+typedef struct {
+ char *text;
+ PangoAttrList *char_attributes;
+ GodParagraphAttributes *para_attributes;
+ int indent;
+} GodTextModelParagraph;
+
+typedef void (*GodTextModelParagraphForeachCallback) (GodTextModel *text,
+ GodTextModelParagraph *paragraph,
+ gpointer user_data);
+
+struct GodTextModel {
+ GObject parent;
+ GodTextModelPrivate *priv;
+};
+
+struct GodTextModelClass {
+ GObjectClass parent_class;
+
+ const char *(*get_text) (GodTextModel *text);
+ void (*set_text) (GodTextModel *text, const char *text_value);
+ void (*set_paragraph_attributes) (GodTextModel *text, int start, int end, GodParagraphAttributes *attributes);
+ void (*set_pango_attributes) (GodTextModel *text, int start, int end, GList *attributes);
+ void (*set_indent) (GodTextModel *text, int start, int end, int indent);
+ const GodDefaultAttributes * (*get_default_attributes) (GodTextModel *text);
+ void (*paragraph_foreach) (GodTextModel *text, GodTextModelParagraphForeachCallback callback, gpointer user_data);
+};
+
+GType god_text_model_get_type (void);
+
+/* Set routines*/
+GodTextModel *god_text_model_new (void);
+void god_text_model_set_text (GodTextModel *text,
+ const char *text_value);
+#if 0
+void *god_text_model_append_paragraph (GodTextModel *text,
+ PangoLayout *layout,
+ GodParagraphAttributes *para_attr);
+#endif
+void god_text_model_set_paragraph_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GodParagraphAttributes *para_attr);
+void god_text_model_set_pango_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GList *char_attrs);
+void god_text_model_set_indent (GodTextModel *text,
+ int start,
+ int end,
+ int indent);
+const GodDefaultAttributes *god_text_model_get_default_attributes (GodTextModel *text);
+
+/* Get routines */
+const char *god_text_model_get_text (GodTextModel *text);
+int god_text_model_get_length (GodTextModel *text);
+void god_text_model_paragraph_foreach (GodTextModel *text,
+ GodTextModelParagraphForeachCallback callback,
+ gpointer user_data);
+
+
+G_END_DECLS
+
+#endif /* GOD_TEXT_MODEL_H */
--- /dev/null
+++ lib/goffice/drawing/god-anchor.c
@@ -0,0 +1,122 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-anchor.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-anchor.h"
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodAnchorPrivate_ {
+ GoRect rect;
+};
+
+GodAnchor *
+god_anchor_new (void)
+{
+ GodAnchor *anchor;
+
+ anchor = g_object_new (GOD_ANCHOR_TYPE, NULL);
+
+ return anchor;
+}
+
+void
+god_anchor_get_rect (GodAnchor *anchor,
+ GoRect *rect)
+{
+ if (GOD_ANCHOR_GET_CLASS (anchor)->get_rect) {
+ GOD_ANCHOR_GET_CLASS (anchor)->get_rect (anchor, rect);
+ } else {
+ rect->top = 0;
+ rect->left = 0;
+ rect->bottom = 0;
+ rect->right = 0;
+ }
+}
+
+void
+god_anchor_set_rect (GodAnchor *anchor,
+ const GoRect *rect)
+{
+ if (GOD_ANCHOR_GET_CLASS (anchor)->set_rect)
+ GOD_ANCHOR_GET_CLASS (anchor)->set_rect (anchor, rect);
+}
+
+static void
+god_anchor_init (GObject *object)
+{
+ GodAnchor *anchor = GOD_ANCHOR (object);
+ anchor->priv = g_new0 (GodAnchorPrivate, 1);
+ anchor->priv->rect.top = 0.0;
+ anchor->priv->rect.left = 0.0;
+ anchor->priv->rect.bottom = 0.0;
+ anchor->priv->rect.right = 0.0;
+}
+
+static void
+god_anchor_finalize (GObject *object)
+{
+ GodAnchor *anchor = GOD_ANCHOR (object);
+
+ g_free (anchor->priv);
+ anchor->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+real_god_anchor_get_rect (GodAnchor *anchor,
+ GoRect *rect)
+{
+ *rect = anchor->priv->rect;
+}
+
+static void
+real_god_anchor_set_rect (GodAnchor *anchor,
+ const GoRect *rect)
+{
+ anchor->priv->rect = *rect;
+}
+
+static void
+god_anchor_class_init (GodAnchorClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_anchor_finalize;
+
+ class->get_rect = real_god_anchor_get_rect;
+ class->set_rect = real_god_anchor_set_rect;
+}
+
+GSF_CLASS (GodAnchor, god_anchor,
+ god_anchor_class_init, god_anchor_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-anchor.h
@@ -0,0 +1,55 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-anchor.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#ifndef GOD_ANCHOR_H
+#define GOD_ANCHOR_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <utils/go-units.h>
+
+G_BEGIN_DECLS
+
+#define GOD_ANCHOR_TYPE (god_anchor_get_type ())
+#define GOD_ANCHOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_ANCHOR_TYPE, GodAnchor))
+#define GOD_ANCHOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_ANCHOR_TYPE, GodAnchorClass))
+#define GOD_ANCHOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_ANCHOR_TYPE, GodAnchorClass))
+#define IS_GOD_ANCHOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_ANCHOR_TYPE))
+#define IS_GOD_ANCHOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_ANCHOR_TYPE))
+
+typedef struct GodAnchorPrivate_ GodAnchorPrivate;
+
+typedef struct {
+ GObject parent;
+ GodAnchorPrivate *priv;
+} GodAnchor;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ void (*get_rect) (GodAnchor *anchor, GoRect *rect);
+ void (*set_rect) (GodAnchor *anchor, const GoRect *rect);
+} GodAnchorClass;
+
+GType god_anchor_get_type (void);
+
+GodAnchor *god_anchor_new (void);
+void god_anchor_get_rect (GodAnchor *anchor,
+ GoRect *rect);
+void god_anchor_set_rect (GodAnchor *anchor,
+ const GoRect *anchor_value);
+
+
+
+G_END_DECLS
+
+#endif /* GOD_ANCHOR_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-group.c
@@ -0,0 +1,97 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-drawing-group.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-drawing-group.h"
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodDrawingGroupPrivate_ {
+ GodImageStore *image_store;
+};
+
+static void
+ensure_image_store (GodDrawingGroup *drawing_group)
+{
+ if (drawing_group->priv->image_store == NULL)
+ drawing_group->priv->image_store =
+ god_image_store_new();
+}
+
+GodDrawingGroup *
+god_drawing_group_new (void)
+{
+ GodDrawingGroup *drawing_group;
+
+ drawing_group = g_object_new (GOD_DRAWING_GROUP_TYPE, NULL);
+
+ return drawing_group;
+}
+
+GodImageStore *
+god_drawing_group_get_image_store (GodDrawingGroup *group)
+{
+ ensure_image_store (group);
+ g_object_ref (group->priv->image_store);
+ return group->priv->image_store;
+}
+
+static void
+god_drawing_group_init (GObject *object)
+{
+ GodDrawingGroup *group = GOD_DRAWING_GROUP (object);
+ group->priv = g_new0 (GodDrawingGroupPrivate, 1);
+}
+
+static void
+god_drawing_group_dispose (GObject *object)
+{
+ GodDrawingGroup *group = GOD_DRAWING_GROUP (object);
+
+ if (group->priv->image_store)
+ g_object_unref (group->priv->image_store);
+ g_free (group->priv);
+ group->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_drawing_group_class_init (GodDrawingGroupClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_drawing_group_dispose;
+}
+
+GSF_CLASS (GodDrawingGroup, god_drawing_group,
+ god_drawing_group_class_init, god_drawing_group_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-drawing.h
@@ -0,0 +1,60 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-drawing.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_DRAWING_H
+#define GOD_DRAWING_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-shape.h>
+#include <drawing/god-drawing-group.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DRAWING_TYPE (god_drawing_get_type ())
+#define GOD_DRAWING(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DRAWING_TYPE, GodDrawing))
+#define GOD_DRAWING_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DRAWING_TYPE, GodDrawingClass))
+#define IS_GOD_DRAWING(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DRAWING_TYPE))
+#define IS_GOD_DRAWING_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DRAWING_TYPE))
+
+typedef struct GodDrawingPrivate_ GodDrawingPrivate;
+
+typedef struct {
+ GObject parent;
+ GodDrawingPrivate *priv;
+} GodDrawing;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodDrawingClass;
+
+GType god_drawing_get_type (void);
+GodDrawing *god_drawing_new (void);
+
+/* Return value is reffed. */
+GodShape *god_drawing_get_root_shape (GodDrawing *drawing);
+void god_drawing_set_root_shape (GodDrawing *drawing,
+ GodShape *root_shape);
+
+/* Return value is reffed. */
+GodShape *god_drawing_get_background (GodDrawing *drawing);
+void god_drawing_set_background (GodDrawing *drawing,
+ GodShape *root_shape);
+
+/* Return value is reffed. */
+GodDrawingGroup *god_drawing_get_drawing_group (GodDrawing *drawing);
+void god_drawing_set_drawing_group (GodDrawing *drawing,
+ GodDrawingGroup *drawing_group);
+
+
+G_END_DECLS
+
+#endif /* GOD_DRAWING_H */
--- /dev/null
+++ lib/goffice/drawing/god-paragraph-attributes.h
@@ -0,0 +1,63 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-paragraph-attributes.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_PARAGRAPH_ATTRIBUTES_H
+#define GOD_PARAGRAPH_ATTRIBUTES_H
+
+#include <glib-object.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GOD_PARAGRAPH_ATTRIBUTES_TYPE (god_paragraph_attributes_get_type ())
+#define GOD_PARAGRAPH_ATTRIBUTES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_PARAGRAPH_ATTRIBUTES_TYPE, GodParagraphAttributes))
+#define GOD_PARAGRAPH_ATTRIBUTES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_PARAGRAPH_ATTRIBUTES_TYPE, GodParagraphAttributesClass))
+#define GOD_PARAGRAPH_ATTRIBUTES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_PARAGRAPH_ATTRIBUTES_TYPE, GodParagraphAttributesClass))
+#define IS_GOD_PARAGRAPH_ATTRIBUTES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_PARAGRAPH_ATTRIBUTES_TYPE))
+#define IS_GOD_PARAGRAPH_ATTRIBUTES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_PARAGRAPH_ATTRIBUTES_TYPE))
+
+typedef struct GodParagraphAttributesPrivate_ GodParagraphAttributesPrivate;
+
+typedef struct {
+ GObject parent;
+ GodParagraphAttributesPrivate *priv;
+} GodParagraphAttributes;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodParagraphAttributesClass;
+
+typedef enum {
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_INDENT = 1 << 0,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_BEFORE = 1 << 1,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_AFTER = 1 << 2,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALIGNMENT = 1 << 3,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_CHARACTER = 1 << 4,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_INDENT = 1 << 5,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_SIZE = 1 << 6,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_FAMILY = 1 << 7,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALL = ((1 << 8) - 1),
+} GodParagraphAttributesFlags;
+
+typedef enum {
+ GOD_PARAGRAPH_ALIGNMENT_LEFT = 0,
+ GOD_PARAGRAPH_ALIGNMENT_CENTER = 1,
+ GOD_PARAGRAPH_ALIGNMENT_RIGHT = 2,
+ GOD_PARAGRAPH_ALIGNMENT_JUSTIFY = 3
+} GodParagraphAlignment;
+
+GType god_paragraph_attributes_get_type (void);
+GodParagraphAttributes *god_paragraph_attributes_new (void);
+
+
+G_END_DECLS
+
+#endif /* GOD_PARAGRAPH_ATTRIBUTES_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-renderer-gdk.c
@@ -0,0 +1,571 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-drawing-renderer-gdk.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-drawing-renderer-gdk.h"
+#include <gsf/gsf-impl-utils.h>
+#include <gdk/gdk.h>
+#include <string.h>
+#include <math.h>
+
+static GObjectClass *parent_class;
+
+struct GodDrawingRendererGdkPrivate_ {
+ GodDrawing *drawing;
+ GdkDrawable *drawable;
+ GdkGC *gc;
+ GodAnchor *extents;
+ go_unit_t x_units_per_pixel;
+ go_unit_t y_units_per_pixel;
+};
+
+GodDrawingRendererGdk *
+god_drawing_renderer_gdk_new (void)
+{
+ GodDrawingRendererGdk *renderer;
+
+ renderer = g_object_new (GOD_DRAWING_RENDERER_GDK_TYPE, NULL);
+
+ return renderer;
+}
+
+static void
+update_units_per_pixel (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->drawable && renderer->priv->extents) {
+ gint pixel_width, pixel_height;
+ GoRect extent_rect;
+ gdk_drawable_get_size (renderer->priv->drawable, &pixel_width, &pixel_height);
+ god_anchor_get_rect (renderer->priv->extents, &extent_rect);
+
+ renderer->priv->x_units_per_pixel = (extent_rect.right - extent_rect.left) / pixel_width;
+ renderer->priv->y_units_per_pixel = (extent_rect.bottom - extent_rect.top) / pixel_height;
+ } else {
+ renderer->priv->x_units_per_pixel = 0;
+ renderer->priv->y_units_per_pixel = 0;
+ }
+}
+
+GodDrawing *
+god_drawing_renderer_gdk_get_drawing (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->drawing)
+ g_object_ref (renderer->priv->drawing);
+ return renderer->priv->drawing;
+}
+
+void
+god_drawing_renderer_gdk_set_drawing (GodDrawingRendererGdk *renderer,
+ GodDrawing *drawing)
+{
+ if (renderer->priv->drawing)
+ g_object_unref (renderer->priv->drawing);
+ renderer->priv->drawing = drawing;
+ if (renderer->priv->drawing)
+ g_object_ref (renderer->priv->drawing);
+}
+
+GdkDrawable *
+god_drawing_renderer_gdk_get_drawable (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->drawable)
+ g_object_ref (renderer->priv->drawable);
+ return renderer->priv->drawable;
+}
+
+void
+god_drawing_renderer_gdk_set_drawable (GodDrawingRendererGdk *renderer,
+ GdkDrawable *drawable)
+{
+ if (renderer->priv->drawable)
+ g_object_unref (renderer->priv->drawable);
+ renderer->priv->drawable = drawable;
+ if (renderer->priv->drawable)
+ g_object_ref (renderer->priv->drawable);
+ update_units_per_pixel (renderer);
+}
+
+GdkGC *
+god_drawing_renderer_gdk_get_gc (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->gc)
+ g_object_ref (renderer->priv->gc);
+ return renderer->priv->gc;
+}
+
+void
+god_drawing_renderer_gdk_set_gc (GodDrawingRendererGdk *renderer,
+ GdkGC *gc)
+{
+ if (renderer->priv->gc)
+ g_object_unref (renderer->priv->gc);
+ renderer->priv->gc = gc;
+ if (renderer->priv->gc)
+ g_object_ref (renderer->priv->gc);
+ update_units_per_pixel (renderer);
+}
+
+GodAnchor *
+god_drawing_renderer_gdk_get_extents (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->extents)
+ g_object_ref (renderer->priv->extents);
+ return renderer->priv->extents;
+}
+
+void
+god_drawing_renderer_gdk_set_extents (GodDrawingRendererGdk *renderer,
+ GodAnchor *extents)
+{
+ if (renderer->priv->extents)
+ g_object_unref (renderer->priv->extents);
+ renderer->priv->extents = extents;
+ if (renderer->priv->extents)
+ g_object_ref (renderer->priv->extents);
+ update_units_per_pixel (renderer);
+}
+
+static GdkPixbuf *
+get_pixbuf (GodDrawingRendererGdk *renderer,
+ int which_pic)
+{
+ GodDrawingGroup *drawing_group;
+ GdkPixbuf *ret_val = NULL;
+
+ if (which_pic < 0)
+ return NULL;
+
+ drawing_group = god_drawing_get_drawing_group (renderer->priv->drawing);
+ if (drawing_group) {
+ GodImageStore *image_store = god_drawing_group_get_image_store (drawing_group);
+ if (which_pic < god_image_store_get_image_count (image_store)) {
+ GodImage *image = god_image_store_get_image (image_store, which_pic);
+ ret_val = god_image_get_pixbuf (image);
+ g_object_unref (image);
+ }
+ g_object_unref (image_store);
+ g_object_unref (drawing_group);
+ }
+ return ret_val;
+}
+
+typedef struct {
+ GodDrawingRendererGdk *renderer;
+ GdkRectangle *rect;
+ long long y_ofs;
+ const GodDefaultAttributes *default_attributes;
+} DrawTextContext;
+
+#ifdef PANGO_HACK
+static gboolean
+make_absolute (PangoAttribute *attr, gpointer user_data)
+{
+ DrawTextContext *draw_context = user_data;
+ if (attr->klass->type == PANGO_ATTR_SIZE &&
+ ! ((PangoAttrSize *) attr)->absolute) {
+ PangoAttrSize *size_attr = (PangoAttrSize *) attr;
+ size_attr->size = GO_PT_TO_UN ((long long) size_attr->size) / draw_context->renderer->priv->y_units_per_pixel;
+ size_attr->absolute = TRUE;
+ }
+ return FALSE;
+}
+#endif
+
+static void
+draw_text (GodTextModel *text,
+ GodTextModelParagraph *paragraph,
+ gpointer user_data)
+{
+ int height;
+ PangoLayout *layout;
+ DrawTextContext *draw_context = user_data;
+ double space_before = 0;
+ double space_after = 0;
+ double indent = 0;
+ const GList *iterator;
+ PangoAttrList *attributes;
+ GodParagraphAlignment alignment = GOD_PARAGRAPH_ALIGNMENT_LEFT;
+ const GodParagraphAttributes *default_para_attributes;
+ gunichar bullet_character = 0;
+ double bullet_indent = 0;
+ double bullet_size = 1.0;
+ char *bullet_family = NULL;
+ PangoFontDescription *bullet_desc;
+ PangoAttrIterator *attr_iterator;
+
+ if (draw_context->default_attributes) {
+ default_para_attributes = god_default_attributes_get_paragraph_attributes_for_indent ((GodDefaultAttributes *)draw_context->default_attributes, paragraph->indent);
+ if (default_para_attributes) {
+ g_object_get ((GodParagraphAttributes *) default_para_attributes,
+ "space_before", &space_before,
+ "space_after", &space_after,
+ "indent", &indent,
+ "alignment", &alignment,
+ "bullet_character", &bullet_character,
+ "bullet_indent", &bullet_indent,
+ "bullet_size", &bullet_size,
+ "bullet_family", &bullet_family,
+ NULL);
+ }
+ }
+ if (paragraph->para_attributes) {
+ GodParagraphAttributesFlags flags;
+ g_object_get (paragraph->para_attributes,
+ "flags", &flags,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_BEFORE)
+ g_object_get (paragraph->para_attributes,
+ "space_before", &space_before,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_AFTER)
+ g_object_get (paragraph->para_attributes,
+ "space_after", &space_after,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_INDENT)
+ g_object_get (paragraph->para_attributes,
+ "indent", &indent,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALIGNMENT)
+ g_object_get (paragraph->para_attributes,
+ "alignment", &alignment,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_CHARACTER)
+ g_object_get (paragraph->para_attributes,
+ "bullet_character", &bullet_character,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_INDENT)
+ g_object_get (paragraph->para_attributes,
+ "bullet_indent", &bullet_indent,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_SIZE)
+ g_object_get (paragraph->para_attributes,
+ "bullet_size", &bullet_size,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_FAMILY) {
+ g_free (bullet_family);
+ bullet_family = NULL;
+ g_object_get (paragraph->para_attributes,
+ "bullet_family", &bullet_family,
+ NULL);
+ }
+ }
+ draw_context->y_ofs += space_before;
+
+ layout = pango_layout_new (gdk_pango_context_get_for_screen (gdk_screen_get_default()));
+ pango_layout_set_alignment (layout, alignment == GOD_PARAGRAPH_ALIGNMENT_JUSTIFY ? PANGO_ALIGN_LEFT : alignment);
+ pango_layout_set_width (layout, draw_context->rect->width * PANGO_SCALE);
+ if (strchr (paragraph->text, 0xb)) {
+ int i;
+ char *paragraph_text = g_strdup (paragraph->text);
+ for (i = 0; paragraph_text[i]; i++) {
+ if (paragraph_text[i] == 0xb)
+ paragraph_text[i] = '\r';
+ }
+ pango_layout_set_text (layout, paragraph_text, -1);
+ g_free (paragraph_text);
+ } else {
+ pango_layout_set_text (layout, paragraph->text, -1);
+ }
+ pango_layout_set_auto_dir (layout, FALSE);
+ if (paragraph->char_attributes)
+ attributes = pango_attr_list_copy (paragraph->char_attributes);
+ else
+ attributes = pango_attr_list_new ();
+ if (draw_context->default_attributes) {
+ iterator = god_default_attributes_get_pango_attributes_for_indent ((GodDefaultAttributes *)draw_context->default_attributes, paragraph->indent);
+ for (; iterator; iterator = iterator->next) {
+ PangoAttribute *attr = pango_attribute_copy (iterator->data);
+ attr->start_index = 0;
+ attr->end_index = -1;
+ pango_attr_list_insert_before (attributes, attr);
+ }
+ }
+#ifdef PANGO_HACK
+ pango_attr_list_filter (attributes, make_absolute, draw_context);
+#endif
+ pango_layout_set_attributes (layout, attributes);
+ attr_iterator = pango_attr_list_get_iterator (attributes);
+ bullet_desc = pango_font_description_new ();
+ pango_attr_iterator_get_font (attr_iterator, bullet_desc, NULL, NULL);
+ pango_attr_iterator_destroy (attr_iterator);
+ pango_attr_list_unref (attributes);
+ gdk_draw_layout (draw_context->renderer->priv->drawable,
+ draw_context->renderer->priv->gc,
+ draw_context->rect->x + indent / draw_context->renderer->priv->x_units_per_pixel,
+ draw_context->rect->y + draw_context->y_ofs / draw_context->renderer->priv->y_units_per_pixel,
+ layout);
+
+ pango_layout_get_size (layout, NULL, &height);
+
+ g_object_unref (layout);
+ layout = NULL;
+
+ if (bullet_character != 0 &&
+ bullet_character != 0xe011 &&
+ bullet_size != 0 &&
+ bullet_family != NULL) {
+ char utf8[7];
+ int length;
+ layout = pango_layout_new (gdk_pango_context_get_for_screen (gdk_screen_get_default()));
+ pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
+ length = g_unichar_to_utf8 (bullet_character, utf8);
+ pango_layout_set_text (layout, utf8, length);
+ pango_layout_set_auto_dir (layout, FALSE);
+ pango_font_description_set_size (bullet_desc, pango_font_description_get_size (bullet_desc) * sqrt (bullet_size));
+ pango_font_description_set_family (bullet_desc, bullet_family);
+ pango_layout_set_font_description (layout, bullet_desc);
+
+ gdk_draw_layout (draw_context->renderer->priv->drawable,
+ draw_context->renderer->priv->gc,
+ draw_context->rect->x + bullet_indent / draw_context->renderer->priv->x_units_per_pixel,
+ draw_context->rect->y + draw_context->y_ofs / draw_context->renderer->priv->y_units_per_pixel,
+ layout);
+ pango_font_description_free (bullet_desc);
+ g_object_unref (layout);
+ layout = NULL;
+ }
+
+ draw_context->y_ofs += height * draw_context->renderer->priv->y_units_per_pixel / PANGO_SCALE;
+ draw_context->y_ofs += space_after;
+
+#if 0
+ g_print ("space before: %f\n", space_before);
+ g_print ("space after: %f\n", space_after);
+ g_print ("indent: %f\n", indent);
+ g_print ("x_units: %lld\n", draw_context->renderer->priv->x_units_per_pixel);
+ g_print ("y_units: %lld\n", draw_context->renderer->priv->y_units_per_pixel);
+#endif
+}
+
+static void
+god_drawing_renderer_gdk_render_shape (GodDrawingRendererGdk *renderer,
+ GdkRectangle *area,
+ GodShape *shape)
+{
+ GodAnchor *anchor;
+ GdkRectangle rect;
+ GdkRectangle intersection;
+ GoRect anchor_rect;
+
+ anchor = god_shape_get_anchor (shape);
+ if (anchor) {
+ god_anchor_get_rect (anchor, &anchor_rect);
+ rect.x = anchor_rect.left / renderer->priv->x_units_per_pixel;
+ rect.width = anchor_rect.right / renderer->priv->x_units_per_pixel - rect.x;
+ rect.y = anchor_rect.top / renderer->priv->y_units_per_pixel;
+ rect.height = anchor_rect.bottom / renderer->priv->y_units_per_pixel - rect.y;
+ g_object_unref (anchor);
+ } else {
+ rect.x = 0;
+ rect.y = 0;
+ gdk_drawable_get_size (renderer->priv->drawable, &(rect.width), &(rect.height));
+ }
+
+ if (!gdk_rectangle_intersect (area, &rect, &intersection))
+ return;
+
+ {
+ GodPropertyTable *prop_table;
+ gboolean filled;
+ GodFillType fill_type;
+ GodTextModel *text_model;
+ DrawTextContext *draw_context;
+
+ prop_table = god_shape_get_prop_table (shape);
+ filled = god_property_table_get_flag (prop_table,
+ GOD_PROPERTY_FILLED,
+ TRUE);
+ fill_type = god_property_table_get_int (prop_table,
+ GOD_PROPERTY_FILL_TYPE,
+ GOD_FILL_TYPE_SOLID);
+ if (filled && fill_type == GOD_FILL_TYPE_PICTURE) {
+ int which_pic = god_property_table_get_int (prop_table,
+ GOD_PROPERTY_BLIP_ID,
+ -1);
+ GdkPixbuf *pixbuf = get_pixbuf (renderer, which_pic);
+ if (pixbuf) {
+ GdkPixbuf *to_blit = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf),
+ gdk_pixbuf_get_has_alpha (pixbuf),
+ gdk_pixbuf_get_bits_per_sample (pixbuf),
+ intersection.width, intersection.height);
+ double offset_x, offset_y;
+ double scale_x, scale_y;
+
+ scale_x = (double) rect.width / (double) gdk_pixbuf_get_width (pixbuf);
+ scale_y = (double) rect.height / (double) gdk_pixbuf_get_height (pixbuf);
+ offset_x = rect.x - intersection.x;
+ offset_y = rect.y - intersection.y;
+
+ gdk_pixbuf_scale (pixbuf,
+ to_blit,
+ 0,
+ 0,
+ intersection.width,
+ intersection.height,
+ offset_x,
+ offset_y,
+ scale_x,
+ scale_y,
+ GDK_INTERP_HYPER);
+
+
+ gdk_draw_pixbuf (renderer->priv->drawable,
+ renderer->priv->gc,
+ to_blit,
+ 0,
+ 0,
+ intersection.x,
+ intersection.y,
+ intersection.width,
+ intersection.height,
+ GDK_RGB_DITHER_NORMAL,
+ intersection.x,
+ intersection.y);
+
+ g_object_unref (to_blit);
+ g_object_unref (pixbuf);
+ }
+ }
+ if (filled && fill_type == GOD_FILL_TYPE_SOLID) {
+ GdkColor old_color, color;
+ guint32 colornum = god_property_table_get_uint (prop_table,
+ GOD_PROPERTY_FILL_COLOR,
+ 0xffffff);
+ GdkGCValues values;
+
+ gdk_gc_get_values (renderer->priv->gc,
+ &values);
+ old_color = values.foreground;
+
+ color.red = (colornum & 0xff0000) >> 16;
+ color.blue = (colornum & 0xff00) >> 8;
+ color.green = colornum & 0xff;
+
+ color.red = color.red | (color.red << 8);
+ color.blue = color.blue | (color.blue << 8);
+ color.green = color.green + (color.green << 8);
+
+ gdk_gc_set_rgb_fg_color (renderer->priv->gc, &color);
+
+ gdk_draw_rectangle (renderer->priv->drawable,
+ renderer->priv->gc,
+ TRUE,
+ intersection.x,
+ intersection.y,
+ intersection.width,
+ intersection.height);
+
+ gdk_gc_set_foreground (renderer->priv->gc, &old_color);
+ }
+ text_model = god_shape_get_text_model (shape);
+ draw_context = g_new (DrawTextContext, 1);
+ draw_context->renderer = renderer;
+ draw_context->rect = ▭
+ draw_context->y_ofs = 0;
+ draw_context->default_attributes = god_text_model_get_default_attributes (text_model);
+ god_text_model_paragraph_foreach (text_model, draw_text, draw_context);
+ g_object_unref (prop_table);
+ }
+
+ {
+ int i, child_count;
+ GodShape *child;
+ child_count = god_shape_get_child_count (shape);
+ for (i = 0; i < child_count; i++) {
+ child = god_shape_get_child (shape, i);
+ god_drawing_renderer_gdk_render_shape (renderer, area, child);
+ }
+ }
+}
+
+void
+god_drawing_renderer_gdk_render (GodDrawingRendererGdk *renderer,
+ GdkRectangle *area)
+{
+ GodShape *shape;
+
+ update_units_per_pixel (renderer);
+ shape = god_drawing_get_background (renderer->priv->drawing);
+ if (shape) {
+ god_drawing_renderer_gdk_render_shape (renderer,
+ area,
+ shape);
+ g_object_unref (shape);
+ }
+
+ shape = god_drawing_get_root_shape (renderer->priv->drawing);
+ if (shape) {
+ god_drawing_renderer_gdk_render_shape (renderer,
+ area,
+ shape);
+ g_object_unref (shape);
+ }
+}
+
+static void
+god_drawing_renderer_gdk_init (GObject *object)
+{
+ GodDrawingRendererGdk *renderer = GOD_DRAWING_RENDERER_GDK (object);
+
+ renderer->priv = g_new0 (GodDrawingRendererGdkPrivate, 1);
+}
+
+static void
+god_drawing_renderer_gdk_dispose (GObject *object)
+{
+ GodDrawingRendererGdk *renderer = GOD_DRAWING_RENDERER_GDK (object);
+
+ if (renderer->priv == NULL)
+ return;
+
+ if (renderer->priv->drawing)
+ g_object_unref (renderer->priv->drawing);
+ if (renderer->priv->drawable)
+ g_object_unref (renderer->priv->drawable);
+ if (renderer->priv->gc)
+ g_object_unref (renderer->priv->gc);
+ if (renderer->priv->extents)
+ g_object_unref (renderer->priv->extents);
+ g_free (renderer->priv);
+ renderer->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_drawing_renderer_gdk_class_init (GodDrawingRendererGdkClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_drawing_renderer_gdk_dispose;
+}
+
+GSF_CLASS (GodDrawingRendererGdk, god_drawing_renderer_gdk,
+ god_drawing_renderer_gdk_class_init, god_drawing_renderer_gdk_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-shape.c
@@ -0,0 +1,269 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-shape.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-shape.h"
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodShapePrivate_ {
+ GodShape *parent;
+ GPtrArray *children; /* Of type GodShape. */
+ GodPropertyTable *prop_table;
+ GodAnchor *anchor;
+ GodTextModel *text_model;
+};
+
+static GPtrArray*
+g_ptr_array_insert_val (GPtrArray *array,
+ guint index,
+ gpointer data)
+{
+ g_ptr_array_add (array, data);
+ memmove (array->pdata + index + 1,
+ array->pdata + index,
+ array->len - index - 1);
+ g_ptr_array_index (array, index) = data;
+ return array;
+}
+
+static void
+ensure_prop_table (GodShape *shape)
+{
+ if (shape->priv->prop_table == NULL)
+ shape->priv->prop_table =
+ god_property_table_new();
+}
+
+static void
+ensure_text_model (GodShape *shape)
+{
+ if (shape->priv->text_model == NULL)
+ shape->priv->text_model =
+ god_text_model_new();
+}
+
+GodShape *
+god_shape_new (void)
+{
+ GodShape *shape;
+
+ shape = g_object_new (GOD_SHAPE_TYPE, NULL);
+
+ return shape;
+}
+
+void
+god_shape_append_child (GodShape *parent,
+ GodShape *child)
+{
+ god_shape_insert_child (parent, child, -1);
+}
+
+/* pos can be -1 to represent the end. */
+void
+god_shape_insert_child (GodShape *parent,
+ GodShape *child,
+ int pos)
+{
+ g_return_if_fail (parent != NULL);
+ g_return_if_fail (child != NULL);
+ g_return_if_fail (child->priv->parent == NULL);
+
+ if (pos == -1)
+ pos = parent->priv->children->len;
+
+ g_ptr_array_insert_val (parent->priv->children, pos, child);
+ g_object_ref (child);
+ child->priv->parent = parent;
+}
+
+/* pos must be an actual position */
+void
+god_shape_delete_child (GodShape *parent,
+ int pos)
+{
+ GodShape *child = g_ptr_array_remove_index (parent->priv->children, pos);
+ g_object_unref (child);
+}
+
+/* old_pos must be an actual position. new_pos can be -1 to represent the end. */
+void
+god_shape_reorder_child (GodShape *parent,
+ int old_pos,
+ int new_pos)
+{
+ GodShape *child = g_ptr_array_remove_index (parent->priv->children, old_pos);
+ child->priv->parent = NULL;
+ god_shape_insert_child (parent, child, new_pos);
+ g_object_unref (child);
+}
+
+int
+god_shape_get_child_count (GodShape *parent)
+{
+ return parent->priv->children->len;
+}
+
+GodShape *
+god_shape_get_child (GodShape *parent,
+ int pos)
+{
+ GodShape *child;
+
+ g_return_val_if_fail (pos < god_shape_get_child_count (parent), NULL);
+
+ child = g_ptr_array_index (parent->priv->children, pos);
+
+ g_return_val_if_fail (child, NULL);
+
+ g_object_ref (child);
+ return child;
+}
+
+GodPropertyTable *
+god_shape_get_prop_table (GodShape *shape)
+{
+ ensure_prop_table (shape);
+ g_object_ref (shape->priv->prop_table);
+ return shape->priv->prop_table;
+}
+
+void
+god_shape_set_prop_table (GodShape *shape,
+ GodPropertyTable *prop_table)
+{
+ if (shape->priv->prop_table)
+ g_object_unref (shape->priv->prop_table);
+ shape->priv->prop_table = prop_table;
+ if (shape->priv->prop_table)
+ g_object_ref (shape->priv->prop_table);
+}
+
+GodAnchor *
+god_shape_get_anchor (GodShape *shape)
+{
+ if (shape->priv->anchor)
+ g_object_ref (shape->priv->anchor);
+ return shape->priv->anchor;
+}
+
+void
+god_shape_set_anchor (GodShape *shape,
+ GodAnchor *anchor)
+{
+ if (shape->priv->anchor)
+ g_object_unref (shape->priv->anchor);
+ shape->priv->anchor = anchor;
+ if (shape->priv->anchor)
+ g_object_ref (shape->priv->anchor);
+}
+
+GodTextModel *
+god_shape_get_text_model (GodShape *shape)
+{
+ ensure_text_model (shape);
+ g_object_ref (shape->priv->text_model);
+ return shape->priv->text_model;
+}
+
+void
+god_shape_set_text_model (GodShape *shape,
+ GodTextModel *text_model)
+{
+ if (shape->priv->text_model)
+ g_object_unref (shape->priv->text_model);
+ shape->priv->text_model = text_model;
+ if (shape->priv->text_model)
+ g_object_ref (shape->priv->text_model);
+}
+
+const char *
+god_shape_get_text (GodShape *shape)
+{
+ if (shape->priv->text_model)
+ return god_text_model_get_text (shape->priv->text_model);
+ else
+ return NULL;
+}
+
+void
+god_shape_set_text (GodShape *shape,
+ const char *text)
+{
+ ensure_text_model (shape);
+ god_text_model_set_text (shape->priv->text_model, text);
+}
+
+static void
+god_shape_init (GObject *object)
+{
+ GodShape *shape = GOD_SHAPE (object);
+ shape->priv = g_new0 (GodShapePrivate, 1);
+ shape->priv->children = g_ptr_array_new ();
+ shape->priv->parent = NULL;
+}
+
+static void
+god_shape_dispose (GObject *object)
+{
+ GodShape *shape = GOD_SHAPE (object);
+ guint i;
+
+ if (shape->priv == NULL)
+ return;
+
+ for (i = 0; i < shape->priv->children->len; i++)
+ g_object_unref (g_ptr_array_index (shape->priv->children, i));
+ g_ptr_array_free (shape->priv->children, TRUE);
+ if (shape->priv->prop_table)
+ g_object_unref (shape->priv->prop_table);
+ if (shape->priv->anchor)
+ g_object_unref (shape->priv->anchor);
+ if (shape->priv->text_model)
+ g_object_unref (shape->priv->text_model);
+ g_free (shape->priv);
+ shape->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_shape_class_init (GodShapeClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_shape_dispose;
+}
+
+GSF_CLASS (GodShape, god_shape,
+ god_shape_class_init, god_shape_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-paragraph-attributes.c
@@ -0,0 +1,249 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-paragraph-attributes.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-paragraph-attributes.h"
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+static GObjectClass *parent_class;
+
+struct GodParagraphAttributesPrivate_ {
+ GodParagraphAttributesFlags flags;
+ double indent;
+ double space_before;
+ double space_after;
+ GodParagraphAlignment alignment;
+ gunichar bullet_character;
+ double bullet_indent;
+ double bullet_size;
+ char *bullet_family;
+};
+
+enum {
+ PROP_0,
+ PROP_FLAGS,
+ PROP_INDENT,
+ PROP_SPACE_BEFORE,
+ PROP_SPACE_AFTER,
+ PROP_ALIGNMENT,
+ PROP_BULLET_CHARACTER,
+ PROP_BULLET_INDENT,
+ PROP_BULLET_SIZE,
+ PROP_BULLET_FAMILY,
+};
+
+GodParagraphAttributes *
+god_paragraph_attributes_new (void)
+{
+ GodParagraphAttributes *paragraph;
+
+ paragraph = g_object_new (GOD_PARAGRAPH_ATTRIBUTES_TYPE, NULL);
+
+ return paragraph;
+}
+
+static void
+god_paragraph_attributes_init (GObject *object)
+{
+ GodParagraphAttributes *paragraph = GOD_PARAGRAPH_ATTRIBUTES (object);
+ paragraph->priv = g_new0 (GodParagraphAttributesPrivate, 1);
+ paragraph->priv->indent = 0;
+ paragraph->priv->space_before = 0;
+ paragraph->priv->space_after = 0;
+ paragraph->priv->alignment = GOD_PARAGRAPH_ALIGNMENT_LEFT;
+ paragraph->priv->bullet_character = 0;
+ paragraph->priv->bullet_indent = 0;
+ paragraph->priv->bullet_size = 1.0;
+ paragraph->priv->bullet_family = NULL;
+ paragraph->priv->flags = 0;
+}
+
+static void
+god_paragraph_attributes_finalize (GObject *object)
+{
+ GodParagraphAttributes *paragraph = GOD_PARAGRAPH_ATTRIBUTES (object);
+
+ g_free (paragraph->priv->bullet_family);
+ g_free (paragraph->priv);
+ paragraph->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+god_paragraph_attributes_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ GodParagraphAttributes *paragraph = GOD_PARAGRAPH_ATTRIBUTES (object);
+
+ switch (prop_id) {
+ case PROP_INDENT:
+ paragraph->priv->indent = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_INDENT;
+ break;
+ case PROP_SPACE_BEFORE:
+ paragraph->priv->space_before = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_BEFORE;
+ break;
+ case PROP_SPACE_AFTER:
+ paragraph->priv->space_after = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_AFTER;
+ break;
+ case PROP_ALIGNMENT:
+ paragraph->priv->alignment = g_value_get_uint (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALIGNMENT;
+ break;
+ case PROP_BULLET_CHARACTER:
+ paragraph->priv->bullet_character = g_value_get_uint (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_CHARACTER;
+ break;
+ case PROP_BULLET_INDENT:
+ paragraph->priv->bullet_indent = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_INDENT;
+ break;
+ case PROP_BULLET_SIZE:
+ paragraph->priv->bullet_size = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_SIZE;
+ break;
+ case PROP_BULLET_FAMILY:
+ g_free (paragraph->priv->bullet_family);
+ paragraph->priv->bullet_family = g_value_dup_string (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_FAMILY;
+ break;
+ }
+}
+
+static void
+god_paragraph_attributes_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GodParagraphAttributes *paragraph = GOD_PARAGRAPH_ATTRIBUTES (object);
+
+ switch (prop_id){
+ case PROP_FLAGS:
+ g_value_set_uint (value, paragraph->priv->flags);
+ break;
+ case PROP_INDENT:
+ g_value_set_double (value, paragraph->priv->indent);
+ break;
+ case PROP_SPACE_BEFORE:
+ g_value_set_double (value, paragraph->priv->space_before);
+ break;
+ case PROP_SPACE_AFTER:
+ g_value_set_double (value, paragraph->priv->space_after);
+ break;
+ case PROP_ALIGNMENT:
+ g_value_set_uint (value, paragraph->priv->alignment);
+ break;
+ case PROP_BULLET_CHARACTER:
+ g_value_set_uint (value, paragraph->priv->bullet_character);
+ break;
+ case PROP_BULLET_INDENT:
+ g_value_set_double (value, paragraph->priv->bullet_indent);
+ break;
+ case PROP_BULLET_SIZE:
+ g_value_set_double (value, paragraph->priv->bullet_size);
+ break;
+ case PROP_BULLET_FAMILY:
+ g_value_set_string (value, paragraph->priv->bullet_family);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+god_paragraph_attributes_class_init (GodParagraphAttributesClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_paragraph_attributes_finalize;
+ object_class->get_property = god_paragraph_attributes_get_property;
+ object_class->set_property = god_paragraph_attributes_set_property;
+
+ g_object_class_install_property (object_class, PROP_FLAGS,
+ g_param_spec_uint ("flags",
+ _( "Flags" ),
+ _( "Flags" ),
+ 0, GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALL, 0,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class, PROP_INDENT,
+ g_param_spec_double ("indent",
+ _( "Indent" ),
+ _( "Indent" ),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_SPACE_BEFORE,
+ g_param_spec_double ("space_before",
+ _( "Space Before" ),
+ _( "Space Before" ),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_SPACE_AFTER,
+ g_param_spec_double ("space_after",
+ _( "Space After" ),
+ _( "Space After" ),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_ALIGNMENT,
+ g_param_spec_uint ("alignment",
+ _( "Alignment" ),
+ _( "Alignment" ),
+ GOD_PARAGRAPH_ALIGNMENT_LEFT, GOD_PARAGRAPH_ALIGNMENT_JUSTIFY, GOD_PARAGRAPH_ALIGNMENT_LEFT,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_BULLET_CHARACTER,
+ g_param_spec_uint ("bullet_character",
+ _( "Bullet Character" ),
+ _( "Bullet Character" ),
+ 0, 0xffffffff, 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_BULLET_INDENT,
+ g_param_spec_double ("bullet_indent",
+ _( "Bullet Indent" ),
+ _( "Bullet Indent" ),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_BULLET_SIZE,
+ g_param_spec_double ("bullet_size",
+ _( "Bullet Size" ),
+ _( "Bullet Size" ),
+ 0, G_MAXDOUBLE, 1.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_BULLET_FAMILY,
+ g_param_spec_string ("bullet_family",
+ _( "Bullet Family" ),
+ _( "Bullet Family" ),
+ NULL,
+ G_PARAM_READWRITE));
+}
+
+GSF_CLASS (GodParagraphAttributes, god_paragraph_attributes,
+ god_paragraph_attributes_class_init, god_paragraph_attributes_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-image-store.h
@@ -0,0 +1,59 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-image-store.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_IMAGE_STORE_H
+#define GOD_IMAGE_STORE_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-image.h>
+
+G_BEGIN_DECLS
+
+#define GOD_IMAGE_STORE_TYPE (god_image_store_get_type ())
+#define GOD_IMAGE_STORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_IMAGE_STORE_TYPE, GodImageStore))
+#define GOD_IMAGE_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_IMAGE_STORE_TYPE, GodImageStoreClass))
+#define IS_GOD_IMAGE_STORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_IMAGE_STORE_TYPE))
+#define IS_GOD_IMAGE_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_IMAGE_STORE_TYPE))
+
+typedef struct GodImageStorePrivate_ GodImageStorePrivate;
+
+typedef struct {
+ GObject parent;
+ GodImageStorePrivate *priv;
+} GodImageStore;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodImageStoreClass;
+
+GType god_image_store_get_type (void);
+GodImageStore *god_image_store_new (void);
+
+/* Tree functions */
+void god_image_store_append_image (GodImageStore *store,
+ GodImage *image);
+void god_image_store_insert_image (GodImageStore *store,
+ GodImage *image,
+ int pos);
+void god_image_store_delete_image (GodImageStore *store,
+ int pos);
+void god_image_store_reorder_image (GodImageStore *store,
+ int old_pos,
+ int new_pos);
+int god_image_store_get_image_count (GodImageStore *store);
+/* Return value is reffed. */
+GodImage *god_image_store_get_image (GodImageStore *store,
+ int pos);
+
+G_END_DECLS
+
+#endif /* GOD_IMAGE_STORE_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-view.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *//* vim: set sw=8: */
+#ifndef GOD_DRAWING_VIEW_H
+#define GOD_DRAWING_VIEW_H
+
+/**
+ * god-drawing-view.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-drawing.h>
+#include <drawing/god-anchor.h>
+#include <gdk/gdkdrawable.h>
+#include <gtk/gtkdrawingarea.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DRAWING_VIEW_TYPE (god_drawing_view_get_type ())
+#define GOD_DRAWING_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DRAWING_VIEW_TYPE, GodDrawingView))
+#define GOD_DRAWING_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DRAWING_VIEW_TYPE, GodDrawingViewClass))
+#define IS_GOD_DRAWING_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DRAWING_VIEW_TYPE))
+#define IS_GOD_DRAWING_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DRAWING_VIEW_TYPE))
+
+typedef struct GodDrawingViewPrivate_ GodDrawingViewPrivate;
+
+typedef struct {
+ GtkDrawingArea parent;
+ GodDrawingViewPrivate *priv;
+} GodDrawingView;
+
+typedef struct {
+ GtkDrawingAreaClass parent_class;
+} GodDrawingViewClass;
+
+GType god_drawing_view_get_type (void);
+GodDrawingView *god_drawing_view_new (void);
+
+/* Return value is reffed. */
+GodDrawing *god_drawing_view_get_drawing (GodDrawingView *renderer);
+void god_drawing_view_set_drawing (GodDrawingView *renderer,
+ GodDrawing *drawing);
+
+/* Return value is reffed. */
+GodAnchor *god_drawing_view_get_extents (GodDrawingView *renderer);
+void god_drawing_view_set_extents (GodDrawingView *renderer,
+ GodAnchor *extents);
+
+G_END_DECLS
+
+#endif /* GOD_DRAWING_VIEW_H */
--- /dev/null
+++ lib/goffice/drawing/god-property-table.c
@@ -0,0 +1,993 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-property-table.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2004
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-property-table.h"
+#include <gsf/gsf-impl-utils.h>
+
+#include <gsf/gsf-utils.h>
+#include <stdio.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodPropertyTablePrivate_ {
+ GHashTable *attrs;
+};
+
+#define GR_END 0x00
+#define GR_MACRO 0x04
+#define GR_COMMAND_BUTTON 0x05
+#define GR_GROUP 0x06
+#define GR_CLIPBOARD_FORMAT 0x07
+#define GR_PICTURE_OPTIONS 0x08
+#define GR_PICTURE_FORMULA 0x09
+#define GR_CHECKBOX_LINK 0x0A
+#define GR_RADIO_BUTTON 0x0B
+#define GR_SCROLLBAR 0x0C
+#define GR_NOTE_STRUCTURE 0x0D
+#define GR_SCROLLBAR_FORMULA 0x0E
+#define GR_GROUP_BOX_DATA 0x0F
+#define GR_EDIT_CONTROL_DATA 0x10
+#define GR_RADIO_BUTTON_DATA 0x11
+#define GR_CHECKBOX_DATA 0x12
+#define GR_LISTBOX_DATA 0x13
+#define GR_CHECKBOX_FORMULA 0x14
+#define GR_COMMON_OBJ_DATA 0x15
+
+static GValue *
+g_value_new (GType type)
+{
+ GValue *value = g_new0 (GValue, 1);
+ g_value_init (value, type);
+ return value;
+}
+
+static void
+g_value_free (gpointer data)
+{
+ GValue *value = data;
+ g_value_unset (value);
+ g_free (value);
+}
+
+void
+god_property_table_set (GodPropertyTable *prop_table, GodPropertyID id, GValue *value)
+{
+ g_hash_table_insert (prop_table->priv->attrs, g_strdup(id), value);
+}
+
+GValue *
+god_property_table_get (GodPropertyTable *prop_table, GodPropertyID id)
+{
+ g_return_val_if_fail (prop_table != NULL, NULL);
+ return g_hash_table_lookup (prop_table->priv->attrs, id);
+}
+
+void
+god_property_table_set_flag (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gboolean val)
+{
+ GValue *value = g_value_new(G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_uint (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ guint32 val)
+{
+ GValue *value = g_value_new(G_TYPE_UINT);
+ g_value_set_uint (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_int (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gint32 val)
+{
+ GValue *value = g_value_new(G_TYPE_INT);
+ g_value_set_int (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_length (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ go_unit_t val)
+{
+ GValue *value = g_value_new(G_TYPE_INT64);
+ g_value_set_int64 (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_pointer (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gpointer val)
+{
+ GValue *value = g_value_new(G_TYPE_POINTER);
+ g_value_set_pointer (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_array (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ GArray *val)
+{
+ GValue *value = g_value_new(G_TYPE_POINTER);
+ g_value_set_pointer (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_markup (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ PangoAttrList *list)
+{
+ GValue *value = g_value_new (PANGO_TYPE_ATTR_LIST);
+ g_value_set_pointer (value, list);
+ god_property_table_set (prop_table, id, value);
+}
+
+gboolean
+god_property_table_get_flag (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gboolean default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_BOOLEAN (value), default_value);
+
+ return g_value_get_boolean (value);
+}
+
+guint32
+god_property_table_get_uint (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ guint32 default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_UINT (value), default_value);
+
+ return g_value_get_uint (value);
+}
+
+gint32
+god_property_table_get_int (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gint32 default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_INT (value), default_value);
+
+ return g_value_get_int (value);
+}
+
+go_unit_t
+god_property_table_get_length (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ go_unit_t default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_INT64 (value), default_value);
+
+ return g_value_get_int64 (value);
+}
+
+gpointer
+god_property_table_get_pointer (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gpointer default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_POINTER (value), default_value);
+
+ return g_value_get_pointer (value);
+}
+
+GArray *
+god_property_table_get_array (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ GArray *default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_POINTER (value), default_value);
+
+ return g_value_get_pointer (value);
+}
+
+PangoAttrList *
+god_property_table_get_markup (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ PangoAttrList *default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_POINTER (value), default_value);
+
+ return g_value_get_pointer (value);
+}
+
+GodPropertyTable *
+god_property_table_new (void)
+{
+ GodPropertyTable *prop_table;
+
+ prop_table = g_object_new (GOD_PROPERTY_TABLE_TYPE, NULL);
+
+ return prop_table;
+}
+
+static void
+god_property_table_init (GObject *object)
+{
+ GodPropertyTable *prop_table = GOD_PROPERTY_TABLE (object);
+ prop_table->priv = g_new0 (GodPropertyTablePrivate, 1);
+ prop_table->priv->attrs = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_value_free);
+}
+
+static void
+god_property_table_finalize (GObject *object)
+{
+ GodPropertyTable *prop_table = GOD_PROPERTY_TABLE (object);
+
+ g_hash_table_destroy (prop_table->priv->attrs);
+ g_free (prop_table->priv);
+ prop_table->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+god_property_table_class_init (GodPropertyTableClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_property_table_finalize;
+}
+
+GSF_CLASS (GodPropertyTable, god_property_table,
+ god_property_table_class_init, god_property_table_init,
+ G_TYPE_OBJECT)
+
+#if 0
+/********************************************************************************/
+
+GODrawingPropertyTable *
+ms_obj_new (GODrawingPropertyTableAttrBag *attrs)
+{
+ GODrawingPropertyTable *obj = g_new0 (GODrawingPropertyTable, 1);
+
+ obj->excel_type = (unsigned)-1; /* Set to undefined */
+ obj->excel_type_name = NULL;
+ obj->id = -1;
+ obj->gnum_obj = NULL;
+ obj->attrs = (attrs != NULL) ? attrs : ms_obj_attr_bag_new ();
+ obj->combo_in_autofilter = FALSE;
+ obj->is_linked = FALSE;
+ obj->comment_pos.col = obj->comment_pos.row = -1;
+
+ return obj;
+}
+
+void
+ms_obj_delete (GODrawingPropertyTable *obj)
+{
+ if (obj) {
+ if (obj->gnum_obj) {
+ g_object_unref (obj->gnum_obj);
+ obj->gnum_obj = NULL;
+ }
+ if (obj->attrs) {
+ ms_obj_attr_bag_destroy (obj->attrs);
+ obj->attrs = NULL;
+ }
+ g_free (obj);
+ }
+}
+
+/* S59EOE.HTM */
+char *
+ms_read_TXO (BiffQuery *q)
+{
+ static char const * const orientations [] = {
+ "Left to right",
+ "Top to Bottom",
+ "Bottom to Top on Side",
+ "Top to Bottom on Side"
+ };
+ static char const * const haligns [] = {
+ "At left", "Horizontaly centered",
+ "At right", "Horizontaly justified"
+ };
+ static char const * const valigns [] = {
+ "At top", "Verticaly centered",
+ "At bottom", "Verticaly justified"
+ };
+
+ guint16 const options = GSF_LE_GET_GUINT16 (q->data);
+ guint16 const orient = GSF_LE_GET_GUINT16 (q->data + 2);
+ guint16 const text_len = GSF_LE_GET_GUINT16 (q->data + 10);
+/* guint16 const num_formats = GSF_LE_GET_GUINT16 (q->data + 12);*/
+ int const halign = (options >> 1) & 0x7;
+ int const valign = (options >> 4) & 0x7;
+ char *text;
+ guint16 peek_op;
+
+ if (text_len == 0)
+ return NULL;
+
+ g_return_val_if_fail (orient <= 3, NULL);
+ g_return_val_if_fail (1 <= halign && halign <= 4, NULL);
+ g_return_val_if_fail (1 <= valign && valign <= 4, NULL);
+
+ if (ms_biff_query_peek_next (q, &peek_op) && peek_op == BIFF_CONTINUE) {
+ ms_biff_query_next (q);
+
+ if ((int)q->length < text_len) {
+ g_warning ("Broken continue in TXO record");
+ text = g_strdup ("Broken continue");
+ } else
+ text = ms_biff_get_chars (q->data + 1, text_len,
+ *(q->data) != 0);
+
+ if (ms_biff_query_peek_next (q, &peek_op) &&
+ peek_op == BIFF_CONTINUE)
+ ms_biff_query_next (q);
+ else
+ g_warning ("Unusual, TXO text with no formatting has 0x%x @ 0x%x", peek_op, q->streamPos);
+ } else {
+ if (text_len > 0)
+ g_warning ("TXO len of %d but no continue", text_len);
+ text = g_strdup ("");
+ }
+
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug > 0) {
+ printf ("{ TextObject\n");
+ printf ("Text '%s'\n", text);
+ printf ("is %s, %s & %s;\n",
+ orientations[orient], haligns[halign], valigns[valign]);
+ printf ("}; /* TextObject */\n");
+ }
+#endif
+ return text;
+}
+
+#ifndef NO_DEBUG_EXCEL
+#define ms_obj_dump(data, len, data_left, name) ms_obj_dump_impl (data, len, data_left, name)
+static void
+ms_obj_dump_impl (guint8 const *data, int len, int data_left, char const *name)
+{
+ if (ms_excel_object_debug < 2)
+ return;
+
+ printf ("{ %s \n", name);
+ if (len+4 > data_left) {
+ printf ("/* invalid length %d (0x%x) > %d(0x%x)*/\n",
+ len+4, len+4, data_left, data_left);
+ len = data_left - 4;
+ }
+ if (ms_excel_object_debug > 2)
+ gsf_mem_dump (data, len+4);
+ printf ("}; /* %s */\n", name);
+}
+#else
+#define ms_obj_dump (data, len, data_left, name)
+#endif
+
+/* S59DAD.HTM */
+static gboolean
+ms_obj_read_pre_biff8_obj (BiffQuery *q, MSContainer *container, GODrawingPropertyTable *obj)
+{
+ guint16 peek_op, tmp, len;
+ guint8 const *data;
+ gboolean const has_fmla = GSF_LE_GET_GUINT16 (q->data+26) != 0;
+
+ /* undocumented */
+ gboolean const has_name = GSF_LE_GET_GUINT16 (q->data+30) != 0;
+
+#if 0
+ guint16 const flags = GSF_LE_GET_GUINT16(q->data+8);
+#endif
+ guint8 *anchor = g_malloc (MS_ANCHOR_SIZE);
+ memcpy (anchor, q->data+8, MS_ANCHOR_SIZE);
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_ptr (MS_OBJ_ATTR_ANCHOR, anchor));
+
+ obj->excel_type = GSF_LE_GET_GUINT16(q->data + 4);
+ obj->id = GSF_LE_GET_GUINT32(q->data + 6);
+
+ switch (obj->excel_type) {
+ case 0: /* group */
+ break;
+ case 1: /* line */
+ tmp = GSF_LE_GET_GUINT8 (q->data+40);
+ if (GSF_LE_GET_GUINT16 (q->data + 10) == 0 &&
+ GSF_LE_GET_GUINT16 (q->data + 14) < 20) {
+ g_warning("%hhu", tmp);
+ }
+ if (GSF_LE_GET_GUINT8 (q->data+38) & 0x0F)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_flag (MS_OBJ_ATTR_ARROW_END));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+34)));
+
+ if (tmp == 1 || tmp == 2)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_H));
+ if (tmp >= 2)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_V));
+ break;
+
+ case 2: /* rectangle */
+ break;
+ case 3: /* oval */
+ break;
+ case 4: /* arc */
+ break;
+ case 5: /* chart */
+ break;
+ case 6: /* textbox */
+ /* gsf_mem_dump (q->data+34, q->length - 34); */
+ if (GSF_LE_GET_GUINT8 (q->data+36) > 0) {
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_flag (MS_OBJ_ATTR_FILLED));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+35)));
+ }
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_FONT_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+34)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+38)));
+
+ /* only pull in the text if it exists */
+ len = GSF_LE_GET_GUINT16 (q->data + 44);
+ if (len > 0) {
+ data = q->data + 70;
+ g_return_val_if_fail ((unsigned)(data - q->data) < q->length, TRUE);
+
+ g_return_val_if_fail (!has_fmla, TRUE); /* how would this happen */
+
+ /* skip the obj name if defined */
+ if (has_name) {
+ data += *data + ((*data & 0x1) ? 1 : 2); /* padding byte */
+ g_return_val_if_fail ((unsigned)(data - q->data) < q->length, TRUE);
+ }
+
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_ptr (MS_OBJ_ATTR_TEXT,
+ g_strndup (data, len)));
+ }
+ break;
+
+ case 7: /* button */
+ break;
+ case 8: /* picture */
+ break;
+ case 9: /* polygon */
+
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+35)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+38)));
+
+ if (ms_biff_query_peek_next (q, &peek_op) &&
+ peek_op == BIFF_COORDLIST) {
+ unsigned i, n;
+ guint tmp;
+ GArray *array;
+
+ ms_biff_query_next (q);
+ n = q->length / 2;
+ array = g_array_set_size (
+ g_array_new (FALSE, FALSE, sizeof (double)), n + 2);
+
+ for (i = 0; i < n ; i++) {
+ tmp = GSF_LE_GET_GUINT16 (q->data + 2*i);
+ g_array_index (array, double, i) = (double)tmp/ 16384.;
+ }
+ g_array_index (array, double, i) = g_array_index (array, double, 0);
+ g_array_index (array, double, i+1) = g_array_index (array, double, 1);
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_array (MS_OBJ_ATTR_POLYGON_COORDS, array));
+ }
+ break;
+
+ case 0xB : /* check box */
+ break;
+ case 0xC : /* option button */
+ break;
+ case 0xD : /* edit box */
+ break;
+ case 0xE : /* label */
+ break;
+ case 0xF : /* dialog frame */
+ break;
+ case 0x10 : /* spinner & scrollbar (layout is the same) */
+ case 0x11 :
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_VALUE,
+ GSF_LE_GET_GUINT16 (q->data+48)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MIN,
+ GSF_LE_GET_GUINT16 (q->data+50)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MAX,
+ GSF_LE_GET_GUINT16 (q->data+52)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_INC,
+ GSF_LE_GET_GUINT16 (q->data+54)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_PAGE,
+ GSF_LE_GET_GUINT16 (q->data+56)));
+
+ {
+ GnmExpr const *ref;
+ guint16 len;
+ guint8 const *last = q->data + q->length;
+ guint8 const *ptr = q->data + 64;
+
+ ptr += 1 + *ptr; /* object name */
+ if ((ptr - q->data) & 1) ptr++; /* align on word */
+ if (ptr >= last) break;
+
+ ptr += 2 + GSF_LE_GET_GUINT16 (ptr); /* the macro */
+ if ((ptr - q->data) & 1) ptr++; /* align on word */
+ if (ptr >= last) break;
+
+ len = GSF_LE_GET_GUINT16 (ptr+2); /* the assigned macro */
+ ref = ms_container_parse_expr (container, ptr + 8, len);
+ if (ref != NULL)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
+ }
+ break;
+ case 0x12 : /* list box */
+ break;
+ case 0x13 : /* group box */
+ break;
+ case 0x14 : /* drop down */
+ obj->combo_in_autofilter =
+ (GSF_LE_GET_GUINT16 (q->data + 8) & 0x8000) ? TRUE : FALSE;
+ break;
+ default :
+ ;
+ }
+
+ return FALSE;
+}
+
+/* S59DAD.HTM */
+static gboolean
+ms_obj_read_biff8_obj (BiffQuery *q, MSContainer *container, GODrawingPropertyTable *obj)
+{
+ guint8 *data;
+ gint32 data_len_left;
+ gboolean hit_end = FALSE;
+ gboolean next_biff_record_maybe_imdata = FALSE;
+
+ g_return_val_if_fail (q, TRUE);
+ g_return_val_if_fail (q->ls_op == BIFF_OBJ, TRUE);
+
+ data = q->data;
+ data_len_left = q->length;
+
+#if 0
+ ms_biff_query_dump (q);
+#endif
+
+ /* Scan through the pseudo BIFF substream */
+ while (data_len_left > 0 && !hit_end) {
+ guint16 const record_type = GSF_LE_GET_GUINT16(data);
+
+ /* All the sub-records seem to have this layout
+ * 2001/Mar/29 JEG : liars. Ok not all records have this
+ * layout. Create a list box. It seems to do something
+ * unique. It acts like an end, and has no length specified.
+ */
+ guint16 len = GSF_LE_GET_GUINT16(data+2);
+
+ /* 1st record must be COMMON_OBJ*/
+ g_return_val_if_fail (obj->excel_type >= 0 ||
+ record_type == GR_COMMON_OBJ_DATA,
+ TRUE);
+
+ switch (record_type) {
+ case GR_END:
+ g_return_val_if_fail (len == 0, TRUE);
+ /* ms_obj_dump (data, len, data_len_left, "ObjEnd"); */
+ hit_end = TRUE;
+ break;
+
+ case GR_MACRO :
+ ms_obj_dump (data, len, data_len_left, "MacroObject");
+ break;
+
+ case GR_COMMAND_BUTTON :
+ ms_obj_dump (data, len, data_len_left, "CommandButton");
+ break;
+
+ case GR_GROUP :
+ ms_obj_dump (data, len, data_len_left, "Group");
+ break;
+
+ case GR_CLIPBOARD_FORMAT :
+ ms_obj_dump (data, len, data_len_left, "ClipboardFmt");
+ break;
+
+ case GR_PICTURE_OPTIONS :
+ if (len == 2) {
+ guint16 opt = GSF_LE_GET_GUINT16 (data + 4);
+
+ obj->is_linked = (opt & 0x2) ? TRUE : FALSE;
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug >= 1) {
+ printf ("{ /* PictOpt */\n");
+ printf ("value = %x;\n", opt);
+ printf ("}; /* PictOpt */\n");
+ }
+#endif
+ } else {
+ /* no docs on this so be careful */
+ g_warning ("PictOpt record with size other than 2");
+ }
+
+ next_biff_record_maybe_imdata = TRUE;
+ break;
+
+ case GR_PICTURE_FORMULA :
+ ms_obj_dump (data, len, data_len_left, "PictFormula");
+ break;
+
+ case GR_CHECKBOX_LINK :
+ ms_obj_dump (data, len, data_len_left, "CheckboxLink");
+ break;
+
+ case GR_RADIO_BUTTON :
+ ms_obj_dump (data, len, data_len_left, "RadioButton");
+ break;
+
+ case GR_SCROLLBAR :
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_VALUE,
+ GSF_LE_GET_GUINT16 (data+8)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MIN,
+ GSF_LE_GET_GUINT16 (data+10)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MAX,
+ GSF_LE_GET_GUINT16 (data+12)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_INC,
+ GSF_LE_GET_GUINT16 (data+14)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_PAGE,
+ GSF_LE_GET_GUINT16 (data+16)));
+ ms_obj_dump (data, len, data_len_left, "ScrollBar");
+ break;
+
+ case GR_NOTE_STRUCTURE :
+ ms_obj_dump (data, len, data_len_left, "Note");
+ break;
+
+ case GR_SCROLLBAR_FORMULA : {
+ guint16 const expr_len = GSF_LE_GET_GUINT16 (data+4);
+ GnmExpr const *ref = ms_container_parse_expr (container, data+10, expr_len);
+ if (ref != NULL)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
+ ms_obj_dump (data, len, data_len_left, "ScrollbarFmla");
+ break;
+ }
+
+ case GR_GROUP_BOX_DATA :
+ ms_obj_dump (data, len, data_len_left, "GroupBoxData");
+ break;
+
+ case GR_EDIT_CONTROL_DATA :
+ ms_obj_dump (data, len, data_len_left, "EditCtrlData");
+ break;
+
+ case GR_RADIO_BUTTON_DATA :
+ ms_obj_dump (data, len, data_len_left, "RadioData");
+ break;
+
+ case GR_CHECKBOX_DATA :
+ ms_obj_dump (data, len, data_len_left, "CheckBoxData");
+ break;
+
+ case GR_LISTBOX_DATA : {
+ /* FIXME : find some docs for this
+ * It seems as if list box data does not conform to
+ * the docs. It acts like an end and has no size.
+ */
+ hit_end = TRUE;
+ len = data_len_left - 4;
+
+ ms_obj_dump (data, len, data_len_left, "ListBoxData");
+ break;
+ }
+
+ case GR_CHECKBOX_FORMULA : {
+ guint16 const expr_len = GSF_LE_GET_GUINT16 (data+4);
+ GnmExpr const *ref = ms_container_parse_expr (container, data+10, expr_len);
+ if (ref != NULL)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
+ ms_obj_dump (data, len, data_len_left, "CheckBoxFmla");
+ break;
+ }
+
+ case GR_COMMON_OBJ_DATA : {
+ guint16 const options =GSF_LE_GET_GUINT16 (data+8);
+
+ /* Multiple objects in 1 record ?? */
+ g_return_val_if_fail (obj->excel_type == -1, TRUE);
+
+ obj->excel_type = GSF_LE_GET_GUINT16(data+4);
+ obj->id = GSF_LE_GET_GUINT16(data+6);
+
+ /* Undocumented. It appears that combos for filters are marked
+ * with flag 0x100
+ */
+ obj->combo_in_autofilter =
+ (obj->excel_type == 0x14) && (options & 0x100);
+
+#ifndef NO_DEBUG_EXCEL
+ /* only print when debug is enabled */
+ if (ms_excel_object_debug == 0)
+ break;
+
+ printf ("OBJECT TYPE = %d\n", obj->excel_type);
+ if (options&0x0001)
+ printf ("Locked;\n");
+ if (options&0x0010)
+ printf ("Printable;\n");
+ if (options&0x2000)
+ printf ("AutoFilled;\n");
+ if (options&0x4000)
+ printf ("AutoLines;\n");
+
+ if (ms_excel_object_debug > 4) {
+ /* According to the docs this should not fail
+ * but there appears to be a flag at 0x200 for
+ * scrollbars and 0x100 for combos
+ * associated with filters.
+ */
+ if ((options & 0x9fee) != 0)
+ printf ("WARNING : Why is option not 0 (%x)\n",
+ options & 0x9fee);
+ }
+#endif
+ }
+ break;
+
+ default:
+ printf ("ERROR : Unknown Obj record 0x%x len 0x%x dll %d;\n",
+ record_type, len, data_len_left);
+ }
+
+ if (data_len_left < len+4)
+ printf ("record len %d (0x%x) > %d\n", len+4, len+4, data_len_left);
+
+ /* FIXME : We need a structure akin to the escher code to do this properly */
+ for (data_len_left -= len+4; data_len_left < 0; ) {
+ guint16 peek_op;
+
+ printf ("deficit of %d\n", data_len_left);
+
+ /* FIXME : what do we expect here ??
+ * I've seen what seem to be embedded drawings
+ * but I am not sure what is embedding what.
+ */
+ if (!ms_biff_query_peek_next (q, &peek_op) ||
+ (peek_op != BIFF_CONTINUE &&
+ peek_op != BIFF_MS_O_DRAWING &&
+ peek_op != BIFF_TXO &&
+ peek_op != BIFF_OBJ)) {
+ printf ("0x%x vs 0x%x\n", q->opcode, peek_op);
+ return TRUE;
+ }
+
+ ms_biff_query_next (q);
+ data_len_left += q->length;
+ printf ("merged in 0x%x with len %d\n", q->opcode, q->length);
+ }
+ data = q->data + q->length - data_len_left;
+ }
+
+ /* The ftEnd record should have been the last */
+ if (data_len_left > 0) {
+ printf("OBJ : unexpected extra data after Object End record;\n");
+ gsf_mem_dump (data, data_len_left);
+ return TRUE;
+ }
+
+ /* Catch underflow too */
+ g_return_val_if_fail (data_len_left == 0, TRUE);
+
+ /* FIXME : Throw away the IMDATA that may follow.
+ * I am not sure when the IMDATA does follow, or how to display it,
+ * but very careful in case it is not there.
+ */
+ if (next_biff_record_maybe_imdata) {
+ guint16 op;
+
+ if (ms_biff_query_peek_next (q, &op) && op == BIFF_IMDATA) {
+ printf ("Reading trailing IMDATA;\n");
+ ms_biff_query_next (q);
+ excel_read_IMDATA (q, FALSE);
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * ms_read_OBJ :
+ * @q : The biff record to start with.
+ * @container : The object's container
+ * @attrs : an OPTIONAL hash of object attributes.
+ */
+void
+ms_read_OBJ (BiffQuery *q, MSContainer *container, GODrawingPropertyTableAttrBag *attrs)
+{
+ static char const * const object_type_names[] = {
+ "Group", /* 0x00 */
+ "Line", /* 0x01 */
+ "Rectangle", /* 0x02 */
+ "Oval", /* 0x03 */
+ "Arc", /* 0x04 */
+ "Chart", /* 0x05 */
+ "TextBox", /* 0x06 */
+ "Button", /* 0x07 */
+ "Picture", /* 0x08 */
+ "Polygon", /* 0x09 */
+ NULL, /* 0x0A */
+ "CheckBox", /* 0x0B */
+ "Option", /* 0x0C */
+ "Edit", /* 0x0D */
+ "Label", /* 0x0E */
+ "Dialog", /* 0x0F */
+ "Spinner", /* 0x10 */
+ "Scroll", /* 0x11 */
+ "List", /* 0x12 */
+ "Group", /* 0x13 */
+ "Combo", /* 0x14 */
+ NULL, NULL, NULL, NULL, /* 0x15 - 0x18 */
+ "Comment", /* 0x19 */
+ NULL, NULL, NULL, NULL, /* 0x1A - 0x1D */
+ "MS Drawing" /* 0x1E */
+ };
+
+ gboolean errors;
+ GODrawingPropertyTable *obj = ms_obj_new (attrs);
+
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug > 0)
+ printf ("{ /* OBJ start */\n");
+#endif
+ errors = (container->ver >= MS_BIFF_V8)
+ ? ms_obj_read_biff8_obj (q, container, obj)
+ : ms_obj_read_pre_biff8_obj (q, container, obj);
+
+ if (errors) {
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug > 0)
+ printf ("}; /* OBJ error 1 */\n");
+#endif
+ ms_obj_delete (obj);
+ return;
+ }
+
+ obj->excel_type_name = NULL;
+ if (obj->excel_type < (int)G_N_ELEMENTS (object_type_names))
+ obj->excel_type_name = object_type_names [obj->excel_type];
+ if (obj->excel_type_name == NULL)
+ obj->excel_type_name = "Unknown";
+
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug > 0) {
+ printf ("Object (%d) is a '%s'\n", obj->id, obj->excel_type_name);
+ printf ("}; /* OBJ end */\n");
+ }
+#endif
+
+ if (container->vtbl->create_obj != NULL)
+ obj->gnum_obj = (*container->vtbl->create_obj) (container, obj);
+
+ /* Chart, There should be a BOF next */
+ if (obj->excel_type == 0x5 &&
+ ms_excel_chart_read_BOF (q, container, obj->gnum_obj)) {
+ ms_obj_delete (obj);
+ return;
+ }
+
+#if 0
+ g_warning ("registered obj %d\n", obj->id);
+#endif
+ ms_container_add_obj (container, obj);
+}
+#endif
--- /dev/null
+++ lib/goffice/drawing/Makefile.am
@@ -0,0 +1,31 @@
+noinst_LTLIBRARIES = libgoffice-drawing.la
+
+AM_CFLAGS = $(GNOME_CFLAGS) $(GSF_CFLAGS)
+
+libgoffice_drawing_la_SOURCES = \
+ god-anchor.c \
+ god-anchor.h \
+ god-default-attributes.c \
+ god-default-attributes.h \
+ god-drawing-group.c \
+ god-drawing-group.h \
+ god-drawing-renderer-gdk.c \
+ god-drawing-renderer-gdk.h \
+ god-drawing-view.c \
+ god-drawing-view.h \
+ god-drawing.c \
+ god-drawing.h \
+ god-image-store.c \
+ god-image-store.h \
+ god-image.c \
+ god-image.h \
+ god-paragraph-attributes.c \
+ god-paragraph-attributes.h \
+ god-property-table.c \
+ god-property-table.h \
+ god-shape.c \
+ god-shape.h \
+ god-text-model.c \
+ god-text-model.h
+
+include $(srcdir)/../goffice.mk
--- /dev/null
+++ lib/goffice/drawing/god-image.h
@@ -0,0 +1,53 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-image.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_IMAGE_H
+#define GOD_IMAGE_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-property-table.h>
+#include <drawing/god-anchor.h>
+#include <drawing/god-text-model.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GOD_IMAGE_TYPE (god_image_get_type ())
+#define GOD_IMAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_IMAGE_TYPE, GodImage))
+#define GOD_IMAGE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_IMAGE_TYPE, GodImageClass))
+#define IS_GOD_IMAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_IMAGE_TYPE))
+#define IS_GOD_IMAGE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_IMAGE_TYPE))
+
+typedef struct GodImagePrivate_ GodImagePrivate;
+
+typedef struct {
+ GObject parent;
+ GodImagePrivate *priv;
+} GodImage;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodImageClass;
+
+GType god_image_get_type (void);
+GodImage *god_image_new (void);
+
+GdkPixbuf *god_image_get_pixbuf (GodImage *image);
+/* Instead of setting the pixbuf, you set the image data. */
+void god_image_set_image_data (GodImage *image,
+ const char *format,
+ const guint8 *data,
+ guint32 length);
+
+G_END_DECLS
+
+#endif /* GOD_IMAGE_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-group.h
@@ -0,0 +1,48 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-drawing-group.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#ifndef GOD_DRAWING_GROUP_H
+#define GOD_DRAWING_GROUP_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-image-store.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DRAWING_GROUP_TYPE (god_drawing_group_get_type ())
+#define GOD_DRAWING_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DRAWING_GROUP_TYPE, GodDrawingGroup))
+#define GOD_DRAWING_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DRAWING_GROUP_TYPE, GodDrawingGroupClass))
+#define GOD_DRAWING_GROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_DRAWING_GROUP_TYPE, GodDrawingGroupClass))
+#define IS_GOD_DRAWING_GROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DRAWING_GROUP_TYPE))
+#define IS_GOD_DRAWING_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DRAWING_GROUP_TYPE))
+
+typedef struct GodDrawingGroupPrivate_ GodDrawingGroupPrivate;
+
+typedef struct {
+ GObject parent;
+ GodDrawingGroupPrivate *priv;
+} GodDrawingGroup;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodDrawingGroupClass;
+
+GType god_drawing_group_get_type (void);
+
+GodDrawingGroup *god_drawing_group_new (void);
+
+GodImageStore *god_drawing_group_get_image_store (GodDrawingGroup *drawing_group);
+
+G_END_DECLS
+
+#endif /* GOD_DRAWING_GROUP_H */
--- /dev/null
+++ lib/goffice/drawing/god-shape.h
@@ -0,0 +1,80 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-shape.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_SHAPE_H
+#define GOD_SHAPE_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-property-table.h>
+#include <drawing/god-anchor.h>
+#include <drawing/god-text-model.h>
+
+G_BEGIN_DECLS
+
+#define GOD_SHAPE_TYPE (god_shape_get_type ())
+#define GOD_SHAPE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_SHAPE_TYPE, GodShape))
+#define GOD_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_SHAPE_TYPE, GodShapeClass))
+#define IS_GOD_SHAPE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_SHAPE_TYPE))
+#define IS_GOD_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_SHAPE_TYPE))
+
+typedef struct GodShapePrivate_ GodShapePrivate;
+
+typedef struct {
+ GObject parent;
+ GodShapePrivate *priv;
+} GodShape;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodShapeClass;
+
+GType god_shape_get_type (void);
+GodShape *god_shape_new (void);
+
+/* Tree functions */
+void god_shape_append_child (GodShape *parent,
+ GodShape *child);
+void god_shape_insert_child (GodShape *parent,
+ GodShape *child,
+ int pos);
+void god_shape_delete_child (GodShape *parent,
+ int pos);
+void god_shape_reorder_child (GodShape *parent,
+ int old_pos,
+ int new_pos);
+int god_shape_get_child_count (GodShape *parent);
+/* Return value is reffed. */
+GodShape *god_shape_get_child (GodShape *parent,
+ int pos);
+
+/* Return value is reffed. */
+GodPropertyTable *god_shape_get_prop_table (GodShape *shape);
+void god_shape_set_prop_table (GodShape *shape,
+ GodPropertyTable *prop_table);
+
+/* Return value is reffed. */
+GodAnchor *god_shape_get_anchor (GodShape *shape);
+void god_shape_set_anchor (GodShape *shape,
+ GodAnchor *anchor);
+
+/* Return value is reffed. */
+GodTextModel *god_shape_get_text_model (GodShape *shape);
+void god_shape_set_text_model (GodShape *shape,
+ GodTextModel *text);
+
+const char *god_shape_get_text (GodShape *shape);
+void god_shape_set_text (GodShape *shape,
+ const char *text_value);
+
+G_END_DECLS
+
+#endif /* GOD_SHAPE_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_radar/plugin.xml.in
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin id="GOffice_plot_radar">
+ <information>
+ <_name>Charting : Radial plots</_name>
+ <_description>Radial/Radar plots</_description>
+ </information>
+ <loader type="Gnumeric_Builtin:module">
+ <attribute name="module_file" value="radar"/>
+ </loader>
+ <services>
+ <service type="plot_engine" id="GogRadarPlot">
+ <information>
+ <_description>Radar plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_engine" id="GogRadarAreaPlot">
+ <information>
+ <_description>Radar Area plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_type" id="radar">
+ <file>plot-types.xml</file>
+ <information>
+ <_description>Default radar plot types</_description>
+ </information>
+ </service>
+ </services>
+</plugin>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_radar/plot-types.xml.in
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Types xmlns:graph="http://www.gnumeric.org/graph_v2.dtd">
+ <Family _name="Radar" sample_image_file="radar.xpm"/>
+
+ <Type _name="Radar" row="1" col="1"
+ engine="GogRadarPlot" family="Radar"
+ _description="Radar plot."
+ sample_image_file="chart_radar_1_1.png">
+ </Type>
+ <Type _name="Dotted Radar" row="1" col="2"
+ engine="GogRadarPlot" family="Radar"
+ _description="Radar plot with dots."
+ sample_image_file="chart_radar_1_2.png">
+ <property name="default-style-has-markers">True</property>
+ </Type>
+ <Type _name="Area Radar" row="1" col="3"
+ engine="GogRadarAreaPlot" family="Radar"
+ _description="Area radar plot."
+ sample_image_file="chart_radar_1_3.png">
+ <property name="default-style-fills-area">True</property>
+ </Type>
+</Types>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_radar/Makefile.am
@@ -0,0 +1,24 @@
+goffice_graph_radardir = $(gnumeric_plugindir)/plot_radar
+xmldir = $(goffice_graph_radardir)
+gladedir = $(goffice_graph_radardir)
+
+goffice_graph_radar_LTLIBRARIES = radar.la
+radar_la_LDFLAGS = -module $(GOFFICE_PLUGIN_FLAGS)
+radar_la_SOURCES = \
+ gog-radar.c \
+ gog-radar.h
+
+xml_in_files = plugin.xml.in plot-types.xml.in
+xml_DATA = $(xml_in_files:.xml.in=.xml)
+
+ at INTLTOOL_XML_RULE@
+
+# do not use the intl-tool stuff to merge the text back
+# its simpler to just use gettext directly
+plot-types.xml : plot-types.xml.in
+ cp $< $@
+
+EXTRA_DIST = $(xml_in_files)
+DISTCLEANFILES = $(xml_in_files:.xml.in=.xml)
+
+include $(srcdir)/../../../goffice-plugins.mk
--- /dev/null
+++ lib/goffice/graph/plugins/plot_radar/gog-radar.h
@@ -0,0 +1,51 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-radar.h
+ *
+ * Copyright (C) 2004 Michael Devine (mdevine at cs.stanford.edu)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_RADAR_H
+#define GOG_RADAR_H
+
+#include <goffice/graph/gog-plot-impl.h>
+
+G_BEGIN_DECLS
+
+/*-----------------------------------------------------------------------------
+ *
+ * GogRadarPlot
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+typedef struct {
+ GogPlot base;
+ gboolean default_style_has_markers;
+ unsigned num_elements;
+ double minima, maxima;
+} GogRadarPlot;
+
+#define GOG_RADAR_PLOT_TYPE (gog_radar_plot_get_type ())
+#define GOG_RADAR_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RADAR_PLOT_TYPE, GogRadarPlot))
+#define GOG_IS_PLOT_RADAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RADAR_PLOT_TYPE))
+
+GType gog_radar_plot_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_RADAR_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_radar/gog-radar.c
@@ -0,0 +1,490 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-radar.c
+ *
+ * Copyright (C) 2004 Michael Devine (mdevine at cs.stanford.edu)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-radar.h"
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-marker.h>
+#include <goffice/utils/go-math.h>
+
+#include <module-plugin-defs.h>
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+typedef struct {
+ GogPlotClass base;
+} GogRadarPlotClass;
+
+enum {
+ PLOT_PROP_0,
+ PLOT_PROP_DEFAULT_STYLE_HAS_MARKERS
+};
+
+GNUMERIC_MODULE_PLUGIN_INFO_DECL;
+
+typedef struct {
+ GogSeries base;
+} GogRadarSeries;
+typedef GogSeriesClass GogRadarSeriesClass;
+
+#define GOG_RADAR_SERIES_TYPE (gog_radar_series_get_type ())
+#define GOG_RADAR_SERIES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RADAR_SERIES_TYPE, GogRadarSeries))
+#define GOG_IS_RADAR_SERIES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RADAR_SERIES_TYPE))
+
+static GType gog_radar_series_get_type (void);
+static GType gog_radar_view_get_type (void);
+
+/*-----------------------------------------------------------------------------
+ *
+ * GogRadarPlot
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+/*
+ * Accessor for setting GOGRadarPlot member variables.
+ *
+ * \param obj The radar plot as a GObject. Must not be NULL.
+ */
+static void
+gog_radar_plot_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogRadarPlot *radar = GOG_RADAR_PLOT (obj);
+
+ switch (param_id) {
+ case PLOT_PROP_DEFAULT_STYLE_HAS_MARKERS:
+ radar->default_style_has_markers = g_value_get_boolean (value);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+ gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
+}
+
+/*
+ * Accessor for getting GOGRadarPlot member variables.
+ */
+static void
+gog_radar_plot_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogRadarPlot *radar = GOG_RADAR_PLOT (obj);
+
+ switch (param_id) {
+ case PLOT_PROP_DEFAULT_STYLE_HAS_MARKERS:
+ g_value_set_boolean (value, radar->default_style_has_markers);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static char const *
+gog_radar_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ /* xgettext : the base for how to name radar plot objects
+ * eg The 2nd radar plot in a chart will be called
+ * PlotRadar2 */
+ return N_("PlotRadar");
+}
+
+static void
+gog_radar_plot_update (GogObject *obj)
+{
+ GogRadarPlot * model = GOG_RADAR_PLOT(obj);
+ GogRadarSeries const *series;
+ unsigned num_elements = 0;
+ double val_min, val_max, tmp_min, tmp_max;
+ GSList *ptr;
+
+ val_min = DBL_MAX;
+ val_max = -DBL_MAX;
+ for (ptr = model->base.series; ptr != NULL; ptr = ptr->next) {
+ series = ptr->data;
+ if (!gog_series_is_valid (GOG_SERIES (series)))
+ continue;
+
+ if (num_elements < series->base.num_elements)
+ num_elements = series->base.num_elements;
+ go_data_vector_get_minmax (GO_DATA_VECTOR (
+ series->base.values[1].data), &tmp_min, &tmp_max);
+ if (val_min > tmp_min) val_min = tmp_min;
+ if (val_max < tmp_max) val_max = tmp_max;
+ }
+
+ model->num_elements = num_elements;
+
+ if (model->minima != val_min || model->maxima != val_max) {
+ model->minima = val_min;
+ model->maxima = val_max;
+ gog_axis_bound_changed (model->base.axis [GOG_AXIS_RADIAL], GOG_OBJECT (model));
+ }
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static GogAxisSet
+gog_radar_plot_axis_set_pref (GogPlot const *plot)
+{
+ return GOG_AXIS_SET_RADAR;
+}
+
+static gboolean
+gog_radar_plot_axis_set_is_valid (GogPlot const *plot, GogAxisSet type)
+{
+ return type == GOG_AXIS_SET_RADAR;
+}
+
+static gboolean
+gog_radar_plot_axis_set_assign (GogPlot *plot, GogAxisSet type)
+{
+ return type == GOG_AXIS_SET_RADAR;
+}
+
+static GOData *
+gog_radar_plot_axis_get_bounds (GogPlot *plot, GogAxisType axis,
+ GogPlotBoundInfo * bounds)
+{
+ GSList *ptr;
+ GogRadarPlot *radar = GOG_RADAR_PLOT (plot);
+
+ switch (axis) {
+ case GOG_AXIS_CIRCULAR:
+ bounds->val.minima = 0.;
+ bounds->val.maxima = radar->num_elements;
+ bounds->logical.minima = 0.;
+ bounds->logical.maxima = go_nan;
+ bounds->is_discrete = TRUE;
+
+ for (ptr = plot->series; ptr != NULL ; ptr = ptr->next)
+ if (gog_series_is_valid (GOG_SERIES (ptr->data)))
+ return GOG_SERIES (ptr->data)->values[0].data;
+ break;
+ case GOG_AXIS_RADIAL:
+ /* clip at the outer bound, but allow inner to round nicely */
+ bounds->val.minima = radar->minima;
+ bounds->val.maxima = bounds->logical.maxima = radar->maxima;
+ bounds->is_discrete = FALSE;
+ break;
+ default:
+ g_warning("gog_radar_plot_axis_bounds: bad axis");
+ break;
+ }
+
+ return NULL;
+}
+
+static void
+gog_radar_plot_class_init (GogPlotClass *gog_plot_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) gog_plot_klass;
+ GogObjectClass *gog_object_klass = (GogObjectClass *) gog_plot_klass;
+
+ /* Override methods of GObject */
+ gobject_klass->set_property = gog_radar_plot_set_property;
+ gobject_klass->get_property = gog_radar_plot_get_property;
+
+ /* Fill in GOGObject superclass values */
+ gog_object_klass->update = gog_radar_plot_update;
+ gog_object_klass->type_name = gog_radar_plot_type_name;
+ gog_object_klass->view_type = gog_radar_view_get_type ();
+
+ g_object_class_install_property (gobject_klass,
+ PLOT_PROP_DEFAULT_STYLE_HAS_MARKERS,
+ g_param_spec_boolean ("default-style-has-markers", NULL,
+ "Should the default style of a series include markers",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ {
+ static GogSeriesDimDesc dimensions[] = {
+ { N_("Labels"), GOG_SERIES_SUGGESTED, TRUE,
+ GOG_DIM_LABEL, GOG_MS_DIM_CATEGORIES },
+ { N_("Values"), GOG_SERIES_REQUIRED, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_VALUES }
+ };
+ gog_plot_klass->desc.series.dim = dimensions;
+ gog_plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
+ gog_plot_klass->desc.series.style_fields = (GOG_STYLE_LINE
+ | GOG_STYLE_MARKER);
+ }
+
+ /* Fill in GogPlotClass methods */
+ gog_plot_klass->desc.num_series_min = 1;
+ gog_plot_klass->desc.num_series_max = G_MAXINT;
+ gog_plot_klass->series_type = gog_radar_series_get_type();
+ gog_plot_klass->axis_set_pref = gog_radar_plot_axis_set_pref;
+ gog_plot_klass->axis_set_is_valid = gog_radar_plot_axis_set_is_valid;
+ gog_plot_klass->axis_set_assign = gog_radar_plot_axis_set_assign;
+ gog_plot_klass->axis_get_bounds = gog_radar_plot_axis_get_bounds;
+}
+
+static void
+gog_radar_plot_init (GogRadarPlot *radar)
+{
+ radar->base.vary_style_by_element = FALSE;
+ radar->default_style_has_markers = FALSE;
+ radar->num_elements = 0;
+}
+
+GSF_CLASS (GogRadarPlot, gog_radar_plot,
+ gog_radar_plot_class_init, gog_radar_plot_init,
+ GOG_PLOT_TYPE)
+
+/*****************************************************************************/
+
+#define GOG_RADAR_AREA_PLOT_TYPE (gog_radar_area_plot_get_type ())
+#define GOG_RADAR_AREA_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RADAR_AREA_PLOT_TYPE, GogRadarAreaPlot))
+#define GOG_IS_PLOT_RADAR_AREA(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RADAR_AREA_PLOT_TYPE))
+
+typedef GogRadarPlot GogRadarAreaPlot;
+typedef GogRadarPlotClass GogRadarAreaPlotClass;
+
+GType gog_radar_area_plot_get_type (void);
+
+static char const *
+gog_radar_area_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ /* xgettext : the base for how to name bar/col plot objects
+ * eg The 2nd line plot in a chart will be called
+ * PlotRadarArea2
+ */
+ return N_("PlotRadarArea");
+}
+static void
+gog_radar_area_plot_class_init (GogObjectClass *gog_klass)
+{
+ GogPlotClass *plot_klass = (GogPlotClass *) gog_klass;
+
+ plot_klass->desc.series.style_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL;
+ plot_klass->series_type = gog_radar_series_get_type();
+
+ gog_klass->type_name = gog_radar_area_plot_type_name;
+}
+GSF_CLASS (GogRadarAreaPlot, gog_radar_area_plot,
+ gog_radar_area_plot_class_init, NULL,
+ GOG_RADAR_PLOT_TYPE)
+
+/*****************************************************************************/
+
+typedef GogPlotView GogRadarView;
+typedef GogPlotViewClass GogRadarViewClass;
+
+static double
+fmin (double a, double b)
+{
+ return (a < b) ? a : b;
+}
+
+static void
+gog_radar_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogRadarPlot const *model = GOG_RADAR_PLOT (view->model);
+ unsigned center_x, center_y;
+ GSList *ptr;
+ ArtVpath *path;
+ gboolean const is_area = GOG_IS_PLOT_RADAR_AREA (model);
+ GogAxisMap *map;
+
+ map = gog_axis_map_new (GOG_PLOT (model)->axis[GOG_AXIS_RADIAL],
+ 0.,
+ fmin (view->allocation.h, view->allocation.w) / 2.0);
+
+ if (!gog_axis_map_is_valid (map)) {
+ gog_axis_map_free (map);
+ return;
+ }
+
+ /* center things */
+ center_x = view->allocation.x + view->allocation.w/2.0;
+ center_y = view->allocation.y + view->allocation.h/2.0;
+
+ path = g_alloca ((model->num_elements + 2) * sizeof (ArtVpath));
+ for (ptr = model->base.series; ptr != NULL; ptr = ptr->next) {
+
+ GogRadarSeries *series = GOG_RADAR_SERIES (ptr->data);
+ GogStyle *style;
+ gboolean closed_shape;
+ unsigned count;
+ double *vals;
+
+ if (!gog_series_is_valid (GOG_SERIES (series)))
+ continue;
+
+ style = GOG_STYLED_OBJECT (series)->style;
+
+ gog_renderer_push_style (view->renderer, style);
+
+ closed_shape = (series->base.num_elements == model->num_elements);
+ vals = go_data_vector_get_values (GO_DATA_VECTOR (series->base.values[1].data));
+ for (count = 0; count < series->base.num_elements; count++) {
+ double rho, theta, x, y;
+
+ if (!go_finite (vals [count])) {
+ closed_shape = FALSE;
+ continue;
+ }
+
+ theta = count * 2.0 * M_PI / model->num_elements;
+ rho = gog_axis_map_to_canvas (map, vals[count]);
+
+ x = center_x + rho * sin (theta);
+ y = center_y - rho * cos (theta);
+
+ path[count].code = ((count != 0 && !isnan (vals[count-1]))
+ ? ART_LINETO : ART_MOVETO);
+ path[count].x = x;
+ path[count].y = y;
+
+ gog_renderer_draw_marker(view->renderer, x, y);
+ }
+
+ if (series->base.num_elements == model->num_elements
+ && go_finite(vals[count-1])) {
+ path[count].code = ART_LINETO;
+ path[count].x = path[0].x;
+ path[count].y = path[0].y;
+ count++;
+ }
+ path[count].code = ART_END;
+
+ if (closed_shape && is_area)
+ gog_renderer_draw_polygon (view->renderer, path, FALSE, bbox);
+ else
+ gog_renderer_draw_path (view->renderer, path, bbox);
+
+ gog_renderer_pop_style (view->renderer);
+ }
+
+ gog_axis_map_free (map);
+}
+
+static gboolean
+gog_radar_view_info_at_point (GogView *view, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name)
+{
+ double radius = fmin (view->allocation.h, view->allocation.w)/2.0;
+
+ x -= view->allocation.x + view->allocation.w/2.;
+ y -= view->allocation.y + view->allocation.h/2.;
+ if ((x*x + y*y) > (radius*radius))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+gog_radar_view_class_init (GogViewClass *view_klass)
+{
+ view_klass->render = gog_radar_view_render;
+ view_klass->info_at_point = gog_radar_view_info_at_point;
+ view_klass->clip = TRUE;
+}
+
+static GSF_CLASS (GogRadarView, gog_radar_view,
+ gog_radar_view_class_init, NULL,
+ GOG_PLOT_VIEW_TYPE)
+
+
+/*****************************************************************************/
+
+static GogStyledObjectClass *series_parent_klass;
+
+static void
+gog_radar_series_update (GogObject *obj)
+{
+ GogRadarSeries *series = GOG_RADAR_SERIES (obj);
+ unsigned old_num = series->base.num_elements;
+ double *vals;
+ unsigned len = 0;
+
+ if (series->base.values[1].data != NULL) {
+ vals = go_data_vector_get_values (GO_DATA_VECTOR (series->base.values[1].data));
+ len = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ }
+ series->base.num_elements = len;
+
+ /* queue plot and axis for redraw */
+ gog_object_request_update (GOG_OBJECT (series->base.plot));
+ if (old_num != len)
+ gog_object_request_update (GOG_OBJECT (series->base.plot->axis[GOG_AXIS_CIRCULAR]));
+
+ if (old_num != series->base.num_elements)
+ gog_plot_request_cardinality_update (series->base.plot);
+
+ if (((GogObjectClass *)series_parent_klass)->update)
+ ((GogObjectClass *)series_parent_klass)->update(obj);
+}
+
+static void
+gog_radar_series_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ GogSeries *series = GOG_SERIES (gso);
+ GogRadarPlot const *plot;
+
+ series_parent_klass->init_style (gso, style);
+ if (series->plot == NULL)
+ return;
+
+ plot = GOG_RADAR_PLOT (series->plot);
+ if (!plot->default_style_has_markers) {
+ style->disable_theming |= GOG_STYLE_MARKER;
+ if (style->marker.auto_shape) {
+ GOMarker *m = go_marker_new ();
+ go_marker_set_shape (m, GO_MARKER_NONE);
+ gog_style_set_marker (style, m);
+ }
+ }
+}
+
+static void
+gog_radar_series_class_init (GogStyledObjectClass *gso_klass)
+{
+ GogObjectClass * obj_klass = (GogObjectClass *) gso_klass;
+
+ series_parent_klass = g_type_class_peek_parent (gso_klass);
+ gso_klass->init_style = gog_radar_series_init_style;
+ obj_klass->update = gog_radar_series_update;
+}
+
+GSF_CLASS (GogRadarSeries, gog_radar_series,
+ gog_radar_series_class_init, NULL,
+ GOG_SERIES_TYPE)
+
+void
+plugin_init (void)
+{
+ gog_radar_plot_get_type ();
+ gog_radar_area_plot_get_type ();
+}
+
+void
+plugin_cleanup (void)
+{
+}
--- /dev/null
+++ lib/goffice/graph/plugins/plot_surface/gog-contour-prefs.glade
@@ -0,0 +1,73 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window1">
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+
+ <child>
+ <widget class="GtkTable" id="gog_contour_prefs">
+ <property name="border_width">5</property>
+ <property name="visible">True</property>
+ <property name="n_rows">1</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">5</property>
+ <property name="column_spacing">5</property>
+ <child>
+ <widget class="GtkLabel" id="levels_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Slices number:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">5</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">levels</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="levels">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">6 1 256 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_surface/plugin.xml.in
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin id="GOffice_plot_surface">
+ <information>
+ <_name>Surface Charts</_name>
+ <_description>Surface charts</_description>
+ </information>
+ <loader type="Gnumeric_Builtin:module">
+ <attribute name="module_file" value="surface.la"/>
+ </loader>
+ <services>
+ <service type="plot_engine" id="GogContourPlot">
+ <information>
+ <_description>Contour plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_type" id="surface">
+ <file>plot-types.xml</file>
+ <information>
+ <_description>Default surface plot types</_description>
+ </information>
+ </service>
+ </services>
+</plugin>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_surface/plot-types.xml.in
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Types xmlns:graph="http://www.gnumeric.org/graph_v2.dtd">
+ <Family _name="Surface" sample_image_file="surface.xpm"/>
+
+ <Type _name="Contour" row="1" col="1"
+ engine="GogContourPlot" family="Surface"
+ _description="Contour plot."
+ sample_image_file="chart_area_1_3.png">
+ </Type>
+</Types>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_surface/gog-contour-prefs.c
@@ -0,0 +1,67 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-bubble-prefs.c
+ *
+ * Copyright (C) 2004 Jean Brefort (jean.brefort at normalesup.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-surface.h"
+//#include <src/plugin.h>
+//#include <src/gui-util.h>
+#include <plugin.h>
+#include <gui-util.h>
+
+#include <glade/glade-xml.h>
+#include <gtk/gtkspinbutton.h>
+
+#include <string.h>
+
+GtkWidget *gog_contour_plot_pref (GogContourPlot *plot, GnmCmdContext *cc);
+
+static void
+cb_levels_changed (GtkSpinButton *btn, GObject *plot)
+{
+ g_object_set (plot, "levels", gtk_spin_button_get_value_as_int (btn), NULL);
+}
+
+GtkWidget *
+gog_contour_plot_pref (GogContourPlot *plot, GnmCmdContext *cc)
+{
+ GtkWidget *w;
+ char const *dir = gnm_plugin_get_dir_name (
+ plugins_get_plugin_by_id ("GOffice_plot_surface"));
+ char *path = g_build_filename (dir, "gog-contour-prefs.glade", NULL);
+ GladeXML *gui = gnm_glade_xml_new (cc, path, "gog_contour_prefs", NULL);
+
+ g_free (path);
+ if (gui == NULL)
+ return NULL;
+
+
+ w = glade_xml_get_widget (gui, "levels");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), plot->levels);
+ g_signal_connect (G_OBJECT (w),
+ "value_changed",
+ G_CALLBACK (cb_levels_changed), plot);
+
+ w = glade_xml_get_widget (gui, "gog_contour_prefs");
+ g_object_set_data_full (G_OBJECT (w),
+ "state", gui, (GDestroyNotify)g_object_unref);
+
+ return w;
+}
--- /dev/null
+++ lib/goffice/graph/plugins/plot_surface/Makefile.am
@@ -0,0 +1,27 @@
+goffice_graph_surfacedir = $(gnumeric_plugindir)/plot_surface
+xmldir = $(goffice_graph_surfacedir)
+gladedir = $(goffice_graph_surfacedir)
+
+goffice_graph_surface_LTLIBRARIES = surface.la
+surface_la_LDFLAGS = -module $(GOFFICE_PLUGIN_FLAGS)
+surface_la_SOURCES = \
+ gog-surface.c \
+ gog-surface.h \
+ gog-contour-prefs.c
+
+xml_in_files = plugin.xml.in plot-types.xml.in
+xml_DATA = $(xml_in_files:.xml.in=.xml)
+
+ at INTLTOOL_XML_RULE@
+
+glade_DATA = gog-contour-prefs.glade
+
+# do not use the intl-tool stuff to merge the text back
+# its simpler to just use gettext directly
+plot-types.xml : plot-types.xml.in
+ cp $< $@
+
+EXTRA_DIST = $(xml_in_files) $(glade_DATA)
+DISTCLEANFILES = $(xml_in_files:.xml.in=.xml)
+
+include $(srcdir)/../../../goffice-plugins.mk
--- /dev/null
+++ lib/goffice/graph/plugins/plot_surface/gog-surface.c
@@ -0,0 +1,1323 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-surface.c
+ *
+ * Copyright (C) 2004 Jean Brefort (jean.brefort at normalesup.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/utils/go-format.h>
+#include <goffice/utils/go-math.h>
+#include <goffice/utils/go-color.h>
+#include "gog-surface.h"
+
+#include <module-plugin-defs.h>
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+#include <locale.h>
+#include <string.h>
+
+typedef struct {
+ GogPlotClass base;
+} GogContourPlotClass;
+
+GNUMERIC_MODULE_PLUGIN_INFO_DECL;
+
+static GogObjectClass *plot_contour_parent_klass;
+
+typedef struct {
+ GogSeries base;
+
+ unsigned rows, columns;
+} GogSurfaceSeries;
+typedef GogSeriesClass GogSurfaceSeriesClass;
+
+#define GOG_SURFACE_SERIES_TYPE (gog_surface_series_get_type ())
+#define GOG_SURFACE_SERIES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_SURFACE_SERIES_TYPE, GogSurfaceSeries))
+#define GOG_IS_SURFACE_SERIES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_SURFACE_SERIES_TYPE))
+
+static GType gog_surface_series_get_type (void);
+static GType gog_contour_view_get_type (void);
+
+static gboolean
+vary_uniformly (GODataVector *vec)
+{
+ int len = go_data_vector_get_len (vec), i = 0;
+ double x, x0;
+ if (len < 2)
+ return TRUE;
+ x0 = go_data_vector_get_value (vec, 0);
+ x = go_data_vector_get_value (vec, 1);
+ if (!go_finite( x0) || !go_finite (x))
+ return FALSE;
+ if (x > x0) {
+ for (i = 2; i < len; i++) {
+ x0 = go_data_vector_get_value (vec, i);
+ if (!go_finite (x0) || (x0 <= x))
+ return FALSE;
+ x = x0;
+ }
+ } else if (x < x0) {
+ for (i = 2; i < len; i++) {
+ x0 = go_data_vector_get_value (vec, i);
+ if (!go_finite (x0) || (x0 >= x))
+ return FALSE;
+ x = x0;
+ }
+ }
+ return TRUE;
+}
+
+/*-----------------------------------------------------------------------------
+ *
+ * GogContourPlot
+ *
+ *-----------------------------------------------------------------------------
+ */
+static char const *
+gog_contour_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ /* xgettext : the base for how to name contour plot objects
+ */
+ return N_("PlotContour");
+}
+
+extern gpointer gog_contour_plot_pref (GogContourPlot *plot, GnmCmdContext *cc);
+static gpointer
+gog_contour_plot_editor (GogObject *item,
+ G_GNUC_UNUSED GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ return gog_contour_plot_pref (GOG_CONTOUR_PLOT (item), cc);
+}
+static void
+gog_contour_plot_clear_formats (GogContourPlot *plot)
+{
+ if (plot->x.fmt != NULL) {
+ go_format_unref (plot->x.fmt);
+ plot->x.fmt = NULL;
+ }
+ if (plot->y.fmt != NULL) {
+ go_format_unref (plot->y.fmt);
+ plot->y.fmt = NULL;
+ }
+}
+
+static void
+gog_contour_plot_update (GogObject *obj)
+{
+ unsigned i;
+ GogContourPlot * model = GOG_CONTOUR_PLOT(obj);
+ GogSurfaceSeries * series = GOG_SURFACE_SERIES (model->base.series->data);
+ GODataVector *vec;
+ double tmp_min, tmp_max;
+
+ if (model->x.fmt == NULL)
+ model->x.fmt = go_data_preferred_fmt (series->base.values[0].data);
+ if (model->y.fmt == NULL)
+ model->y.fmt = go_data_preferred_fmt (series->base.values[1].data);
+
+ if (series->base.num_elements != model->levels) {
+ series->base.num_elements = model->levels;
+ gog_plot_request_cardinality_update (&(model->base));
+ }
+
+ vec = GO_DATA_VECTOR (series->base.values[0].data);
+ if (vary_uniformly (vec))
+ go_data_vector_get_minmax (vec, &tmp_min, &tmp_max);
+ else
+ tmp_min = tmp_max = go_nan;
+ if ((model->columns != series->columns)
+ || (tmp_min != model->x.minima)
+ || (tmp_max != model->x.maxima)) {
+ model->columns = series->columns;
+ model->x.minima = tmp_min;
+ model->x.maxima = tmp_max;
+ gog_axis_bound_changed (model->base.axis[0], GOG_OBJECT (model));
+ }
+
+ vec = GO_DATA_VECTOR (series->base.values[1].data);
+ if (vary_uniformly (vec))
+ go_data_vector_get_minmax (vec, &tmp_min, &tmp_max);
+ else
+ tmp_min = tmp_max = go_nan;
+ if ((model->rows != series->rows)
+ || (tmp_min != model->y.minima)
+ || (tmp_max != model->y.maxima)) {
+ model->rows = series->rows;
+ model->y.minima = tmp_min;
+ model->y.maxima = tmp_max;
+ gog_axis_bound_changed (model->base.axis[1], GOG_OBJECT (model));
+ }
+ model->step = (model->zmax - model->zmin) / ((model->levels > 0)? model->levels: 1);
+ if (isnan (model->step) || model->step == 0.)
+ model->step = 1.;
+ for (i = 0; i <= model->levels; i++)
+ model->limits[i] = model->zmin + i * model->step;
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+ if (plot_contour_parent_klass->update)
+ plot_contour_parent_klass->update (obj);
+}
+
+static GogAxisSet
+gog_contour_plot_axis_set_pref (GogPlot const *plot)
+{
+ return GOG_AXIS_SET_XY;
+}
+
+static gboolean
+gog_contour_plot_axis_set_is_valid (GogPlot const *plot, GogAxisSet type)
+{
+ return type == GOG_AXIS_SET_XY;
+}
+
+static gboolean
+gog_contour_plot_axis_set_assign (GogPlot *plot, GogAxisSet type)
+{
+ return type == GOG_AXIS_SET_XY;
+}
+
+static GOData *
+gog_contour_plot_axis_get_bounds (GogPlot *plot, GogAxisType axis,
+ GogPlotBoundInfo * bounds)
+{
+ GogSurfaceSeries *series = GOG_SURFACE_SERIES (plot->series->data);
+ GogContourPlot *contour = GOG_CONTOUR_PLOT (plot);
+ GODataVector *vec;
+ double min, max;
+ GOFormat *fmt;
+ if (axis == GOG_AXIS_X) {
+ vec = GO_DATA_VECTOR (series->base.values[0].data);
+ fmt = contour->x.fmt;
+ min = contour->x.minima;
+ max = contour->x.maxima;
+ } else {
+ vec = GO_DATA_VECTOR (series->base.values[1].data);
+ fmt = contour->y.fmt;
+ min = contour->y.minima;
+ max = contour->y.maxima;
+ }
+ if (bounds->fmt == NULL && fmt != NULL)
+ bounds->fmt = go_format_ref (fmt);
+ if (go_finite (min)) {
+ bounds->logical.minima = bounds->val.minima = min;
+ bounds->logical.maxima = bounds->val.maxima = max;
+ bounds->is_discrete = FALSE;
+ } else {
+ bounds->val.minima = 0.;
+ bounds->logical.minima = 0.;
+ bounds->logical.maxima = go_nan;
+ bounds->is_discrete = TRUE;
+ bounds->center_on_ticks = TRUE;
+ bounds->val.maxima = (axis == GOG_AXIS_X)? series->columns: series->rows;
+ }
+ return (GOData*) vec;
+}
+
+static void
+gog_contour_plot_foreach_elem (GogPlot *plot, gboolean only_visible,
+ GogEnumFunc func, gpointer data)
+{
+ unsigned i;
+ char *label;
+ static char separator = 0;
+ GogStyle *style = gog_style_new ();
+ GogTheme *theme = gog_object_get_theme (GOG_OBJECT (plot));
+ GogContourPlot *contour = GOG_CONTOUR_PLOT (plot);
+ GOColor *color;
+
+ if (separator == 0) {
+ struct lconv *lc = localeconv ();
+ separator = (strcmp (lc->decimal_point, ","))? ',': ';';
+ }
+ /* build the colors table */
+ color = g_new0 (GOColor, (contour->levels > 0)? contour->levels: 1);
+ if (contour->levels < 2)
+ color[0] = RGBA_WHITE;
+ else for (i = 0; i < contour->levels; i++) {
+ gog_theme_fillin_style (theme, style, GOG_OBJECT (plot->series->data), i, FALSE);
+ color[i] = style->fill.pattern.back;
+ }
+ g_object_unref (style);
+
+ style = gog_style_new ();/*dup (GOG_STYLED_OBJECT (plot->series->data)->style);*/
+ style->interesting_fields = GOG_STYLE_FILL;
+ style->disable_theming = GOG_STYLE_ALL;
+ style->fill.type = GOG_FILL_STYLE_PATTERN;
+ style->fill.pattern.pattern = GO_PATTERN_SOLID;
+
+ for (i = 0; i < contour->levels; i++) {
+ style->fill.pattern.back = color[i];
+ label = g_strdup_printf ("[%g%c %g%c", contour->limits[i], separator,
+ contour->limits[i + 1], (i == contour->levels - 1)? ']':'[');
+ (func) (i, style, label, data);
+ g_free (label);
+ }
+ g_object_unref (style);
+ g_free (color);
+}
+
+static void
+gog_contour_plot_finalize (GObject *obj)
+{
+ GogContourPlot *plot = GOG_CONTOUR_PLOT (obj);
+ gog_contour_plot_clear_formats (plot);
+ if (plot->limits != NULL)
+ g_free (plot->limits);
+ G_OBJECT_CLASS (plot_contour_parent_klass)->finalize (obj);
+}
+
+enum {
+ GOG_CONTOUR_PROP_0,
+ GOG_CONTOUR_PROP_LEVELS
+};
+
+static void
+gog_contour_plot_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogContourPlot *plot = GOG_CONTOUR_PLOT (obj);
+
+ switch (param_id) {
+ case GOG_CONTOUR_PROP_LEVELS : {
+ unsigned levels = g_value_get_uint (value);
+ if (plot->levels != levels) {
+ unsigned i;
+ g_free (plot->limits);
+ plot->limits = g_new (double, levels + 1);
+ plot->levels = levels;
+ plot->step = (plot->zmax - plot->zmin) / levels;
+ if (isnan (plot->step) || plot->step == 0.)
+ plot->step = 1.;
+ for (i = 0; i < plot->levels; i++)
+ plot->limits[i] = plot->zmin + i * plot->step;
+ gog_plot_request_cardinality_update (GOG_PLOT (plot));
+ }
+ break;
+ }
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_contour_plot_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogContourPlot *plot = GOG_CONTOUR_PLOT (obj);
+
+ switch (param_id) {
+ case GOG_CONTOUR_PROP_LEVELS :
+ g_value_set_uint (value, plot->levels);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_contour_plot_class_init (GogPlotClass *gog_plot_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) gog_plot_klass;
+ GogObjectClass *gog_object_klass = (GogObjectClass *) gog_plot_klass;
+
+ plot_contour_parent_klass = g_type_class_peek_parent (gog_plot_klass);
+
+ gobject_klass->set_property = gog_contour_plot_set_property;
+ gobject_klass->get_property = gog_contour_plot_get_property;
+ gobject_klass->finalize = gog_contour_plot_finalize;
+
+ /* Fill in GOGObject superclass values */
+ gog_object_klass->update = gog_contour_plot_update;
+ gog_object_klass->type_name = gog_contour_plot_type_name;
+ gog_object_klass->view_type = gog_contour_view_get_type ();
+ gog_object_klass->editor = gog_contour_plot_editor;
+
+ g_object_class_install_property (gobject_klass, GOG_CONTOUR_PROP_LEVELS,
+ g_param_spec_uint ("levels", "levels",
+ "Number of slices.",
+ 1, 256, 6, /* max as 256 is not more stupid than another value */
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ {
+ static GogSeriesDimDesc dimensions[] = {
+ { N_("X"), GOG_SERIES_SUGGESTED, FALSE,
+ GOG_DIM_LABEL, GOG_MS_DIM_CATEGORIES },
+ { N_("Y"), GOG_SERIES_SUGGESTED, FALSE,
+ GOG_DIM_LABEL, GOG_MS_DIM_CATEGORIES },
+ { N_("Z"), GOG_SERIES_REQUIRED, FALSE,
+ GOG_DIM_MATRIX, GOG_MS_DIM_VALUES },
+ };
+ gog_plot_klass->desc.series.dim = dimensions;
+ gog_plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
+ gog_plot_klass->desc.series.style_fields = GOG_STYLE_LINE;
+ }
+
+ /* Fill in GogPlotClass methods */
+ gog_plot_klass->desc.num_series_min = 1;
+ gog_plot_klass->desc.num_series_max = G_MAXINT;
+ gog_plot_klass->series_type = gog_surface_series_get_type();
+ gog_plot_klass->axis_set_pref = gog_contour_plot_axis_set_pref;
+ gog_plot_klass->axis_set_is_valid = gog_contour_plot_axis_set_is_valid;
+ gog_plot_klass->axis_set_assign = gog_contour_plot_axis_set_assign;
+ gog_plot_klass->axis_get_bounds = gog_contour_plot_axis_get_bounds;
+ gog_plot_klass->foreach_elem = gog_contour_plot_foreach_elem;
+}
+
+static void
+gog_contour_plot_init (GogContourPlot *contour)
+{
+ contour->levels = 6;
+ contour->rows = contour->columns = 0;
+ contour->limits = g_new (double, 7);
+ contour->base.vary_style_by_element = TRUE;
+ contour->x.minima = contour->x.maxima = contour->y.minima = contour->y.maxima = go_nan;
+ contour->x.fmt = contour->y.fmt = NULL;
+}
+
+GSF_CLASS (GogContourPlot, gog_contour_plot,
+ gog_contour_plot_class_init, gog_contour_plot_init,
+ GOG_PLOT_TYPE)
+
+/*****************************************************************************/
+
+typedef GogPlotView GogContourView;
+typedef GogPlotViewClass GogContourViewClass;
+
+static void
+gog_contour_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogContourPlot const *plot = GOG_CONTOUR_PLOT (view->model);
+ GogSurfaceSeries const *series = GOG_SURFACE_SERIES (plot->base.series->data);
+ GODataMatrix *mat = GO_DATA_MATRIX (series->base.values[2].data);
+ GODataVector *x_vec = 0, *y_vec = 0;
+ GogAxisMap *x_map, *y_map;
+ double zval0, zval1, zval2, zval3, t;
+ double x[4], y[4], zval[4];
+ unsigned z[4];
+ unsigned z0 = 0, z1 = 0, z2 = 0, z3 = 0, zmin, zmax, nans, nan = 0;
+ unsigned i, j, k, kmax, l, lmax, p, r = 0, s, h;
+ GogRenderer *rend = view->renderer;
+ GogStyle *style;
+ GogTheme *theme = gog_object_get_theme (GOG_OBJECT (plot));
+ double x0, x1, y0, y1;
+ ArtVpath *path, *lines;
+ GOColor *color;
+ gboolean cw;
+
+ x_map = gog_axis_map_new (plot->base.axis[0],
+ view->residual.x , view->residual.w);
+ y_map = gog_axis_map_new (plot->base.axis[1],
+ view->residual.y + view->residual.h,
+ -view->residual.h);
+
+ if (!(gog_axis_map_is_valid (x_map) &&
+ gog_axis_map_is_valid (y_map))) {
+ gog_axis_map_free (x_map);
+ gog_axis_map_free (y_map);
+ return;
+ }
+
+ /* Set cw to ensure that polygons will allways be drawn clockwise */
+ if (gog_axis_is_discrete (plot->base.axis[0])) {
+ x0 = gog_axis_map_to_canvas(x_map, 0.);
+ x1 = gog_axis_map_to_canvas(x_map, 1.);
+ }else {
+ x_vec = GO_DATA_VECTOR (series->base.values[0].data);
+ x0 = gog_axis_map_to_canvas(x_map, go_data_vector_get_value (x_vec, 0));
+ x1 = gog_axis_map_to_canvas(x_map, go_data_vector_get_value (x_vec, 1));
+ }
+ if (gog_axis_is_discrete (plot->base.axis[1])) {
+ y0 = gog_axis_map_to_canvas(y_map, 0.);
+ y1 = gog_axis_map_to_canvas(y_map, 1.);
+ }else {
+ y_vec = GO_DATA_VECTOR (series->base.values[1].data);
+ y0 = gog_axis_map_to_canvas(y_map, go_data_vector_get_value (y_vec, 0));
+ y1 = gog_axis_map_to_canvas(y_map, go_data_vector_get_value (y_vec, 1));
+ }
+ cw = (x1 > x0) == (y1 > y0);
+
+ style = gog_style_new ();
+ path = art_new (ArtVpath, 10);
+ /* build the colors table */
+ color = g_new0 (GOColor, (plot->levels > 0)? plot->levels: 1);
+ if (plot->levels < 2)
+ color[0] = RGBA_WHITE;
+ else for (i = 0; i < plot->levels; i++) {
+ gog_theme_fillin_style (theme, style, GOG_OBJECT (series), i, FALSE);
+ color[i] = style->fill.pattern.back;
+ }
+ g_object_unref (style);
+
+ /* clip to avoid problems with logarithmic axes */
+ gog_renderer_clip_push (rend, &(view->residual));
+
+ style = gog_style_new ();
+ style->interesting_fields = GOG_STYLE_FILL;
+ style->disable_theming = GOG_STYLE_ALL;
+ style->fill.type = GOG_FILL_STYLE_PATTERN;
+ style->fill.pattern.pattern = GO_PATTERN_SOLID;
+
+ lines = art_new (ArtVpath, lmax = 64);
+ l = 0;
+
+ for (j = 1; j < series->columns; j++) {
+ if (gog_axis_is_discrete (plot->base.axis[0])) {
+ x0 = gog_axis_map_to_canvas(x_map, j - 1);
+ x1 = gog_axis_map_to_canvas(x_map, j);
+ }else {
+ x0 = gog_axis_map_to_canvas(x_map, go_data_vector_get_value (x_vec, j - 1));
+ x1 = gog_axis_map_to_canvas(x_map, go_data_vector_get_value (x_vec, j));
+ }
+
+ for (i = 1; i < series->rows; i++) {
+ if (gog_axis_is_discrete (plot->base.axis[1])) {
+ y0 = gog_axis_map_to_canvas(y_map, i - 1);
+ y1 = gog_axis_map_to_canvas(y_map, i);
+ }else {
+ y0 = gog_axis_map_to_canvas(y_map, go_data_vector_get_value (y_vec, i - 1));
+ y1 = gog_axis_map_to_canvas(y_map, go_data_vector_get_value (y_vec, i));
+ }
+ nans = 0;
+ nan = 4;
+ zmin = plot->levels;
+ zmax = 0;
+ zval0 = go_data_matrix_get_value (mat, i - 1, j - 1);
+ if (!isnan (zval0)) {
+ z0 = floor ((zval0 - plot->zmin) / plot->step);
+ if (z0 == plot->levels && z0 > 0)
+ z0--;
+ if (z0 > zmax)
+ zmax = z0;
+ if (z0 < zmin) {
+ zmin = z0;
+ r = 0;
+ }
+ } else {
+ nans++;
+ nan = 0;
+ }
+ zval1 = go_data_matrix_get_value (mat, i - 1, j);
+ if (!isnan (zval1)) {
+ z1 = floor ((zval1 - plot->zmin) / plot->step);
+ if (z1 == plot->levels && z1 > 0)
+ z1--;
+ if (z1 > zmax)
+ zmax = z1;
+ if (z1 < zmin) {
+ zmin = z1;
+ r = 1;
+ }
+ } else {
+ nans++;
+ nan = 1;
+ }
+ zval2 = go_data_matrix_get_value (mat, i, j);
+ if (!isnan (zval2)) {
+ z2 = floor ((zval2 - plot->zmin) / plot->step);
+ if (z2 == plot->levels && z2 > 0)
+ z2--;
+ if (z2 > zmax)
+ zmax = z2;
+ if (z2 < zmin) {
+ zmin = z2;
+ r = 2;
+ }
+ } else {
+ nans++;
+ nan = 2;
+ }
+ zval3 = go_data_matrix_get_value (mat, i, j - 1);
+ if (!isnan (zval3)) {
+ z3 = floor ((zval3 - plot->zmin) / plot->step);
+ if (z3 == plot->levels && z3 > 0)
+ z3--;
+ if (z3 > zmax)
+ zmax = z3;
+ if (z3 < zmin) {
+ zmin = z3;
+ r = 3;
+ }
+ } else {
+ nans++;
+ nan = 3;
+ }
+ if (nans > 1)
+ continue;
+ /* Build the x, y and z arrays for the tile */
+ k = r;
+ s = 0;
+ do {
+ if (k != nan) {
+ switch (k) {
+ case 0:
+ x[s] = x0;
+ y[s] = y0;
+ z[s] = z0;
+ zval[s++] = zval0;
+ break;
+ case 1:
+ x[s] = x1;
+ y[s] = y0;
+ z[s] = z1;
+ zval[s++] = zval1;
+ break;
+ case 2:
+ x[s] = x1;
+ y[s] = y1;
+ z[s] = z2;
+ zval[s++] = zval2;
+ break;
+ default:
+ x[s] = x0;
+ y[s] = y1;
+ z[s] = z3;
+ zval[s++] = zval3;
+ }
+ }
+ if (cw) {
+ k++;
+ k %= 4;
+ } else {
+ if (k == 0)
+ k = 3;
+ else
+ k--;
+ }
+ } while (k != r);
+ if (zmin == zmax) {
+ /* paint everything with one color*/
+ style->fill.pattern.back = color[zmin];
+ gog_renderer_push_style (rend, style);
+ path[0].code = ART_MOVETO;
+ for (k = 0; k < s; ) {
+ path[k].x = x[k];
+ path[k].y = y[k];
+ path[++k].code = ART_LINETO;
+ }
+ path[k].x = x[0];
+ path[k].y = y[0];
+ path[k + 1].code = ART_END;
+ /* narrow parameter is TRUE below to avoid border effects */
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ } else {
+ kmax = 3 - nans;
+ if (!nans && (((z0 < z1) && (z1 > z2) && (z2 < z3) && (z3 > z0)) ||
+ ((z0 > z1) && (z1 < z2) && (z2 > z3) && (z3 < z0)))) {
+ /* we have a saddle point */
+ /* first find the most probable altitude of the saddle point */
+ unsigned zn, zx;
+ gboolean crossing = FALSE, up = FALSE, odd;
+ double xl[8], yl[8];
+ /* crossing is TRUE if the saddle point occurs at a slices border */
+ zn = MAX (z[0], z[2]);
+ if (zval[1] > zval[3])
+ zx = (zval[3] == plot->limits[z[3]])? z[3] - 1: z[3];
+ else
+ zx = (zval[1] == plot->limits[z[1]])? z[1] - 1: z[1];
+ odd = (zx - zn) % 2;
+ if (odd) {
+ if ((zx - zn) == 1) {
+ double sum = 0.;
+ sum += (z[0] == zn)? zval[0]: plot->limits[zn];
+ sum += (z[1] == zx)? zval[1]: plot->limits[zx + 1];
+ sum += (z[2] == zn)? zval[2]: plot->limits[zn];
+ sum += (z[3] == zx)? zval[3]: plot->limits[zx + 1];
+ sum /= 4.;
+ if (fabs ((sum - plot->limits[zx]) / plot->step) < DBL_EPSILON)
+ crossing = TRUE;
+ else
+ up = (sum - plot->limits[zx]) < 0;
+ } else
+ crossing = TRUE;
+ zn = (zn + zx) / 2;
+ zx = zn + 1;
+ } else
+ zn = zx = (zn + zx) / 2;
+ /* low values slices */
+ if (z[0] < zn) {
+ k = z[0];
+ style->fill.pattern.back = color[k];
+ k++;
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_END;
+ path[0].x = path[3].x = x[0];
+ path[0].y = path[3].y = y[0];
+ if ((l + 3) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[0]) / (zval[3] - zval[0]);
+ xl[7] = lines[l].x = path[1].x = x[0] + t * (x[3] - x[0]);
+ yl[7] = lines[l++].y = path[1].y = y[0] + t * (y[3] - y[0]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[0]) / (zval[1] - zval[0]);
+ xl[0] = lines[l].x = path[2].x = x[0] + t * (x[1] - x[0]);
+ yl[0] = lines[l++].y = path[2].y = y[0] + t * (y[1] - y[0]);
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ while (k < zn) {
+ style->fill.pattern.back = color[k];
+ k++;
+ path[0].x = path[4].x = xl[7];
+ path[0].y = path[4].y = yl[7];
+ path[3].x = xl[0];
+ path[3].y = yl[0];
+ if ((l + 3) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[0]) / (zval[3] - zval[0]);
+ xl[7] = lines[l].x = path[1].x = x[0] + t * (x[3] - x[0]);
+ yl[7] = lines[l++].y = path[1].y = y[0] + t * (y[3] - y[0]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[0]) / (zval[1] - zval[0]);
+ xl[0] = lines[l].x = path[2].x = x[0] + t * (x[1] - x[0]);
+ yl[0] = lines[l++].y = path[2].y = y[0] + t * (y[1] - y[0]);
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ }
+ } else
+ xl[0] = xl[7] = -1.;
+ if (z[2] < zn) {
+ k = z[2];
+ style->fill.pattern.back = color[k];
+ k++;
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_END;
+ path[0].x = path[3].x = x[2];
+ path[0].y = path[3].y = y[2];
+ if ((l + 3) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[2]) / (zval[1] - zval[2]);
+ xl[3] = lines[l].x = path[1].x = x[2] + t * (x[1] - x[2]);
+ yl[3] = lines[l++].y = path[1].y = y[2] + t * (y[1] - y[2]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[2]) / (zval[3] - zval[2]);
+ xl[4] = lines[l].x = path[2].x = x[2] + t * (x[3] - x[2]);
+ yl[4] = lines[l++].y = path[2].y = y[2] + t * (y[3] - y[2]);
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ while (k < zn) {
+ style->fill.pattern.back = color[k];
+ k++;
+ path[0].x = path[4].x = xl[3];
+ path[0].y = path[4].y = yl[3];
+ path[3].x = xl[4];
+ path[3].y = yl[4];
+ if ((l + 3) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[2]) / (zval[1] - zval[2]);
+ xl[3] = lines[l].x = path[1].x = x[2] + t * (x[1] - x[2]);
+ yl[3] = lines[l++].y = path[1].y = y[2] + t * (y[1] - y[2]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[2]) / (zval[3] - zval[2]);
+ xl[4] = lines[l].x = path[2].x = x[2] + t * (x[3] - x[2]);
+ yl[4] = lines[l++].y = path[2].y = y[2] + t * (y[3] - y[2]);
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ }
+ } else
+ xl[3] = xl[4] = -1.;
+ /* high values slices */
+ k = z[1];
+ if (zval[1] == plot->limits[k])
+ k--;
+ if (k > zx) {
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_END;
+ path[0].x = path[3].x = x[1];
+ path[0].y = path[3].y = y[1];
+ if ((l + 3) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[1]) / (zval[0] - zval[1]);
+ xl[1] = lines[l].x = path[1].x = x[1] + t * (x[0] - x[1]);
+ yl[1] = lines[l++].y = path[1].y = y[1] + t * (y[0] - y[1]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[1]) / (zval[2] - zval[1]);
+ xl[2] = lines[l].x = path[2].x = x[1] + t * (x[2] - x[1]);
+ yl[2] = lines[l++].y = path[2].y = y[1] + t * (y[2] - y[1]);
+ style->fill.pattern.back = color[k];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ k--;
+ while (k > zx) {
+ path[0].x = path[4].x = xl[1];
+ path[0].y = path[4].y = yl[1];
+ path[3].x = xl[2];
+ path[3].y = yl[2];
+ if ((l + 3) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[1]) / (zval[0] - zval[1]);
+ xl[1] = lines[l].x = path[1].x = x[1] + t * (x[0] - x[1]);
+ yl[1] = lines[l++].y = path[1].y = y[1] + t * (y[0] - y[1]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[1]) / (zval[2] - zval[1]);
+ xl[2] = lines[l].x = path[2].x = x[1] + t * (x[2] - x[1]);
+ yl[2] = lines[l++].y = path[2].y = y[1] + t * (y[2] - y[1]);
+ style->fill.pattern.back = color[k];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ k--;
+ }
+ } else
+ xl[1] = xl[2] = -1.;
+ k = z[3];
+ if (zval[3] == plot->limits[k])
+ k--;
+ if (k > zx) {
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_END;
+ path[0].x = path[3].x = x[3];
+ path[0].y = path[3].y = y[3];
+ if ((l + 3) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[3]) / (zval[2] - zval[3]);
+ xl[5] = lines[l].x = path[1].x = x[3] + t * (x[2] - x[3]);
+ yl[5] = lines[l++].y = path[1].y = y[3] + t * (y[2] - y[3]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[3]) / (zval[0] - zval[3]);
+ xl[6] = lines[l].x = path[2].x = x[3] + t * (x[0] - x[3]);
+ yl[6] = lines[l++].y = path[2].y = y[3] + t * (y[0] - y[3]);
+ style->fill.pattern.back = color[k];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ k--;
+ while (k > zx) {
+ path[0].x = path[4].x = xl[5];
+ path[0].y = path[4].y = yl[5];
+ path[3].x = xl[6];
+ path[3].y = yl[6];
+ if ((l + 3) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[3]) / (zval[2] - zval[3]);
+ xl[5] = lines[l].x = path[1].x = x[3] + t * (x[2] - x[3]);
+ yl[5] = lines[l++].y = path[1].y = y[3] + t * (y[2] - y[3]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[3]) / (zval[0] - zval[3]);
+ xl[6] = lines[l].x = path[2].x = x[3] + t * (x[0] - x[3]);
+ yl[6] = lines[l++].y = path[2].y = y[3] + t * (y[0] - y[3]);
+ style->fill.pattern.back = color[k];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ k--;
+ }
+ } else
+ xl[5] = xl[6] = -1.;
+ /* middle values slices */
+ if (odd) {
+ if (crossing) {
+ double xb[4], yb[4], xc, yc;
+ for (k = 0; k < 4; k++) {
+ s = (k + 1) % 4;
+ t = (plot->limits[zx] - zval[s]) / (zval[k] - zval[s]);
+ xb[k] = x[s] + t * (x[k] - x[s]);
+ yb[k] = y[s] + t * (y[k] - y[s]);
+ }
+ if ((l + 5) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ lines[l].x = xb[0];
+ lines[l++].y = yb[0];
+ lines[l].code = ART_LINETO;
+ lines[l].x = xb[2];
+ lines[l++].y = yb[2];
+ lines[l].code = ART_MOVETO_OPEN;
+ lines[l].x = xb[1];
+ lines[l++].y = yb[1];
+ lines[l].code = ART_LINETO;
+ lines[l].x = xb[3];
+ lines[l++].y = yb[3];
+ /* calculate the coordinates xc and yc of crossing point */
+ t = ((xb[1] - xb[0]) * (yb[3] - yb[1])
+ + (xb[1] - xb[3]) * (yb[1] - yb[0])) /
+ ((xb[2] - xb[0]) * (yb[3] - yb[1])
+ + (xb[1] - xb[3]) * (yb[2] - yb[0]));
+ xc = xb[0] + t * (xb[2] - xb[0]);
+ yc = yb[0] + t * (yb[2] - yb[0]);
+ /* fill */
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_LINETO;
+ if (xl[0] < 0.) {
+ path[4].x = path[0].x = x[0];
+ path[4].y = path[0].y = y[0];
+ path[5].code = ART_END;
+ } else {
+ path[5].x = path[0].x = xl[7];
+ path[5].y = path[0].y = yl[7];
+ path[4].x = xl[0];
+ path[4].y = yl[0];
+ path[5].code = ART_LINETO;
+ path[6].code = ART_END;
+ }
+ path[1].x = xb[3];
+ path[1].y = yb[3];
+ path[2].x = xc;
+ path[2].y = yc;
+ path[3].x = xb[0];
+ path[3].y = yb[0];
+ style->fill.pattern.back = color[zn];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ if (xl[2] < 0.) {
+ path[4].x = path[0].x = x[2];
+ path[4].y = path[0].y = y[2];
+ path[5].code = ART_END;
+ } else {
+ path[5].x = path[0].x = xl[3];
+ path[5].y = path[0].y = yl[3];
+ path[4].x = xl[4];
+ path[4].y = yl[4];
+ path[5].code = ART_LINETO;
+ path[6].code = ART_END;
+ }
+ path[1].x = xb[1];
+ path[1].y = yb[1];
+ path[3].x = xb[2];
+ path[3].y = yb[2];
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ if (xl[2] < 0.) {
+ path[4].x = path[0].x = x[1];
+ path[4].y = path[0].y = y[1];
+ path[5].code = ART_END;
+ } else {
+ path[5].x = path[0].x = xl[1];
+ path[5].y = path[0].y = yl[1];
+ path[4].x = xl[2];
+ path[4].y = yl[2];
+ path[5].code = ART_LINETO;
+ path[6].code = ART_END;
+ }
+ path[1].x = xb[0];
+ path[1].y = yb[0];
+ path[3].x = xb[1];
+ path[3].y = yb[1];
+ style->fill.pattern.back = color[zx];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ if (xl[6] < 0.) {
+ path[4].x = path[0].x = x[3];
+ path[4].y = path[0].y = y[3];
+ path[5].code = ART_END;
+ } else {
+ path[5].x = path[0].x = xl[5];
+ path[5].y = path[0].y = yl[5];
+ path[4].x = xl[6];
+ path[4].y = yl[6];
+ path[5].code = ART_LINETO;
+ path[6].code = ART_END;
+ }
+ path[1].x = xb[2];
+ path[1].y = yb[2];
+ path[3].x = xb[3];
+ path[3].y = yb[3];
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ } else {
+ if (up) {
+ /* saddle point is in the lower slice */
+ /* draw the upper slices */
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ if (xl[1] < 0.) {
+ path[4].code = ART_END;
+ path[0].x = path[3].x = x[1];
+ path[0].y = path[3].y = y[1];
+ } else {
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ path[0].x = path[4].x = xl[1];
+ path[0].y = path[4].y = yl[1];
+ path[3].x = xl[2];
+ path[3].y = yl[2];
+ }
+ if ((l + 5) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[zx] - zval[1]) / (zval[0] - zval[1]);
+ xl[1] = lines[l].x = path[1].x = x[1] + t * (x[0] - x[1]);
+ yl[1] = lines[l++].y = path[1].y = y[1] + t * (y[0] - y[1]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[zx] - zval[1]) / (zval[2] - zval[1]);
+ xl[2] = lines[l].x = path[2].x = x[1] + t * (x[2] - x[1]);
+ yl[2] = lines[l++].y = path[2].y = y[1] + t * (y[2] - y[1]);
+ style->fill.pattern.back = color[zx];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ if (xl[5] < 0.) {
+ path[4].code = ART_END;
+ path[0].x = path[3].x = x[3];
+ path[0].y = path[3].y = y[3];
+ } else {
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ path[0].x = path[4].x = xl[5];
+ path[0].y = path[4].y = yl[5];
+ path[3].x = xl[6];
+ path[3].y = yl[6];
+ }
+ if ((l + 5) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[zx] - zval[3]) / (zval[2] - zval[3]);
+ xl[5] = lines[l].x = path[1].x = x[3] + t * (x[2] - x[3]);
+ yl[5] = lines[l++].y = path[1].y = y[3] + t * (y[2] - y[3]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[zx] - zval[3]) / (zval[0] - zval[3]);
+ xl[6] = lines[l].x = path[2].x = x[3] + t * (x[0] - x[3]);
+ yl[6] = lines[l++].y = path[2].y = y[3] + t * (y[0] - y[3]);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ } else {
+ /* saddle point is in the upper slice */
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ if (xl[0] < 0.) {
+ path[4].code = ART_END;
+ path[0].x = path[3].x = x[0];
+ path[0].y = path[3].y = y[0];
+ } else {
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ path[0].x = path[4].x = xl[7];
+ path[0].y = path[4].y = yl[7];
+ path[3].x = xl[0];
+ path[3].y = yl[0];
+ }
+ if ((l + 5) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[0]) / (zval[3] - zval[0]);
+ xl[7] = lines[l].x = path[1].x = x[0] + t * (x[3] - x[0]);
+ yl[7] = lines[l++].y = path[1].y = y[0] + t * (y[3] - y[0]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[0]) / (zval[1] - zval[0]);
+ xl[0] = lines[l].x = path[2].x = x[0] + t * (x[1] - x[0]);
+ yl[0] = lines[l++].y = path[2].y = y[0] + t * (y[1] - y[0]);
+ style->fill.pattern.back = color[zn];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ if (xl[2] < 0.) {
+ path[4].code = ART_END;
+ path[0].x = path[3].x = x[2];
+ path[0].y = path[3].y = y[2];
+ } else {
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ path[0].x = path[4].x = xl[3];
+ path[0].y = path[4].y = yl[3];
+ path[3].x = xl[4];
+ path[3].y = yl[4];
+ }
+ lines[l].code = ART_MOVETO_OPEN;
+ t = (plot->limits[k] - zval[2]) / (zval[1] - zval[2]);
+ xl[3] = lines[l].x = path[1].x = x[2] + t * (x[1] - x[2]);
+ yl[3] = lines[l++].y = path[1].y = y[2] + t * (y[1] - y[2]);
+ lines[l].code = ART_LINETO;
+ t = (plot->limits[k] - zval[2]) / (zval[3] - zval[2]);
+ xl[4] = lines[l].x = path[2].x = x[2] + t * (x[3] - x[2]);
+ yl[4] = lines[l++].y = path[2].y = y[2] + t * (y[3] - y[2]);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ zn = zx;
+ }
+ /* draw the saddle containing slice */
+ k = 0;
+ for (s = 0; s < 8; s++) {
+ path[k].code = (k)? ART_LINETO: ART_MOVETO;
+ if (xl[s] < 0.) {
+ if (s == 7)
+ break;
+ else if (s > 0)
+ s++;
+ r = s / 2;
+ path[k].x = x[r];
+ path[k++].y = y[r];
+ } else {
+ path[k].x = xl[s];
+ path[k++].y = yl[s];
+ }
+ }
+ path[k].code = ART_LINETO;
+ path[k].x = path[0].x;
+ path[k++].y = path[0].y;
+ path[k].code = ART_END;
+ style->fill.pattern.back = color[zn];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ }
+ } else {
+ k = 0;
+ for (s = 0; s < 8; s++) {
+ path[k].code = (k)? ART_LINETO: ART_MOVETO;
+ if (xl[s] < 0.) {
+ if (s == 7)
+ break;
+ else if (s > 0)
+ s++;
+ r = s / 2;
+ path[k].x = x[r];
+ path[k++].y = y[r];
+ } else {
+ path[k].x = xl[s];
+ path[k++].y = yl[s];
+ }
+ }
+ path[k].code = ART_LINETO;
+ path[k].x = path[0].x;
+ path[k++].y = path[0].y;
+ path[k].code = ART_END;
+ style->fill.pattern.back = color[zx];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ }
+ } else {
+ /* no saddle point visible */
+ if ((l + (zmax - zmin) * 2 + 1) >= lmax)
+ lines = art_renew (lines, ArtVpath, lmax += 64);
+ path[0].code = ART_MOVETO;
+ path[0].x = x[0];
+ path[0].y = y[0];
+ p = 1;
+ k = 1;
+ s = 0;
+ r = kmax;
+ while (zmin < zmax) {
+ style->fill.pattern.back = color[zmin];
+ gog_renderer_push_style (rend, style);
+ while (z[k] <= zmin && k < kmax) {
+ path[p].code = ART_LINETO;
+ path[p].x = x[k];
+ path[p++].y = y[k++];
+ }
+ while (z[r] <= zmin && r > 0)
+ r--;
+
+ zmin++;
+ t = (plot->limits[zmin] - zval[k - 1]) / (zval[k] - zval[k - 1]);
+ path[p].code = ART_LINETO;
+ lines[l].code = ART_MOVETO_OPEN;
+ lines[l].x = path[p].x = x[k - 1] + t * (x[k] - x[k - 1]);
+ lines[l++].y = path[p++].y = y[k - 1] + t * (y[k] - y[k - 1]);
+ path[p].code = ART_LINETO;
+ lines[l].code = ART_LINETO;
+ if (r < kmax) {
+ t = (plot->limits[zmin] - zval[r]) / (zval[r + 1] - zval[r]);
+ lines[l].x = path[p].x = x[r] + t * (x[r + 1] - x[r]);
+ lines[l++].y = path[p++].y = y[r] + t * (y[r + 1] - y[r]);
+ } else {
+ t = (plot->limits[zmin] - zval[r]) / (zval[0] - zval[r]);
+ lines[l].x = path[p].x = x[r] + t * (x[0] - x[r]);
+ lines[l++].y = path[p++].y = y[r] + t * (y[0] - y[r]);
+ }
+ if (s == 0) {
+ for (h = r + 1; h <= kmax; h++) {
+ path[p].code = ART_LINETO;
+ path[p].x = x[h];
+ path[p++].y = y[h];
+ }
+ } else {
+ for (h = r + 1; h < s; h++) {
+ path[p].code = ART_LINETO;
+ path[p].x = x[h];
+ path[p++].y = y[h];
+ }
+ }
+ s = r + 1;
+ path[p].code = ART_LINETO;
+ path[p].x = path[0].x;
+ path[p++].y = path[0].y;
+ path[p].code = ART_END;
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ path[0].x = lines[l - 1].x;
+ path[0].y = lines[l - 1].y;
+ path[1].x = lines[l - 2].x;
+ path[1].y = lines[l - 2].y;
+ p = 2;
+ }
+ while (k < s) {
+ path[p].code = ART_LINETO;
+ path[p].x = x[k];
+ path[p++].y = y[k++];
+ }
+ path[p].code = ART_LINETO;
+ path[p].x = path[0].x;
+ path[p++].y = path[0].y;
+ path[p].code = ART_END;
+ style->fill.pattern.back = color[zmin];
+ gog_renderer_push_style (rend, style);
+ gog_renderer_draw_polygon (rend, path, TRUE, NULL);
+ gog_renderer_pop_style (rend);
+ }
+ }
+ }
+ }
+ lines[l].code = ART_END;
+ gog_renderer_push_style (rend, GOG_STYLED_OBJECT (series)->style);
+ gog_renderer_draw_path (rend, lines, NULL);
+ gog_renderer_pop_style (rend);
+ gog_renderer_clip_pop (rend);
+ art_free (lines);
+ art_free (path);
+ g_object_unref (style);
+ gog_axis_map_free (x_map);
+ gog_axis_map_free (y_map);
+}
+
+static void
+gog_contour_view_class_init (GogViewClass *view_klass)
+{
+ view_klass->render = gog_contour_view_render;
+}
+
+static GSF_CLASS (GogContourView, gog_contour_view,
+ gog_contour_view_class_init, NULL,
+ GOG_PLOT_VIEW_TYPE)
+
+/*****************************************************************************/
+
+static GogStyledObjectClass *series_parent_klass;
+
+static void
+gog_surface_series_update (GogObject *obj)
+{
+ GogSurfaceSeries *series = GOG_SURFACE_SERIES (obj);
+ GOMatrixSize size, old_size;
+ GogContourPlot *plot = GOG_CONTOUR_PLOT (series->base.plot);
+ GODataMatrix *mat;
+ GODataVector *vec;
+ int length;
+ size.rows = 0;
+ size.columns = 0;
+ if (series->base.values[2].data != NULL) {
+ old_size.rows = series->rows;
+ old_size.columns = series->columns;
+ mat = GO_DATA_MATRIX (series->base.values[2].data);
+ go_data_matrix_get_values (mat);
+ size = go_data_matrix_get_size (mat);
+ go_data_matrix_get_minmax (mat, &plot->zmin, &plot->zmax);
+ }
+ if (series->base.values[0].data != NULL) {
+ vec = GO_DATA_VECTOR (series->base.values[0].data);
+ go_data_vector_get_values (vec);
+ length = go_data_vector_get_len (vec);
+ if (length < size.columns)
+ size.columns = length;
+ }
+ if (series->base.values[1].data != NULL) {
+ vec = GO_DATA_VECTOR (series->base.values[1].data);
+ go_data_vector_get_values (vec);
+ length = go_data_vector_get_len (vec);
+ if (length < size.rows)
+ size.rows = length;
+ }
+ series->rows = size.rows;
+ series->columns = size.columns;
+ series->base.num_elements = plot->levels;
+
+ /* queue plot for redraw */
+ gog_object_request_update (GOG_OBJECT (series->base.plot));
+ gog_plot_request_cardinality_update (series->base.plot);
+
+ if (series_parent_klass->base.update)
+ series_parent_klass->base.update (obj);
+}
+
+static void
+gog_surface_series_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ series_parent_klass->init_style (gso, style);
+}
+
+static void
+gog_surface_series_class_init (GogStyledObjectClass *gso_klass)
+{
+ GogObjectClass * obj_klass = (GogObjectClass *) gso_klass;
+
+ series_parent_klass = g_type_class_peek_parent (gso_klass);
+ gso_klass->init_style = gog_surface_series_init_style;
+ obj_klass->update = gog_surface_series_update;
+}
+
+
+GSF_CLASS (GogSurfaceSeries, gog_surface_series,
+ gog_surface_series_class_init, NULL,
+ GOG_SERIES_TYPE)
+
+void
+plugin_init (void)
+{
+ gog_contour_plot_get_type ();
+}
+
+void
+plugin_cleanup (void)
+{
+}
--- /dev/null
+++ lib/goffice/graph/plugins/plot_surface/gog-surface.h
@@ -0,0 +1,57 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-surface.h
+ *
+ * Copyright (C) 2004 Jean Brefort (jean.brefort at normalesup.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_SURFACE_H
+#define GOG_SURFACE_H
+
+#include <goffice/graph/gog-plot-impl.h>
+
+G_BEGIN_DECLS
+
+/*-----------------------------------------------------------------------------
+ *
+ * GogContourPlot
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+typedef struct {
+ GogPlot base;
+
+ unsigned levels;
+ unsigned rows, columns;
+ double zmin, zmax;
+ double step, *limits;
+ struct {
+ double minima, maxima;
+ GOFormat *fmt;
+ } x, y;
+} GogContourPlot;
+
+#define GOG_CONTOUR_PLOT_TYPE (gog_contour_plot_get_type ())
+#define GOG_CONTOUR_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_CONTOUR_PLOT_TYPE, GogContourPlot))
+#define GOG_IS_PLOT_CONTOUR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_CONTOUR_PLOT_TYPE))
+
+GType gog_contour_plot_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_SURFACE_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_xy/plugin.xml.in
@@ -0,0 +1,27 @@
+<plugin id="GOffice_plot_xy">
+ <information>
+ <_name>Charting : XY/Scatter/Bubble</_name>
+ <_description>2D plots</_description>
+ </information>
+ <loader type="Gnumeric_Builtin:module">
+ <attribute name="module_file" value="xy"/>
+ </loader>
+ <services>
+ <service type="plot_engine" id="GogXYPlot">
+ <information>
+ <_description>2D scatter plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_engine" id="GogBubblePlot">
+ <information>
+ <_description>Bubble plotting engine</_description>
+ </information>
+ </service>
+ <service type="plot_type" id="plot_xy">
+ <file>plot-types.xml</file>
+ <information>
+ <_description>Stock Scatter plot types</_description>
+ </information>
+ </service>
+ </services>
+</plugin>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_xy/gog-xy.h
@@ -0,0 +1,82 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-xy.h
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_XY_PLOT_H
+#define GOG_XY_PLOT_H
+
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-series-impl.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GogPlot base;
+
+ struct {
+ double minima, maxima;
+ GOFormat *fmt;
+ } x, y;
+} Gog2DPlot;
+
+typedef struct {
+ Gog2DPlot base;
+ gboolean default_style_has_markers;
+ gboolean default_style_has_lines;
+} GogXYPlot;
+
+typedef struct {
+ Gog2DPlot base;
+ gboolean size_as_area;
+ gboolean in_3d;
+ gboolean show_negatives;
+ float bubble_scale;
+} GogBubblePlot;
+
+#define GOG_2D_PLOT_TYPE (gog_2d_plot_get_type ())
+#define GOG_2D_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_2D_PLOT_TYPE, Gog2DPlot))
+#define GOG_IS_2D_PLOT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_2D_PLOT_TYPE))
+
+GType gog_2d_plot_get_type (void);
+
+#define GOG_XY_PLOT_TYPE (gog_xy_plot_get_type ())
+#define GOG_XY_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_XY_PLOT_TYPE, GogXYPlot))
+#define GOG_IS_XY_PLOT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_XY_PLOT_TYPE))
+
+GType gog_xy_plot_get_type (void);
+
+#define GOG_BUBBLE_PLOT_TYPE (gog_bubble_plot_get_type ())
+#define GOG_BUBBLE_PLOT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_BUBBLE_PLOT_TYPE, GogBubblePlot))
+#define GOG_IS_BUBBLE_PLOT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_BUBBLE_PLOT_TYPE))
+
+GType gog_bubble_plot_get_type (void);
+
+typedef struct {
+ GogSeries base;
+ GogErrorBar *x_errors, *y_errors;
+} GogXYSeries;
+
+#define GOG_XY_SERIES_TYPE (gog_xy_series_get_type ())
+#define GOG_XY_SERIES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_XY_SERIES_TYPE, GogXYSeries))
+#define GOG_IS_XY_SERIES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_XY_SERIES_TYPE))
+
+G_END_DECLS
+
+#endif /* GOG_XY_SERIES_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_xy/plot-types.xml.in
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Types xmlns:graph="http://www.gnumeric.org/graph_v2.dtd">
+ <Family _name="XY" sample_image_file="scatter.xpm"/>
+ <Family _name="Bubble" sample_image_file="bubble.xpm"/>
+
+ <Type _name="XY Points" row="1" col="1"
+ engine="GogXYPlot" family="XY"
+ _description="Markers at each point."
+ sample_image_file="chart_scatter_1_1.png">
+ <property name="default-style-has-lines">FALSE</property>
+ </Type>
+ <!-- really 3_1 -->
+ <Type _name="XY" row="2" col="1"
+ engine="GogXYPlot" family="XY"
+ _description="Linearly interpolate between multi-dimensional points,with markers at each point."
+ sample_image_file="chart_scatter_3_1.png">
+ </Type>
+ <Type _name="XY Lines" row="2" col="2"
+ engine="GogXYPlot" family="XY"
+ _description="Linearly interpolate between multi-dimensional points."
+ sample_image_file="chart_scatter_3_2.png">
+ <property name="default-style-has-markers">FALSE</property>
+ </Type>
+ <Type _name="Bubble" row="1" col="1"
+ engine="GogBubblePlot" family="Bubble"
+ _description="Multi-dimensional points with circle at each point."
+ sample_image_file="chart_bubble_1_1.png">
+ </Type>
+</Types>
+
+
+<!--
+ <Major>
+ <_name>Bubble</_name>
+ <sample_image_file>bubble.xpm</sample_image_file>
+ <Minor>
+ <_name>Bubble</_name>
+ <_description>Plot X, Y and bubble size.</_description>
+ <sample_image_file>chart_bubble_1_1.png</sample_image_file>
+ <position row="1" col="1" />
+ <graph:Type name="Scatter">
+ <with_marker>true</with_marker>
+ <auto_allocate_bubble_size>true</auto_allocate_bubble_size>
+ </graph:Type>
+ </Minor>
+ </Major>
+ -->
--- /dev/null
+++ lib/goffice/graph/plugins/plot_xy/Makefile.am
@@ -0,0 +1,27 @@
+goffice_graph_xydir = $(gnumeric_plugindir)/plot_xy
+xmldir = $(goffice_graph_xydir)
+gladedir = $(goffice_graph_xydir)
+
+goffice_graph_xy_LTLIBRARIES = xy.la
+xy_la_LDFLAGS = -module $(GOFFICE_PLUGIN_FLAGS)
+xy_la_SOURCES = \
+ gog-xy.c \
+ gog-xy.h \
+ gog-bubble-prefs.c
+
+xml_in_files = plugin.xml.in plot-types.xml.in
+xml_DATA = $(xml_in_files:.xml.in=.xml) gog-bubble-prefs.glade
+
+ at INTLTOOL_XML_RULE@
+
+glade_DATA = gog-bubble-prefs.glade
+
+# do not use the intl-tool stuff to merge the text back
+# its simpler to just use gettext directly
+plot-types.xml : plot-types.xml.in
+ cp $< $@
+
+EXTRA_DIST = $(xml_in_files) $(glade_DATA)
+DISTCLEANFILES = $(xml_in_files:.xml.in=.xml)
+
+include $(srcdir)/../../../goffice-plugins.mk
--- /dev/null
+++ lib/goffice/graph/plugins/plot_xy/gog-bubble-prefs.c
@@ -0,0 +1,129 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-bubble-prefs.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-xy.h"
+//#include <src/plugin.h>
+//#include <src/gui-util.h>
+#include <plugin.h>
+#include <gui-util.h>
+
+#include <glade/glade-xml.h>
+#include <gtk/gtkradiobutton.h>
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtkspinbutton.h>
+
+#include <string.h>
+
+GtkWidget *gog_bubble_plot_pref (GogBubblePlot *bubble, GnmCmdContext *cc);
+
+static void
+cb_type_changed (GtkToggleButton* button, GObject *bubble)
+{
+ if (gtk_toggle_button_get_active (button))
+ g_object_set (bubble, "size_as_area",
+ strcmp (gtk_widget_get_name ((GtkWidget*) button), "area")? FALSE: TRUE, NULL);
+}
+
+static void
+cb_style_changed (GtkToggleButton* button, GObject *bubble)
+{
+ g_object_set (bubble, "vary_style_by_element",
+ gtk_toggle_button_get_active (button), NULL);
+}
+
+static void
+cb_3d_changed (GtkToggleButton* button, GObject *bubble)
+{
+ g_object_set (bubble, "in_3d",
+ gtk_toggle_button_get_active (button), NULL);
+}
+
+static void
+cb_negatives_changed (GtkToggleButton* button, GObject *bubble)
+{
+ g_object_set (bubble, "show_negatives",
+ gtk_toggle_button_get_active (button), NULL);
+}
+
+static void
+cb_scale_changed (GtkAdjustment *adj, GObject *bubble)
+{
+ g_object_set (bubble, "bubble_scale", adj->value / 100., NULL);
+}
+
+GtkWidget *
+gog_bubble_plot_pref (GogBubblePlot *bubble, GnmCmdContext *cc)
+{
+ GtkWidget *w;
+ char const *dir = gnm_plugin_get_dir_name (
+ plugins_get_plugin_by_id ("GOffice_plot_xy"));
+ char *path = g_build_filename (dir, "gog-bubble-prefs.glade", NULL);
+ GladeXML *gui = gnm_glade_xml_new (cc, path, "gog_bubble_prefs", NULL);
+
+ g_free (path);
+ if (gui == NULL)
+ return NULL;
+
+ w = glade_xml_get_widget (gui, "area");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), bubble->size_as_area);
+ g_signal_connect (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_type_changed), bubble);
+
+ w = glade_xml_get_widget (gui, "diameter");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), !bubble->size_as_area);
+ g_signal_connect (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_type_changed), bubble);
+
+ w = glade_xml_get_widget (gui, "vary_style_by_element");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), bubble->base.base.vary_style_by_element);
+ g_signal_connect (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_style_changed), bubble);
+
+ w = glade_xml_get_widget (gui, "3d");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), bubble->in_3d);
+ g_signal_connect (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_3d_changed), bubble);
+#warning "Hide 3d button while not supported"
+ gtk_widget_hide (w);
+
+ w = glade_xml_get_widget (gui, "scale");
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), bubble->bubble_scale * 100.);
+ g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
+ "value_changed",
+ G_CALLBACK (cb_scale_changed), bubble);
+
+ w = glade_xml_get_widget (gui, "show_negative_values");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), bubble->show_negatives);
+ g_signal_connect (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_negatives_changed), bubble);
+
+ w = glade_xml_get_widget (gui, "gog_bubble_prefs");
+ g_object_set_data_full (G_OBJECT (w),
+ "state", gui, (GDestroyNotify)g_object_unref);
+
+ return w;
+}
--- /dev/null
+++ lib/goffice/graph/plugins/plot_xy/gog-bubble-prefs.glade
@@ -0,0 +1,246 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window1">
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkTable" id="gog_bubble_prefs">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="3d">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">3_d</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="area">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Sur_face</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="diameter">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Dia_meter</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">area</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="size_display">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Size represented by:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">area</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="vary_style_by_element">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Vary colors by bubble</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="show_negative_values">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Show _negative values</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="scale_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="scale_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Bubbles scaled to</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">scale</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="scale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">100 0 200 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="percent">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">% of default size</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/plugins/plot_xy/gog-xy.c
@@ -0,0 +1,1065 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-xy.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "gog-xy.h"
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/graph/gog-error-bar.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-marker.h>
+#include <goffice/utils/go-format.h>
+#include <goffice/utils/go-math.h>
+
+#include <module-plugin-defs.h>
+#include <glib/gi18n.h>
+#include <gtk/gtklabel.h>
+#include <gsf/gsf-impl-utils.h>
+#include <math.h>
+
+typedef struct {
+ GogPlotClass base;
+
+ void (*adjust_bounds) (Gog2DPlot *model, double *x_min, double *x_max, double *y_min, double *y_max);
+} Gog2DPlotClass;
+
+typedef Gog2DPlotClass GogXYPlotClass;
+
+typedef Gog2DPlotClass GogBubblePlotClass;
+
+GNUMERIC_MODULE_PLUGIN_INFO_DECL;
+
+static GogObjectClass *plot2d_parent_klass;
+static void gog_2d_plot_adjust_bounds (Gog2DPlot *model, double *x_min, double *x_max, double *y_min, double *y_max);
+
+#define GOG_2D_PLOT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_2D_PLOT_TYPE, Gog2DPlotClass))
+
+static void
+gog_2d_plot_clear_formats (Gog2DPlot *plot2d)
+{
+ if (plot2d->x.fmt != NULL) {
+ go_format_unref (plot2d->x.fmt);
+ plot2d->x.fmt = NULL;
+ }
+ if (plot2d->y.fmt != NULL) {
+ go_format_unref (plot2d->y.fmt);
+ plot2d->y.fmt = NULL;
+ }
+}
+
+static void
+gog_2d_plot_update (GogObject *obj)
+{
+ Gog2DPlot *model = GOG_2D_PLOT (obj);
+ GogXYSeries const *series = NULL;
+ double x_min, x_max, y_min, y_max, tmp_min, tmp_max;
+ GSList *ptr;
+ gboolean is_discrete = FALSE;
+
+ x_min = y_min = DBL_MAX;
+ x_max = y_max = -DBL_MAX;
+ gog_2d_plot_clear_formats (model);
+ for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
+ series = ptr->data;
+ if (!gog_series_is_valid (GOG_SERIES (series)))
+ continue;
+
+ go_data_vector_get_minmax (GO_DATA_VECTOR (
+ series->base.values[1].data), &tmp_min, &tmp_max);
+ if (y_min > tmp_min) y_min = tmp_min;
+ if (y_max < tmp_max) y_max = tmp_max;
+ if (model->y.fmt == NULL)
+ model->y.fmt = go_data_preferred_fmt (series->base.values[1].data);
+
+ if (series->base.values[0].data != NULL) {
+ go_data_vector_get_minmax (GO_DATA_VECTOR (
+ series->base.values[0].data), &tmp_min, &tmp_max);
+
+ if (!go_finite (tmp_min) || !go_finite (tmp_max) ||
+ tmp_min > tmp_max) {
+ tmp_min = 0;
+ tmp_max = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[1].data));
+
+ is_discrete = TRUE;
+ } else if (model->x.fmt == NULL)
+ model->x.fmt = go_data_preferred_fmt (series->base.values[0].data);
+ } else {
+ tmp_min = 0;
+ tmp_max = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ is_discrete = TRUE;
+ }
+
+ if (x_min > tmp_min) x_min = tmp_min;
+ if (x_max < tmp_max) x_max = tmp_max;
+ }
+
+ /*adjust bounds to allow large markers or bubbles*/
+ gog_2d_plot_adjust_bounds (model, &x_min, &x_max, &y_min, &y_max);
+ /* add room for error bars */
+ if (gog_error_bar_is_visible (series->x_errors)) {
+ gog_error_bar_get_minmax (series->x_errors, &tmp_min, &tmp_max);
+ if (x_min > tmp_min)
+ x_min = tmp_min;
+ if (x_max < tmp_max)
+ x_max = tmp_max;
+ }
+ if (gog_error_bar_is_visible (series->y_errors)) {
+ gog_error_bar_get_minmax (series->y_errors, &tmp_min, &tmp_max);
+ if (y_min > tmp_min)
+ y_min = tmp_min;
+ if (y_max < tmp_max)
+ y_max = tmp_max;
+ }
+
+ if (model->x.minima != x_min || model->x.maxima != x_max) {
+ model->x.minima = x_min;
+ model->x.maxima = x_max;
+ gog_axis_bound_changed (model->base.axis[0], GOG_OBJECT (model));
+ }
+ if (model->y.minima != y_min || model->y.maxima != y_max) {
+ model->y.minima = y_min;
+ model->y.maxima = y_max;
+ gog_axis_bound_changed (model->base.axis[1], GOG_OBJECT (model));
+ }
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+ if (plot2d_parent_klass->update)
+ plot2d_parent_klass->update (obj);
+}
+
+static void
+gog_2d_plot_real_adjust_bounds (Gog2DPlot *model, double *x_min, double *x_max, double *y_min, double *y_max)
+{
+}
+
+static void
+gog_2d_plot_adjust_bounds (Gog2DPlot *model, double *x_min, double *x_max, double *y_min, double *y_max)
+{
+ Gog2DPlotClass *klass = GOG_2D_PLOT_GET_CLASS (model);
+ klass->adjust_bounds (model, x_min, x_max, y_min, y_max);
+}
+
+static GogAxisSet
+gog_2d_plot_axis_set_pref (GogPlot const *plot)
+{
+ return GOG_AXIS_SET_XY;
+}
+
+static gboolean
+gog_2d_plot_axis_set_is_valid (GogPlot const *plot, GogAxisSet type)
+{
+ return type == GOG_AXIS_SET_XY;
+}
+
+static gboolean
+gog_2d_plot_axis_set_assign (GogPlot *plot, GogAxisSet type)
+{
+ return type == GOG_AXIS_SET_XY;
+}
+
+static GOData *
+gog_2d_plot_axis_get_bounds (GogPlot *plot, GogAxisType axis,
+ GogPlotBoundInfo *bounds)
+{
+ Gog2DPlot *model = GOG_2D_PLOT (plot);
+
+ if (axis == GOG_AXIS_X) {
+ GSList *ptr;
+
+ bounds->val.minima = model->x.minima;
+ bounds->val.maxima = model->x.maxima;
+ bounds->is_discrete = model->x.minima > model->x.maxima ||
+ !go_finite (model->x.minima) ||
+ !go_finite (model->x.maxima);
+ if (bounds->fmt == NULL && model->x.fmt != NULL)
+ bounds->fmt = go_format_ref (model->x.fmt);
+
+ for (ptr = plot->series; ptr != NULL ; ptr = ptr->next)
+ if (gog_series_is_valid (GOG_SERIES (ptr->data)))
+ return GOG_SERIES (ptr->data)->values[0].data;
+ return NULL;
+ }
+
+ if (axis == GOG_AXIS_Y) {
+ bounds->val.minima = model->y.minima;
+ bounds->val.maxima = model->y.maxima;
+ if (bounds->fmt == NULL && model->y.fmt != NULL)
+ bounds->fmt = go_format_ref (model->y.fmt);
+ }
+ return NULL;
+}
+
+static void
+gog_2d_finalize (GObject *obj)
+{
+ gog_2d_plot_clear_formats (GOG_2D_PLOT (obj));
+ G_OBJECT_CLASS (plot2d_parent_klass)->finalize (obj);
+}
+
+static GType gog_xy_view_get_type (void);
+static GType gog_xy_series_get_type (void);
+
+static void
+gog_2d_plot_class_init (GogPlotClass *plot_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) plot_klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) plot_klass;
+ Gog2DPlotClass *gog_2d_plot_klass = (Gog2DPlotClass*) plot_klass;
+
+ gog_2d_plot_klass->adjust_bounds = gog_2d_plot_real_adjust_bounds;
+
+ plot2d_parent_klass = g_type_class_peek_parent (plot_klass);
+
+ gobject_klass->finalize = gog_2d_finalize;
+
+ gog_klass->update = gog_2d_plot_update;
+ gog_klass->view_type = gog_xy_view_get_type ();
+
+ plot_klass->desc.num_series_min = 1;
+ plot_klass->desc.num_series_max = G_MAXINT;
+ plot_klass->series_type = gog_xy_series_get_type ();
+ plot_klass->axis_set_pref = gog_2d_plot_axis_set_pref;
+ plot_klass->axis_set_is_valid = gog_2d_plot_axis_set_is_valid;
+ plot_klass->axis_set_assign = gog_2d_plot_axis_set_assign;
+ plot_klass->axis_get_bounds = gog_2d_plot_axis_get_bounds;
+}
+
+static void
+gog_2d_plot_init (Gog2DPlot *plot2d)
+{
+ plot2d->base.vary_style_by_element = FALSE;
+ plot2d->x.fmt = plot2d->y.fmt = NULL;
+}
+
+GSF_CLASS (Gog2DPlot, gog_2d_plot,
+ gog_2d_plot_class_init, gog_2d_plot_init,
+ GOG_PLOT_TYPE)
+
+enum {
+ GOG_XY_PROP_0,
+ GOG_XY_PROP_DEFAULT_STYLE_HAS_MARKERS,
+ GOG_XY_PROP_DEFAULT_STYLE_HAS_LINES
+};
+
+static GogObjectClass *xy_parent_klass;
+
+#define GOG_XY_PLOT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_XY_PLOT_TYPE, GogXYPlotClass))
+
+static char const *
+gog_xy_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ /* xgettext : the base for how to name scatter plot objects
+ * eg The 2nd plot in a chart will be called
+ * PlotXY2 */
+ return N_("PlotXY");
+}
+
+static void
+gog_xy_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogXYPlot *xy = GOG_XY_PLOT (obj);
+ switch (param_id) {
+ case GOG_XY_PROP_DEFAULT_STYLE_HAS_MARKERS:
+ xy->default_style_has_markers = g_value_get_boolean (value);
+ break;
+ case GOG_XY_PROP_DEFAULT_STYLE_HAS_LINES:
+ xy->default_style_has_lines = g_value_get_boolean (value);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+static void
+gog_xy_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogXYPlot const *xy = GOG_XY_PLOT (obj);
+ switch (param_id) {
+ case GOG_XY_PROP_DEFAULT_STYLE_HAS_MARKERS:
+ g_value_set_boolean (value, xy->default_style_has_markers);
+ break;
+ case GOG_XY_PROP_DEFAULT_STYLE_HAS_LINES:
+ g_value_set_boolean (value, xy->default_style_has_lines);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_xy_plot_class_init (GogPlotClass *plot_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) plot_klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) plot_klass;
+
+ xy_parent_klass = g_type_class_peek_parent (plot_klass);
+
+ gobject_klass->set_property = gog_xy_set_property;
+ gobject_klass->get_property = gog_xy_get_property;
+
+ g_object_class_install_property (gobject_klass, GOG_XY_PROP_DEFAULT_STYLE_HAS_MARKERS,
+ g_param_spec_boolean ("default-style-has-markers", NULL,
+ "Should the default style of a series include markers",
+ TRUE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, GOG_XY_PROP_DEFAULT_STYLE_HAS_LINES,
+ g_param_spec_boolean ("default-style-has-lines", NULL,
+ "Should the default style of a series include lines",
+ TRUE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ gog_klass->type_name = gog_xy_plot_type_name;
+
+ {
+ static GogSeriesDimDesc dimensions[] = {
+ { N_("X"), GOG_SERIES_SUGGESTED, FALSE,
+ GOG_DIM_INDEX, GOG_MS_DIM_CATEGORIES },
+ { N_("Y"), GOG_SERIES_REQUIRED, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_VALUES },
+/* Names of the error data are not translated since they are not used */
+ { "Y+err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_plus1 },
+ { "Y-err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_minus1 },
+ { "X+err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_plus2 },
+ { "X-err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_minus2 }
+ };
+ plot_klass->desc.series.dim = dimensions;
+ plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
+ plot_klass->desc.series.style_fields = GOG_STYLE_LINE | GOG_STYLE_MARKER;
+ }
+}
+
+static void
+gog_xy_plot_init (GogXYPlot *xy)
+{
+ xy->default_style_has_markers = TRUE;
+ xy->default_style_has_lines = TRUE;
+}
+
+GSF_CLASS (GogXYPlot, gog_xy_plot,
+ gog_xy_plot_class_init, gog_xy_plot_init,
+ GOG_2D_PLOT_TYPE)
+
+/*****************************************************************************/
+
+static GogObjectClass *bubble_parent_klass;
+
+#define GOG_BUBBLE_PLOT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_BUBBLE_PLOT_TYPE, GogBubblePlotClass))
+
+static void gog_bubble_plot_adjust_bounds (Gog2DPlot *model, double *x_min, double *x_max, double *y_min, double *y_max);
+
+static char const *
+gog_bubble_plot_type_name (G_GNUC_UNUSED GogObject const *item)
+{
+ return N_("PlotBubble");
+}
+
+extern gpointer gog_bubble_plot_pref (GogBubblePlot *bubble, GnmCmdContext *cc);
+static gpointer
+gog_bubble_plot_editor (GogObject *item, G_GNUC_UNUSED GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ return gog_bubble_plot_pref (GOG_BUBBLE_PLOT (item), cc);
+}
+
+enum {
+ GOG_BUBBLE_PROP_0,
+ GOG_BUBBLE_PROP_AS_AREA,
+ GOG_BUBBLE_PROP_SHOW_NEGATIVES,
+ GOG_BUBBLE_PROP_IN_3D,
+ GOG_BUBBLE_PROP_SCALE
+};
+
+static void
+gog_bubble_plot_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogBubblePlot *bubble = GOG_BUBBLE_PLOT (obj);
+
+ switch (param_id) {
+ case GOG_BUBBLE_PROP_AS_AREA :
+ bubble->size_as_area = g_value_get_boolean (value);
+ break;
+ case GOG_BUBBLE_PROP_SHOW_NEGATIVES :
+ bubble->show_negatives = g_value_get_boolean (value);
+ break;
+ case GOG_BUBBLE_PROP_IN_3D :
+ bubble->in_3d = g_value_get_boolean (value);
+ break;
+ case GOG_BUBBLE_PROP_SCALE :
+ bubble->bubble_scale = g_value_get_float (value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ /* none of the attributes triggers a size change yet.
+ * When we add data labels we'll need it */
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_bubble_plot_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogBubblePlot *bubble = GOG_BUBBLE_PLOT (obj);
+
+ switch (param_id) {
+ case GOG_BUBBLE_PROP_AS_AREA :
+ g_value_set_boolean (value, bubble->size_as_area);
+ break;
+ case GOG_BUBBLE_PROP_SHOW_NEGATIVES :
+ g_value_set_boolean (value, bubble->show_negatives);
+ break;
+ case GOG_BUBBLE_PROP_IN_3D :
+ g_value_set_boolean (value, bubble->in_3d);
+ break;
+ case GOG_BUBBLE_PROP_SCALE :
+ g_value_set_float (value, bubble->bubble_scale);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_bubble_plot_class_init (GogPlotClass *plot_klass)
+{
+ GogObjectClass *gog_klass = (GogObjectClass *) plot_klass;
+ GObjectClass *gobject_klass = (GObjectClass *) plot_klass;
+ Gog2DPlotClass *gog_2d_plot_klass = (Gog2DPlotClass*) plot_klass;
+
+ bubble_parent_klass = g_type_class_peek_parent (plot_klass);
+ gobject_klass->set_property = gog_bubble_plot_set_property;
+ gobject_klass->get_property = gog_bubble_plot_get_property;
+
+ gog_2d_plot_klass->adjust_bounds = gog_bubble_plot_adjust_bounds;
+ gog_klass->type_name = gog_bubble_plot_type_name;
+
+ gog_klass->editor = gog_bubble_plot_editor;
+
+ g_object_class_install_property (gobject_klass, GOG_BUBBLE_PROP_AS_AREA,
+ g_param_spec_boolean ("size_as_area", "size_as_area",
+ "Display size as area instead of diameter",
+ TRUE,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, GOG_BUBBLE_PROP_SHOW_NEGATIVES,
+ g_param_spec_boolean ("show_negatives", "show_negatives",
+ "Draw bubbles for negative values",
+ FALSE,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, GOG_BUBBLE_PROP_IN_3D,
+ g_param_spec_boolean ("in_3d", "in_3d",
+ "Draw 3d bubbles",
+ FALSE,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, GOG_BUBBLE_PROP_SCALE,
+ g_param_spec_float ("bubble_scale", "bubble_scale",
+ "Fraction of default radius used for display.",
+ 0., 2., 1.,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ {
+ static GogSeriesDimDesc dimensions[] = {
+ { N_("X"), GOG_SERIES_SUGGESTED, FALSE,
+ GOG_DIM_INDEX, GOG_MS_DIM_CATEGORIES },
+ { N_("Y"), GOG_SERIES_REQUIRED, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_VALUES },
+ { N_("Bubble"), GOG_SERIES_REQUIRED, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_BUBBLES },
+/* Names of the error data are not translated since they are not used */
+ { "Y+err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_plus1 },
+ { "Y-err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_minus1 },
+ { "X+err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_plus2 },
+ { "X-err", GOG_SERIES_ERRORS, FALSE,
+ GOG_DIM_VALUE, GOG_MS_DIM_ERR_minus2 }
+ };
+ plot_klass->desc.series.dim = dimensions;
+ plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
+ plot_klass->desc.series.style_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL;
+ }
+}
+
+#define BUBBLE_MAX_RADIUS_RATIO 8.
+static void
+gog_bubble_plot_adjust_bounds (Gog2DPlot *model, double *x_min, double *x_max, double *y_min, double *y_max)
+{
+ /* Add room for bubbles*/
+ double tmp;
+ double factor = BUBBLE_MAX_RADIUS_RATIO / GOG_BUBBLE_PLOT (model)->bubble_scale - 2.;
+ tmp = (*x_max - *x_min) / factor;
+ *x_min -= tmp;
+ *x_max += tmp;
+ tmp = (*y_max - *y_min) / factor;
+ *y_min -= tmp;
+ *y_max += tmp;
+}
+
+static void
+gog_bubble_plot_init (GogBubblePlot *bubble)
+{
+ bubble->size_as_area = TRUE;
+ bubble->in_3d = FALSE;
+ bubble->show_negatives = FALSE;
+ bubble->bubble_scale = 1.0;
+}
+
+GSF_CLASS (GogBubblePlot, gog_bubble_plot,
+ gog_bubble_plot_class_init, gog_bubble_plot_init,
+ GOG_2D_PLOT_TYPE)
+
+/*****************************************************************************/
+typedef GogPlotView GogXYView;
+typedef GogPlotViewClass GogXYViewClass;
+
+#define MAX_ARC_SEGMENTS 64
+
+static void
+bubble_draw_circle (GogView *view, double x, double y, double radius)
+{
+ double theta, dt = 2 * M_PI / MAX_ARC_SEGMENTS;
+ int i;
+ ArtVpath path[MAX_ARC_SEGMENTS + 2];
+ path[0].x = path[MAX_ARC_SEGMENTS].x = x + radius;
+ path[0].y = path[MAX_ARC_SEGMENTS].y = y;
+ path[0].code = ART_MOVETO;
+#warning what about small bubbles. With a very small radius, libart emits lot of warnings.
+ if (radius < 1.) radius = 1.;
+ for (i = 1, theta = dt; i < MAX_ARC_SEGMENTS; i++, theta += dt) {
+ path[i].x = x + radius * cos (theta);
+ /* must turn clockwise for gradients */
+ path[i].y = y - radius * sin (theta);
+ path[i].code = ART_LINETO;
+ }
+ path[MAX_ARC_SEGMENTS].code = ART_LINETO;
+ path[MAX_ARC_SEGMENTS + 1].code = ART_END;
+ gog_renderer_draw_polygon (view->renderer, path, FALSE, &view->residual);
+}
+
+typedef struct {
+ double x, y;
+} MarkerData;
+
+static void
+gog_xy_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ Gog2DPlot const *model = GOG_2D_PLOT (view->model);
+ unsigned num_series;
+ GogAxisMap *x_map, *y_map;
+ GogXYSeries const *series = NULL;
+ unsigned i ,j ,k ,n, tmp;
+ GogTheme *theme = gog_object_get_theme (GOG_OBJECT (model));
+ GogStyle *neg_style = NULL;
+ GSList *ptr;
+ double const *y_vals, *x_vals = NULL, *z_vals = NULL;
+ double x = 0., y = 0., z, x_canvas = 0., y_canvas = 0.;
+ double zmax, rmax = 0.;
+ double x_margin_min, x_margin_max, y_margin_min, y_margin_max, margin;
+ double xerrmin, xerrmax, yerrmin, yerrmax;
+ double prev_x = 0., prev_y = 0.;
+ double prev_x_canvas = 0., prev_y_canvas = 0.;
+ ArtVpath path[3];
+ GogStyle *style = NULL;
+ gboolean valid, prev_valid, show_marks, show_lines, show_negatives, in_3d, size_as_area = TRUE;
+
+ MarkerData **markers;
+ unsigned *num_markers;
+
+ x_map = gog_axis_map_new (model->base.axis[0],
+ view->residual.x , view->residual.w);
+ y_map = gog_axis_map_new (model->base.axis[1],
+ view->residual.y + view->residual.h,
+ -view->residual.h);
+
+ if (!(gog_axis_map_is_valid (x_map) &&
+ gog_axis_map_is_valid (y_map))) {
+ gog_axis_map_free (x_map);
+ gog_axis_map_free (y_map);
+ return;
+ }
+
+ gog_renderer_clip_push (view->renderer, &view->allocation);
+
+ for (num_series = 0, ptr = model->base.series ; ptr != NULL ; ptr = ptr->next, num_series++);
+ markers = g_alloca (num_series * sizeof (MarkerData *));
+ num_markers = g_alloca (num_series * sizeof (unsigned));
+
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_END;
+ for (j = 0, ptr = model->base.series ; ptr != NULL ; ptr = ptr->next, j++) {
+ series = ptr->data;
+ markers[j] = NULL;
+
+ if (!gog_series_is_valid (GOG_SERIES (series)))
+ continue;
+
+ y_vals = go_data_vector_get_values (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ n = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ if (series->base.values[0].data) {
+ x_vals = go_data_vector_get_values (
+ GO_DATA_VECTOR (series->base.values[0].data));
+ tmp = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[0].data));
+ if (n > tmp)
+ n = tmp;
+ }
+ style = GOG_STYLED_OBJECT (series)->style;
+ if (GOG_IS_BUBBLE_PLOT (model)) {
+ double zmin;
+ go_data_vector_get_minmax (GO_DATA_VECTOR (series->base.values[2].data), &zmin, &zmax);
+ show_negatives = GOG_BUBBLE_PLOT (view->model)->show_negatives;
+ if ((! go_finite (zmax)) || (!show_negatives && (zmax <= 0))) continue;
+ rmax = MIN (view->residual.w, view->residual.h) / BUBBLE_MAX_RADIUS_RATIO
+ * GOG_BUBBLE_PLOT (view->model)->bubble_scale;
+ size_as_area = GOG_BUBBLE_PLOT (view->model)->size_as_area;
+ in_3d = GOG_BUBBLE_PLOT (view->model)->in_3d;
+ if (show_negatives) {
+ zmin = fabs (zmin);
+ if (zmin > zmax) zmax = zmin;
+ neg_style = gog_style_dup (GOG_STYLED_OBJECT (series)->style);
+ neg_style->fill.type = GOG_FILL_STYLE_PATTERN;
+ neg_style->fill.pattern.pattern = GO_PATTERN_SOLID;
+ neg_style->fill.pattern.back = RGBA_WHITE;
+ }
+ if (model->base.vary_style_by_element)
+ style = gog_style_dup (style);
+
+ z_vals = go_data_vector_get_values (
+ GO_DATA_VECTOR (series->base.values[2].data));
+ tmp = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[2].data));
+ if (n > tmp)
+ n = tmp;
+ }
+
+ if (n <= 0)
+ continue;
+
+ show_marks = gog_style_is_marker_visible (style);
+ show_lines = gog_style_is_line_visible (style);
+ if (!show_marks && !show_lines)
+ continue;
+
+ if (show_marks && !GOG_IS_BUBBLE_PLOT (model))
+ markers[j] = g_new (MarkerData, n);
+
+ prev_valid = FALSE;
+ gog_renderer_push_style (view->renderer, style);
+ margin = gog_renderer_line_size (view->renderer, 1);
+ x_margin_min = view->allocation.x - margin;
+ x_margin_max = view->allocation.x + view->allocation.w + margin;
+ y_margin_min = view->allocation.y - margin;
+ y_margin_max = view->allocation.y + view->allocation.h + margin;
+
+ k = 0;
+ for (i = 1 ; i <= n ; i++) {
+ x = x_vals ? *x_vals++ : i;
+ y = *y_vals++;
+ valid = !isnan (y) && !isnan (x);
+ if (valid) {
+ /* We are checking with go_finite here because isinf
+ if not available everywhere. Note, that NANs
+ have been ruled out. */
+ if (!go_finite (y))
+ y = 0; /* excel is just sooooo consistent */
+ if (!go_finite (x))
+ x = i;
+ x_canvas = gog_axis_map_to_canvas (x_map, x);
+ y_canvas = gog_axis_map_to_canvas (y_map, y);
+ if (GOG_IS_BUBBLE_PLOT(model)) {
+ z = *z_vals++;
+ if (!go_finite (z)) continue;
+ if (z < 0) {
+ if (GOG_BUBBLE_PLOT(model)->show_negatives) {
+ gog_renderer_push_style (view->renderer, neg_style);
+ bubble_draw_circle (view, x_canvas, y_canvas,
+ ((size_as_area)? sqrt (- z / zmax): - z / zmax) * rmax);
+ gog_renderer_pop_style (view->renderer);
+ } else continue;
+ } else {
+ if (model->base.vary_style_by_element)
+ gog_theme_fillin_style (theme, style, GOG_OBJECT (series),
+ model->base.index_num + i - 1, FALSE);
+ bubble_draw_circle (view, x_canvas, y_canvas, ((size_as_area)? sqrt (z / zmax): z / zmax) * rmax);
+ }
+ } else if (prev_valid && show_lines) {
+ path[0].x = prev_x_canvas;
+ path[0].y = prev_y_canvas;
+ path[1].x = x_canvas;
+ path[1].y = y_canvas;
+ gog_renderer_draw_path (view->renderer, path, NULL);
+ }
+ }
+
+ /* draw error bars after line */
+ if (prev_valid) {
+ if (gog_error_bar_is_visible (series->x_errors)) {
+ GogErrorBar const *bar = series->x_errors;
+ if (gog_error_bar_get_bounds (bar, i - 2, &xerrmin, &xerrmax)) {
+ gog_error_bar_render (bar, view->renderer,
+ x_map, y_map,
+ prev_x, prev_y,
+ xerrmin, xerrmax, TRUE);
+ }
+ }
+ if (gog_error_bar_is_visible (series->y_errors)) {
+ GogErrorBar const *bar = series->y_errors;
+ if (gog_error_bar_get_bounds (bar, i - 2, &yerrmin, &yerrmax)) {
+ gog_error_bar_render (bar, view->renderer,
+ x_map, y_map, prev_x, prev_y,
+ yerrmin, yerrmax, FALSE);
+ }
+ }
+ }
+
+ /* draw marker after line */
+ if (prev_valid && show_marks &&
+ x_margin_min <= prev_x_canvas && prev_x_canvas <= x_margin_max &&
+ y_margin_min <= prev_y_canvas && prev_y_canvas <= y_margin_max) {
+ markers[j][k].x = prev_x_canvas;
+ markers[j][k].y = prev_y_canvas;
+ k++;
+ }
+
+ prev_x_canvas = x_canvas;
+ prev_y_canvas = y_canvas;
+ prev_x = x;
+ prev_y = y;
+ prev_valid = valid;
+ }
+
+ /* draw error bars after line */
+ if (gog_error_bar_is_visible (series->x_errors)) {
+ GogErrorBar const *bar = series->x_errors;
+ if (gog_error_bar_get_bounds (bar, i - 2, &xerrmin, &xerrmax)) {
+ gog_error_bar_render (bar, view->renderer,
+ x_map, y_map, prev_x, prev_y,
+ xerrmin, xerrmax, TRUE);
+ }
+ }
+ if (gog_error_bar_is_visible (series->y_errors)) {
+ GogErrorBar const *bar = series->y_errors;
+ if (gog_error_bar_get_bounds (bar, i - 2, &yerrmin, &yerrmax)) {
+ gog_error_bar_render (bar, view->renderer,
+ x_map, y_map, prev_x, prev_y,
+ yerrmin, yerrmax, FALSE);
+ }
+ }
+
+ /* draw marker after line */
+ if (prev_valid && show_marks &&
+ x_margin_min <= prev_x_canvas && prev_x_canvas <= x_margin_max &&
+ y_margin_min <= prev_y_canvas && prev_y_canvas <= y_margin_max) {
+ markers[j][k].x = x_canvas;
+ markers[j][k].y = y_canvas;
+ k++;
+ }
+
+ gog_renderer_pop_style (view->renderer);
+ num_markers[j] = k;
+ }
+
+ if (GOG_IS_BUBBLE_PLOT (model)) {
+ if (model->base.vary_style_by_element)
+ g_object_unref (style);
+ if (((GogBubblePlot*)model)->show_negatives)
+ g_object_unref (neg_style);
+ }
+
+ gog_renderer_clip_pop (view->renderer);
+
+ if (!GOG_IS_BUBBLE_PLOT (model))
+ for (j = 0, ptr = model->base.series ; ptr != NULL ; ptr = ptr->next, j++) {
+ if (markers[j] != NULL) {
+ series = ptr->data;
+ style = GOG_STYLED_OBJECT (series)->style;
+ gog_renderer_push_style (view->renderer, style);
+ for (k = 0; k < num_markers[j]; k++)
+ gog_renderer_draw_marker (view->renderer,
+ markers[j][k].x,
+ markers[j][k].y);
+ gog_renderer_pop_style (view->renderer);
+ g_free (markers[j]);
+ }
+ }
+
+ gog_axis_map_free (x_map);
+ gog_axis_map_free (y_map);
+}
+
+static gboolean
+gog_xy_view_info_at_point (GogView *view, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name)
+{
+ return FALSE;
+}
+
+static void
+gog_xy_view_class_init (GogViewClass *view_klass)
+{
+ view_klass->render = gog_xy_view_render;
+ view_klass->info_at_point = gog_xy_view_info_at_point;
+ view_klass->clip = FALSE;
+}
+
+static GSF_CLASS (GogXYView, gog_xy_view,
+ gog_xy_view_class_init, NULL,
+ GOG_PLOT_VIEW_TYPE)
+
+/*****************************************************************************/
+
+typedef GogSeriesClass GogXYSeriesClass;
+
+enum {
+ SERIES_PROP_0,
+ SERIES_PROP_XERRORS,
+ SERIES_PROP_YERRORS
+};
+
+static GogStyledObjectClass *series_parent_klass;
+
+static void
+gog_xy_series_update (GogObject *obj)
+{
+ double *x_vals = NULL, *y_vals = NULL;
+ int x_len = 0, y_len = 0;
+ GogXYSeries *series = GOG_XY_SERIES (obj);
+ unsigned old_num = series->base.num_elements;
+
+ if (series->base.values[1].data != NULL) {
+ y_vals = go_data_vector_get_values (GO_DATA_VECTOR (series->base.values[1].data));
+ y_len = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[1].data));
+ }
+ if (GOG_IS_BUBBLE_PLOT (series->base.plot)) {
+ double *z_vals = NULL;
+ int z_len = 0;
+ if (series->base.values[2].data != NULL) {
+ z_vals = go_data_vector_get_values (GO_DATA_VECTOR (series->base.values[2].data));
+ z_len = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[2].data));
+ if (y_len > z_len) y_len = z_len;
+ }
+ }
+ if (series->base.values[0].data != NULL) {
+ x_vals = go_data_vector_get_values (GO_DATA_VECTOR (series->base.values[0].data));
+ x_len = go_data_vector_get_len (
+ GO_DATA_VECTOR (series->base.values[0].data));
+ } else
+ x_len = y_len;
+ series->base.num_elements = MIN (x_len, y_len);
+
+ /* queue plot for redraw */
+ gog_object_request_update (GOG_OBJECT (series->base.plot));
+ if (old_num != series->base.num_elements)
+ gog_plot_request_cardinality_update (series->base.plot);
+
+ if (series_parent_klass->base.update)
+ series_parent_klass->base.update (obj);
+}
+
+static void
+gog_xy_series_init (GObject *obj)
+{
+ GogXYSeries *series = GOG_XY_SERIES (obj);
+
+ series->x_errors = series->y_errors = NULL;
+}
+
+static void
+gog_xy_series_finalize (GObject *obj)
+{
+ GogXYSeries *series = GOG_XY_SERIES (obj);
+
+ if (series->x_errors != NULL) {
+ g_object_unref (series->x_errors);
+ series->x_errors = NULL;
+ }
+
+ if (series->y_errors != NULL) {
+ g_object_unref (series->y_errors);
+ series->y_errors = NULL;
+ }
+
+ G_OBJECT_CLASS (series_parent_klass)->finalize (obj);
+}
+
+static void
+gog_xy_series_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ GogSeries *series = GOG_SERIES (gso);
+ GogXYPlot const *plot;
+
+ series_parent_klass->init_style (gso, style);
+ if (series->plot == NULL ||
+ GOG_IS_BUBBLE_PLOT (series->plot))
+ return;
+
+ plot = GOG_XY_PLOT (series->plot);
+ if (!plot->default_style_has_markers) {
+ style->disable_theming |= GOG_STYLE_MARKER;
+ if (style->marker.auto_shape) {
+ GOMarker *m = go_marker_new ();
+ go_marker_set_shape (m, GO_MARKER_NONE);
+ gog_style_set_marker (style, m);
+ }
+ }
+ if (!plot->default_style_has_lines) {
+ style->disable_theming |= GOG_STYLE_LINE;
+ if (style->line.auto_dash)
+ style->line.dash_type = GO_LINE_NONE;
+ if (style->line.auto_color) {
+ style->line.width = 0;
+ style->line.color = RGBA_BLACK;
+ }
+ }
+}
+
+static void
+gog_xy_series_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogXYSeries *series= GOG_XY_SERIES (obj);
+ GogErrorBar* bar;
+
+ switch (param_id) {
+ case SERIES_PROP_XERRORS :
+ bar = g_value_get_object (value);
+ if (series->x_errors == bar)
+ return;
+ if (bar) {
+ bar = gog_error_bar_dup (bar);
+ bar->series = GOG_SERIES (series);
+ bar->dim_i = 0;
+ bar->error_i = series->base.plot->desc.series.num_dim - 2;
+ }
+ if (!series->base.needs_recalc) {
+ series->base.needs_recalc = TRUE;
+ gog_object_emit_changed (GOG_OBJECT (series), FALSE);
+ }
+ if (series->x_errors != NULL)
+ g_object_unref (series->x_errors);
+ series->x_errors = bar;
+ break;
+ case SERIES_PROP_YERRORS :
+ bar = g_value_get_object (value);
+ if (series->y_errors == bar)
+ return;
+ if (bar) {
+ bar = gog_error_bar_dup (bar);
+ bar->series = GOG_SERIES (series);
+ bar->dim_i = 1;
+ bar->error_i = series->base.plot->desc.series.num_dim - 4;
+ }
+ if (!series->base.needs_recalc) {
+ series->base.needs_recalc = TRUE;
+ gog_object_emit_changed (GOG_OBJECT (series), FALSE);
+ }
+ if (series->y_errors != NULL)
+ g_object_unref (series->y_errors);
+ series->y_errors = bar;
+ break;
+ }
+}
+
+static void
+gog_xy_series_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogXYSeries *series= GOG_XY_SERIES (obj);
+
+ switch (param_id) {
+ case SERIES_PROP_XERRORS :
+ g_value_set_object (value, series->x_errors);
+ break;
+ case SERIES_PROP_YERRORS :
+ g_value_set_object (value, series->y_errors);
+ break;
+ }
+}
+
+static void
+gog_xy_series_populate_editor (GogSeries *series,
+ GtkNotebook *book,
+ GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ GtkWidget *error_page;
+ error_page = gog_error_bar_prefs (series, "y-errors", FALSE, dalloc, cc);
+ gtk_notebook_prepend_page (book, error_page, gtk_label_new (_("Y Error bars")));
+ error_page = gog_error_bar_prefs (series,"x-errors", TRUE, dalloc, cc);
+ gtk_notebook_prepend_page (book, error_page, gtk_label_new (_("X Error bars")));
+}
+
+static void
+gog_xy_series_class_init (GogStyledObjectClass *gso_klass)
+{
+ GogObjectClass *gog_klass = (GogObjectClass *)gso_klass;
+ GObjectClass *gobject_klass = (GObjectClass *) gso_klass;
+ GogSeriesClass *gog_series_klass = (GogSeriesClass*) gso_klass;
+
+ series_parent_klass = g_type_class_peek_parent (gso_klass);
+ gog_klass->update = gog_xy_series_update;
+ gso_klass->init_style = gog_xy_series_init_style;
+ gobject_klass->finalize = gog_xy_series_finalize;
+ gobject_klass->set_property = gog_xy_series_set_property;
+ gobject_klass->get_property = gog_xy_series_get_property;
+ gog_series_klass->populate_editor = gog_xy_series_populate_editor;
+
+ g_object_class_install_property (gobject_klass, SERIES_PROP_XERRORS,
+ g_param_spec_object ("x-errors", "x-errors",
+ "GogErrorBar *",
+ GOG_ERROR_BAR_TYPE, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, SERIES_PROP_YERRORS,
+ g_param_spec_object ("y-errors", "y-errors",
+ "GogErrorBar *",
+ GOG_ERROR_BAR_TYPE, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+}
+
+static GSF_CLASS (GogXYSeries, gog_xy_series,
+ gog_xy_series_class_init, gog_xy_series_init,
+ GOG_SERIES_TYPE)
+
+void
+plugin_init (void)
+{
+ gog_xy_plot_get_type ();
+ gog_bubble_plot_get_type ();
+}
+
+void
+plugin_cleanup (void)
+{
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-color-group.h
@@ -0,0 +1,60 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-color-group.h - Utility to keep a shered memory of custom colors
+ * between arbitrary widgets.
+ * Copyright 2000, Michael Levy
+ * Copyright 2001, Almer S. Tigelaar
+ *
+ * Authors:
+ * Michael Levy (mlevy at genoscope.cns.fr)
+ * Revised and polished by:
+ * Almer S. Tigelaar <almer at gnome.org>
+ * Rewritten yet again by
+ * Jody Goldberg <jody at gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef GO_COLOR_GROUP_H
+#define GO_COLOR_GROUP_H
+
+#include <glib-object.h>
+#include <goffice/utils/go-color.h>
+
+G_BEGIN_DECLS
+
+#define GO_COLOR_GROUP_HISTORY_SIZE 8
+
+typedef struct {
+ GObject parent;
+
+ char *name;
+ gpointer context;
+
+ GOColor history[GO_COLOR_GROUP_HISTORY_SIZE];
+} GOColorGroup;
+
+#define GO_COLOR_GROUP_TYPE (go_color_group_get_type ())
+#define GO_COLOR_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GO_COLOR_GROUP_TYPE, GOColorGroup))
+#define IS_GO_COLOR_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GO_COLOR_GROUP_TYPE))
+
+GType go_color_group_get_type (void);
+GOColorGroup *go_color_group_find (char const *name, gpointer context);
+GOColorGroup *go_color_group_fetch (char const *name, gpointer context);
+void go_color_group_add_color (GOColorGroup *cg, GOColor c);
+
+G_END_DECLS
+
+#endif /* GO_COLOR_GROUP_H */
--- /dev/null
+++ lib/goffice/gui-utils/go-action-combo-pixmaps.c
@@ -0,0 +1,236 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-action-combo-pixmaps.c: A custom GtkAction to chose among a set of images
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <goffice/goffice-config.h>
+#include "go-action-combo-pixmaps.h"
+#include "go-combo-pixmaps.h"
+#include "go-combo-box.h"
+
+//#include <src/gui-util.h>
+#include <gui-util.h>
+
+#include <gtk/gtkaction.h>
+#include <gtk/gtktoolitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkwidget.h>
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+
+typedef struct {
+ GtkToolItem base;
+ GOComboPixmaps *combo; /* container has a ref, not us */
+} GOToolComboPixmaps;
+typedef GtkToolItemClass GOToolComboPixmapsClass;
+
+#define GO_TOOL_COMBO_PIXMAPS_TYPE (go_tool_combo_pixmaps_get_type ())
+#define GO_TOOL_COMBO_PIXMAPS(o) (G_TYPE_CHECK_INSTANCE_CAST (o, GO_TOOL_COMBO_PIXMAPS_TYPE, GOToolComboPixmaps))
+#define IS_GO_TOOL_COMBO_PIXMAPS(o) (G_TYPE_CHECK_INSTANCE_TYPE (o, GO_TOOL_COMBO_PIXMAPS_TYPE))
+
+static GType go_tool_combo_pixmaps_get_type (void);
+static gboolean
+go_tool_combo_pixmaps_set_tooltip (GtkToolItem *tool_item, GtkTooltips *tooltips,
+ char const *tip_text,
+ char const *tip_private)
+{
+ GOToolComboPixmaps *self = (GOToolComboPixmaps *)tool_item;
+ go_combo_box_set_tooltip (GO_COMBO_BOX (self->combo), tooltips,
+ tip_text, tip_private);
+ return TRUE;
+}
+static void
+go_tool_combo_pixmaps_class_init (GtkToolItemClass *tool_item_klass)
+{
+ tool_item_klass->set_tooltip = go_tool_combo_pixmaps_set_tooltip;
+}
+
+static GSF_CLASS (GOToolComboPixmaps, go_tool_combo_pixmaps,
+ go_tool_combo_pixmaps_class_init, NULL,
+ GTK_TYPE_TOOL_ITEM)
+
+/*****************************************************************************/
+
+struct _GOActionComboPixmaps {
+ GtkAction base;
+ GOActionComboPixmapsElement const *elements;
+ int ncols, nrows;
+
+ gboolean updating_proxies;
+ int selected_id;
+};
+typedef GtkActionClass GOActionComboPixmapsClass;
+
+static GObjectClass *combo_pixmaps_parent;
+static void
+go_action_combo_pixmaps_connect_proxy (GtkAction *a, GtkWidget *proxy)
+{
+ GTK_ACTION_CLASS (combo_pixmaps_parent)->connect_proxy (a, proxy);
+
+ if (GTK_IS_IMAGE_MENU_ITEM (proxy)) { /* set the icon */
+ GOActionComboPixmaps *paction = (GOActionComboPixmaps *)a;
+ GtkWidget *image = gtk_image_new_from_stock (
+ paction->elements[0].stock_id, GTK_ICON_SIZE_MENU);
+ gtk_widget_show (image);
+ gtk_image_menu_item_set_image (
+ GTK_IMAGE_MENU_ITEM (proxy), image);
+ }
+}
+
+static void
+cb_selection_changed (GOComboPixmaps *combo, int id, GOActionComboPixmaps *paction)
+{
+ GSList *ptr;
+ if (paction->updating_proxies)
+ return;
+ paction->selected_id = id;
+
+ paction->updating_proxies = TRUE;
+ ptr = gtk_action_get_proxies (GTK_ACTION (paction));
+ for ( ; ptr != NULL ; ptr = ptr->next)
+ if (IS_GO_COMBO_PIXMAPS (ptr->data) &&
+ go_combo_pixmaps_get_selected (ptr->data, NULL) != id)
+ go_combo_pixmaps_select_id (ptr->data, id);
+ paction->updating_proxies = FALSE;
+
+ gtk_action_activate (GTK_ACTION (paction));
+}
+
+static GtkWidget *
+go_action_combo_pixmaps_create_tool_item (GtkAction *a)
+{
+ GOActionComboPixmaps *paction = (GOActionComboPixmaps *)a;
+ GOToolComboPixmaps *tool = g_object_new (GO_TOOL_COMBO_PIXMAPS_TYPE, NULL);
+ GOActionComboPixmapsElement const *el= paction->elements;
+
+ tool->combo = go_combo_pixmaps_new (paction->ncols);
+ for ( ; el->stock_id != NULL ; el++)
+ go_combo_pixmaps_add_element (tool->combo,
+ gtk_widget_render_icon (GTK_WIDGET (tool->combo),
+ el->stock_id,
+ GTK_ICON_SIZE_LARGE_TOOLBAR,
+ "GOActionComboPixmaps"),
+ el->id, _(el->untranslated_tooltip));
+ go_combo_pixmaps_select_id (tool->combo, paction->selected_id);
+
+ go_combo_box_set_relief (GO_COMBO_BOX (tool->combo), GTK_RELIEF_NONE);
+ go_combo_box_set_tearable (GO_COMBO_BOX (tool->combo), TRUE);
+ gnm_widget_disable_focus (GTK_WIDGET (tool->combo));
+ gtk_container_add (GTK_CONTAINER (tool), GTK_WIDGET (tool->combo));
+ gtk_widget_show (GTK_WIDGET (tool->combo));
+ gtk_widget_show (GTK_WIDGET (tool));
+
+ g_signal_connect (G_OBJECT (tool->combo),
+ "changed",
+ G_CALLBACK (cb_selection_changed), a);
+
+ return GTK_WIDGET (tool);
+}
+
+static GtkWidget *
+go_action_combo_pixmaps_create_menu_item (GtkAction *a)
+{
+ GOActionComboPixmaps *paction = (GOActionComboPixmaps *)a;
+ GOMenuPixmaps *submenu = go_menu_pixmaps_new (paction->ncols);
+ GOActionComboPixmapsElement const *el= paction->elements;
+ GtkWidget *item = gtk_image_menu_item_new ();
+
+ for ( ; el->stock_id != NULL ; el++)
+ go_menu_pixmaps_add_element (submenu,
+ gtk_widget_render_icon (GTK_WIDGET (item),
+ el->stock_id,
+ GTK_ICON_SIZE_MENU,
+ "GOActionComboPixmaps"),
+ el->id);
+
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), GTK_WIDGET (submenu));
+ gtk_widget_show (GTK_WIDGET (submenu));
+ g_signal_connect (G_OBJECT (submenu),
+ "changed",
+ G_CALLBACK (cb_selection_changed), a);
+ return item;
+}
+
+static void
+go_action_combo_pixmaps_finalize (GObject *obj)
+{
+ combo_pixmaps_parent->finalize (obj);
+}
+
+static void
+go_action_combo_pixmaps_class_init (GtkActionClass *gtk_act_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *)gtk_act_klass;
+
+ combo_pixmaps_parent = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = go_action_combo_pixmaps_finalize;
+
+ gtk_act_klass->create_tool_item = go_action_combo_pixmaps_create_tool_item;
+ gtk_act_klass->create_menu_item = go_action_combo_pixmaps_create_menu_item;
+ gtk_act_klass->connect_proxy = go_action_combo_pixmaps_connect_proxy;
+}
+
+GSF_CLASS (GOActionComboPixmaps, go_action_combo_pixmaps,
+ go_action_combo_pixmaps_class_init, NULL,
+ GTK_TYPE_ACTION)
+
+GOActionComboPixmaps *
+go_action_combo_pixmaps_new (char const *name,
+ GOActionComboPixmapsElement const *elements,
+ int ncols, int nrows)
+{
+ GOActionComboPixmaps *paction;
+
+ g_return_val_if_fail (ncols > 0, NULL);
+ g_return_val_if_fail (nrows > 0, NULL);
+ g_return_val_if_fail (elements != NULL, NULL);
+
+ paction = g_object_new (go_action_combo_pixmaps_get_type (),
+ "name", name,
+ NULL);
+ paction->elements = elements;
+ paction->ncols = ncols;
+ paction->nrows = nrows;
+ paction->selected_id = elements[0].id;
+
+ return paction;
+}
+
+int
+go_action_combo_pixmaps_get_selected (GOActionComboPixmaps *paction, int *indx)
+{
+ g_return_val_if_fail (IS_GO_ACTION_COMBO_PIXMAPS (paction), 0);
+
+ return paction->selected_id;
+}
+
+gboolean
+go_action_combo_pixmaps_select_id (GOActionComboPixmaps *paction, int id)
+{
+ gboolean res = TRUE;
+ GSList *ptr = gtk_action_get_proxies (GTK_ACTION (paction));
+
+ paction->selected_id = id;
+ for ( ; ptr != NULL ; ptr = ptr->next)
+ if (IS_GO_TOOL_COMBO_PIXMAPS (ptr->data))
+ res |= go_combo_pixmaps_select_id (
+ GO_TOOL_COMBO_PIXMAPS (ptr->data)->combo, id);
+
+ return res;
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-font-sel.glade
@@ -0,0 +1,272 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="font-toplevel">
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkTable" id="toplevel-table">
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">5</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">4</property>
+ <property name="column_spacing">4</property>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Family:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">family_combo</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="family_combo">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBoxEntry" id="size_combo">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Size:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_CENTER</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">size_combo</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="underline_combo">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Underline:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">underline_combo</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="checkbutton1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Bold</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">5</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="checkbutton2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Italic</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">5</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="checkbutton3">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">Stri_kethrough</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">5</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="colour_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Co_lor:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="font-preview-frame">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_ETCHED_IN</property>
+
+ <child>
+ <widget class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Preview</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">label_item</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">5</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/gui-utils/go-dock-band.h
@@ -0,0 +1,161 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* go-dock-band.h
+
+ Copyright (C) 1998 Free Software Foundation
+ All rights reserved.
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Ettore Perazzoli <ettore at comm2000.it>
+*/
+/*
+ @NOTATION@
+*/
+
+#ifndef _GO_DOCK_BAND_H
+#define _GO_DOCK_BAND_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_TYPE_DOCK_BAND (go_dock_band_get_type ())
+#define GO_DOCK_BAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GO_TYPE_DOCK_BAND, GoDockBand))
+#define GO_DOCK_BAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GO_TYPE_DOCK_BAND, GoDockBandClass))
+#define GO_IS_DOCK_BAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GO_TYPE_DOCK_BAND))
+#define GO_IS_DOCK_BAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GO_TYPE_DOCK_BAND))
+#define GO_DOCK_BAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GO_TYPE_DOCK_BAND, GoDockBandClass))
+
+typedef struct _GoDockBand GoDockBand;
+typedef struct _GoDockBandPrivate GoDockBandPrivate;
+typedef struct _GoDockBandClass GoDockBandClass;
+typedef struct _GoDockBandChild GoDockBandChild;
+
+#include <goffice/gui-utils/go-dock.h>
+#include <goffice/gui-utils/go-dock-item.h>
+#include <goffice/gui-utils/go-dock-layout.h>
+
+struct _GoDockBand
+{
+ GtkContainer container;
+
+ GList *children; /* GoDockBandChild */
+
+ GList *floating_child; /* GoDockBandChild */
+
+ /* This used to remember the allocation before the drag begin: it is
+ necessary to do so because we actually decide what docking action
+ happens depending on it, instead of using the current allocation
+ (which might be constantly changing while the user drags things
+ around). */
+ GtkAllocation drag_allocation;
+
+ guint tot_offsets;
+
+ guint max_space_requisition : 16;
+ guint num_children : 8;
+ guint new_for_drag : 1;
+ guint doing_drag : 1;
+ guint orientation : 1;
+
+ /*< private >*/
+ GoDockBandPrivate *_priv;
+};
+
+struct _GoDockBandClass
+{
+ GtkContainerClass parent_class;
+
+ gpointer dummy[2];
+};
+
+struct _GoDockBandChild
+{
+ GtkWidget *widget;
+
+ GtkAllocation drag_allocation;
+
+ /* Maximum (requested) offset from the previous child. */
+ guint16 offset;
+
+ /* Actual offset. */
+ guint16 real_offset;
+
+ guint16 drag_offset;
+
+ guint16 prev_space, foll_space;
+ guint16 drag_prev_space, drag_foll_space;
+
+ guint16 max_space_requisition;
+};
+
+GtkWidget *go_dock_band_new (void);
+GtkType go_dock_band_get_type (void) G_GNUC_CONST;
+
+void go_dock_band_set_orientation (GoDockBand *band,
+ GtkOrientation orientation);
+GtkOrientation go_dock_band_get_orientation (GoDockBand *band);
+
+gboolean go_dock_band_insert (GoDockBand *band,
+ GtkWidget *child,
+ guint offset,
+ gint position);
+gboolean go_dock_band_prepend (GoDockBand *band,
+ GtkWidget *child,
+ guint offset);
+gboolean go_dock_band_append (GoDockBand *band,
+ GtkWidget *child,
+ guint offset);
+
+void go_dock_band_set_child_offset (GoDockBand *band,
+ GtkWidget *child,
+ guint offset);
+guint go_dock_band_get_child_offset (GoDockBand *band,
+ GtkWidget *child);
+void go_dock_band_move_child (GoDockBand *band,
+ GList *old_child,
+ guint new_num);
+
+guint go_dock_band_get_num_children (GoDockBand *band);
+
+void go_dock_band_drag_begin (GoDockBand *band,
+ GoDockItem *item);
+gboolean go_dock_band_drag_to (GoDockBand *band,
+ GoDockItem *item,
+ gint x, gint y);
+void go_dock_band_drag_end (GoDockBand *band,
+ GoDockItem *item);
+
+GoDockItem *go_dock_band_get_item_by_name (GoDockBand *band,
+ const char *name,
+ guint *position_return,
+ guint *offset_return);
+
+void go_dock_band_layout_add (GoDockBand *band,
+ GoDockLayout *layout,
+ GoDockPlacement placement,
+ guint band_num);
+
+#if 1 /* defined(GO_UI_INTERNAL) */
+gint _bonobo_dock_band_handle_key_nav (GoDockBand *band,
+ GoDockItem *item,
+ GdkEventKey *event);
+#endif /* GO_UI_INTERNAL */
+
+G_END_DECLS
+
+#endif
--- /dev/null
+++ lib/goffice/gui-utils/go-gui-utils.h
@@ -0,0 +1,41 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-gui-utils.h - Misc GTK+ utilities
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef GO_GUI_UTILS_H
+#define GO_GUI_UTILS_H
+
+G_BEGIN_DECLS
+
+#include <gtk/gtkwidget.h>
+#include <glade/glade-xml.h>
+#include <goffice/app/goffice-app.h>
+
+void go_editable_enters (GtkWindow *window, GtkWidget *w);
+
+GtkWidget *go_gtk_button_new_with_stock_image (char const *text,
+ char const *stock_id);
+
+GladeXML *go_libglade_new (char const *gladefile, char const *root,
+ char const *domain, GOCmdContext *cc);
+
+GdkPixbuf *go_pixbuf_intelligent_scale (GdkPixbuf *pixbuf,
+ guint width, guint height);
+
+G_END_DECLS
+
+#endif /* GO_GUI_UTILS_H */
--- /dev/null
+++ lib/goffice/gui-utils/go-action-combo-pixmaps.h
@@ -0,0 +1,51 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-action-combo-pixmaps.h: A custom GtkAction to chose among a set of images
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **/
+
+#ifndef __GO_ACTION_COMBO_PIXMAPS_H__
+#define __GO_ACTION_COMBO_PIXMAPS_H__
+
+#include <glib-object.h>
+#include <goffice/gui-utils/go-combo-pixmaps.h>
+
+G_BEGIN_DECLS
+
+#define GO_ACTION_COMBO_PIXMAPS_TYPE (go_action_combo_pixmaps_get_type ())
+#define GO_ACTION_COMBO_PIXMAPS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_ACTION_COMBO_PIXMAPS_TYPE, GOActionComboPixmaps))
+#define IS_GO_ACTION_COMBO_PIXMAPS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_ACTION_COMBO_PIXMAPS_TYPE))
+
+typedef struct _GOActionComboPixmaps GOActionComboPixmaps;
+typedef struct {
+ char const *untranslated_tooltip;
+ char const *stock_id;
+ int id;
+} GOActionComboPixmapsElement;
+
+GType go_action_combo_pixmaps_get_type (void);
+GOActionComboPixmaps *
+ go_action_combo_pixmaps_new (char const *name,
+ GOActionComboPixmapsElement const *elements,
+ int ncols, int nrows);
+int go_action_combo_pixmaps_get_selected (GOActionComboPixmaps *action, int *indx);
+gboolean go_action_combo_pixmaps_select_id (GOActionComboPixmaps *action, int id);
+
+G_END_DECLS
+
+#endif /* __GO_ACTION_COMBO_PIXMAPS_H__ */
--- /dev/null
+++ lib/goffice/gui-utils/go-dock-item-grip.c
@@ -0,0 +1,363 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/**
+ * go-dock-item-grip.c
+ *
+ * Author:
+ * Michael Meeks
+ *
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ */
+
+#include "gnumeric-config.h"
+#include <glib/gi18n.h>
+#include "go-a11y.h"
+#include "go-dock-band.h"
+#include "go-dock-item-grip.h"
+#include <glib-object.h>
+#include <atk/atkstateset.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkaccessible.h>
+#include <gtk/gtkbindings.h>
+#include <libgnome/gnome-macros.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#define DRAG_HANDLE_SIZE 10
+
+enum {
+ ACTIVATE,
+ LAST_SIGNAL
+};
+static guint signals [LAST_SIGNAL];
+
+static AtkObjectClass *a11y_parent_class = NULL;
+
+GNOME_CLASS_BOILERPLATE (GoDockItemGrip, go_dock_item_grip,
+ GtkWidget, GTK_TYPE_WIDGET)
+
+static gint
+go_dock_item_grip_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GdkRectangle *clip = &event->area;
+ GdkRectangle *rect = &widget->allocation;
+ GoDockItemGrip *grip = (GoDockItemGrip *) widget;
+ GtkShadowType shadow = GTK_SHADOW_OUT;
+
+ gtk_paint_handle (widget->style,
+ widget->window,
+ GTK_WIDGET_STATE (widget),
+ shadow,
+ clip, widget, "dockitem",
+ rect->x, rect->y, rect->width, rect->height,
+ grip->item->orientation);
+
+ if (GTK_WIDGET_HAS_FOCUS (widget)) {
+ gint focus_width;
+ gint focus_pad;
+ GdkRectangle focus;
+
+ gtk_widget_style_get (GTK_WIDGET (widget),
+ "focus-line-width", &focus_width,
+ "focus-padding", &focus_pad,
+ NULL);
+
+ focus = *rect;
+ focus.x += widget->style->xthickness + focus_pad;
+ focus.y += widget->style->ythickness + focus_pad;
+ focus.width -= 2 * (widget->style->xthickness + focus_pad);
+ focus.height -= 2 * (widget->style->xthickness + focus_pad);
+
+ gtk_paint_focus (widget->style, widget->window,
+ GTK_WIDGET_STATE (widget),
+ clip, widget, "dockitem",
+ focus.x, focus.y,
+ focus.width, focus.height);
+ }
+
+ return FALSE;
+}
+
+static void
+grip_item_a11y_initialize (AtkObject *accessible, gpointer widget)
+{
+ accessible->role = ATK_ROLE_SEPARATOR;
+ atk_object_set_name (accessible, "grip");
+
+ a11y_parent_class->initialize (accessible, widget);
+}
+
+static AtkStateSet*
+grip_item_a11y_ref_state_set (AtkObject *accessible)
+{
+ AtkStateSet *state_set;
+ GoDockItemGrip *grip;
+ GtkWidget *widget;
+
+ state_set = a11y_parent_class->ref_state_set (accessible);
+ widget = GTK_ACCESSIBLE (accessible)->widget;
+ if (widget == NULL)
+ return state_set;
+
+ grip = GO_DOCK_ITEM_GRIP (widget);
+
+ if (grip == NULL)
+ return state_set;
+
+ if (grip->item->orientation == GTK_ORIENTATION_VERTICAL) {
+ atk_state_set_add_state (state_set, ATK_STATE_VERTICAL);
+ atk_state_set_remove_state (state_set, ATK_STATE_HORIZONTAL);
+ } else {
+ atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL);
+ atk_state_set_remove_state (state_set, ATK_STATE_VERTICAL);
+ }
+
+ return state_set;
+}
+
+static GoDock *
+get_dock (GtkWidget *widget)
+{
+ while (widget && !GO_IS_DOCK (widget))
+ widget = widget->parent;
+
+ return (GoDock *) widget;
+}
+
+static void
+go_dock_item_grip_dock (GoDockItemGrip *grip)
+{
+ GoDock *dock;
+ int placement;
+
+ g_return_if_fail (GO_IS_DOCK_ITEM_GRIP (grip));
+
+ if (!grip->item->is_floating)
+ return;
+
+ dock = get_dock (GTK_WIDGET (grip->item));
+ g_return_if_fail (dock != NULL);
+
+ go_dock_item_unfloat (grip->item);
+
+ g_object_ref (G_OBJECT (grip->item));
+ gtk_container_remove (
+ GTK_CONTAINER (
+ GTK_WIDGET (grip->item)->parent),
+ GTK_WIDGET (grip->item));
+
+ if (grip->item->orientation == GTK_ORIENTATION_HORIZONTAL)
+ placement = GO_DOCK_TOP;
+ else
+ placement = GO_DOCK_LEFT;
+
+ go_dock_add_item (
+ dock, grip->item,
+ placement, 2, 0, 0, TRUE);
+ g_object_unref (G_OBJECT (grip->item));
+}
+
+static void
+go_dock_item_grip_undock (GoDockItemGrip *grip)
+{
+ guint x, y;
+
+ g_return_if_fail (GO_IS_DOCK_ITEM_GRIP (grip));
+
+ if (grip->item->is_floating)
+ return;
+
+ gdk_window_get_position (
+ GTK_WIDGET (grip)->window, &x, &y);
+
+ go_dock_item_detach (grip->item, x, y);
+}
+
+enum {
+ ACTION_DOCK,
+ ACTION_UNDOCK,
+ ACTION_LAST
+};
+
+static gboolean
+go_dock_item_grip_do_action (AtkAction *action,
+ gint i)
+{
+ GoDockItemGrip *grip;
+ GtkWidget *widget;
+
+ widget = GTK_ACCESSIBLE (action)->widget;
+ if (widget == NULL)
+ return FALSE;
+
+ grip = GO_DOCK_ITEM_GRIP (widget);
+
+ if (grip->item->behavior & GO_DOCK_ITEM_BEH_LOCKED)
+ return FALSE;
+
+ switch (i) {
+ case ACTION_DOCK:
+ go_dock_item_grip_dock (grip);
+ break;
+ case ACTION_UNDOCK:
+ go_dock_item_grip_undock (grip);
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static gint
+go_dock_item_grip_get_n_actions (AtkAction *action)
+{
+ GoDockItemGrip *grip;
+ GtkWidget *widget;
+
+ widget = GTK_ACCESSIBLE (action)->widget;
+ if (widget == NULL)
+ return 0;
+
+ grip = GO_DOCK_ITEM_GRIP (widget);
+
+ if (grip->item->behavior & GO_DOCK_ITEM_BEH_LOCKED)
+ return 0;
+ else
+ return ACTION_LAST;
+}
+
+static void
+grip_item_a11y_class_init (AtkObjectClass *klass)
+{
+ a11y_parent_class = g_type_class_peek_parent (klass);
+
+ klass->initialize = grip_item_a11y_initialize;
+ klass->ref_state_set = grip_item_a11y_ref_state_set;
+}
+
+static AtkObject *
+go_dock_item_grip_get_accessible (GtkWidget *widget)
+{
+#if 0
+ AtkObject *accessible;
+ static GType a11y_type = 0;
+
+ if (!a11y_type) {
+ AtkActionIface action_if;
+
+ a11y_type = go_a11y_get_derived_type_for (
+ GO_TYPE_DOCK_ITEM_GRIP,
+ NULL, grip_item_a11y_class_init);
+
+ memset (&action_if, 0, sizeof (AtkActionIface));
+ action_if.do_action = go_dock_item_grip_do_action;
+ action_if.get_n_actions = go_dock_item_grip_get_n_actions;
+
+ go_a11y_add_actions_interface (
+ a11y_type, &action_if,
+ ACTION_DOCK, "dock", _("Dock the toolbar"), "<Enter>",
+ ACTION_UNDOCK, "undock", _("Un dock the toolbar"), "<Enter>",
+ -1);
+ }
+
+ if ((accessible = go_a11y_get_atk_object (widget)))
+ return accessible;
+
+ return go_a11y_set_atk_object_ret (
+ widget, g_object_new (a11y_type, NULL));
+#else
+ return NULL;
+#endif
+}
+
+static void
+go_dock_item_grip_activate (GoDockItemGrip *grip)
+{
+ if (grip->item->is_floating)
+ go_dock_item_grip_dock (grip);
+ else
+ go_dock_item_grip_undock (grip);
+}
+
+static void
+go_dock_item_grip_instance_init (GoDockItemGrip *grip)
+{
+ GTK_WIDGET_SET_FLAGS (grip, GTK_CAN_FOCUS);
+ GTK_WIDGET_SET_FLAGS (grip, GTK_NO_WINDOW);
+}
+
+static GoDockBand *
+get_dock_band (GtkWidget *widget)
+{
+ while (widget && !GO_IS_DOCK_BAND (widget))
+ widget = widget->parent;
+
+ return (GoDockBand *) widget;
+}
+
+static gint
+go_dock_item_grip_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ gboolean had_focus = GTK_WIDGET_HAS_FOCUS (widget);
+ GoDockBand *band = get_dock_band (widget);
+ GoDockItemGrip *grip = (GoDockItemGrip *) widget;
+
+ if (!grip->item->is_floating && band &&
+ _bonobo_dock_band_handle_key_nav (band, grip->item, event))
+ {
+ if (had_focus && !GTK_WIDGET_HAS_FOCUS (widget))
+ gtk_widget_grab_focus (widget);
+ return TRUE;
+ }
+
+ return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
+}
+
+static void
+go_dock_item_grip_class_init (GoDockItemGripClass *klass)
+{
+ GtkBindingSet *binding_set;
+ GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ widget_class->expose_event = go_dock_item_grip_expose;
+#if 0
+ widget_class->get_accessible = go_dock_item_grip_get_accessible;
+#endif
+ widget_class->key_press_event = go_dock_item_grip_key_press_event;
+
+ klass->activate = go_dock_item_grip_activate;
+
+ binding_set = gtk_binding_set_by_class (klass);
+
+ signals[ACTIVATE] =
+ g_signal_new ("activate",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (
+ GoDockItemGripClass, activate),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ widget_class->activate_signal = signals[ACTIVATE];
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Return, 0,
+ "activate", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Enter, 0,
+ "activate", 0);
+}
+
+GtkWidget *
+go_dock_item_grip_new (GoDockItem *item)
+{
+ GoDockItemGrip *grip = g_object_new (
+ GO_TYPE_DOCK_ITEM_GRIP, NULL);
+
+ grip->item = item;
+
+ return GTK_WIDGET (grip);
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-color-group.c
@@ -0,0 +1,213 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * color-group.c - Utility to keep a shared memory of custom colors
+ * between arbitrary widgets.
+ * Copyright 2000, Michael Levy
+ * Copyright 2001, Almer S. Tigelaar
+ * Copyright 2004, Jody Goldberg
+ *
+ * Authors:
+ * Michael Levy (mlevy at genoscope.cns.fr)
+ * Revised and polished by:
+ * Almer S. Tigelaar <almer at gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-color-group.h"
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+typedef struct {
+ GObjectClass base;
+
+ void (*history_changed) (GOColorGroup *group);
+} GOColorGroupClass;
+
+enum {
+ HISTORY_CHANGED,
+ LAST_SIGNAL
+};
+
+static GObjectClass *go_color_group_parent_class;
+static guint go_color_group_signals [LAST_SIGNAL] = { 0 };
+static GHashTable *go_color_groups = NULL;
+
+static void
+go_color_group_finalize (GObject *obj)
+{
+ GOColorGroup *cg = GO_COLOR_GROUP (obj);
+
+ /* make this name available */
+ if (cg->name) {
+ g_hash_table_remove (go_color_groups, cg);
+ g_free (cg->name);
+ cg->name = NULL;
+ }
+
+ (go_color_group_parent_class->finalize) (obj);
+}
+
+static void
+go_color_group_class_init (GOColorGroupClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass*) klass;
+
+ object_class->finalize = &go_color_group_finalize;
+ go_color_group_parent_class = g_type_class_peek (G_TYPE_OBJECT);
+ go_color_group_signals [HISTORY_CHANGED] =
+ g_signal_new ("history-changed",
+ GO_COLOR_GROUP_TYPE,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOColorGroupClass, history_changed),
+ (GSignalAccumulator) NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+go_color_group_init (GOColorGroup *cg)
+{
+ int i;
+
+ cg->name = NULL;
+ cg->context = NULL;
+ for (i = 0 ; i < GO_COLOR_GROUP_HISTORY_SIZE ; i++)
+ cg->history[i] = RGBA_BLACK;
+}
+
+GSF_CLASS (GOColorGroup, go_color_group,
+ go_color_group_class_init, go_color_group_init,
+ G_TYPE_OBJECT)
+
+/**
+ * go_color_group_find :
+ * @name :
+ * @context :
+ *
+ * Look up the name/context specific color-group. Return NULL if it is not found.
+ * No reference is added if it is found.
+ */
+GOColorGroup *
+go_color_group_find (char const *name, gpointer context)
+{
+ GOColorGroup tmp_key;
+
+ if (go_color_groups == NULL)
+ return NULL;
+
+ g_return_val_if_fail(name != NULL, NULL);
+
+ tmp_key.name = (char *)name;
+ tmp_key.context = context;
+ return (GOColorGroup *) g_hash_table_lookup (go_color_groups, &tmp_key);
+ }
+
+static guint
+cg_hash (GOColorGroup const *key)
+{
+ return g_str_hash (key->name);
+}
+
+static gint
+cg_equal (GOColorGroup const *a, GOColorGroup const *b)
+{
+ if (a == b)
+ return TRUE;
+ if (a->context != b->context)
+ return FALSE;
+ return g_str_equal (a->name, b->name);
+}
+
+/**
+ * go_color_group_fetch :
+ * @name :
+ * @context :
+ *
+ * if name is NULL or a name not currently in use by another group
+ * then a new group is created and returned. If name was NULL
+ * then the new group is given a unique name prefixed by "__cg_autogen_name__"
+ * (thereby insuring namespace separation).
+ * If name was already used by a group then the reference count is
+ * incremented and a pointer to the group is returned.
+ */
+GOColorGroup *
+go_color_group_fetch (const gchar *name, gpointer context)
+{
+ GOColorGroup *cg;
+ gchar *new_name;
+
+ if (go_color_groups == NULL)
+ go_color_groups = g_hash_table_new (
+ (GHashFunc) cg_hash, (GEqualFunc) cg_equal);
+
+ if (name == NULL) {
+ static gint count = 0;
+
+ while (1) {
+ new_name = g_strdup_printf("color_group_number_%i", count++);
+ if (go_color_group_find (new_name, context) == NULL)
+ break;
+ g_free (new_name);
+ }
+ } else {
+ new_name = g_strdup (name);
+ cg = go_color_group_find (new_name, context);
+ if (cg != NULL) {
+ g_free (new_name);
+ g_object_ref (G_OBJECT (cg));
+ return cg;
+ }
+ }
+
+ cg = g_object_new (go_color_group_get_type (), NULL);
+
+ g_return_val_if_fail(cg != NULL, NULL);
+
+ cg->name = new_name;
+ cg->context = context;
+
+ /* lastly register this name */
+ g_hash_table_insert (go_color_groups, cg, cg);
+
+ return cg;
+}
+
+/**
+ * go_color_group_add_color :
+ * @cg : #GOColorGroup
+ * @c : the color
+ *
+ * Potentially slide the history to add the new colour. If it was already in
+ * the history reorder.
+ **/
+void
+go_color_group_add_color (GOColorGroup *cg, GOColor c)
+{
+ unsigned i;
+ g_return_if_fail (IS_GO_COLOR_GROUP (cg));
+
+ for (i = GO_COLOR_GROUP_HISTORY_SIZE ; i-- > 0 ;)
+ if (cg->history[i] == c)
+ break;
+ for ( ; i < GO_COLOR_GROUP_HISTORY_SIZE-1 ; i++)
+ cg->history [i] = cg->history [i+1];
+ cg->history [GO_COLOR_GROUP_HISTORY_SIZE-1] = c;
+ g_signal_emit (G_OBJECT (cg),
+ go_color_group_signals [HISTORY_CHANGED], 0);
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-combo-text.c
@@ -0,0 +1,395 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gnumeric-combo-text: A combo box for selecting from a list.
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-combo-text.h"
+#include "go-combo-box.h"
+#include "go-marshalers.h"
+
+#include <gtk/gtksignal.h>
+#include <gtk/gtkentry.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkscrolledwindow.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+struct _GoComboText {
+ GOComboBox parent;
+
+ GCompareFunc cmp_func;
+
+ GtkWidget *entry;
+ GtkWidget *list;
+ GtkWidget *scroll;
+ int rows;
+};
+
+typedef struct {
+ GOComboBoxClass base;
+
+ gboolean (* selection_changed) (GoComboText *ct, GtkTreeSelection *selection);
+ gboolean (* entry_changed) (GoComboText *ct, char const *new_str);
+} GoComboTextClass;
+
+#define GO_COMBO_TEXT_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, go_combo_text_get_type (), GoComboTextClass)
+
+enum {
+ SELECTION_CHANGED,
+ ENTRY_CHANGED,
+ LAST_SIGNAL
+};
+static guint combo_text_signals [LAST_SIGNAL] = { 0 };
+
+/**
+ * A utility wrapper around g_signal_emitv because it does not initiialize the
+ * result to FALSE if there is no handler.
+ */
+static gboolean
+go_signal_emit (GoComboText *ct, int signal,
+ gconstpointer arg, int default_ret)
+{
+ gboolean result;
+ GValue ret = { 0, };
+ GValue instance_and_parm [2] = { { 0, }, { 0, } };
+
+ g_value_init (instance_and_parm + 0, GO_TYPE_COMBO_TEXT);
+ g_value_set_instance (instance_and_parm + 0, G_OBJECT (ct));
+
+ g_value_init (instance_and_parm + 1, G_TYPE_POINTER);
+ g_value_set_pointer (instance_and_parm + 1, (gpointer)arg);
+
+ g_value_init (&ret, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&ret, default_ret);
+
+ g_signal_emitv (instance_and_parm, combo_text_signals [signal], 0, &ret);
+ result = g_value_get_boolean (&ret);
+
+ g_value_unset (instance_and_parm + 0);
+ g_value_unset (instance_and_parm + 1);
+
+ return result;
+}
+
+static void
+cb_entry_activate (GtkWidget *entry, gpointer ct)
+{
+ char const *text = gtk_entry_get_text (GTK_ENTRY (entry));
+
+ if (go_signal_emit (GO_COMBO_TEXT (ct), ENTRY_CHANGED, text, TRUE))
+ go_combo_text_set_text (GO_COMBO_TEXT (ct), text,
+ GO_COMBO_TEXT_CURRENT);
+}
+
+static void
+cb_list_changed (GtkTreeSelection *selection,
+ gpointer data)
+{
+ GoComboText *ct = GO_COMBO_TEXT (data);
+ GtkEntry *entry = GTK_ENTRY (ct->entry);
+ gboolean accept_change;
+ GtkTreeModel *store;
+ GtkTreeIter iter;
+ char const *text;
+
+ if (gtk_tree_selection_get_selected (selection, &store, &iter))
+ gtk_tree_model_get (store, &iter, 0, &text, -1);
+ else
+ text = "";
+
+ accept_change = TRUE;
+ if (go_signal_emit (ct, SELECTION_CHANGED, selection, TRUE))
+ accept_change = go_signal_emit (ct, ENTRY_CHANGED, text, TRUE);
+ if (accept_change)
+ gtk_entry_set_text (entry, text);
+
+ go_combo_box_popup_hide (GO_COMBO_BOX (data));
+}
+
+static void
+cb_scroll_size_request (GtkWidget *widget, GtkRequisition *requisition,
+ GoComboText *ct)
+{
+ GtkRequisition list_req;
+ int mon_width, mon_height;
+ GdkRectangle rect;
+ GdkScreen *screen;
+
+ /* In a Xinerama setup, use geometry of the actual display unit. */
+ screen = gtk_widget_get_screen (widget);
+ if (screen == NULL)
+ /* Looks like this will happen when
+ * embedded as a bonobo component */
+ screen = gdk_screen_get_default ();
+
+ gdk_screen_get_monitor_geometry (screen, 0, &rect);
+ mon_width = rect.width;
+ mon_height = rect.height;
+
+ gtk_widget_size_request (ct->list, &list_req);
+ if (requisition->height < list_req.height) {
+ int height = list_req.height;
+ GtkWidget const *w = ct->list;
+
+ if (w != NULL) {
+ /* Make room for a whole number of items which don't
+ * overflow the screen, but no more than 20. */
+ int avail_height, nitems;
+
+ avail_height = mon_height - 20
+ - GTK_CONTAINER (widget)->border_width * 2 + 4;
+ nitems = MIN (20, avail_height * ct->rows / w->requisition.height);
+ height = nitems * w->requisition.height / ct->rows;
+ if (height > list_req.height)
+ height = list_req.height;
+ }
+
+ /* FIXME : Why do we need 4 ??
+ * without it things end up scrolling.
+ */
+ requisition->height = height +
+ GTK_CONTAINER (widget)->border_width * 2 + 4;
+ }
+
+ requisition->width = MAX (requisition->width,
+ ct->entry->allocation.width +
+ GTK_CONTAINER (widget)->border_width * 2);
+ requisition->width = MIN (requisition->width, mon_width - 20);
+ requisition->height = MIN (requisition->height, mon_height - 20);
+}
+
+static void
+cb_screen_changed (GoComboText *ct, GdkScreen *previous_screen)
+{
+ GtkWidget *w = GTK_WIDGET (ct);
+ GdkScreen *screen = gtk_widget_has_screen (w)
+ ? gtk_widget_get_screen (w)
+ : NULL;
+
+ if (screen) {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (ct->scroll);
+ gtk_window_set_screen (GTK_WINDOW (toplevel), screen);
+ }
+}
+
+static void
+go_combo_text_init (GoComboText *ct)
+{
+ GtkCellRenderer *renderer;
+ GtkListStore *store;
+ GtkTreeViewColumn *column;
+
+ ct->rows = 0;
+ ct->entry = gtk_entry_new ();
+ ct->list = gtk_tree_view_new ();
+ g_object_set (G_OBJECT (ct->list), NULL);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ct->list), FALSE);
+ store = gtk_list_store_new (1, G_TYPE_STRING);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (ct->list), GTK_TREE_MODEL (store));
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (
+ NULL,
+ renderer, "text", 0, NULL);
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (ct->list), column);
+ g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (ct->list))),
+ "changed",
+ G_CALLBACK (cb_list_changed), (gpointer) ct);
+
+ ct->scroll = gtk_scrolled_window_new (NULL, NULL);
+
+ gtk_scrolled_window_set_policy (
+ GTK_SCROLLED_WINDOW (ct->scroll),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_add_with_viewport (
+ GTK_SCROLLED_WINDOW (ct->scroll), ct->list);
+ gtk_container_set_focus_hadjustment (
+ GTK_CONTAINER (ct->list),
+ gtk_scrolled_window_get_hadjustment (
+ GTK_SCROLLED_WINDOW (ct->scroll)));
+ gtk_container_set_focus_vadjustment (
+ GTK_CONTAINER (ct->list),
+ gtk_scrolled_window_get_vadjustment (
+ GTK_SCROLLED_WINDOW (ct->scroll)));
+
+ g_signal_connect (G_OBJECT (ct->entry),
+ "activate",
+ GTK_SIGNAL_FUNC (cb_entry_activate), (gpointer) ct);
+ g_signal_connect (G_OBJECT (ct->scroll),
+ "size_request",
+ G_CALLBACK (cb_scroll_size_request), (gpointer) ct);
+
+ gtk_widget_show (ct->entry);
+ go_combo_box_construct (GO_COMBO_BOX (ct),
+ ct->entry, ct->scroll, ct->list);
+
+ g_signal_connect (G_OBJECT (ct),
+ "screen-changed", G_CALLBACK (cb_screen_changed),
+ NULL);
+}
+
+static void
+go_combo_text_destroy (GtkObject *object)
+{
+ GtkObjectClass *parent;
+ GoComboText *ct = GO_COMBO_TEXT (object);
+
+ if (ct->list != NULL) {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (ct),
+ G_CALLBACK (cb_screen_changed), NULL);
+ ct->list = NULL;
+ }
+
+ parent = g_type_class_peek (GO_COMBO_BOX_TYPE);
+ if (parent && parent->destroy)
+ (*parent->destroy) (object);
+}
+
+static void
+go_combo_text_class_init (GtkObjectClass *klass)
+{
+ klass->destroy = &go_combo_text_destroy;
+
+ combo_text_signals [SELECTION_CHANGED] = g_signal_new ("selection_changed",
+ GO_TYPE_COMBO_TEXT,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GoComboTextClass, selection_changed),
+ (GSignalAccumulator) NULL, NULL,
+ go__BOOLEAN__POINTER,
+ G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
+ combo_text_signals [ENTRY_CHANGED] = g_signal_new ("entry_changed",
+ GO_TYPE_COMBO_TEXT,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GoComboTextClass, entry_changed),
+ (GSignalAccumulator) NULL, NULL,
+ go__BOOLEAN__POINTER,
+ G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
+}
+
+/**
+ * go_combo_text_new :
+ * @cmp_func : an optional comparison routine.
+ */
+GtkWidget*
+go_combo_text_new (GCompareFunc cmp_func)
+{
+ GoComboText *ct;
+
+ if (cmp_func == NULL)
+ cmp_func = &g_str_equal;
+
+ ct = g_object_new (GO_TYPE_COMBO_TEXT, NULL);
+ ct->cmp_func = cmp_func;
+ return GTK_WIDGET (ct);
+}
+
+GtkWidget *
+go_combo_text_glade_new (void)
+{
+ return go_combo_text_new (NULL);
+}
+
+GSF_CLASS (GoComboText, go_combo_text,
+ go_combo_text_class_init, go_combo_text_init,
+ GO_COMBO_BOX_TYPE)
+
+GtkWidget *
+go_combo_text_get_entry (GoComboText *ct)
+{
+ return ct->entry;
+}
+
+/**
+ * go_combo_text_set_text :
+ * @ct :
+ * @text : the label for the new item
+ * @start : where to begin the search in the list.
+ *
+ * return TRUE if the item is found in the list.
+ */
+gboolean
+go_combo_text_set_text (GoComboText *ct, const gchar *text,
+ GoComboTextSearch start)
+{
+ gboolean found = FALSE, result;
+ GtkTreeView *list = GTK_TREE_VIEW (ct->list);
+ GtkTreeIter iter;
+ GtkTreeModel *store;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (list);
+ char *label;
+
+ /* Be careful */
+ result = start != GO_COMBO_TEXT_FROM_TOP &&
+ gtk_tree_selection_get_selected (selection, &store, &iter);
+
+ if (result) {
+ if (start == GO_COMBO_TEXT_NEXT)
+ result = gtk_tree_model_iter_next (store, &iter);
+ for (; result ; result = gtk_tree_model_iter_next (store, &iter)) {
+ gtk_tree_model_get (store, &iter, 0, &label, -1);
+ if (ct->cmp_func (label, text))
+ break;
+ g_free (label);
+ }
+ } else
+ store = gtk_tree_view_get_model (list);
+
+ if (!result)
+ for (result = gtk_tree_model_get_iter_first (store, &iter);
+ result;
+ result = gtk_tree_model_iter_next (store, &iter)) {
+ gtk_tree_model_get (store, &iter, 0, &label, -1);
+ if (ct->cmp_func (label, text))
+ break;
+ g_free (label);
+ }
+
+ g_signal_handlers_block_by_func (G_OBJECT (selection),
+ G_CALLBACK (cb_list_changed),
+ (gpointer) ct);
+ gtk_tree_selection_unselect_all (selection);
+
+ /* Use visible label rather than supplied text just in case */
+ if (result) {
+ GtkTreePath *path = gtk_tree_model_get_path (store, &iter);
+ gtk_tree_selection_select_iter (selection, &iter);
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (ct->list), path, NULL, FALSE);
+ gtk_tree_path_free (path);
+ gtk_entry_set_text (GTK_ENTRY (ct->entry), label);
+ g_free (label);
+ found = TRUE;
+ } else
+ gtk_entry_set_text (GTK_ENTRY (ct->entry), text);
+
+ g_signal_handlers_unblock_by_func (G_OBJECT (selection),
+ G_CALLBACK (cb_list_changed),
+ (gpointer) ct);
+ return found;
+}
+
+/**
+ * go_combo_text_add_item :
+ * @ct : The text combo that will get the new element.
+ * @label : the user visible label for the new item
+ * @key : The unique key to identify this item.
+ *
+ * It is ok to have multiple items with the same label, but the key must be
+ * unique.
+ */
+void
+go_combo_text_add_item (GoComboText *ct, char const *label)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ g_return_if_fail (label != NULL);
+
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (ct->list)));
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, label, -1);
+ ct->rows++;
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-color-palette.h
@@ -0,0 +1,66 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-color-palette.h - A color selector palette
+ * Copyright 2000-2004, Ximian, Inc.
+ *
+ * Authors:
+ * This code was extracted from widget-color-combo.c
+ * written by Miguel de Icaza (miguel at kernel.org) and
+ * Dom Lachowicz (dominicl at seas.upenn.edu). The extracted
+ * code was re-packaged into a separate object by
+ * Michael Levy (mlevy at genoscope.cns.fr)
+ * And later revised and polished by
+ * Almer S. Tigelaar (almer at gnome.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef GO_GO_COLOR_PALETTE_H
+#define GO_GO_COLOR_PALETTE_H
+
+#include <goffice/gui-utils/go-color-group.h>
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GOColorPalette GOColorPalette;
+
+#define GO_COLOR_PALETTE_TYPE (go_color_palette_get_type ())
+#define GO_COLOR_PALETTE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GO_COLOR_PALETTE_TYPE, GOColorPalette))
+#define GO_COLOR_PALETTE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST(k), GO_COLOR_PALETTE_TYPE)
+#define IS_GO_COLOR_PALETTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GO_COLOR_PALETTE_TYPE))
+
+GType go_color_palette_get_type (void);
+
+GtkWidget *go_color_palette_new (char const *no_color_label,
+ GOColor default_color,
+ GOColorGroup *color_group);
+GtkWidget *go_color_palette_make_menu (char const *no_color_label,
+ GOColor default_color,
+ GOColorGroup *color_group,
+ char const *custom_dialog_title,
+ GOColor current_color);
+
+void go_color_palette_set_title (GOColorPalette *p, char const *title);
+void go_color_palette_set_group (GOColorPalette *p, GOColorGroup *cg);
+void go_color_palette_set_current_color (GOColorPalette *p, GOColor color);
+void go_color_palette_set_color_to_default (GOColorPalette *p);
+GOColor go_color_palette_get_current_color (GOColorPalette *p,
+ gboolean *is_default, gboolean *is_custom);
+void go_color_palette_set_allow_alpha (GOColorPalette *p, gboolean allow_alpha);
+
+G_END_DECLS
+
+#endif /* GO_PALETTE_H */
--- /dev/null
+++ lib/goffice/gui-utils/go-dock-item.c
@@ -0,0 +1,1802 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* go-dock-item.c
+ *
+ * Copyright (C) 1998 Ettore Perazzoli
+ * Copyright (C) 1998 Elliot Lee
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ * All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+*/
+
+/*
+ * NB. this may look like a GtkBin, but it contains
+ * a GoDockItemGrip in addition to it's child,
+ * stranger things have been done in the name of
+ * bin-compat.
+ */
+
+#include <gnumeric-config.h>
+#include <glib/gi18n.h>
+#include "go-dock-item.h"
+#include "go-dock-band.h"
+#include "go-dock-item-grip.h"
+#include "go-ui-marshal.h"
+
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtktoolbar.h>
+#include <gtk/gtkwindow.h>
+
+#include <glib/gi18n.h>
+#include <libgnome/gnome-macros.h>
+
+struct _GoDockItemPrivate
+{
+ GtkWidget *child;
+ GtkWidget *grip;
+
+ GtkWidget *float_window;
+ GtkWidget *float_window_box;
+};
+
+GNOME_CLASS_BOILERPLATE (GoDockItem, go_dock_item,
+ GtkBin, GTK_TYPE_BIN);
+
+enum {
+ PROP_0,
+ PROP_SHADOW,
+ PROP_ORIENTATION,
+ PROP_PREFERRED_WIDTH,
+ PROP_PREFERRED_HEIGHT
+};
+
+#define DRAG_HANDLE_SIZE 10
+
+enum {
+ DOCK_DRAG_BEGIN,
+ DOCK_DRAG_END,
+ DOCK_DRAG_MOTION,
+ DOCK_DETACH,
+ ORIENTATION_CHANGED,
+ LAST_SIGNAL
+};
+
+/* this function is not public, but should be exported */
+void go_dock_item_set_behavior (GoDockItem *dock_item,
+ GoDockItemBehavior behavior);
+
+
+static guint get_preferred_width (GoDockItem *item);
+static guint get_preferred_height (GoDockItem *item);
+
+static void go_dock_item_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void go_dock_item_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void go_dock_item_finalize (GObject *object);
+static void go_dock_item_map (GtkWidget *widget);
+static void go_dock_item_unmap (GtkWidget *widget);
+static void go_dock_item_realize (GtkWidget *widget);
+static void go_dock_item_unrealize (GtkWidget *widget);
+static void go_dock_item_style_set (GtkWidget *widget,
+ GtkStyle *previous_style);
+static void go_dock_item_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void go_dock_item_size_allocate (GtkWidget *widget,
+ GtkAllocation *real_allocation);
+static void go_dock_item_add (GtkContainer *container,
+ GtkWidget *widget);
+static void go_dock_item_remove (GtkContainer *container,
+ GtkWidget *widget);
+static void go_dock_item_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data);
+static void go_dock_item_paint (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean go_dock_item_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gboolean go_dock_item_button_changed (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean go_dock_item_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+
+static void go_dock_item_float_window_size_request (GtkWidget *widget, GtkRequisition *requisition, gpointer data);
+static void go_dock_item_float_window_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer data);
+
+static gboolean go_dock_item_float_window_expose (GtkWidget *widget, GdkEventExpose *event, gpointer data);
+static gboolean go_dock_item_float_window_button_changed (GtkWidget *widget, GdkEventButton *event, gpointer data);
+static gboolean go_dock_item_float_window_motion (GtkWidget *widget, GdkEventMotion *event, gpointer data);
+
+static guint dock_item_signals[LAST_SIGNAL] = { 0 };
+
+
+/* Helper functions. */
+
+static gboolean
+check_guint_arg (GObject *object,
+ const gchar *name,
+ guint *value_return)
+{
+ GParamSpec *pspec;
+
+ g_return_val_if_fail (object != NULL, FALSE);
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), name);
+ if (pspec != NULL) {
+ GValue value = { 0, };
+
+ g_value_init (&value, G_TYPE_UINT);
+ g_object_get_property (G_OBJECT (object), name, &value);
+ *value_return = g_value_get_uint (&value);
+ g_value_unset (&value);
+
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+static guint
+get_preferred_width (GoDockItem *dock_item)
+{
+ GtkWidget *child;
+ guint preferred_width;
+
+ child = dock_item->_priv->child;
+
+ if (!child)
+ return 0;
+
+ if (! check_guint_arg (G_OBJECT (child), "preferred_width", &preferred_width))
+ {
+ GtkRequisition child_requisition;
+
+ gtk_widget_get_child_requisition (child, &child_requisition);
+ preferred_width = child_requisition.width;
+ }
+
+ if (dock_item->orientation == GTK_ORIENTATION_HORIZONTAL)
+ preferred_width += GO_DOCK_ITEM_NOT_LOCKED (dock_item) ? DRAG_HANDLE_SIZE : 0;
+
+ preferred_width += GTK_CONTAINER (dock_item)->border_width * 2;
+
+ return preferred_width;
+}
+
+static guint
+get_preferred_height (GoDockItem *dock_item)
+{
+ GtkWidget *child;
+ guint preferred_height;
+
+ child = dock_item->_priv->child;
+
+ if (!child)
+ return 0;
+
+ if (! check_guint_arg (G_OBJECT (child), "preferred_height", &preferred_height))
+ {
+ GtkRequisition child_requisition;
+
+ gtk_widget_get_child_requisition (child, &child_requisition);
+ preferred_height = child_requisition.height;
+ }
+
+ if (dock_item->orientation == GTK_ORIENTATION_VERTICAL)
+ preferred_height += GO_DOCK_ITEM_NOT_LOCKED (dock_item) ? DRAG_HANDLE_SIZE : 0;
+
+ preferred_height += GTK_CONTAINER (dock_item)->border_width * 2;
+
+ return preferred_height;
+}
+
+static void
+go_dock_item_class_init (GoDockItemClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
+
+ gobject_class = (GObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+ container_class = (GtkContainerClass *) klass;
+
+ gobject_class->set_property = go_dock_item_set_property;
+ gobject_class->get_property = go_dock_item_get_property;
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_SHADOW,
+ g_param_spec_enum ("shadow",
+ _("Shadow type"),
+ _("Shadow type"),
+ GTK_TYPE_SHADOW_TYPE,
+ GTK_SHADOW_OUT,
+ (G_PARAM_READABLE |
+ G_PARAM_WRITABLE)));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_ORIENTATION,
+ g_param_spec_enum ("orientation",
+ _("Orientation"),
+ _("Orientation"),
+ GTK_TYPE_ORIENTATION,
+ GTK_ORIENTATION_HORIZONTAL,
+ (G_PARAM_READABLE |
+ G_PARAM_WRITABLE)));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_PREFERRED_WIDTH,
+ g_param_spec_uint ("preferred_width",
+ _("Preferred width"),
+ _("Preferred width"),
+ 0, G_MAXINT, 0,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (
+ gobject_class,
+ PROP_PREFERRED_HEIGHT,
+ g_param_spec_uint ("preferred_height",
+ _("Preferred height"),
+ _("Preferred height"),
+ 0, G_MAXINT, 0,
+ G_PARAM_READABLE));
+
+ dock_item_signals[DOCK_DRAG_BEGIN] =
+ g_signal_new ("dock_drag_begin",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GoDockItemClass,
+ dock_drag_begin),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ dock_item_signals[DOCK_DRAG_MOTION] =
+ g_signal_new ("dock_drag_motion",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GoDockItemClass, dock_drag_motion),
+ NULL, NULL,
+ gnm__VOID__INT_INT,
+ G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
+
+ dock_item_signals[DOCK_DRAG_END] =
+ g_signal_new ("dock_drag_end",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GoDockItemClass, dock_drag_end),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ dock_item_signals[DOCK_DETACH] =
+ g_signal_new ("dock_detach",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GoDockItemClass, dock_detach),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ dock_item_signals[ORIENTATION_CHANGED] =
+ g_signal_new ("orientation_changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GoDockItemClass, orientation_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__ENUM,
+ G_TYPE_NONE, 1, GTK_TYPE_ORIENTATION);
+
+ gobject_class->finalize = go_dock_item_finalize;
+
+ widget_class->map = go_dock_item_map;
+ widget_class->unmap = go_dock_item_unmap;
+ widget_class->realize = go_dock_item_realize;
+ widget_class->unrealize = go_dock_item_unrealize;
+ widget_class->style_set = go_dock_item_style_set;
+ widget_class->size_request = go_dock_item_size_request;
+ widget_class->size_allocate = go_dock_item_size_allocate;
+ widget_class->expose_event = go_dock_item_expose;
+ widget_class->button_press_event = go_dock_item_button_changed;
+ widget_class->button_release_event = go_dock_item_button_changed;
+ widget_class->motion_notify_event = go_dock_item_motion;
+
+ container_class->add = go_dock_item_add;
+ container_class->remove = go_dock_item_remove;
+ container_class->forall = go_dock_item_forall;
+}
+
+static void
+go_dock_item_instance_init (GoDockItem *dock_item)
+{
+ GTK_WIDGET_UNSET_FLAGS (dock_item, GTK_NO_WINDOW);
+
+ dock_item->_priv = g_new0 (GoDockItemPrivate, 1);
+
+ dock_item->_priv->grip = go_dock_item_grip_new (dock_item);
+ dock_item->_priv->float_window = NULL;
+
+ gtk_widget_set_parent (dock_item->_priv->grip, GTK_WIDGET (dock_item));
+ gtk_widget_show (dock_item->_priv->grip);
+
+ dock_item->bin_window = NULL;
+ dock_item->float_window = NULL;
+ dock_item->shadow_type = GTK_SHADOW_OUT;
+
+ dock_item->orientation = GTK_ORIENTATION_HORIZONTAL;
+ dock_item->behavior = GO_DOCK_ITEM_BEH_NORMAL;
+
+ dock_item->float_window_mapped = FALSE;
+ dock_item->is_floating = FALSE;
+ dock_item->in_drag = FALSE;
+
+ dock_item->dragoff_x = 0;
+ dock_item->dragoff_y = 0;
+
+ dock_item->float_x = 0;
+ dock_item->float_y = 0;
+}
+
+static void
+go_dock_item_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GoDockItem *dock_item;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (object));
+
+ dock_item = GO_DOCK_ITEM (object);
+
+ switch (param_id)
+ {
+ case PROP_SHADOW:
+ go_dock_item_set_shadow_type (dock_item, g_value_get_enum (value));
+ break;
+ case PROP_ORIENTATION:
+ go_dock_item_set_orientation (dock_item, g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+go_dock_item_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GoDockItem *dock_item;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (object));
+
+ dock_item = GO_DOCK_ITEM (object);
+
+ switch (param_id)
+ {
+ case PROP_SHADOW:
+ g_value_set_enum (value, go_dock_item_get_shadow_type (dock_item));
+ break;
+ case PROP_ORIENTATION:
+ g_value_set_enum (value, go_dock_item_get_orientation (dock_item));
+ break;
+ case PROP_PREFERRED_HEIGHT:
+ g_value_set_uint (value, get_preferred_height (dock_item));
+ break;
+ case PROP_PREFERRED_WIDTH:
+ g_value_set_uint (value, get_preferred_width (dock_item));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+go_dock_item_finalize (GObject *object)
+{
+ GoDockItem *di;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (object));
+
+ di = GO_DOCK_ITEM (object);
+
+ g_free (di->name);
+ di->name = NULL;
+
+ g_free (di->_priv);
+ di->_priv = NULL;
+
+ GNOME_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+static void
+go_dock_item_map (GtkWidget *widget)
+{
+ GtkBin *bin;
+ GoDockItem *di;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (widget));
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
+
+ bin = GTK_BIN (widget);
+ di = GO_DOCK_ITEM (widget);
+
+ gdk_window_show (di->bin_window);
+ if (! di->is_floating)
+ gdk_window_show (widget->window);
+
+ if (di->is_floating && !di->float_window_mapped)
+ go_dock_item_detach (di, di->float_x, di->float_y);
+
+ if (bin->child
+ && GTK_WIDGET_VISIBLE (bin->child)
+ && !GTK_WIDGET_MAPPED (bin->child))
+ gtk_widget_map (bin->child);
+
+ if (di->_priv->grip
+ && GTK_WIDGET_VISIBLE (di->_priv->grip)
+ && !GTK_WIDGET_MAPPED (di->_priv->grip))
+ gtk_widget_map (di->_priv->grip);
+}
+
+static void
+go_dock_item_unmap (GtkWidget *widget)
+{
+ GoDockItem *di;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (widget));
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
+
+ di = GO_DOCK_ITEM (widget);
+
+ gdk_window_hide (widget->window);
+ if (di->float_window_mapped)
+ {
+ gtk_widget_hide (GTK_WIDGET (di->_priv->float_window));
+ di->float_window_mapped = FALSE;
+ }
+
+ if (di->_priv->grip)
+ gtk_widget_unmap (di->_priv->grip);
+}
+
+static void
+go_dock_item_realize (GtkWidget *widget)
+{
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ GoDockItem *di;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (widget));
+
+ di = GO_DOCK_ITEM (widget);
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = (gtk_widget_get_events (widget)
+ | GDK_EXPOSURE_MASK);
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
+ gdk_window_set_user_data (widget->window, widget);
+
+ attributes.x = 0;
+ attributes.y = 0;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.event_mask |= (gtk_widget_get_events (widget) |
+ GDK_EXPOSURE_MASK |
+ GDK_BUTTON1_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_KEY_PRESS_MASK);
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ di->bin_window = gdk_window_new (widget->window, &attributes, attributes_mask);
+ gdk_window_set_user_data (di->bin_window, widget);
+
+ if (GTK_BIN (di)->child)
+ gtk_widget_set_parent_window (GTK_BIN (di)->child, di->bin_window);
+
+ gtk_widget_set_parent_window (di->_priv->grip, di->bin_window);
+
+ di->_priv->float_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_screen (GTK_WINDOW (di->_priv->float_window), gtk_widget_get_screen (widget));
+ gtk_window_set_decorated (GTK_WINDOW (di->_priv->float_window), FALSE);
+
+ g_signal_connect (di->_priv->float_window, "size_allocate",
+ G_CALLBACK (go_dock_item_float_window_size_allocate),
+ di);
+
+ g_signal_connect (di->_priv->float_window, "size_request",
+ G_CALLBACK (go_dock_item_float_window_size_request),
+ di);
+ g_signal_connect (di->_priv->float_window, "expose_event",
+ G_CALLBACK (go_dock_item_float_window_expose),
+ di);
+
+ g_signal_connect (di->_priv->float_window, "button_press_event",
+ G_CALLBACK (go_dock_item_float_window_button_changed),
+ di);
+
+ g_signal_connect (di->_priv->float_window, "button_release_event",
+ G_CALLBACK (go_dock_item_float_window_button_changed),
+ di);
+
+ g_signal_connect (di->_priv->float_window, "motion_notify_event",
+ G_CALLBACK (go_dock_item_float_window_motion),
+ di);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+ gtk_style_set_background (widget->style, widget->window, GTK_WIDGET_STATE (di));
+ gtk_style_set_background (widget->style, di->bin_window, GTK_WIDGET_STATE (di));
+ gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
+
+ if (di->is_floating)
+ go_dock_item_detach (di, di->float_x, di->float_y);
+}
+
+static void
+go_dock_item_unrealize (GtkWidget *widget)
+{
+ GoDockItem *di;
+ GoDockItemPrivate *priv;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (widget));
+
+ di = GO_DOCK_ITEM (widget);
+ priv = di->_priv;
+
+ gdk_window_set_user_data (di->bin_window, NULL);
+ gdk_window_destroy (di->bin_window);
+ di->bin_window = NULL;
+
+ if (di->float_window_mapped)
+ go_dock_item_unfloat (di);
+
+ gtk_widget_destroy (GTK_WIDGET (di->_priv->float_window));
+ di->_priv->float_window = NULL;
+
+ GNOME_CALL_PARENT (GTK_WIDGET_CLASS, unrealize, (widget));
+}
+
+static void
+go_dock_item_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ GoDockItem *di;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (widget));
+
+ di = GO_DOCK_ITEM (widget);
+
+ if (GTK_WIDGET_REALIZED (widget) &&
+ !GTK_WIDGET_NO_WINDOW (widget))
+ {
+ gtk_style_set_background (widget->style, widget->window,
+ widget->state);
+ gtk_style_set_background (widget->style, di->bin_window, widget->state);
+ if (GTK_WIDGET_DRAWABLE (widget))
+ gdk_window_clear (widget->window);
+ }
+}
+
+static void
+size_request (GtkWidget *widget,
+ GtkRequisition *requisition,
+ GoDockItem *dock_item)
+{
+
+ GtkBin *bin;
+ GtkRequisition child_requisition;
+
+ bin = GTK_BIN (widget);
+
+ /* If our child is not visible, we still request its size, since
+ we won't have any useful hint for our size otherwise. */
+ if (bin->child != NULL)
+ gtk_widget_size_request (bin->child, &child_requisition);
+ else
+ {
+ child_requisition.width = 0;
+ child_requisition.height = 0;
+ }
+
+ if (dock_item->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ requisition->width =
+ GO_DOCK_ITEM_NOT_LOCKED (dock_item) ? DRAG_HANDLE_SIZE : 0;
+ if (bin->child != NULL)
+ {
+ requisition->width += child_requisition.width;
+ requisition->height = child_requisition.height;
+ }
+ else
+ requisition->height = 0;
+ }
+ else
+ {
+ requisition->height =
+ GO_DOCK_ITEM_NOT_LOCKED (dock_item) ? DRAG_HANDLE_SIZE : 0;
+ if (bin->child != NULL)
+ {
+ requisition->width = child_requisition.width;
+ requisition->height += child_requisition.height;
+ }
+ else
+ requisition->width = 0;
+ }
+
+ requisition->width += GTK_CONTAINER (widget)->border_width * 2;
+ requisition->height += GTK_CONTAINER (widget)->border_width * 2;
+}
+
+static void
+go_dock_item_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+
+ GoDockItem *dock_item;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (widget));
+ g_return_if_fail (requisition != NULL);
+
+ dock_item = GO_DOCK_ITEM (widget);
+
+ size_request (widget, requisition, dock_item);
+
+}
+
+static void
+go_dock_item_float_window_size_request (GtkWidget *widget,
+ GtkRequisition *requisition,
+ gpointer data)
+{
+ GoDockItem *dock_item;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (requisition != NULL);
+
+ dock_item = GO_DOCK_ITEM (data);
+
+ size_request (widget, requisition, dock_item);
+
+}
+
+static void
+grip_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ GtkAllocation *child_allocation,
+ GtkWidget *grip,
+ GoDockItem *di)
+{
+ GtkWidget *child = GTK_BIN (widget)->child;
+
+ GtkAllocation grip_alloc = *allocation;
+
+ grip_alloc.x = grip_alloc.y = 0;
+
+ if (di->orientation != GTK_ORIENTATION_HORIZONTAL) {
+
+ grip_alloc.height = DRAG_HANDLE_SIZE;
+ child_allocation->y += DRAG_HANDLE_SIZE;
+
+ } else {
+
+ grip_alloc.width = DRAG_HANDLE_SIZE;
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ child_allocation->x += DRAG_HANDLE_SIZE;
+ else {
+ GtkRequisition child_requisition;
+
+ gtk_widget_get_child_requisition (child, &child_requisition);
+ grip_alloc.x = child_requisition.width;
+ }
+ }
+
+ gtk_widget_size_allocate (grip, &grip_alloc);
+}
+
+static void
+go_dock_item_float_window_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation,
+ gpointer data)
+{
+ GtkBin *bin;
+ GoDockItem *di;
+ GtkRequisition child_requisition;
+ GtkAllocation child_allocation;
+ GtkWidget *child, *grip;
+ int border_width;
+ GList *list;
+
+ di = GO_DOCK_ITEM (data);
+
+ bin = GTK_BIN(widget);
+ child = bin->child;
+ border_width = GTK_CONTAINER (widget)->border_width;
+
+ /* Grip and InternalToolbar are the children */
+ list = gtk_container_get_children (GTK_CONTAINER (child));
+
+ grip = list->data;
+
+ child_allocation.x = border_width;
+ child_allocation.y = border_width;
+
+ if (GO_DOCK_ITEM_NOT_LOCKED(di))
+ grip_size_allocate (widget, allocation, &child_allocation, grip, di);
+
+ list = list->next;
+ child = list->data;
+
+ gtk_widget_get_child_requisition (child, &child_requisition);
+
+ child_allocation.width = child_requisition.width + 2 * border_width;
+ child_allocation.height = child_requisition.height + 2 * border_width;
+
+ gtk_widget_size_allocate (child, &child_allocation);
+
+}
+
+static void
+go_dock_item_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkBin *bin;
+ GoDockItem *di;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (widget));
+ g_return_if_fail (allocation != NULL);
+
+ bin = GTK_BIN (widget);
+ di = GO_DOCK_ITEM (widget);
+
+ widget->allocation = *allocation;
+
+ if (GTK_WIDGET_REALIZED (widget))
+ gdk_window_move_resize (widget->window,
+ widget->allocation.x,
+ widget->allocation.y,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
+ {
+ GtkWidget *child;
+ GtkAllocation child_allocation;
+ int border_width;
+
+ child = bin->child;
+ border_width = GTK_CONTAINER (widget)->border_width;
+
+ child_allocation.x = border_width;
+ child_allocation.y = border_width;
+
+ if (GO_DOCK_ITEM_NOT_LOCKED(di))
+ grip_size_allocate (widget, allocation, &child_allocation,di->_priv->grip, di);
+
+ if (!di->is_floating)
+ {
+ child_allocation.width = MAX (1, (int) widget->allocation.width - 2 * border_width);
+ child_allocation.height = MAX (1, (int) widget->allocation.height - 2 * border_width);
+
+ if (GO_DOCK_ITEM_NOT_LOCKED (di))
+ {
+ if (di->orientation == GTK_ORIENTATION_HORIZONTAL)
+ child_allocation.width = MAX ((int) child_allocation.width - DRAG_HANDLE_SIZE, 1);
+ else
+ child_allocation.height = MAX ((int) child_allocation.height - DRAG_HANDLE_SIZE, 1);
+ }
+
+ if (GTK_WIDGET_REALIZED (di))
+ gdk_window_move_resize (di->bin_window,
+ 0,
+ 0,
+ widget->allocation.width,
+ widget->allocation.height);
+ }
+
+ gtk_widget_size_allocate (bin->child, &child_allocation);
+
+ }
+}
+
+static void
+window_paint (GtkWidget *widget,
+ GdkEventExpose *event,
+ GoDockItem *di)
+{
+
+ GdkWindow *window;
+ GtkWidget *grip;
+ GtkContainer *container;
+
+ if (!di->is_floating) {
+
+ window = di->bin_window;
+ container = GTK_CONTAINER (di);
+ grip = di->_priv->grip;
+
+ } else {
+
+ GtkBin *bin;
+ GtkWidget *child;
+ GList *list;
+
+ bin = GTK_BIN (widget);
+ child = bin->child;
+ list = gtk_container_get_children (GTK_CONTAINER (child));
+
+ window = child->window;
+ grip = list->data;
+ container = GTK_CONTAINER (child);
+ }
+
+ if (!event)
+ gtk_paint_box(widget->style,
+ window,
+ GTK_WIDGET_STATE (widget),
+ di->shadow_type,
+ NULL, widget,
+ "dockitem_bin",
+ 0, 0, -1, -1);
+ else
+ gtk_paint_box(widget->style,
+ window,
+ GTK_WIDGET_STATE (widget),
+ di->shadow_type,
+ &event->area, widget,
+ "dockitem_bin",
+ 0, 0, -1, -1);
+
+ if (GO_DOCK_ITEM_NOT_LOCKED (di))
+ gtk_container_propagate_expose (
+ container, grip , event);
+}
+
+static void
+go_dock_item_float_window_paint (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer data)
+{
+ GoDockItem *di;
+
+ di = GO_DOCK_ITEM (data);
+
+ if (di->is_floating)
+ window_paint (widget, event, di);
+}
+
+static void
+go_dock_item_paint (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GoDockItem *di;
+
+ di = GO_DOCK_ITEM (widget);
+
+ if (!di->is_floating)
+ window_paint (widget, event, di);
+
+}
+
+static gboolean
+go_dock_item_float_window_expose (GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer data)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (GTK_WIDGET_DRAWABLE (widget))
+ {
+ go_dock_item_float_window_paint (widget, event, data);
+
+ if (GTK_WIDGET_CLASS (parent_class)->expose_event)
+ return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+go_dock_item_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (GTK_WIDGET_DRAWABLE (widget) && event->window != widget->window)
+ {
+ go_dock_item_paint (widget, event);
+
+ if (GTK_WIDGET_CLASS (parent_class)->expose_event)
+ return GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
+ }
+
+ return FALSE;
+}
+
+static void
+go_dock_item_drag_end (GoDockItem *di)
+{
+ gdk_display_pointer_ungrab
+ (gtk_widget_get_display (GTK_WIDGET (di)),
+ GDK_CURRENT_TIME);
+
+ di->in_drag = FALSE;
+
+ g_signal_emit (di, dock_item_signals [DOCK_DRAG_END], 0);
+}
+
+static gboolean
+button_changed (GtkWidget *widget,
+ GdkEventButton *event,
+ GoDockItem *di)
+{
+
+ gboolean event_handled = FALSE;
+
+ if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
+ {
+ GtkWidget *child;
+ gboolean in_handle;
+
+ if (!di->is_floating)
+ child = di->_priv->child;
+ else
+ child = GTK_WIDGET (go_dock_item_get_child (di));
+
+ switch (di->orientation)
+ {
+ case GTK_ORIENTATION_HORIZONTAL:
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ in_handle = event->x < DRAG_HANDLE_SIZE;
+ else
+ in_handle = event->x > widget->allocation.width - DRAG_HANDLE_SIZE;
+ break;
+ case GTK_ORIENTATION_VERTICAL:
+ in_handle = event->y < DRAG_HANDLE_SIZE;
+ break;
+ default:
+ in_handle = FALSE;
+ break;
+ }
+
+ if (!child)
+ {
+ in_handle = FALSE;
+ event_handled = TRUE;
+ }
+
+ if (in_handle)
+ {
+ di->dragoff_x = event->x;
+ di->dragoff_y = event->y;
+
+ go_dock_item_grab_pointer (di);
+
+ g_signal_emit (di , dock_item_signals[DOCK_DRAG_BEGIN], 0);
+
+ event_handled = TRUE;
+ }
+ }
+ else if (event->type == GDK_BUTTON_RELEASE && di->in_drag)
+ {
+ go_dock_item_drag_end (di);
+ event_handled = TRUE;
+ }
+
+ return event_handled;
+}
+
+static gboolean
+go_dock_item_float_window_button_changed (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+
+ GoDockItem *di;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ di = GO_DOCK_ITEM (data);
+
+ if (!GO_DOCK_ITEM_NOT_LOCKED(di))
+ return FALSE;
+
+ return button_changed (widget, event, di);
+
+}
+
+static gboolean
+go_dock_item_button_changed (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GoDockItem *di;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ di = GO_DOCK_ITEM (widget);
+
+ if (event->window != di->bin_window)
+ return FALSE;
+
+ if (!GO_DOCK_ITEM_NOT_LOCKED(widget))
+ return FALSE;
+
+ return button_changed (widget, event, di);
+
+}
+
+static gboolean
+widget_motion (GtkWidget *widget,
+ GdkEventMotion *event,
+ GoDockItem *di)
+{
+ GdkWindow *root_window;
+ gint new_x, new_y;
+
+ root_window = gdk_screen_get_root_window
+ (gdk_drawable_get_screen (GDK_DRAWABLE (event->window)));
+
+ gdk_window_get_pointer (root_window, &new_x, &new_y, NULL);
+
+ new_x -= di->dragoff_x;
+ new_y -= di->dragoff_y;
+
+ g_signal_emit (GTK_WIDGET (di), dock_item_signals[DOCK_DRAG_MOTION], 0,
+ new_x, new_y);
+
+ return TRUE;
+}
+
+static gboolean
+go_dock_item_float_window_motion (GtkWidget *widget,
+ GdkEventMotion *event,
+ gpointer data)
+{
+ GoDockItem *di;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ di = GO_DOCK_ITEM (data);
+
+ if (!di->in_drag)
+ return FALSE;
+
+ return widget_motion (widget, event, di);
+}
+
+static gboolean
+go_dock_item_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GoDockItem *di;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ di = GO_DOCK_ITEM (widget);
+
+ if (!di->in_drag)
+ return FALSE;
+
+ if (event->window != di->bin_window)
+ return FALSE;
+
+ return widget_motion (widget, event, di);
+}
+
+static void
+go_dock_item_add (GtkContainer *container,
+ GtkWidget *widget)
+{
+ GoDockItem *dock_item;
+ GoDockItemPrivate *priv;
+ GParamSpec *pspec;
+
+ dock_item = GO_DOCK_ITEM (container);
+ priv = dock_item->_priv;
+
+ g_return_if_fail (GO_IS_DOCK_ITEM (container));
+
+ /* Is this needed ? We hit this assertion when
+ calling from go_dock_item_unfloat()
+ */
+
+ g_return_if_fail (GTK_BIN (container)->child == NULL);
+ g_assert (priv->child == NULL);
+
+ g_return_if_fail (widget->parent == NULL);
+
+ /* Claim the base reference to the widget, so that it doesn't get owned by the
+ * floating window.
+ */
+ g_object_ref (widget);
+ gtk_object_sink (GTK_OBJECT (widget));
+
+ gtk_widget_set_parent_window (widget, dock_item->bin_window);
+ dock_item->_priv->child = widget;
+ GNOME_CALL_PARENT (GTK_CONTAINER_CLASS, add, (container, widget));
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (widget),
+ "orientation");
+ if (pspec != NULL) {
+ GValue value = { 0, };
+
+ g_value_init (&value, GTK_TYPE_ORIENTATION);
+ g_value_set_enum (&value, dock_item->orientation);
+ g_object_set_property (G_OBJECT (widget), "orientation", &value);
+ g_value_unset (&value);
+ }
+}
+
+static void
+go_dock_item_set_floating (GoDockItem *item, gboolean val)
+{
+ item->is_floating = val;
+
+ /* If there is a child and it supports the 'is_floating' flag
+ * set that too.
+ */
+ if (item->bin.child != NULL &&
+ g_object_class_find_property (G_OBJECT_GET_CLASS (item->bin.child),
+ "is_floating") != NULL)
+ {
+ GValue value = { 0, };
+ g_value_init (&value, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&value, val);
+ g_object_set_property (G_OBJECT (item->bin.child), "is_floating", &value);
+ g_value_unset (&value);
+ }
+}
+
+static void
+go_dock_item_remove (GtkContainer *container,
+ GtkWidget *widget)
+{
+ GoDockItem *di;
+
+ g_return_if_fail (GO_IS_DOCK_ITEM (container));
+
+ di = GO_DOCK_ITEM (container);
+
+ if (widget == di->_priv->grip)
+ {
+ gboolean grip_was_visible;
+
+ grip_was_visible = GTK_WIDGET_VISIBLE (widget);
+
+ gtk_widget_unparent (widget);
+ di->_priv->grip = NULL;
+
+ if (grip_was_visible)
+ gtk_widget_queue_resize (GTK_WIDGET (di));
+
+ return;
+ }
+
+ g_return_if_fail (di->_priv->child == widget);
+ g_assert (di->_priv->child == di->bin.child);
+ g_object_unref (di->_priv->child);
+ di->_priv->child = NULL;
+
+ GNOME_CALL_PARENT (GTK_CONTAINER_CLASS,
+ remove, (container, widget));
+
+}
+
+static void
+go_dock_item_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ GtkBin *bin = (GtkBin *) container;
+ GoDockItem *di = (GoDockItem *) container;
+
+ g_return_if_fail (callback != NULL);
+
+ if (di->float_window_mapped)
+ return; /* The owner of the widgets is the floating window, not the item */
+
+ if (di->_priv->grip)
+ callback (di->_priv->grip, callback_data);
+
+ if (bin->child)
+ callback (bin->child, callback_data);
+}
+
+/**
+ * go_dock_item_construct:
+ * @new: a #GoDockItem.
+ * @name: Name for the new item
+ * @behavior: Behavior for the new item
+ *
+ * Description: Constructs the @new GoDockItem named @name, with the
+ * specified @behavior.
+ *
+ * Returns: A new GoDockItem widget.
+ **/
+void
+go_dock_item_construct (GoDockItem *new,
+ const gchar *name,
+ GoDockItemBehavior behavior)
+{
+ g_return_if_fail (new != NULL);
+ g_return_if_fail (GO_IS_DOCK_ITEM (new));
+
+ new->name = g_strdup (name);
+ new->behavior = behavior;
+
+ if (behavior & GO_DOCK_ITEM_BEH_LOCKED)
+ {
+ gtk_widget_hide (new->_priv->grip);
+ GTK_WIDGET_UNSET_FLAGS (new->_priv->grip, GTK_CAN_FOCUS);
+ }
+}
+
+/**
+ * go_dock_item_new:
+ * @name: Name for the new item
+ * @behavior: Behavior for the new item
+ *
+ * Description: Create a new GoDockItem named @name, with the
+ * specified @behavior.
+ *
+ * Returns: A new GoDockItem widget.
+ **/
+GtkWidget *
+go_dock_item_new (const gchar *name,
+ GoDockItemBehavior behavior)
+{
+ GoDockItem *new;
+
+ new = GO_DOCK_ITEM (g_object_new (go_dock_item_get_type (), NULL));
+
+ go_dock_item_construct (new, name, behavior);
+
+ return GTK_WIDGET (new);
+}
+
+/**
+ * go_dock_item_get_child:
+ * @item: A GoDockItem widget
+ *
+ * Description: Retrieve the child of @item.
+ *
+ * Returns: The child of @item.
+ **/
+GtkWidget *
+go_dock_item_get_child (GoDockItem *item)
+{
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (item), NULL);
+
+ if (item->is_floating)
+ {
+
+ GList *list;
+ GtkWidget *child = GTK_BIN (GTK_WIDGET (item->_priv->float_window))->child;
+
+ list = gtk_container_get_children (GTK_CONTAINER (child));
+
+ while (list)
+ {
+ GtkWidget *widget = list->data;
+
+ if (GTK_IS_TOOLBAR (widget))
+ return widget;
+
+ list = list->next;
+ }
+ g_assert_not_reached ();
+ }
+
+ return GTK_BIN (item)->child;
+}
+
+/**
+ * go_dock_item_get_name:
+ * @item: A GoDockItem widget.
+ *
+ * Description: Retrieve the name of @item.
+ *
+ * Return value: The name of @item as a malloc()ed zero-terminated
+ * string.
+ **/
+gchar *
+go_dock_item_get_name (GoDockItem *item)
+{
+ return g_strdup (item->name);
+}
+
+/**
+ * go_dock_item_set_shadow_type:
+ * @dock_item: A GoDockItem widget
+ * @type: The shadow type for @dock_item
+ *
+ * Description: Set the shadow type for @dock_item.
+ **/
+void
+go_dock_item_set_shadow_type (GoDockItem *dock_item,
+ GtkShadowType type)
+{
+ g_return_if_fail (GO_IS_DOCK_ITEM (dock_item));
+
+ if (dock_item->shadow_type != type)
+ {
+ dock_item->shadow_type = type;
+
+ if (GTK_WIDGET_DRAWABLE (dock_item))
+ gtk_widget_queue_draw (GTK_WIDGET (dock_item));
+ gtk_widget_queue_resize (GTK_WIDGET (dock_item));
+ }
+}
+
+/**
+ * go_dock_item_get_shadow_type:
+ * @dock_item: A GoDockItem widget.
+ *
+ * Description: Retrieve the shadow type of @dock_item.
+ *
+ * Returns: @dock_item's shadow type.
+ **/
+GtkShadowType
+go_dock_item_get_shadow_type (GoDockItem *dock_item)
+{
+ g_return_val_if_fail (dock_item != NULL, GTK_SHADOW_OUT);
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (dock_item), GTK_SHADOW_OUT);
+
+ return dock_item->shadow_type;
+}
+
+/**
+ * go_dock_item_set_orientation:
+ * @dock_item: A GoDockItem widget
+ * @orientation: New orientation for @dock_item
+ *
+ * Description: Set the orientation for @dock_item.
+ *
+ * Returns: %TRUE if the operation succeeds, %FALSE if it fails.
+ **/
+gboolean
+go_dock_item_set_orientation (GoDockItem *dock_item,
+ GtkOrientation orientation)
+{
+ g_return_val_if_fail (dock_item != NULL, FALSE);
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (dock_item), FALSE);
+
+ if (dock_item->orientation != orientation)
+ {
+ if ((orientation == GTK_ORIENTATION_VERTICAL
+ && (dock_item->behavior & GO_DOCK_ITEM_BEH_NEVER_VERTICAL))
+ || (orientation == GTK_ORIENTATION_HORIZONTAL
+ && (dock_item->behavior & GO_DOCK_ITEM_BEH_NEVER_HORIZONTAL)))
+ return FALSE;
+
+ dock_item->orientation = orientation;
+
+ if (dock_item->bin.child != NULL) {
+ GValue value = { 0, };
+
+ g_value_init (&value, GTK_TYPE_ORIENTATION);
+ g_value_set_enum (&value, orientation);
+ g_object_set_property (G_OBJECT (dock_item->bin.child),
+ "orientation", &value);
+ g_value_unset (&value);
+ }
+ if (GTK_WIDGET_DRAWABLE (dock_item))
+ gtk_widget_queue_draw (GTK_WIDGET (dock_item));
+ gtk_widget_queue_resize (GTK_WIDGET (dock_item));
+
+ g_signal_emit (dock_item, dock_item_signals[ORIENTATION_CHANGED], 0, orientation);
+ }
+
+ return TRUE;
+}
+
+/**
+ * go_dock_item_get_orientation:
+ * @dock_item: A GoDockItem widget.
+ *
+ * Description: Retrieve the orientation of @dock_item.
+ *
+ * Returns: The current orientation of @dock_item.
+ **/
+GtkOrientation
+go_dock_item_get_orientation (GoDockItem *dock_item)
+{
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (dock_item),
+ GTK_ORIENTATION_HORIZONTAL);
+
+ return dock_item->orientation;
+}
+
+/**
+ * go_dock_item_set_behavior:
+ * @dock_item: A GoDockItem widget.
+ * @behavior: New behavior for @dock_item
+ *
+ * Description: Set the behavior for @dock_item.
+ */
+void
+go_dock_item_set_behavior (GoDockItem *dock_item,
+ GoDockItemBehavior behavior)
+{
+ g_return_if_fail (GO_IS_DOCK_ITEM (dock_item));
+
+ if (dock_item->behavior == behavior)
+ return;
+
+ dock_item->behavior = behavior;
+
+ if (behavior & GO_DOCK_ITEM_BEH_LOCKED)
+ go_dock_item_set_locked (dock_item, TRUE);
+
+ if (behavior & GO_DOCK_ITEM_BEH_NEVER_FLOATING &&
+ dock_item->is_floating)
+ go_dock_item_unfloat (dock_item);
+
+ if (behavior & GO_DOCK_ITEM_BEH_NEVER_VERTICAL &&
+ dock_item->orientation == GTK_ORIENTATION_VERTICAL)
+ go_dock_item_set_orientation (dock_item, GTK_ORIENTATION_HORIZONTAL);
+
+ if (behavior & GO_DOCK_ITEM_BEH_NEVER_HORIZONTAL &&
+ dock_item->orientation == GTK_ORIENTATION_HORIZONTAL)
+ go_dock_item_set_orientation (dock_item, GTK_ORIENTATION_VERTICAL);
+
+ gtk_widget_queue_resize (GTK_WIDGET (dock_item));
+}
+
+/**
+ * go_dock_item_get_behavior:
+ * @dock_item: A GoDockItem widget.
+ *
+ * Description: Retrieve the behavior of @dock_item.
+ *
+ * Returns: The behavior of @dock_item.
+ **/
+GoDockItemBehavior
+go_dock_item_get_behavior (GoDockItem *dock_item)
+{
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (dock_item),
+ GO_DOCK_ITEM_BEH_NORMAL);
+
+ return dock_item->behavior;
+}
+
+/* Private interface. */
+
+void
+go_dock_item_set_locked (GoDockItem *dock_item,
+ gboolean locked)
+{
+ g_return_if_fail (GO_IS_DOCK_ITEM (dock_item));
+
+ if (locked)
+ {
+ if (!GO_DOCK_ITEM_NOT_LOCKED (dock_item))
+ return;
+
+ dock_item->behavior |= GO_DOCK_ITEM_BEH_LOCKED;
+ gtk_widget_hide (dock_item->_priv->grip);
+ }
+ else
+ {
+ if (GO_DOCK_ITEM_NOT_LOCKED (dock_item))
+ return;
+
+ dock_item->behavior &= ~GO_DOCK_ITEM_BEH_LOCKED;
+ gtk_widget_show (dock_item->_priv->grip);
+ }
+}
+
+void
+go_dock_item_grab_pointer (GoDockItem *item)
+{
+ GdkCursor *fleur;
+ GdkWindow *gdk_window;
+
+ g_assert (GO_IS_DOCK_ITEM (item));
+
+ item->in_drag = TRUE;
+
+ fleur = gdk_cursor_new_for_display
+ (gtk_widget_get_display (GTK_WIDGET (item)),
+ GDK_FLEUR);
+
+ if (item->is_floating) {
+ /* This is not working well...can drag only
+ in the small region of the grip and the first button.
+ To be precise, it just sucks that we can't get a decent
+ grab on the grip itself
+ */
+
+ gdk_window = GTK_WIDGET (item->_priv->float_window)->window;
+ } else {
+ gdk_window = item->bin_window;
+ }
+ /* Hm, not sure this is the right thing to do, but it seems to work. */
+ while (gdk_pointer_grab (gdk_window,
+ FALSE,
+ (GDK_BUTTON1_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_RELEASE_MASK),
+ NULL,
+ fleur,
+ GDK_CURRENT_TIME) != 0);
+
+
+ gdk_cursor_unref (fleur);
+}
+
+gboolean
+go_dock_item_detach (GoDockItem *item, gint x, gint y)
+{
+ GoDockItemPrivate *priv;
+ GtkWidget *widget;
+
+ priv = item->_priv;
+
+ if (item->behavior & GO_DOCK_ITEM_BEH_NEVER_FLOATING)
+ return FALSE;
+
+ item->float_x = x;
+ item->float_y = y;
+
+ go_dock_item_set_floating (item, TRUE);
+
+ if (!GTK_WIDGET_REALIZED (item))
+ return TRUE;
+
+ g_assert (priv->child != NULL);
+ g_assert (priv->grip != NULL);
+
+ if (item->orientation == GTK_ORIENTATION_HORIZONTAL)
+ priv->float_window_box = gtk_vbox_new (FALSE, 0);
+ else
+ priv->float_window_box = gtk_hbox_new (FALSE, 0);
+
+ /*
+
+ <michael> the size allocate etc. stuff looked dubious to me
+ <michael> we shouldn't be overriding size_allocate really when
+ we re-parent the grip into the floating toolbar box
+ <michael> it should all just work in that case, since there's no
+ need to knobble the GTK_BIN stuff,
+ <arvind> by not overriding, the grip is not allocated
+ <michael> hmm,
+ <michael> it should be a child of the container,
+ <michael> we should override,
+ <michael> but not do a signal connection for the float_window
+
+ */
+
+ gtk_container_add (GTK_CONTAINER (item->_priv->float_window), priv->float_window_box);
+
+ widget = priv->grip; /* container_remove() will make priv->grip NULL, so we save it here */
+ g_object_ref (priv->grip);
+ gtk_container_remove (GTK_CONTAINER (item), priv->grip);
+ priv->grip = widget;
+ gtk_box_pack_start (GTK_BOX (priv->float_window_box), priv->grip, FALSE, FALSE, 0);
+ g_object_unref (priv->grip);
+
+ widget = priv->child;
+ g_object_ref (priv->child);
+ gtk_container_remove (GTK_CONTAINER (item), priv->child);
+ priv->child = widget;
+ gtk_box_pack_start (GTK_BOX (priv->float_window_box), priv->child, FALSE, FALSE, 0);
+ g_object_unref (priv->child);
+
+ gtk_window_move (GTK_WINDOW (item->_priv->float_window), x, y);
+ gtk_widget_show_all (GTK_WIDGET (item->_priv->float_window));
+
+ item->float_window_mapped = TRUE;
+
+ gdk_window_hide (GTK_WIDGET (item)->window);
+ gtk_widget_queue_draw (GTK_WIDGET (item));
+
+ gtk_window_set_transient_for (GTK_WINDOW (item->_priv->float_window),
+ (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (item)))));
+
+ g_signal_emit (item, dock_item_signals [DOCK_DETACH], 0);
+
+ return TRUE;
+}
+
+void
+go_dock_item_unfloat (GoDockItem *item)
+{
+ GoDockItemPrivate *priv;
+ gboolean is_realized;
+ GtkWidget *widget;
+
+ priv = item->_priv;
+
+ g_assert (item->float_window_mapped);
+ g_assert (priv->child != NULL);
+ g_assert (priv->grip != NULL);
+
+ is_realized = GTK_WIDGET_REALIZED (item);
+
+ /* Grip */
+ g_object_ref (priv->grip);
+ gtk_container_remove (GTK_CONTAINER (priv->float_window_box), priv->grip);
+
+ if (is_realized)
+ gtk_widget_set_parent_window (priv->grip, item->bin_window);
+
+ gtk_widget_set_parent (priv->grip, GTK_WIDGET (item));
+ g_object_unref (priv->grip);
+
+ /* Child */
+ widget = priv->child;
+ g_object_ref (widget);
+ g_assert (item->bin.child == NULL);
+ gtk_container_remove (GTK_CONTAINER (priv->float_window_box), widget);
+ priv->child = NULL;
+
+ if (is_realized)
+ gtk_widget_set_parent_window (widget, item->bin_window);
+
+ /* priv->child must be NULL at this point, or go_dock_item_add() barfs */
+ gtk_container_add (GTK_CONTAINER (item), widget);
+
+ g_assert (item->bin.child == widget);
+ g_assert (priv->child == widget);
+ g_object_unref (widget);
+
+ /* Window */
+
+ gtk_widget_destroy (priv->float_window_box);
+ priv->float_window_box = NULL;
+
+ gtk_widget_hide (GTK_WIDGET (item->_priv->float_window));
+ gdk_window_show (GTK_WIDGET (item)->window);
+
+ item->float_window_mapped = FALSE;
+ go_dock_item_set_floating (item, FALSE);
+
+ gtk_widget_queue_resize (GTK_WIDGET (item));
+}
+
+void
+go_dock_item_attach (GoDockItem *item,
+ GtkWidget *parent,
+ gint x, gint y)
+{
+ if (GTK_WIDGET (item)->parent != GTK_WIDGET (parent))
+ {
+ GtkWidget *child = item->_priv->child;
+
+ gdk_window_move_resize (GTK_WIDGET (item)->window, -1, -1, 0, 0);
+ g_object_ref (item);
+ gtk_container_remove (GTK_CONTAINER (GTK_WIDGET (item)->parent), GTK_WIDGET (item));
+ gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (item));
+ g_object_unref (item);
+
+ if (item->is_floating)
+ go_dock_item_unfloat (item);
+
+ go_dock_item_grab_pointer (item);
+ }
+}
+
+void
+go_dock_item_drag_floating (GoDockItem *item, gint x, gint y)
+{
+ if (item->is_floating)
+ {
+ gtk_window_move (GTK_WINDOW (item->_priv->float_window), x, y);
+
+ item->float_x = x;
+ item->float_y = y;
+ }
+}
+
+void
+go_dock_item_handle_size_request (GoDockItem *item,
+ GtkRequisition *requisition)
+{
+ GtkBin *bin;
+ GtkContainer *container;
+
+ bin = GTK_BIN (item);
+ container = GTK_CONTAINER (item);
+
+ if (bin->child != NULL)
+ gtk_widget_size_request (bin->child, requisition);
+
+ if (item->orientation == GTK_ORIENTATION_HORIZONTAL)
+ requisition->width += DRAG_HANDLE_SIZE;
+ else
+ requisition->height += DRAG_HANDLE_SIZE;
+
+ requisition->width += container->border_width * 2;
+ requisition->height += container->border_width * 2;
+}
+
+void
+go_dock_item_get_floating_position (GoDockItem *item,
+ gint *x, gint *y)
+{
+ if (GTK_WIDGET_REALIZED (item) && item->is_floating)
+ gtk_window_get_position (GTK_WINDOW (item->_priv->float_window), x, y);
+ else
+ {
+ *x = item->float_x;
+ *y = item->float_y;
+ }
+}
+
+GtkWidget *
+go_dock_item_get_grip (GoDockItem *item)
+{
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (item), NULL);
+
+ if (item->behavior & GO_DOCK_ITEM_BEH_LOCKED)
+ return NULL;
+ else
+ return item->_priv->grip;
+}
+
--- /dev/null
+++ lib/goffice/gui-utils/go-combo-pixmaps.c
@@ -0,0 +1,380 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-combo-pixmaps.c - A pixmap selector combo box
+ * Copyright 2000-2004, Ximian, Inc.
+ *
+ * Authors:
+ * Jody Goldberg <jody at gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-combo-pixmaps.h"
+#include "go-combo-box.h"
+
+#include <gtk/gtkwindow.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkmenu.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkvbox.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <glib/gi18n.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+#define PIXMAP_PREVIEW_WIDTH 15
+#define PIXMAP_PREVIEW_HEIGHT 15
+
+struct _GOComboPixmaps {
+ GOComboBox base;
+
+ int selected_index;
+ int cols;
+ GArray *elements;
+
+ GtkWidget *table, *preview_button;
+ GtkWidget *preview_image;
+ GtkTooltips *tool_tip;
+};
+
+typedef struct {
+ GOComboBoxClass base;
+ void (* changed) (GOComboPixmaps *pixmaps, int id);
+} GOComboPixmapsClass;
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+typedef struct {
+ GdkPixbuf *pixbuf;
+ int id;
+} Element;
+
+static guint go_combo_pixmaps_signals [LAST_SIGNAL] = { 0, };
+static GObjectClass *go_combo_pixmaps_parent_class;
+
+static void
+go_combo_pixmaps_finalize (GObject *object)
+{
+ GOComboPixmaps *combo = GO_COMBO_PIXMAPS (object);
+
+ if (combo->tool_tip) {
+ g_object_unref (combo->tool_tip);
+ combo->tool_tip = NULL;
+ }
+
+ if (combo->elements) {
+ g_array_free (combo->elements, TRUE);
+ combo->elements = NULL;
+ }
+
+ (*go_combo_pixmaps_parent_class->finalize) (object);
+}
+
+static void
+cb_screen_changed (GOComboPixmaps *combo, GdkScreen *previous_screen)
+{
+ GtkWidget *w = GTK_WIDGET (combo);
+ GdkScreen *screen = gtk_widget_has_screen (w)
+ ? gtk_widget_get_screen (w)
+ : NULL;
+
+ if (screen) {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (combo->table);
+ gtk_window_set_screen (GTK_WINDOW (toplevel), screen);
+ }
+}
+
+static void
+emit_change (GOComboPixmaps *combo)
+{
+ if (_go_combo_is_updating (GO_COMBO_BOX (combo)))
+ return;
+ g_signal_emit (combo, go_combo_pixmaps_signals [CHANGED], 0,
+ g_array_index (combo->elements, Element, combo->selected_index).id);
+ go_combo_box_popup_hide (GO_COMBO_BOX (combo));
+}
+
+static void
+go_combo_pixmaps_init (GOComboPixmaps *combo)
+{
+ combo->elements = g_array_new (FALSE, FALSE, sizeof (Element));
+ combo->table = gtk_table_new (1, 1, 0);
+
+ combo->tool_tip = gtk_tooltips_new ();
+ g_object_ref (combo->tool_tip);
+ gtk_object_sink (GTK_OBJECT (combo->tool_tip));
+
+ combo->preview_button = gtk_toggle_button_new ();
+ combo->preview_image = gtk_image_new ();
+ gtk_container_add (GTK_CONTAINER (combo->preview_button),
+ GTK_WIDGET (combo->preview_image));
+
+ g_signal_connect (G_OBJECT (combo),
+ "screen-changed",
+ G_CALLBACK (cb_screen_changed), NULL);
+ g_signal_connect_swapped (combo->preview_button,
+ "clicked",
+ G_CALLBACK (emit_change), combo);
+
+ gtk_widget_show_all (combo->preview_button);
+ gtk_widget_show_all (combo->table);
+ go_combo_box_construct (GO_COMBO_BOX (combo),
+ combo->preview_button, combo->table, combo->table);
+}
+
+static void
+go_combo_pixmaps_class_init (GObjectClass *gobject_class)
+{
+ go_combo_pixmaps_parent_class = g_type_class_ref (GO_COMBO_BOX_TYPE);
+ gobject_class->finalize = go_combo_pixmaps_finalize;
+
+ go_combo_pixmaps_signals [CHANGED] =
+ g_signal_new ("changed",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOComboPixmapsClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+}
+
+GSF_CLASS (GOComboPixmaps, go_combo_pixmaps,
+ go_combo_pixmaps_class_init, go_combo_pixmaps_init,
+ GO_COMBO_BOX_TYPE)
+
+GOComboPixmaps *
+go_combo_pixmaps_new (int ncols)
+{
+ GOComboPixmaps *combo;
+
+ g_return_val_if_fail (ncols > 0, NULL);
+
+ combo = g_object_new (GO_COMBO_PIXMAPS_TYPE, NULL);
+ combo->cols = ncols;
+ return combo;
+}
+
+static gboolean
+swatch_activated (GOComboPixmaps *combo, GtkWidget *button)
+{
+ go_combo_pixmaps_select_index (combo,
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "ItemIndex")));
+ emit_change (combo);
+ return TRUE;
+}
+
+static gboolean
+cb_swatch_release_event (GtkWidget *button, GdkEventButton *event, GOComboPixmaps *combo)
+{
+#warning TODO do I want to check for which button ?
+ return swatch_activated (combo, button);
+}
+static gboolean
+cb_swatch_key_press (GtkWidget *button, GdkEventKey *event, GOComboPixmaps *combo)
+{
+ if (event->keyval == GDK_Return ||
+ event->keyval == GDK_KP_Enter ||
+ event->keyval == GDK_space)
+ return swatch_activated (combo, button);
+ else
+ return FALSE;
+}
+
+/**
+ * go_combo_pixmaps_add_element :
+ * @combo : #GOComboPixmaps
+ * @pixbuf : #GdkPixbuf
+ * @id : an identifier for the callbacks
+ * @tootip : optional
+ *
+ * Absorbs a ref to the pixbuf.
+ **/
+void
+go_combo_pixmaps_add_element (GOComboPixmaps *combo,
+ GdkPixbuf const *pixbuf, int id, char const *tooltip)
+{
+ GtkWidget *button, *box;
+ Element tmp;
+ int col, row;
+
+ g_return_if_fail (IS_GO_COMBO_PIXMAPS (combo));
+
+ /* Wrap inside a vbox with a border so that we can see the focus indicator */
+ box = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box),
+ gtk_image_new_from_pixbuf ((GdkPixbuf *)pixbuf),
+ TRUE, TRUE, 0);
+ g_object_unref ((GdkPixbuf *)pixbuf);
+
+ button = gtk_button_new ();
+ gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_container_add (GTK_CONTAINER (button), box);
+
+ if (tooltip != NULL)
+ gtk_tooltips_set_tip (combo->tool_tip, button,
+ tooltip, NULL);
+
+ col = combo->elements->len;
+ row = col / combo->cols;
+ col = col % combo->cols;
+
+ tmp.pixbuf = (GdkPixbuf *)pixbuf;
+ tmp.id = id;
+ g_array_append_val (combo->elements, tmp);
+ g_object_set_data (G_OBJECT (button), "ItemIndex",
+ GINT_TO_POINTER (combo->elements->len-1));
+ gtk_table_attach (GTK_TABLE (combo->table), button,
+ col, col + 1, row + 1, row + 2,
+ GTK_FILL, GTK_FILL, 1, 1);
+ gtk_widget_show_all (button);
+
+ g_object_connect (button,
+ "signal::button_release_event", G_CALLBACK (cb_swatch_release_event), combo,
+ "signal::key_press_event", G_CALLBACK (cb_swatch_key_press), combo,
+ NULL);
+}
+
+gboolean
+go_combo_pixmaps_select_index (GOComboPixmaps *combo, int i)
+{
+ g_return_val_if_fail (IS_GO_COMBO_PIXMAPS (combo), FALSE);
+ g_return_val_if_fail (0 <= i, FALSE);
+ g_return_val_if_fail (i < (int)combo->elements->len, FALSE);
+
+ combo->selected_index = i;
+ gtk_image_set_from_pixbuf (GTK_IMAGE (combo->preview_image),
+ g_array_index (combo->elements, Element, i).pixbuf);
+
+ return TRUE;
+}
+
+gboolean
+go_combo_pixmaps_select_id (GOComboPixmaps *combo, int id)
+{
+ unsigned i;
+
+ g_return_val_if_fail (IS_GO_COMBO_PIXMAPS (combo), FALSE);
+
+ for (i = 0 ; i < combo->elements->len ; i++)
+ if (g_array_index (combo->elements, Element, i).id == id)
+ break;
+
+ g_return_val_if_fail (i <combo->elements->len, FALSE);
+
+ combo->selected_index = i;
+ gtk_image_set_from_pixbuf (GTK_IMAGE (combo->preview_image),
+ g_array_index (combo->elements, Element, i).pixbuf);
+
+ return TRUE;
+}
+
+int
+go_combo_pixmaps_get_selected (GOComboPixmaps const *combo, int *index)
+{
+ Element *el;
+
+ g_return_val_if_fail (IS_GO_COMBO_PIXMAPS (combo), 0);
+ el = &g_array_index (combo->elements, Element, combo->selected_index);
+
+ if (index != NULL)
+ *index = combo->selected_index;
+ return el->id;
+}
+
+GtkWidget *
+go_combo_pixmaps_get_preview (GOComboPixmaps const *combo)
+{
+ g_return_val_if_fail (IS_GO_COMBO_PIXMAPS (combo), NULL);
+ return combo->preview_button;
+}
+
+/************************************************************************/
+
+struct _GOMenuPixmaps {
+ GtkMenu base;
+ unsigned cols, n;
+};
+typedef struct {
+ GtkMenuClass base;
+ void (* changed) (GOMenuPixmaps *pixmaps, int id);
+} GOMenuPixmapsClass;
+
+static guint go_menu_pixmaps_signals [LAST_SIGNAL] = { 0, };
+static void
+go_menu_pixmaps_class_init (GObjectClass *gobject_class)
+{
+ go_menu_pixmaps_signals [CHANGED] =
+ g_signal_new ("changed",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOMenuPixmapsClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+}
+
+static GSF_CLASS (GOMenuPixmaps, go_menu_pixmaps,
+ go_menu_pixmaps_class_init, NULL,
+ GTK_TYPE_MENU)
+
+GOMenuPixmaps *
+go_menu_pixmaps_new (int ncols)
+{
+ GOMenuPixmaps *submenu = g_object_new (go_menu_pixmaps_get_type (), NULL);
+ submenu->cols = ncols;
+ submenu->n = 0;
+ gtk_widget_show (GTK_WIDGET (submenu));
+ return submenu;
+}
+
+static void
+cb_menu_item_activate (GtkWidget *button, GtkWidget *menu)
+{
+ int id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "ItemID"));
+ g_signal_emit (menu, go_menu_pixmaps_signals [CHANGED], 0, id);
+}
+
+void
+go_menu_pixmaps_add_element (GOMenuPixmaps *menu,
+ GdkPixbuf const *pixbuf, int id)
+{
+ GtkWidget *button;
+ int col, row;
+
+ col = menu->n++;
+ row = col / menu->cols;
+ col = col % menu->cols;
+
+ button = gtk_image_menu_item_new_with_label (" ");
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (button),
+ gtk_image_new_from_pixbuf ((GdkPixbuf *)pixbuf));
+ g_object_unref ((GdkPixbuf *)pixbuf);
+ g_object_set_data (G_OBJECT (button),
+ "ItemID", GINT_TO_POINTER (id));
+ gtk_widget_show_all (button);
+ gtk_menu_attach (GTK_MENU (menu), button,
+ col, col + 1, row + 1, row + 2);
+ g_signal_connect (G_OBJECT (button),
+ "activate",
+ G_CALLBACK (cb_menu_item_activate), menu);
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-action-combo-stack.h
@@ -0,0 +1,44 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-action-combo-stack.h: A custom GtkAction to handle undo/redo style combos
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **/
+
+#ifndef __GO_ACTION_COMBO_STACK_H__
+#define __GO_ACTION_COMBO_STACK_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_ACTION_COMBO_STACK_TYPE (go_action_combo_stack_get_type ())
+#define GO_ACTION_COMBO_STACK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_ACTION_COMBO_STACK_TYPE, GOActionComboStack))
+#define IS_GO_ACTION_COMBO_STACK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_ACTION_COMBO_STACK_TYPE))
+
+typedef struct _GOActionComboStack GOActionComboStack;
+
+GType go_action_combo_stack_get_type (void);
+void go_action_combo_stack_push (GOActionComboStack *a,
+ char const *str, gpointer key);
+void go_action_combo_stack_pop (GOActionComboStack *a, unsigned n);
+void go_action_combo_stack_truncate (GOActionComboStack *a, unsigned n);
+gpointer go_action_combo_stack_selection (GOActionComboStack const *a);
+
+G_END_DECLS
+
+#endif /* __GO_ACTION_COMBO_STACK_H__ */
--- /dev/null
+++ lib/goffice/gui-utils/go-color-palette.c
@@ -0,0 +1,726 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * color-palette.c - A color selector palette
+ * Copyright 2000, 2001, Ximian, Inc.
+ *
+ * Authors:
+ * This code was extracted from widget-color-combo.c
+ * written by Miguel de Icaza (miguel at kernel.org) and
+ * Dom Lachowicz (dominicl at seas.upenn.edu). The extracted
+ * code was re-packaged into a separate object by
+ * Michael Levy (mlevy at genoscope.cns.fr)
+ * And later revised and polished by
+ * Almer S. Tigelaar (almer at gnome.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-color-palette.h"
+#include "go-marshalers.h"
+
+#include <goffice/utils/go-color.h>
+#include <gui-util.h>
+#include <style-color.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtkdrawingarea.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkbutton.h>
+#include <gtk/gtkcolorseldialog.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkcolor.h>
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+
+#include <string.h>
+
+typedef struct _ColorNamePair ColorNamePair;
+struct _GOColorPalette {
+ GtkVBox base;
+
+ GOColorGroup *group;
+ GOColor selection, default_color;
+ gboolean current_is_custom;
+ gboolean current_is_default;
+ gboolean allow_alpha;
+
+ /* only for custom colours */
+ GtkWidget *swatches [GO_COLOR_GROUP_HISTORY_SIZE];
+ GtkTooltips *tip;
+
+ /* The table with our default color names */
+ ColorNamePair const *default_set;
+};
+
+typedef struct {
+ GtkVBoxClass base;
+
+ /* Signals emited by this widget */
+ void (*color_changed) (GOColorPalette *pal, GOColor color,
+ gboolean is_custom, gboolean by_user, gboolean is_default);
+ void (*display_custom_dialog) (GOColorPalette *pal, GtkWidget *dialog);
+} GOColorPaletteClass;
+
+#define COLOR_PREVIEW_WIDTH 12
+#define COLOR_PREVIEW_HEIGHT 12
+
+enum {
+ COLOR_CHANGED,
+ DISPLAY_CUSTOM_DIALOG,
+ LAST_SIGNAL
+};
+
+struct _ColorNamePair {
+ GOColor color;
+ char const *name; /* english name - eg. "white" */
+};
+
+static ColorNamePair const default_color_set [] = {
+ { RGBA_TO_UINT (0x00, 0x00, 0x00, 0xff), N_("black")},
+ { RGBA_TO_UINT (0x99, 0x33, 0x00, 0xff), N_("light brown")},
+ { RGBA_TO_UINT (0x33, 0x33, 0x00, 0xff), N_("brown gold")},
+ { RGBA_TO_UINT (0x00, 0x33, 0x00, 0xff), N_("dark green #2")},
+ { RGBA_TO_UINT (0x00, 0x33, 0x66, 0xff), N_("navy")},
+ { RGBA_TO_UINT (0x00, 0x00, 0x80, 0xff), N_("dark blue")},
+ { RGBA_TO_UINT (0x33, 0x33, 0x99, 0xff), N_("purple #2")},
+ { RGBA_TO_UINT (0x33, 0x33, 0x33, 0xff), N_("very dark gray")},
+
+ { RGBA_TO_UINT (0x80, 0x00, 0x00, 0xff), N_("dark red")},
+ { RGBA_TO_UINT (0xFF, 0x66, 0x00, 0xff), N_("red-orange")},
+ { RGBA_TO_UINT (0x80, 0x80, 0x00, 0xff), N_("gold")},
+ { RGBA_TO_UINT (0x00, 0x80, 0x00, 0xff), N_("dark green")},
+ { RGBA_TO_UINT (0x00, 0x80, 0x80, 0xff), N_("dull blue")},
+ { RGBA_TO_UINT (0x00, 0x00, 0xFF, 0xff), N_("blue")},
+ { RGBA_TO_UINT (0x66, 0x66, 0x99, 0xff), N_("dull purple")},
+ { RGBA_TO_UINT (0x80, 0x80, 0x80, 0xff), N_("dark gray")},
+
+ { RGBA_TO_UINT (0xFF, 0x00, 0x00, 0xff), N_("red")},
+ { RGBA_TO_UINT (0xFF, 0x99, 0x00, 0xff), N_("orange")},
+ { RGBA_TO_UINT (0x99, 0xCC, 0x00, 0xff), N_("lime")},
+ { RGBA_TO_UINT (0x33, 0x99, 0x66, 0xff), N_("dull green")},
+ { RGBA_TO_UINT (0x33, 0xCC, 0xCC, 0xff), N_("dull blue #2")},
+ { RGBA_TO_UINT (0x33, 0x66, 0xFF, 0xff), N_("sky blue #2")},
+ { RGBA_TO_UINT (0x80, 0x00, 0x80, 0xff), N_("purple")},
+ { RGBA_TO_UINT (0x96, 0x96, 0x96, 0xff), N_("gray")},
+
+ { RGBA_TO_UINT (0xFF, 0x00, 0xFF, 0xff), N_("magenta")},
+ { RGBA_TO_UINT (0xFF, 0xCC, 0x00, 0xff), N_("bright orange")},
+ { RGBA_TO_UINT (0xFF, 0xFF, 0x00, 0xff), N_("yellow")},
+ { RGBA_TO_UINT (0x00, 0xFF, 0x00, 0xff), N_("green")},
+ { RGBA_TO_UINT (0x00, 0xFF, 0xFF, 0xff), N_("cyan")},
+ { RGBA_TO_UINT (0x00, 0xCC, 0xFF, 0xff), N_("bright blue")},
+ { RGBA_TO_UINT (0x99, 0x33, 0x66, 0xff), N_("red purple")},
+ { RGBA_TO_UINT (0xC0, 0xC0, 0xC0, 0xff), N_("light gray")},
+
+ { RGBA_TO_UINT (0xFF, 0x99, 0xCC, 0xff), N_("pink")},
+ { RGBA_TO_UINT (0xFF, 0xCC, 0x99, 0xff), N_("light orange")},
+ { RGBA_TO_UINT (0xFF, 0xFF, 0x99, 0xff), N_("light yellow")},
+ { RGBA_TO_UINT (0xCC, 0xFF, 0xCC, 0xff), N_("light green")},
+ { RGBA_TO_UINT (0xCC, 0xFF, 0xFF, 0xff), N_("light cyan")},
+ { RGBA_TO_UINT (0x99, 0xCC, 0xFF, 0xff), N_("light blue")},
+ { RGBA_TO_UINT (0xCC, 0x99, 0xFF, 0xff), N_("light purple")},
+ { RGBA_TO_UINT (0xFF, 0xFF, 0xFF, 0xff), N_("white")},
+
+ { 0, NULL},
+
+ /* Disable these for now, they are mostly repeats */
+ { RGBA_TO_UINT (0x99, 0x99, 0xFF, 0xff), N_("purplish blue")},
+ { RGBA_TO_UINT (0x99, 0x33, 0x66, 0xff), N_("red purple")},
+ { RGBA_TO_UINT (0xFF, 0xFF, 0xCC, 0xff), N_("light yellow")},
+ { RGBA_TO_UINT (0xCC, 0xFF, 0xFF, 0xff), N_("light blue")},
+ { RGBA_TO_UINT (0x66, 0x00, 0x66, 0xff), N_("dark purple")},
+ { RGBA_TO_UINT (0xFF, 0x80, 0x80, 0xff), N_("pink")},
+ { RGBA_TO_UINT (0x00, 0x66, 0xCC, 0xff), N_("sky blue")},
+ { RGBA_TO_UINT (0xCC, 0xCC, 0xFF, 0xff), N_("light purple")},
+
+ { RGBA_TO_UINT (0x00, 0x00, 0x80, 0xff), N_("dark blue")},
+ { RGBA_TO_UINT (0xFF, 0x00, 0xFF, 0xff), N_("magenta")},
+ { RGBA_TO_UINT (0xFF, 0xFF, 0x00, 0xff), N_("yellow")},
+ { RGBA_TO_UINT (0x00, 0xFF, 0xFF, 0xff), N_("cyan")},
+ { RGBA_TO_UINT (0x80, 0x00, 0x80, 0xff), N_("purple")},
+ { RGBA_TO_UINT (0x80, 0x00, 0x00, 0xff), N_("dark red")},
+ { RGBA_TO_UINT (0x00, 0x80, 0x80, 0xff), N_("dull blue")},
+ { RGBA_TO_UINT (0x00, 0x00, 0xFF, 0xff), N_("blue")},
+
+ { 0, NULL},
+};
+
+static guint go_color_palette_signals [LAST_SIGNAL] = { 0, };
+
+static GObjectClass *go_color_palette_parent_class;
+
+static GtkWidget *
+create_color_sel (GObject *action_proxy, GOColor c, GCallback handler, gboolean allow_alpha)
+{
+ char *title = g_object_get_data (G_OBJECT (action_proxy), "title");
+ GtkWidget *w = gtk_color_selection_dialog_new (title);
+ GtkColorSelectionDialog *dialog = GTK_COLOR_SELECTION_DIALOG (w);
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION (dialog->colorsel);
+ GdkColor gdk;
+
+ gtk_widget_hide (dialog->help_button);
+ gtk_color_selection_set_current_color (colorsel,
+ go_color_to_gdk (c, &gdk));
+ gtk_color_selection_set_has_opacity_control (colorsel, allow_alpha);
+
+ g_signal_connect_object (dialog,
+ "response", handler, action_proxy, 0);
+
+ /* require an explicit show _after_ the custom-dialog signal fires */
+ return w;
+}
+
+static gboolean
+handle_color_sel (GtkColorSelectionDialog *dialog,
+ gint response_id, GOColor *res)
+{
+ if (response_id == GTK_RESPONSE_OK) {
+ GdkColor gdk;
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION (dialog->colorsel);
+ guint16 alpha = gtk_color_selection_get_current_alpha (colorsel);
+
+ gtk_color_selection_get_current_color (colorsel, &gdk);
+ *res = GDK_TO_UINT (gdk);
+ alpha >>= 8;
+ *res = UINT_RGBA_CHANGE_A (*res, alpha);
+ }
+ /* destroy _before_ we emit */
+ gtk_object_destroy (GTK_OBJECT (dialog));
+ return response_id == GTK_RESPONSE_OK;
+}
+
+static void
+go_color_palette_finalize (GObject *object)
+{
+ GOColorPalette *pal = GO_COLOR_PALETTE (object);
+
+ if (pal->tip) {
+ g_object_unref (pal->tip);
+ pal->tip = NULL;
+ }
+
+ go_color_palette_set_group (pal, NULL);
+
+ (*go_color_palette_parent_class->finalize) (object);
+}
+
+static void
+go_color_palette_class_init (GObjectClass *gobject_class)
+{
+ gobject_class->finalize = go_color_palette_finalize;
+
+ go_color_palette_parent_class = g_type_class_peek_parent (gobject_class);
+
+ go_color_palette_signals [COLOR_CHANGED] =
+ g_signal_new ("color_changed",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOColorPaletteClass, color_changed),
+ NULL, NULL,
+ go__VOID__INT_BOOLEAN_BOOLEAN_BOOLEAN,
+ G_TYPE_NONE, 4, G_TYPE_INT,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+ go_color_palette_signals [DISPLAY_CUSTOM_DIALOG] =
+ g_signal_new ("display-custom-dialog",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOColorPaletteClass, display_custom_dialog),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+}
+
+GSF_CLASS (GOColorPalette, go_color_palette,
+ go_color_palette_class_init, NULL,
+ GTK_TYPE_VBOX)
+
+/*
+ * Find out if a color is in the default palette (not in the custom colors!)
+ *
+ * Utility function
+ */
+static gboolean
+color_in_palette (ColorNamePair const *set, GOColor color)
+{
+ int i;
+
+ for (i = 0; set[i].name != NULL; i++)
+ if (color == set[i].color)
+ return TRUE;
+ return FALSE;
+}
+
+static void
+set_color (GOColorPalette *pal, GOColor color, gboolean is_custom,
+ gboolean by_user, gboolean is_default)
+{
+ if (is_default)
+ color = pal->default_color;
+ if (!color_in_palette (pal->default_set, color))
+ go_color_group_add_color (pal->group, color);
+ pal->selection = color;
+ pal->current_is_custom = is_custom;
+ pal->current_is_default = is_default;
+ g_signal_emit (pal, go_color_palette_signals [COLOR_CHANGED], 0,
+ color, is_custom, by_user, is_default);
+}
+
+static void
+cb_history_changed (GOColorPalette *pal)
+{
+ int i;
+ GdkColor gdk;
+ GOColorGroup *group = pal->group;
+
+ for (i = 0 ; i < GO_COLOR_GROUP_HISTORY_SIZE ; i++)
+ gtk_widget_modify_bg (pal->swatches [i], GTK_STATE_NORMAL,
+ go_color_to_gdk (group->history[i], &gdk));
+#if 0
+ if (next_swatch != NULL) {
+ next_swatch->style->bg[GTK_STATE_NORMAL] = *new_color;
+ gnome_color_picker_set_i16 (GNOME_COLOR_PICKER (pal->picker),
+ new_color->red, new_color->green, new_color->blue, 0);
+ }
+#endif
+}
+
+static gboolean
+cb_default_release_event (GtkWidget *button, GdkEventButton *event, GOColorPalette *pal)
+{
+ set_color (pal, pal->default_color, FALSE, TRUE, TRUE);
+ return TRUE;
+}
+
+static void
+swatch_activated (GOColorPalette *pal, GtkBin *button)
+{
+ GList *tmp = gtk_container_get_children (GTK_CONTAINER (gtk_bin_get_child (button)));
+ GtkWidget *swatch = (tmp != NULL) ? tmp->data : NULL;
+
+ g_list_free (tmp);
+
+ g_return_if_fail (swatch != NULL);
+
+ set_color (pal, GDK_TO_UINT (swatch->style->bg[GTK_STATE_NORMAL]),
+ FALSE, TRUE, FALSE);
+}
+
+static gboolean
+cb_swatch_release_event (GtkBin *button, GdkEventButton *event, GOColorPalette *pal)
+{
+#warning TODO do I want to check for which button ?
+ swatch_activated (pal, button);
+ return TRUE;
+}
+
+static gboolean
+cb_swatch_key_press (GtkBin *button, GdkEventKey *event, GOColorPalette *pal)
+{
+ if (event->keyval == GDK_Return ||
+ event->keyval == GDK_KP_Enter ||
+ event->keyval == GDK_space) {
+ swatch_activated (pal, button);
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+/*
+ * Create the individual color buttons
+ *
+ * Utility function
+ */
+static GtkWidget *
+go_color_palette_button_new (GOColorPalette *pal, GtkTable* table, GtkTooltips *tip,
+ ColorNamePair const * color_name, gint col, gint row)
+{
+ GtkWidget *button, *swatch, *box;
+ GdkColor gdk;
+
+ swatch = gtk_drawing_area_new ();
+ gtk_widget_modify_bg (swatch, GTK_STATE_NORMAL,
+ go_color_to_gdk (color_name->color, &gdk));
+ gtk_widget_set_size_request (swatch, COLOR_PREVIEW_WIDTH, COLOR_PREVIEW_HEIGHT);
+
+ /* Wrap inside a vbox with a border so that we can see the focus indicator */
+ box = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+ gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET (swatch), TRUE, TRUE, 0);
+
+ button = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_container_add (GTK_CONTAINER (button), box);
+ gtk_tooltips_set_tip (tip, button, _(color_name->name), "");
+
+ gtk_table_attach (table, button, col, col+1, row, row+1,
+ GTK_FILL, GTK_FILL, 0, 0);
+
+ g_object_connect (button,
+ "signal::button_release_event", G_CALLBACK (cb_swatch_release_event), pal,
+ "signal::key_press_event", G_CALLBACK (cb_swatch_key_press), pal,
+ NULL);
+ return swatch;
+}
+
+static void
+cb_combo_custom_response (GtkColorSelectionDialog *dialog,
+ gint response_id, GOColorPalette *pal)
+{
+ GOColor c;
+ if (handle_color_sel (dialog, response_id, &c))
+ set_color (pal, c, TRUE, TRUE, FALSE);
+}
+
+static void
+cb_combo_custom_clicked (GtkWidget *button, GOColorPalette *pal)
+{
+ GtkWidget *dialog = create_color_sel (G_OBJECT (pal), pal->selection,
+ G_CALLBACK (cb_combo_custom_response), pal->allow_alpha);
+ g_signal_emit (pal, go_color_palette_signals [DISPLAY_CUSTOM_DIALOG], 0,
+ dialog);
+ gtk_widget_show (dialog);
+}
+
+void
+go_color_palette_set_title (GOColorPalette *pal, char const *title)
+{
+ g_object_set_data_full (G_OBJECT (pal), "title",
+ g_strdup (title), g_free);
+}
+
+/**
+ * go_color_palette_set_group :
+ * @cg : #GOColorGroup
+ *
+ * Absorb the reference to the group
+ */
+void
+go_color_palette_set_group (GOColorPalette *pal, GOColorGroup *cg)
+{
+ if (pal->group == cg)
+ return;
+
+ if (pal->group) {
+ g_signal_handlers_disconnect_by_func (
+ G_OBJECT (pal->group),
+ G_CALLBACK (cb_history_changed), pal);
+ g_object_unref (G_OBJECT (pal->group));
+ pal->group = NULL;
+ }
+ if (cg != NULL) {
+ pal->group = cg;
+ g_signal_connect_swapped (G_OBJECT (cg),
+ "history-changed",
+ G_CALLBACK (cb_history_changed), pal);
+ }
+}
+static GtkWidget *
+go_color_palette_setup (GOColorPalette *pal,
+ char const *no_color_label,
+ int cols, int rows,
+ ColorNamePair const *color_names)
+{
+ GtkWidget *w, *table;
+ GtkTooltips *tip;
+ int pos, row, col = 0;
+
+ table = gtk_table_new (cols, rows, FALSE);
+
+ if (no_color_label != NULL) {
+ w = gtk_button_new_with_label (no_color_label);
+ gtk_table_attach (GTK_TABLE (table), w,
+ 0, cols, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ g_signal_connect (w,
+ "button_release_event",
+ G_CALLBACK (cb_default_release_event), pal);
+ }
+
+ pal->tip = tip = gtk_tooltips_new ();
+ g_object_ref (pal->tip);
+ gtk_object_sink (GTK_OBJECT (pal->tip));
+
+ for (row = 0; row < rows; row++)
+ for (col = 0; col < cols; col++) {
+ pos = row * cols + col;
+ if (color_names [pos].name == NULL)
+ goto custom_colors;
+ go_color_palette_button_new ( pal,
+ GTK_TABLE (table), GTK_TOOLTIPS (tip),
+ &(color_names [pos]), col, row + 1);
+ }
+
+custom_colors :
+ if (col > 0)
+ row++;
+ for (col = 0; col < cols && col < GO_COLOR_GROUP_HISTORY_SIZE; col++) {
+ ColorNamePair color_name = { 0, N_("custom") };
+ color_name.color = pal->group->history [col];
+ pal->swatches [col] = go_color_palette_button_new (pal,
+ GTK_TABLE (table), GTK_TOOLTIPS (tip),
+ &color_name, col, row + 1);
+ }
+
+ w = gnumeric_button_new_with_stock_image (_("Custom Color..."),
+ GTK_STOCK_SELECT_COLOR);
+ gtk_button_set_alignment (GTK_BUTTON (w), 0., .5);
+ gtk_table_attach (GTK_TABLE (table), w, 0, cols,
+ row + 2, row + 3, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ g_signal_connect (G_OBJECT (w),
+ "clicked",
+ G_CALLBACK (cb_combo_custom_clicked), pal);
+
+ return table;
+}
+
+void
+go_color_palette_set_color_to_default (GOColorPalette *pal)
+{
+ set_color (pal, pal->default_color, FALSE, TRUE, TRUE);
+}
+
+void
+go_color_palette_set_current_color (GOColorPalette *pal, GOColor color)
+{
+ set_color (pal, color,
+ color_in_palette (pal->default_set, color),
+ FALSE, FALSE);
+}
+
+GOColor
+go_color_palette_get_current_color (GOColorPalette *pal,
+ gboolean *is_default, gboolean *is_custom)
+{
+ if (is_default != NULL)
+ *is_default = pal->current_is_default;
+ if (is_custom != NULL)
+ *is_custom = pal->current_is_custom;
+ return pal->selection;
+}
+
+void
+go_color_palette_set_allow_alpha (GOColorPalette *pal, gboolean allow_alpha)
+{
+ pal->allow_alpha = allow_alpha;
+}
+
+GtkWidget *
+go_color_palette_new (char const *no_color_label,
+ GOColor default_color,
+ GOColorGroup *cg)
+{
+ GOColorPalette *pal;
+ int const cols = 8;
+ int const rows = 6;
+ ColorNamePair const *color_names = default_color_set;
+
+ pal = g_object_new (GO_COLOR_PALETTE_TYPE, NULL);
+
+ pal->default_set = color_names;
+ pal->default_color = default_color;
+ pal->selection = default_color;
+ pal->current_is_custom = FALSE;
+ pal->current_is_default = TRUE;
+ go_color_palette_set_group (pal, cg);
+
+ gtk_container_add (GTK_CONTAINER (pal),
+ go_color_palette_setup (pal, no_color_label, cols, rows,
+ pal->default_set));
+ return GTK_WIDGET (pal);
+}
+
+
+/***********************************************************************/
+
+typedef struct {
+ GtkMenu base;
+ gboolean allow_alpha;
+ GOColor selection, default_color;
+} GOMenuColor;
+
+typedef struct {
+ GtkMenuClass base;
+ void (* color_changed) (GOMenuColor *menu, GOColor color,
+ gboolean custom, gboolean by_user, gboolean is_default);
+ void (*display_custom_dialog) (GOColorPalette *pal, GtkWidget *dialog);
+} GOMenuColorClass;
+
+static guint go_menu_color_signals [LAST_SIGNAL] = { 0, };
+
+static void
+go_menu_color_class_init (GObjectClass *gobject_class)
+{
+ go_menu_color_signals [COLOR_CHANGED] =
+ g_signal_new ("color_changed",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOMenuColorClass, color_changed),
+ NULL, NULL,
+ go__VOID__INT_BOOLEAN_BOOLEAN_BOOLEAN,
+ G_TYPE_NONE, 4, G_TYPE_INT,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+ go_menu_color_signals [DISPLAY_CUSTOM_DIALOG] =
+ g_signal_new ("display-custom-dialog",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOColorPaletteClass, display_custom_dialog),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+}
+
+static GSF_CLASS (GOMenuColor, go_menu_color,
+ go_menu_color_class_init, NULL,
+ GTK_TYPE_MENU)
+
+static GtkWidget *
+make_colored_menu_item (char const *label, GOColor c)
+{
+ GtkWidget *button;
+ GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ COLOR_PREVIEW_WIDTH, COLOR_PREVIEW_HEIGHT);
+ gdk_pixbuf_fill (pixbuf, c);
+
+ button = gtk_image_menu_item_new_with_label (label);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (button),
+ gtk_image_new_from_pixbuf (pixbuf));
+ g_object_unref (pixbuf);
+ gtk_widget_show_all (button);
+
+ g_object_set_data (G_OBJECT (button), "go_color", GINT_TO_POINTER (c));
+ return button;
+}
+
+static void
+cb_menu_default_activate (GtkWidget *button, GOMenuColor *menu)
+{
+ menu->selection = menu->default_color;
+ g_signal_emit (menu, go_menu_color_signals [COLOR_CHANGED], 0,
+ menu->selection, FALSE, TRUE, TRUE);
+}
+
+static void
+cb_menu_color_activate (GtkWidget *button, GOMenuColor *menu)
+{
+ GOColor color = GPOINTER_TO_INT (
+ g_object_get_data (G_OBJECT (button), "go_color"));
+ menu->selection = color;
+ g_signal_emit (menu, go_menu_color_signals [COLOR_CHANGED], 0,
+ color, FALSE, TRUE, FALSE);
+}
+static void
+cb_menu_custom_response (GtkColorSelectionDialog *dialog,
+ gint response_id, GOMenuColor *menu)
+{
+ GOColor c;
+ if (handle_color_sel (dialog, response_id, &c)) {
+ menu->selection = c;
+ g_signal_emit (menu, go_menu_color_signals [COLOR_CHANGED], 0,
+ c, TRUE, TRUE, FALSE);
+ }
+}
+
+static void
+cb_menu_custom_activate (GtkWidget *button, GOMenuColor *menu)
+{
+ GtkWidget *dialog = create_color_sel (G_OBJECT (menu), menu->selection,
+ G_CALLBACK (cb_menu_custom_response), menu->allow_alpha);
+ g_signal_emit (menu, go_menu_color_signals [DISPLAY_CUSTOM_DIALOG], 0,
+ dialog);
+ gtk_widget_show (dialog);
+}
+
+/**
+ * go_color_palette_make_menu:
+ * @no_color_labe :
+ * default_color: #GOColor
+ * @cg : #GOColorGroup
+ * @custom_dialog_title :
+ * @current_color : #GOColor
+ *
+ * Create a submenu with a palette of colours. Caller is responsible for
+ * creating an item to point to the submenu.
+ **/
+GtkWidget *
+go_color_palette_make_menu (char const *no_color_label,
+ GOColor default_color,
+ GOColorGroup *cg,
+ char const *custom_dialog_title,
+ GOColor current_color)
+{
+ int cols = 8;
+ int rows = 6;
+ int col, row, pos, table_row = 0;
+ ColorNamePair const *color_names = default_color_set;
+ GtkWidget *w, *submenu;
+
+ submenu = g_object_new (go_menu_color_get_type (), NULL);
+
+ if (no_color_label != NULL) {
+ w = make_colored_menu_item (no_color_label, default_color);
+ gtk_menu_attach (GTK_MENU (submenu), w, 0, cols, 0, 1);
+ g_signal_connect (G_OBJECT (w),
+ "activate",
+ G_CALLBACK (cb_menu_default_activate), submenu);
+ table_row++;
+ }
+ for (row = 0; row < rows; row++, table_row++) {
+ for (col = 0; col < cols; col++) {
+ pos = row * cols + col;
+ if (color_names [pos].name == NULL)
+ goto custom_colors;
+ w = make_colored_menu_item (" ",
+ color_names [pos].color);
+ gtk_menu_attach (GTK_MENU (submenu), w,
+ col, col+1, table_row, table_row+1);
+ g_signal_connect (G_OBJECT (w),
+ "activate",
+ G_CALLBACK (cb_menu_color_activate), submenu);
+ }
+ }
+
+custom_colors :
+ if (col > 0)
+ row++;
+ for (col = 0; col < cols && col < GO_COLOR_GROUP_HISTORY_SIZE; col++) {
+ w = make_colored_menu_item (" ", cg->history[col]);
+ gtk_menu_attach (GTK_MENU (submenu), w,
+ col, col+1, table_row, table_row+1);
+ g_signal_connect (G_OBJECT (w),
+ "activate",
+ G_CALLBACK (cb_menu_color_activate), submenu);
+ }
+ w = gtk_image_menu_item_new_with_label (_("Custom Color..."));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (w),
+ gtk_image_new_from_stock (GTK_STOCK_SELECT_COLOR, GTK_ICON_SIZE_MENU));
+ gtk_widget_show_all (w);
+ gtk_menu_attach (GTK_MENU (submenu), w, 0, cols, row + 2, row + 3);
+ g_signal_connect (G_OBJECT (w),
+ "activate",
+ G_CALLBACK (cb_menu_custom_activate), submenu);
+
+ ((GOMenuColor *)submenu)->selection = current_color;
+ ((GOMenuColor *)submenu)->default_color = default_color;
+ g_object_set_data_full (G_OBJECT (submenu), "title",
+ g_strdup (custom_dialog_title), g_free);
+
+ gtk_widget_show (submenu);
+
+ return submenu;
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-dock-layout.c
@@ -0,0 +1,611 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* go-dock-layout.c
+
+ Copyright (C) 1998 Free Software Foundation
+
+ All rights reserved.
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Ettore Perazzoli <ettore at comm2000.it>
+*/
+/*
+ @NOTATION@
+*/
+
+#include <gnumeric-config.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+
+#include "go-dock-layout.h"
+
+/* TODO: handle incorrect GO_DOCK_ITEM_BEH_EXCLUSIVE situations. */
+
+struct _GoDockLayoutPrivate
+{
+ int dummy;
+ /* Nothing right now, needs to get filled with the private things */
+ /* XXX: When stuff is added, uncomment the allocation in the
+ * go_dock_layout_init function! */
+};
+
+static GObjectClass *parent_class = NULL;
+
+
+
+static void go_dock_layout_class_init (GoDockLayoutClass *class);
+
+static void go_dock_layout_instance_init(GoDockLayout *layout);
+
+static void go_dock_layout_finalize (GObject *object);
+
+static gint item_compare_func (gconstpointer a,
+ gconstpointer b);
+
+static gint compare_item_by_name (gconstpointer a,
+ gconstpointer b);
+
+static gint compare_item_by_pointer (gconstpointer a,
+ gconstpointer b);
+
+static GList *find (GoDockLayout *layout,
+ gconstpointer a,
+ GCompareFunc func);
+
+static void remove_item (GoDockLayout *layout,
+ GList *list);
+
+
+static void
+go_dock_layout_class_init (GoDockLayoutClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->finalize = go_dock_layout_finalize;
+
+ parent_class = g_type_class_ref (G_TYPE_OBJECT);
+}
+
+static void
+go_dock_layout_instance_init (GoDockLayout *layout)
+{
+ layout->_priv = NULL;
+ /* XXX: when there is some private stuff enable this
+ layout->_priv = g_new0(GoDockLayoutPrivate, 1);
+ */
+ layout->items = NULL;
+}
+
+static void
+go_dock_layout_finalize (GObject *object)
+{
+ GoDockLayout *layout;
+
+ layout = GO_DOCK_LAYOUT (object);
+
+ while (layout->items)
+ remove_item (layout, layout->items);
+
+ /* Free the private structure */
+ g_free (layout->_priv);
+ layout->_priv = NULL;
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+
+
+static gint
+item_compare_func (gconstpointer a,
+ gconstpointer b)
+{
+ const GoDockLayoutItem *item_a, *item_b;
+
+ item_a = a;
+ item_b = b;
+
+ if (item_a->placement != item_b->placement)
+ return item_b->placement - item_a->placement;
+
+ if (item_a->placement == GO_DOCK_FLOATING)
+ return 0; /* Floating items don't need to be ordered. */
+ else
+ {
+ if (item_a->position.docked.band_num != item_b->position.docked.band_num)
+ return (item_b->position.docked.band_num
+ - item_a->position.docked.band_num);
+
+ return (item_b->position.docked.band_position
+ - item_a->position.docked.band_position);
+ }
+}
+
+static gint
+compare_item_by_name (gconstpointer a, gconstpointer b)
+{
+ const GoDockItem *item;
+ const gchar *name;
+
+ item = b;
+ name = a;
+
+ return strcmp (name, item->name);
+}
+
+static gint
+compare_item_by_pointer (gconstpointer a, gconstpointer b)
+{
+ return a != b;
+}
+
+static GList *
+find (GoDockLayout *layout, gconstpointer data, GCompareFunc func)
+{
+ GList *p;
+
+ for (p = layout->items; p != NULL; p = p->next)
+ {
+ GoDockLayoutItem *item;
+
+ item = p->data;
+ if (! (* func) (data, item->item))
+ return p;
+ }
+
+ return NULL;
+}
+
+static void
+remove_item (GoDockLayout *layout,
+ GList *list)
+{
+ GoDockItem *item;
+
+ item = ((GoDockLayoutItem *) list->data)->item;
+
+ gtk_widget_unref (GTK_WIDGET (item));
+
+ layout->items = g_list_remove_link (layout->items, list);
+
+ g_free (list->data);
+ g_list_free (list);
+}
+
+
+
+GType
+go_dock_layout_get_type (void)
+{
+ static GType layout_type = 0;
+
+ if (layout_type == 0)
+ {
+ GTypeInfo layout_info = {
+ sizeof (GoDockLayoutClass),
+ NULL, NULL,
+ (GClassInitFunc)go_dock_layout_class_init,
+ NULL, NULL,
+ sizeof (GoDockLayout),
+ 0,
+ (GInstanceInitFunc)go_dock_layout_instance_init
+ };
+
+ layout_type = g_type_register_static (G_TYPE_OBJECT, "GoDockLayout", &layout_info, 0);
+ }
+
+ return layout_type;
+}
+
+/**
+ * go_dock_layout_new:
+ *
+ * Description: Create a new #GoDockLayout widget.
+ *
+ * Returns: The new #GoDockLayout widget.
+ **/
+
+GoDockLayout *
+go_dock_layout_new (void)
+{
+ return GO_DOCK_LAYOUT (g_object_new (GO_TYPE_DOCK_LAYOUT, NULL));
+}
+
+/**
+ * go_dock_layout_add_item:
+ * @layout: A #GoDockLayout widget
+ * @item: The dock item to be added to @layout
+ * @placement: Placement of @item in @layout
+ * @band_num: Band number
+ * @band_position: Position within the band
+ * @offset: Distance from the previous element in the band
+ *
+ * Description: Add @item to @layout with the specified parameters.
+ *
+ * Returns: %TRUE if the operation succeeds, %FALSE if it fails.
+ **/
+gboolean
+go_dock_layout_add_item (GoDockLayout *layout,
+ GoDockItem *item,
+ GoDockPlacement placement,
+ gint band_num,
+ gint band_position,
+ gint offset)
+{
+ GoDockLayoutItem *new;
+
+ new = g_new (GoDockLayoutItem, 1);
+ new->item = item;
+ new->placement = placement;
+ new->position.docked.band_num = band_num;
+ new->position.docked.band_position = band_position;
+ new->position.docked.offset = offset;
+
+ layout->items = g_list_prepend (layout->items, new);
+
+ g_object_ref (item);
+
+ return TRUE;
+}
+
+/**
+ * go_dock_layout_add_floating_item:
+ * @layout: A #GoDockLayout widget
+ * @item: The dock item to be added to @layout
+ * @x: X-coordinate for the floating item
+ * @y: Y-coordinate for the floating item
+ * @orientation: Orientation for the floating item
+ *
+ * Description: Add @item to @layout as a floating item with the
+ * specified (@x, @y) position and @orientation.
+ *
+ * Returns: %TRUE if the operation succeeds, %FALSE if it fails.
+ **/
+
+gboolean
+go_dock_layout_add_floating_item (GoDockLayout *layout,
+ GoDockItem *item,
+ gint x, gint y,
+ GtkOrientation orientation)
+{
+ GoDockLayoutItem *new;
+
+ new = g_new (GoDockLayoutItem, 1);
+ new->item = item;
+ new->placement = GO_DOCK_FLOATING;
+ new->position.floating.x = x;
+ new->position.floating.y = y;
+ new->position.floating.orientation = orientation;
+
+ layout->items = g_list_prepend (layout->items, new);
+
+ g_object_ref (item);
+
+ return TRUE;
+}
+
+/**
+ * go_dock_layout_get_item:
+ * @layout: A #GoDockLayout widget
+ * @item: The #GoDockItem to be retrieved
+ *
+ * Description: Retrieve a layout item.
+ *
+ * Returns: The retrieved #GoDockLayoutItem widget.
+ **/
+GoDockLayoutItem *
+go_dock_layout_get_item (GoDockLayout *layout,
+ GoDockItem *item)
+{
+ GList *list;
+
+ list = find (layout, item, compare_item_by_pointer);
+
+ if (list == NULL)
+ return NULL;
+ else
+ return list->data;
+}
+
+/**
+ * go_dock_layout_get_item_by_name:
+ * @layout: A #GoDockLayout widget
+ * @name: Name of the item to be retrieved
+ *
+ * Description: Retrieve the dock item named @name.
+ *
+ * Returns: The named #GoDockLayoutItem widget.
+ **/
+GoDockLayoutItem *
+go_dock_layout_get_item_by_name (GoDockLayout *layout,
+ const gchar *name)
+{
+ GList *list;
+
+ list = find (layout, name, compare_item_by_name);
+
+ if (list == NULL)
+ return NULL;
+ else
+ return list->data;
+}
+
+/**
+ * go_dock_layout_remove_item:
+ * @layout: A #GoDockLayout widget
+ * @item: The #GoDockItem to be removed
+ *
+ * Description: Remove the specified @item from @layout.
+ *
+ * Returns: %TRUE if the operation succeeds, %FALSE if it fails.
+ **/
+gboolean
+go_dock_layout_remove_item (GoDockLayout *layout,
+ GoDockItem *item)
+{
+ GList *list;
+
+ list = find (layout, item, compare_item_by_pointer);
+ if (list == NULL)
+ return FALSE;
+
+ remove_item (layout, list);
+
+ return TRUE;
+}
+
+/**
+ * go_dock_layout_remove_item_by_name:
+ * @layout: A #GoDockLayout widget
+ * @name: Name of the #GoDockItem to be removed
+ *
+ * Description: Remove the item named @name from @layout.
+ *
+ * Returns: %TRUE if the operation succeeds, %FALSE if it fails.
+ **/
+gboolean
+go_dock_layout_remove_item_by_name (GoDockLayout *layout,
+ const gchar *name)
+{
+ GList *list;
+
+ list = find (layout, name, compare_item_by_name);
+ if (list == NULL)
+ return FALSE;
+
+ remove_item (layout, list);
+
+ return TRUE;
+}
+
+
+
+/**
+ * go_dock_layout_add_to_dock:
+ * @layout: A #GoDockLayout widget
+ * @dock: The #GoDock widget the layout items must be added to
+ *
+ * Description: Add all the items in @layout to the specified @dock.
+ *
+ * Returns: %TRUE if the operation succeeds, %FALSE if it fails.
+ **/
+gboolean
+go_dock_layout_add_to_dock (GoDockLayout *layout,
+ GoDock *dock)
+{
+ GoDockLayoutItem *item;
+ GList *lp;
+ GoDockPlacement last_placement;
+ gint last_band_num;
+
+ if (layout->items == NULL)
+ return FALSE;
+
+ layout->items = g_list_sort (layout->items, item_compare_func);
+
+ item = layout->items->data;
+
+ last_placement = GO_DOCK_FLOATING;
+ last_band_num = 0;
+
+ for (lp = layout->items; lp != NULL; lp = lp->next)
+ {
+ item = lp->data;
+
+ if (item->placement == GO_DOCK_FLOATING)
+ {
+ go_dock_add_floating_item (dock,
+ item->item,
+ item->position.floating.x,
+ item->position.floating.y,
+ item->position.floating.orientation);
+ }
+ else
+ {
+ gboolean need_new;
+
+ if (last_placement != item->placement
+ || last_band_num != item->position.docked.band_num)
+ need_new = TRUE;
+ else
+ need_new = FALSE;
+
+ go_dock_add_item (dock,
+ item->item,
+ item->placement,
+ 0,
+ 0,
+ item->position.docked.offset,
+ need_new);
+
+ last_band_num = item->position.docked.band_num;
+ last_placement = item->placement;
+ }
+
+ gtk_widget_show (GTK_WIDGET (item->item));
+ }
+
+ return TRUE;
+}
+
+
+
+/* Layout string functions. */
+
+/**
+ * go_dock_layout_create_string:
+ * @layout: A #GoDockLayout widget
+ *
+ * Description: Generate a string describing the layout in @layout.
+ *
+ * Returns: The (malloced) layout string for @layout.
+ **/
+gchar *
+go_dock_layout_create_string (GoDockLayout *layout)
+{
+ GList *lp;
+ guint tmp_count, tmp_alloc;
+ gchar **tmp;
+ gchar *retval;
+
+ if (layout->items == NULL)
+ return NULL;
+
+ tmp_alloc = 512;
+ tmp = g_new (gchar *, tmp_alloc);
+
+ tmp_count = 0;
+
+ for (lp = layout->items; lp != NULL; lp = lp->next)
+ {
+ GoDockLayoutItem *i;
+
+ i = lp->data;
+
+ if (tmp_alloc - tmp_count <= 2)
+ {
+ tmp_alloc *= 2;
+ tmp = g_renew (char *, tmp, tmp_alloc);
+ }
+
+ if (i->placement == GO_DOCK_FLOATING)
+ tmp[tmp_count] = g_strdup_printf ("%s\\%d,%d,%d,%d",
+ i->item->name,
+ (gint) i->placement,
+ i->position.floating.x,
+ i->position.floating.y,
+ i->position.floating.orientation);
+ else
+ tmp[tmp_count] = g_strdup_printf ("%s\\%d,%d,%d,%d",
+ i->item->name,
+ (gint) i->placement,
+ i->position.docked.band_num,
+ i->position.docked.band_position,
+ i->position.docked.offset);
+
+ tmp_count++;
+ }
+
+ tmp[tmp_count] = NULL;
+
+ retval = g_strjoinv ("\\", tmp);
+ g_strfreev (tmp);
+
+ return retval;
+}
+
+/**
+ * go_dock_layout_parse_string:
+ * @layout: A #GoDockLayout widget
+ * @string: A layout string to be parsed
+ *
+ * Description: Parse the layout string @string, and move around the
+ * items in @layout accordingly.
+ *
+ * Returns: %TRUE if the operation succeeds, %FALSE if it fails.
+ **/
+gboolean
+go_dock_layout_parse_string (GoDockLayout *layout,
+ const gchar *string)
+{
+ gchar **tmp, **p;
+
+ if (string == NULL)
+ return FALSE;
+
+ tmp = g_strsplit (string, "\\", 0);
+ if (tmp == NULL)
+ return FALSE;
+
+ p = tmp;
+ while (*p != NULL)
+ {
+ GList *lp;
+
+ if (*(p + 1) == NULL)
+ {
+ g_strfreev (tmp);
+ return FALSE;
+ }
+
+ lp = find (layout, *p, compare_item_by_name);
+
+ if (lp != NULL)
+ {
+ GoDockLayoutItem *i;
+ gint p1, p2, p3, p4;
+
+ if (sscanf (*(p + 1), "%d,%d,%d,%d", &p1, &p2, &p3, &p4) != 4)
+ {
+ g_strfreev (tmp);
+ return FALSE;
+ }
+
+ if (p1 != (gint) GO_DOCK_TOP
+ && p1 != (gint) GO_DOCK_BOTTOM
+ && p1 != (gint) GO_DOCK_LEFT
+ && p1 != (gint) GO_DOCK_RIGHT
+ && p1 != (gint) GO_DOCK_FLOATING)
+ return FALSE;
+
+ i = lp->data;
+
+ i->placement = (GoDockPlacement) p1;
+
+ if (i->placement == GO_DOCK_FLOATING)
+ {
+ i->position.floating.x = p2;
+ i->position.floating.y = p3;
+ i->position.floating.orientation = p4;
+ }
+ else
+ {
+ i->position.docked.band_num = p2;
+ i->position.docked.band_position = p3;
+ i->position.docked.offset = p4;
+ }
+ }
+
+ p += 2;
+ }
+
+ g_strfreev (tmp);
+
+ return TRUE;
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-font-sel.h
@@ -0,0 +1,20 @@
+#ifndef GO_FONT_SEL_H
+#define GO_FONT_SEL_H
+
+#include <gtk/gtkwindow.h>
+#include <goffice/utils/goffice-utils.h>
+
+#define GO_FONT_SEL_TYPE (go_font_sel_get_type ())
+#define GO_FONT_SEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GO_FONT_SEL_TYPE, GOFontSel))
+#define IS_GO_FONT_SEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GO_FONT_SEL_TYPE))
+
+typedef struct _GOFontSel GOFontSel;
+
+GType go_font_sel_get_type (void);
+GtkWidget *go_font_sel_new (void);
+void go_font_sel_set_font (GOFontSel *fs, GOFont const *font);
+GOFont const *go_font_sel_get_font (GOFontSel const *fs);
+void go_font_sel_editable_enters (GOFontSel *fs, GtkWindow *dialog);
+void go_font_sel_set_sample_text (GOFontSel *fs, char const *text);
+
+#endif /* GO_FONT_SEL_H */
--- /dev/null
+++ lib/goffice/gui-utils/go-dock.h
@@ -0,0 +1,140 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* go-dock.h
+
+ Copyright (C) 1998 Free Software Foundation
+
+ All rights reserved.
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Ettore Perazzoli <ettore at comm2000.it>
+*/
+/*
+ @NOTATION@
+*/
+
+#ifndef _GO_DOCK_H
+#define _GO_DOCK_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_TYPE_DOCK (go_dock_get_type ())
+#define GO_DOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GO_TYPE_DOCK, GoDock))
+#define GO_DOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GO_TYPE_DOCK, GoDockClass))
+#define GO_IS_DOCK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GO_TYPE_DOCK))
+#define GO_IS_DOCK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GO_TYPE_DOCK))
+#define GO_DOCK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GO_TYPE_DOCK, GoDockClass))
+
+typedef enum
+{
+ GO_DOCK_TOP,
+ GO_DOCK_RIGHT,
+ GO_DOCK_BOTTOM,
+ GO_DOCK_LEFT,
+ GO_DOCK_FLOATING
+} GoDockPlacement;
+
+typedef struct _GoDock GoDock;
+typedef struct _GoDockPrivate GoDockPrivate;
+typedef struct _GoDockClass GoDockClass;
+
+#include <goffice/gui-utils/go-dock-band.h>
+#include <goffice/gui-utils/go-dock-layout.h>
+
+struct _GoDock
+{
+ GtkContainer container;
+
+ GtkWidget *client_area;
+
+ /* GoDockBands associated with this dock. */
+ GList *top_bands;
+ GList *bottom_bands;
+ GList *right_bands;
+ GList *left_bands;
+
+ /* Children that are currently not docked. */
+ GList *floating_children; /* GtkWidget */
+
+ /* Client rectangle before drag. */
+ GtkAllocation client_rect;
+
+ guint floating_items_allowed : 1;
+
+ /*< private >*/
+ GoDockPrivate *_priv;
+};
+
+struct _GoDockClass
+{
+ GtkContainerClass parent_class;
+
+ void (* layout_changed) (GoDock *dock);
+
+ gpointer dummy[4];
+};
+
+GtkWidget *go_dock_new (void);
+GtkType go_dock_get_type (void) G_GNUC_CONST;
+
+void go_dock_allow_floating_items
+ (GoDock *dock,
+ gboolean enable);
+
+void go_dock_add_item (GoDock *dock,
+ GoDockItem *item,
+ GoDockPlacement placement,
+ guint band_num,
+ gint position,
+ guint offset,
+ gboolean in_new_band);
+
+void go_dock_add_floating_item (GoDock *dock,
+ GoDockItem *widget,
+ gint x, gint y,
+ GtkOrientation orientation);
+
+void go_dock_set_client_area (GoDock *dock,
+ GtkWidget *widget);
+
+GtkWidget *go_dock_get_client_area (GoDock *dock);
+
+GoDockItem *go_dock_get_item_by_name (GoDock *dock,
+ const gchar *name,
+ GoDockPlacement *placement_return,
+ guint *num_band_return,
+ guint *band_position_return,
+ guint *offset_return);
+
+GoDockLayout *go_dock_get_layout (GoDock *dock);
+
+gboolean go_dock_add_from_layout (GoDock *dock,
+ GoDockLayout *layout);
+
+/* protected */
+#if 1 /* defined(GO_UI_INTERNAL) */
+gint _bonobo_dock_handle_key_nav (GoDock *dock,
+ GoDockBand *band,
+ GoDockItem *item,
+ GdkEventKey *event);
+#endif /* GO_UI_INTERNAL */
+
+G_END_DECLS
+
+#endif
--- /dev/null
+++ lib/goffice/gui-utils/go-combo-color.h
@@ -0,0 +1,64 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-combo-color.h - A color selector combo box
+ * Copyright 2000, 2001, Ximian, Inc.
+ *
+ * Authors:
+ * Miguel de Icaza (miguel at kernel.org)
+ * Dom Lachowicz (dominicl at seas.upenn.edu)
+ *
+ * Reworked and split up into a separate ColorPalette object:
+ * Michael Levy (mlevy at genoscope.cns.fr)
+ *
+ * And later revised and polished by:
+ * Almer S. Tigelaar (almer at gnome.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef GO_COMBO_COLOR_H
+#define GO_COMBO_COLOR_H
+
+#include <glib-object.h>
+#include <goffice/gui-utils/go-color-group.h>
+#include <goffice/utils/go-color.h>
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define GO_COMBO_COLOR_TYPE (go_combo_color_get_type ())
+#define GO_COMBO_COLOR(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GO_COMBO_COLOR_TYPE, GOComboColor))
+#define IS_GO_COMBO_COLOR(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GO_COMBO_COLOR_TYPE))
+#define GO_COMBO_COLOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST(k), GO_COMBO_COLOR_TYPE)
+
+typedef struct _GOComboColor GOComboColor;
+
+GType go_combo_color_get_type (void);
+GtkWidget *go_combo_color_new (GdkPixbuf *icon,
+ char const *no_color_label,
+ GOColor default_color,
+ GOColorGroup *color_group);
+GOColor go_combo_color_get_color (GOComboColor *cc, gboolean *is_default);
+void go_combo_color_set_color (GOComboColor *cc, GOColor color);
+void go_combo_color_set_color_to_default (GOComboColor *cc);
+void go_combo_color_set_color_gdk (GOComboColor *cc, GdkColor *color);
+GOColor go_combo_color_get_default (GOComboColor *cc);
+void go_combo_color_set_default (GOComboColor *cc, GOColor color);
+
+void go_combo_color_set_allow_alpha (GOComboColor *cc, gboolean allow_alpha);
+void go_combo_color_set_instant_apply (GOComboColor *cc, gboolean active);
+
+G_END_DECLS
+
+#endif /* GO_COMBO_COLOR_H */
--- /dev/null
+++ lib/goffice/gui-utils/go-action-combo-text.h
@@ -0,0 +1,52 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * go-action-combo.h: A custom GOActionCombo to handle undo/redo menus/toolbars
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **/
+
+#ifndef __GO_ACTION_COMBO_TEXT_H__
+#define __GO_ACTION_COMBO_TEXT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_ACTION_COMBO_TEXT_TYPE (go_action_combo_text_get_type ())
+#define GO_ACTION_COMBO_TEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_ACTION_COMBO_TEXT_TYPE, GOActionComboText))
+#define IS_GO_ACTION_COMBO_TEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_ACTION_COMBO_TEXT_TYPE))
+
+typedef struct _GOActionComboText GOActionComboText;
+typedef enum { /* begin the search from : */
+ GO_ACTION_COMBO_SEARCH_FROM_TOP, /* the top of the list */
+ GO_ACTION_COMBO_SEARCH_CURRENT, /* the current selection */
+ GO_ACTION_COMBO_SEARCH_NEXT /* the next element after current */
+} GOActionComboTextSearchDir;
+
+GType go_action_combo_text_get_type (void);
+void go_action_combo_text_add_item (GOActionComboText *a,
+ char const *item);
+void go_action_combo_text_set_width (GOActionComboText *a,
+ char const *largest_elem);
+char const *go_action_combo_text_get_entry (GOActionComboText const *a);
+void go_action_combo_text_set_entry (GOActionComboText *a,
+ char const *text,
+ GOActionComboTextSearchDir dir);
+
+G_END_DECLS
+
+#endif /* __GO_ACTION_COMBO_TEXT_H__ */
--- /dev/null
+++ lib/goffice/gui-utils/go-combo-text.h
@@ -0,0 +1,32 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef GO_COMBO_TEXT_H
+#define GO_COMBO_TEXT_H
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define GO_TYPE_COMBO_TEXT (go_combo_text_get_type ())
+#define GO_COMBO_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GO_TYPE_COMBO_TEXT, GoComboText))
+#define IS_GO_COMBO_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GO_TYPE_COMBO_TEXT))
+
+typedef struct _GoComboText GoComboText;
+
+typedef enum { /* begin the search from : */
+ GO_COMBO_TEXT_FROM_TOP, /* the top of the list */
+ GO_COMBO_TEXT_CURRENT, /* the current selection */
+ GO_COMBO_TEXT_NEXT /* the next element after current */
+} GoComboTextSearch;
+
+GType go_combo_text_get_type (void);
+GtkWidget *go_combo_text_new (GCompareFunc cmp_func);
+GtkWidget *go_combo_text_glade_new (void);
+GtkWidget *go_combo_text_get_entry (GoComboText *ct);
+
+gboolean go_combo_text_set_text (GoComboText *ct, const gchar *label,
+ GoComboTextSearch start);
+void go_combo_text_add_item (GoComboText *ct, const gchar *label);
+
+G_END_DECLS
+
+#endif /* GO_COMBO_TEXT_H */
--- /dev/null
+++ lib/goffice/gui-utils/go-marshalers.list
@@ -0,0 +1,25 @@
+# see glib-genmarshal(1) for a detailed description of the file format,
+# possible parameter types are:
+# VOID indicates no return type, or no extra
+# parameters. if VOID is used as the parameter
+# list, no additional parameters may be present.
+# BOOLEAN for boolean types (gboolean)
+# CHAR for signed char types (gchar)
+# UCHAR for unsigned char types (guchar)
+# INT for signed integer types (gint)
+# UINT for unsigned integer types (guint)
+# LONG for signed long integer types (glong)
+# ULONG for unsigned long integer types (gulong)
+# ENUM for enumeration types (gint)
+# FLAGS for flag enumeration types (guint)
+# FLOAT for single-precision float types (gfloat)
+# DOUBLE for double-precision float types (gdouble)
+# STRING for string types (gchar*)
+# BOXED for boxed (anonymous but reference counted) types (GBoxed*)
+# POINTER for anonymous pointer types (gpointer)
+# OBJECT for GObject or derived types (GObject*)
+# NONE deprecated alias for VOID
+# BOOL deprecated alias for BOOLEAN
+BOOLEAN:OBJECT
+VOID:INT,BOOLEAN,BOOLEAN,BOOLEAN
+BOOLEAN:POINTER
--- /dev/null
+++ lib/goffice/gui-utils/go-combo-box.h
@@ -0,0 +1,80 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gtk-combo-box.h - a customizable combobox
+ * Copyright 2000, 2001, Ximian, Inc.
+ *
+ * Authors:
+ * Miguel de Icaza <miguel at ximian.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _GO_COMBO_BOX_H_
+#define _GO_COMBO_BOX_H_
+
+#include <gtk/gtkhbox.h>
+#include <gtk/gtktooltips.h>
+
+G_BEGIN_DECLS
+
+#define GO_COMBO_BOX_TYPE (go_combo_box_get_type())
+#define GO_COMBO_BOX(o) G_TYPE_CHECK_INSTANCE_CAST ((o), GO_COMBO_BOX_TYPE, GOComboBox)
+#define IS_GO_COMBO_BOX(o) G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_COMBO_BOX_TYPE)
+#define GO_COMBO_BOX_CLASS(k) G_TYPE_CHECK_CLASS_CAST ((k), GO_COMBO_BOX_TYPE, GOComboBoxClass)
+
+typedef struct _GOComboBox GOComboBox;
+typedef struct _GOComboBoxPrivate GOComboBoxPrivate;
+typedef struct _GOComboBoxClass GOComboBoxClass;
+
+struct _GOComboBox {
+ GtkHBox hbox;
+ GOComboBoxPrivate *priv;
+};
+
+struct _GOComboBoxClass {
+ GtkHBoxClass base;
+
+ /* virtual */
+ void (*set_title) (GOComboBox *cbox, char const *title);
+
+ /* invoked when the popup has been hidden, if the signal
+ * returns TRUE, it means it should be killed */
+ gboolean (*pop_down_done) (GOComboBox *cbox, GtkWidget *);
+};
+
+/* public */
+GType go_combo_box_get_type (void);
+void go_combo_box_set_tooltip (GOComboBox *combo, GtkTooltips *tips,
+ char const *text, char const *priv_text);
+void go_combo_box_set_relief (GOComboBox *combo, GtkReliefStyle relief);
+void go_combo_box_set_title (GOComboBox *combo, char const *title);
+char const *go_combo_box_get_title (GOComboBox *combo);
+void go_combo_box_set_tearable (GOComboBox *combo, gboolean tearable);
+
+/* protected */
+void go_combo_box_construct (GOComboBox *combo,
+ GtkWidget *display_widget,
+ GtkWidget *popdown_container,
+ GtkWidget *popdown_focus);
+void go_combo_box_get_pos (GOComboBox *combo, int *x, int *y);
+void go_combo_box_popup_hide (GOComboBox *combo);
+void go_combo_box_popup_display (GOComboBox *combo);
+void go_combo_box_set_display (GOComboBox *combo,
+ GtkWidget *display_widget);
+gboolean _go_combo_is_updating (GOComboBox const *combo);
+
+G_END_DECLS
+
+#endif /* _GO_COMBO_BOX_H_ */
--- /dev/null
+++ lib/goffice/gui-utils/go-combo-box.c
@@ -0,0 +1,765 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gnm-combo-box.c - a customizable combobox
+ * Copyright 2000, 2001, Ximian, Inc.
+ *
+ * Authors:
+ * Miguel de Icaza (miguel at gnu.org)
+ * Adrian E Feiguin (feiguin at ifir.edu.ar)
+ * Paolo Molnaro (lupus at debian.org).
+ * Jon K Hellan (hellan at acm.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-combo-box.h"
+#include "go-marshalers.h"
+
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtktearoffmenuitem.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtkarrow.h>
+#include <gtk/gtkeventbox.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkframe.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+enum {
+ POP_DOWN_DONE,
+ LAST_SIGNAL
+};
+
+struct _GOComboBoxPrivate {
+ GtkWidget *popdown_container;
+ GtkWidget *popdown_focus; /* Popup's toplevel when not torn off */
+ GtkWidget *display_widget;
+
+ /* Internal widgets used to implement the ComboBox */
+ GtkWidget *frame;
+ GtkWidget *arrow_button;
+
+ GtkWidget *toplevel; /* Popup's toplevel when not torn off */
+ GtkWidget *tearoff_window; /* Popup's toplevel when torn off */
+ gboolean torn_off;
+
+ GtkWidget *tearable; /* The tearoff "button" */
+ GtkWidget *popup; /* Popup */
+
+ gboolean updating_buttons;
+};
+static GObjectClass *go_combo_box_parent_class;
+static guint go_combo_box_signals [LAST_SIGNAL] = { 0, };
+
+static void go_combo_set_tearoff_state (GOComboBox *combo, gboolean torn_off);
+
+/**
+ * go_combo_popup_reparent
+ * @popup: Popup
+ * @new_parent: New parent
+ * @unrealize: Unrealize popup if TRUE.
+ *
+ * Reparent the popup, taking care of the refcounting
+ *
+ * Compare with gtk_menu_reparent in gtk/gtkmenu.c
+ */
+static void
+go_combo_popup_reparent (GtkWidget *popup,
+ GtkWidget *new_parent,
+ gboolean unrealize)
+{
+ GtkObject *object = GTK_OBJECT (popup);
+ gboolean was_floating = GTK_OBJECT_FLOATING (object);
+
+ g_object_ref (object);
+ gtk_object_sink (object);
+
+ if (unrealize) {
+ g_object_ref (object);
+ gtk_container_remove (GTK_CONTAINER (popup->parent), popup);
+ gtk_container_add (GTK_CONTAINER (new_parent), popup);
+ g_object_unref (object);
+ }
+ else
+ gtk_widget_reparent (GTK_WIDGET (popup), new_parent);
+ gtk_widget_set_size_request (new_parent, -1, -1);
+
+ if (was_floating)
+ GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
+ else
+ g_object_unref (object);
+}
+
+static void
+go_combo_box_finalize (GObject *object)
+{
+ GOComboBox *combo_box = GO_COMBO_BOX (object);
+
+ g_free (combo_box->priv);
+
+ go_combo_box_parent_class->finalize (object);
+}
+
+static void
+go_combo_box_destroy (GtkObject *object)
+{
+ GtkObjectClass *klass = (GtkObjectClass *)go_combo_box_parent_class;
+ GOComboBox *combo_box = GO_COMBO_BOX (object);
+
+ if (combo_box->priv->toplevel) {
+ gtk_widget_destroy (combo_box->priv->toplevel);
+ g_object_unref (combo_box->priv->toplevel);
+ combo_box->priv->toplevel = NULL;
+ }
+
+ if (combo_box->priv->tearoff_window) {
+ gtk_widget_destroy (combo_box->priv->tearoff_window);
+ g_object_unref (combo_box->priv->tearoff_window);
+ combo_box->priv->tearoff_window = NULL;
+ }
+
+ if (klass->destroy)
+ klass->destroy (object);
+}
+
+/* Cut and paste from gtkwindow.c */
+static void
+do_focus_change (GtkWidget *widget, gboolean in)
+{
+ GdkEventFocus fevent;
+
+ g_object_ref (widget);
+
+ if (in)
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+ else
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ fevent.type = GDK_FOCUS_CHANGE;
+ fevent.window = widget->window;
+ fevent.in = in;
+
+ gtk_widget_event (widget, (GdkEvent *)&fevent);
+
+ g_object_notify (G_OBJECT (widget), "has_focus");
+
+ g_object_unref (widget);
+}
+
+static void
+set_arrow_state (GOComboBox *combo_box, gboolean state)
+{
+ GOComboBoxPrivate *priv = combo_box->priv;
+ g_return_if_fail (!combo_box->priv->updating_buttons);
+
+ combo_box->priv->updating_buttons = TRUE;
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->arrow_button), state);
+ if (GTK_IS_TOGGLE_BUTTON (priv->display_widget))
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->display_widget), state);
+ combo_box->priv->updating_buttons = FALSE;
+}
+
+static void
+go_combo_box_popup_hide_unconditional (GOComboBox *combo_box)
+{
+ gboolean popup_info_destroyed = FALSE;
+
+ g_return_if_fail (combo_box != NULL);
+ g_return_if_fail (IS_GO_COMBO_BOX (combo_box));
+
+ gtk_widget_hide (combo_box->priv->toplevel);
+ gtk_widget_hide (combo_box->priv->popup);
+ if (combo_box->priv->torn_off) {
+ GTK_TEAROFF_MENU_ITEM (combo_box->priv->tearable)->torn_off
+ = FALSE;
+ go_combo_set_tearoff_state (combo_box, FALSE);
+ }
+
+ do_focus_change (combo_box->priv->toplevel, FALSE);
+ gtk_grab_remove (combo_box->priv->toplevel);
+ gdk_display_pointer_ungrab (gtk_widget_get_display (combo_box->priv->toplevel),
+ GDK_CURRENT_TIME);
+
+ g_object_ref (combo_box->priv->popdown_container);
+ g_signal_emit (combo_box,
+ go_combo_box_signals [POP_DOWN_DONE], 0,
+ combo_box->priv->popdown_container, &popup_info_destroyed);
+
+ if (popup_info_destroyed){
+ gtk_container_remove (
+ GTK_CONTAINER (combo_box->priv->frame),
+ combo_box->priv->popdown_container);
+ combo_box->priv->popdown_container = NULL;
+ }
+ g_object_unref (combo_box->priv->popdown_container);
+ set_arrow_state (combo_box, FALSE);
+}
+
+static gboolean
+cb_arrow_pressed (GOComboBox *combo_box)
+{
+ if (!combo_box->priv->updating_buttons) {
+ if (combo_box->priv->toplevel == NULL ||
+ !GTK_WIDGET_VISIBLE (combo_box->priv->toplevel))
+ go_combo_box_popup_display (combo_box);
+ else
+ go_combo_box_popup_hide_unconditional (combo_box);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+go_combo_box_mnemonic_activate (GtkWidget *w, gboolean group_cycling)
+{
+ GOComboBox *combo_box = GO_COMBO_BOX (w);
+ cb_arrow_pressed (combo_box);
+ return TRUE;
+}
+
+static void
+go_combo_box_class_init (GObjectClass *object_class)
+{
+ GtkWidgetClass *widget_class = (GtkWidgetClass *)object_class;
+ go_combo_box_parent_class = g_type_class_peek_parent (object_class);
+
+ object_class->finalize = go_combo_box_finalize;
+ widget_class->mnemonic_activate = go_combo_box_mnemonic_activate;
+ ((GtkObjectClass *)object_class)->destroy = go_combo_box_destroy;
+
+ go_combo_box_signals [POP_DOWN_DONE] = g_signal_new (
+ "pop_down_done",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOComboBoxClass, pop_down_done),
+ NULL, NULL,
+ go__BOOLEAN__OBJECT,
+ G_TYPE_BOOLEAN, 1, G_TYPE_OBJECT);
+}
+
+gboolean
+_go_combo_is_updating (GOComboBox const *combo_box)
+{
+ return combo_box->priv->updating_buttons;
+}
+
+static gint
+cb_combo_keypress (GtkWidget *widget, GdkEventKey *event,
+ GOComboBox *combo_box)
+{
+ if (event->keyval == GDK_Escape) {
+ go_combo_box_popup_hide_unconditional (combo_box);
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+/**
+ * go_combo_popup_tear_off
+ * @combo: Combo box
+ * @set_position: Set to position of popup shell if true
+ *
+ * Tear off the popup
+ *
+ * FIXME:
+ * Gtk popup menus are toplevel windows, not dialogs. I think this is wrong,
+ * and make the popups dialogs. But may be there should be a way to make
+ * them toplevel. We can do this after creating:
+ * GTK_WINDOW (tearoff)->type = GTK_WINDOW_TOPLEVEL;
+ */
+static void
+go_combo_popup_tear_off (GOComboBox *combo, gboolean set_position)
+{
+ int x, y;
+
+ if (!combo->priv->tearoff_window) {
+ GtkWidget *tearoff;
+ gchar *title;
+
+ /* FIXME: made this a toplevel, not a dialog ! */
+ tearoff = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_ref (tearoff);
+ gtk_object_sink (GTK_OBJECT (tearoff));
+ combo->priv->tearoff_window = tearoff;
+ gtk_widget_set_app_paintable (tearoff, TRUE);
+ g_signal_connect (tearoff, "key_press_event",
+ G_CALLBACK (cb_combo_keypress),
+ combo);
+ gtk_widget_realize (tearoff);
+ title = g_object_get_data (G_OBJECT (combo), "gnm-combo-title");
+ if (title)
+ gdk_window_set_title (tearoff->window, title);
+ g_object_set (G_OBJECT (tearoff),
+ "allow_shrink", FALSE,
+ "allow_grow", TRUE,
+ NULL);
+ gtk_window_set_transient_for
+ (GTK_WINDOW (tearoff),
+ GTK_WINDOW (gtk_widget_get_toplevel
+ GTK_WIDGET (combo)));
+ }
+
+ if (GTK_WIDGET_VISIBLE (combo->priv->popup)) {
+ gtk_widget_hide (combo->priv->toplevel);
+
+ gtk_grab_remove (combo->priv->toplevel);
+ gdk_display_pointer_ungrab (gtk_widget_get_display (combo->priv->toplevel),
+ GDK_CURRENT_TIME);
+ }
+
+ go_combo_popup_reparent (combo->priv->popup,
+ combo->priv->tearoff_window, FALSE);
+
+ /* It may have got confused about size */
+ gtk_widget_queue_resize (GTK_WIDGET (combo->priv->popup));
+
+ if (set_position) {
+ go_combo_box_get_pos (combo, &x, &y);
+ gtk_window_move (GTK_WINDOW (combo->priv->tearoff_window), x, y);
+ }
+ gtk_widget_show (GTK_WIDGET (combo->priv->popup));
+ gtk_widget_show (combo->priv->tearoff_window);
+
+}
+
+/**
+ * go_combo_box_popup_hide:
+ * @combo_box: Combo box
+ *
+ * Hide popup, but not when it is torn off.
+ * This is the external interface - for subclasses and apps which expect a
+ * regular combo which doesn't do tearoffs.
+ */
+/* protected */ void
+go_combo_box_popup_hide (GOComboBox *combo_box)
+{
+ if (!combo_box->priv->torn_off)
+ go_combo_box_popup_hide_unconditional (combo_box);
+ else if (GTK_WIDGET_VISIBLE (combo_box->priv->toplevel)) {
+ /* Both popup and tearoff window present. Get rid of just
+ the popup shell. */
+ go_combo_popup_tear_off (combo_box, FALSE);
+ set_arrow_state (combo_box, FALSE);
+ }
+}
+
+/*
+ * Find best location for displaying
+ */
+/* protected */ void
+go_combo_box_get_pos (GOComboBox *combo_box, int *x, int *y)
+{
+ GtkWidget *wcombo = GTK_WIDGET (combo_box);
+ GdkScreen *screen = gtk_widget_get_screen (wcombo);
+ int ph, pw;
+
+ gdk_window_get_origin (wcombo->window, x, y);
+ *y += wcombo->allocation.height + wcombo->allocation.y;
+ *x += wcombo->allocation.x;
+
+ ph = combo_box->priv->popup->allocation.height;
+ pw = combo_box->priv->popup->allocation.width;
+
+ if ((*y + ph) > gdk_screen_get_height (screen))
+ *y = gdk_screen_get_height (screen) - ph;
+
+ if ((*x + pw) > gdk_screen_get_width (screen))
+ *x = gdk_screen_get_width (screen) - pw;
+}
+
+/**
+ * go_combo_tearoff_bg_copy
+ * @combo_box: Combo box
+ *
+ * Copy popup window image to the tearoff window.
+ */
+static void
+go_combo_tearoff_bg_copy (GOComboBox *combo)
+{
+ GdkPixmap *pixmap;
+ GdkGC *gc;
+ GdkGCValues gc_values;
+
+ GtkWidget *widget = combo->priv->popup;
+
+ if (combo->priv->torn_off) {
+ gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+ gc = gdk_gc_new_with_values (widget->window,
+ &gc_values, GDK_GC_SUBWINDOW);
+
+ pixmap = gdk_pixmap_new (widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+
+ gdk_draw_drawable (pixmap, gc,
+ widget->window,
+ 0, 0, 0, 0, -1, -1);
+ g_object_unref (gc);
+
+ gtk_widget_set_size_request (combo->priv->tearoff_window,
+ widget->allocation.width,
+ widget->allocation.height);
+
+ gdk_window_set_back_pixmap
+ (combo->priv->tearoff_window->window, pixmap, FALSE);
+ g_object_unref (pixmap);
+ }
+}
+
+/* protected */ void
+go_combo_box_popup_display (GOComboBox *combo_box)
+{
+ int x, y;
+
+ g_return_if_fail (GO_COMBO_BOX (combo_box) != NULL);
+ g_return_if_fail (combo_box->priv->popdown_container != NULL);
+
+ if (combo_box->priv->torn_off) {
+ /* To give the illusion that tearoff still displays the
+ * popup, we copy the image in the popup window to the
+ * background. Thus, it won't be blank after reparenting */
+ go_combo_tearoff_bg_copy (combo_box);
+
+ /* We force an unrealize here so that we don't trigger
+ * redrawing/ clearing code - we just want to reveal our
+ * backing pixmap.
+ */
+ go_combo_popup_reparent (combo_box->priv->popup,
+ combo_box->priv->toplevel, TRUE);
+ }
+
+ go_combo_box_get_pos (combo_box, &x, &y);
+
+ gtk_window_move (GTK_WINDOW (combo_box->priv->toplevel), x, y);
+ gtk_widget_realize (combo_box->priv->popup);
+ gtk_widget_show (combo_box->priv->popup);
+ gtk_widget_realize (combo_box->priv->toplevel);
+ gtk_widget_show (combo_box->priv->toplevel);
+
+ gtk_widget_grab_focus (combo_box->priv->toplevel);
+ do_focus_change (combo_box->priv->toplevel, TRUE);
+
+ gtk_grab_add (combo_box->priv->toplevel);
+ gdk_pointer_grab (combo_box->priv->toplevel->window, TRUE,
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK,
+ NULL, NULL, GDK_CURRENT_TIME);
+ set_arrow_state (combo_box, TRUE);
+}
+
+static gint
+go_combo_box_button_press (GtkWidget *widget, GdkEventButton *event, GOComboBox *combo_box)
+{
+ GtkWidget *child = gtk_get_event_widget ((GdkEvent *) event);
+ if (child != widget){
+ while (child){
+ if (child == widget)
+ return FALSE;
+ child = child->parent;
+ }
+ }
+
+ go_combo_box_popup_hide (combo_box);
+ return TRUE;
+}
+
+static void
+cb_state_change (GtkWidget *widget, GtkStateType old_state, GOComboBox *combo_box)
+{
+ GtkStateType const new_state = GTK_WIDGET_STATE(widget);
+ gtk_widget_set_state (combo_box->priv->display_widget, new_state);
+}
+
+static void
+go_combo_box_init (GOComboBox *combo_box)
+{
+ GtkWidget *arrow;
+ GdkCursor *cursor;
+
+ combo_box->priv = g_new0 (GOComboBoxPrivate, 1);
+ combo_box->priv->updating_buttons = FALSE;
+
+ combo_box->priv->arrow_button = gtk_toggle_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (combo_box->priv->arrow_button), GTK_RELIEF_NONE);
+ GTK_WIDGET_UNSET_FLAGS (combo_box->priv->arrow_button, GTK_CAN_FOCUS);
+
+ arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (combo_box->priv->arrow_button), arrow);
+ gtk_box_pack_end (GTK_BOX (combo_box), combo_box->priv->arrow_button, FALSE, FALSE, 0);
+ g_signal_connect_swapped (combo_box->priv->arrow_button,
+ "button-press-event",
+ G_CALLBACK (cb_arrow_pressed), combo_box);
+ gtk_widget_show_all (combo_box->priv->arrow_button);
+
+ /*
+ * prelight the display widget when mousing over the arrow.
+ */
+ g_signal_connect (combo_box->priv->arrow_button, "state-changed",
+ G_CALLBACK (cb_state_change), combo_box);
+
+ /*
+ * The pop-down container
+ */
+
+ combo_box->priv->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_widget_ref (combo_box->priv->toplevel);
+ gtk_object_sink (GTK_OBJECT (combo_box->priv->toplevel));
+ g_object_set (G_OBJECT (combo_box->priv->toplevel),
+ "allow_shrink", FALSE,
+ "allow_grow", TRUE,
+ NULL);
+
+ combo_box->priv->popup = gtk_event_box_new ();
+ gtk_container_add (GTK_CONTAINER (combo_box->priv->toplevel),
+ combo_box->priv->popup);
+ gtk_widget_show (combo_box->priv->popup);
+
+ gtk_widget_realize (combo_box->priv->popup);
+ cursor = gdk_cursor_new_for_display (gtk_widget_get_display (GTK_WIDGET (combo_box)), GDK_TOP_LEFT_ARROW);
+ gdk_window_set_cursor (combo_box->priv->popup->window, cursor);
+ gdk_cursor_unref (cursor);
+
+ combo_box->priv->torn_off = FALSE;
+ combo_box->priv->tearoff_window = NULL;
+
+ combo_box->priv->frame = gtk_frame_new (NULL);
+ gtk_container_add (GTK_CONTAINER (combo_box->priv->popup),
+ combo_box->priv->frame);
+ gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->frame), GTK_SHADOW_OUT);
+
+ g_signal_connect (combo_box->priv->toplevel, "button_press_event",
+ G_CALLBACK (go_combo_box_button_press), combo_box);
+ g_signal_connect (combo_box->priv->toplevel, "key_press_event",
+ G_CALLBACK (cb_combo_keypress), combo_box);
+}
+
+GSF_CLASS (GOComboBox, go_combo_box,
+ go_combo_box_class_init, go_combo_box_init,
+ GTK_TYPE_HBOX)
+
+/**
+ * go_combo_box_set_display:
+ * @combo_box: the Combo Box to modify
+ * @display_widget: The widget to be displayed
+
+ * Sets the displayed widget for the @combo_box to be @display_widget
+ */
+/* protected */ void
+go_combo_box_set_display (GOComboBox *combo_box, GtkWidget *display_widget)
+{
+ g_return_if_fail (IS_GO_COMBO_BOX (combo_box));
+ g_return_if_fail (GTK_IS_WIDGET (display_widget));
+
+ if (combo_box->priv->display_widget != NULL &&
+ combo_box->priv->display_widget != display_widget)
+ gtk_container_remove (GTK_CONTAINER (combo_box),
+ combo_box->priv->display_widget);
+
+ combo_box->priv->display_widget = display_widget;
+
+ gtk_box_pack_start (GTK_BOX (combo_box), display_widget, TRUE, TRUE, 0);
+}
+
+static gboolean
+cb_tearable_enter_leave (GtkWidget *w, GdkEventCrossing *event, gpointer data)
+{
+ gboolean const flag = GPOINTER_TO_INT(data);
+ gtk_widget_set_state (w, flag ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
+ return FALSE;
+}
+
+/**
+ * go_combo_set_tearoff_state
+ * @combo_box: Combo box
+ * @torn_off: TRUE: Tear off. FALSE: Pop down and reattach
+ *
+ * Set the tearoff state of the popup
+ *
+ * Compare with gtk_menu_set_tearoff_state in gtk/gtkmenu.c
+ */
+static void
+go_combo_set_tearoff_state (GOComboBox *combo,
+ gboolean torn_off)
+{
+ g_return_if_fail (combo != NULL);
+ g_return_if_fail (IS_GO_COMBO_BOX (combo));
+
+ if (combo->priv->torn_off != torn_off) {
+ combo->priv->torn_off = torn_off;
+
+ if (combo->priv->torn_off) {
+ go_combo_popup_tear_off (combo, TRUE);
+ set_arrow_state (combo, FALSE);
+ } else {
+ gtk_widget_hide (combo->priv->tearoff_window);
+ go_combo_popup_reparent (combo->priv->popup,
+ combo->priv->toplevel,
+ FALSE);
+ }
+ }
+}
+
+static gboolean
+cb_popup_delete (GOComboBox *combo)
+{
+ go_combo_box_popup_hide_unconditional (combo);
+ return TRUE;
+}
+
+static gboolean
+cb_tearable_button_release (GtkWidget *w, GdkEventButton *event,
+ GOComboBox *combo)
+{
+ GtkTearoffMenuItem *tearable;
+
+ g_return_val_if_fail (w != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TEAROFF_MENU_ITEM (w), FALSE);
+
+ tearable = GTK_TEAROFF_MENU_ITEM (w);
+ tearable->torn_off = !tearable->torn_off;
+
+ if (!combo->priv->torn_off) {
+ gboolean need_connect;
+
+ need_connect = (!combo->priv->tearoff_window);
+ go_combo_set_tearoff_state (combo, TRUE);
+ if (need_connect)
+ g_signal_connect_swapped (combo->priv->tearoff_window,
+ "delete_event",
+ G_CALLBACK (cb_popup_delete), combo);
+ } else
+ go_combo_box_popup_hide_unconditional (combo);
+
+ return TRUE;
+}
+
+void
+go_combo_box_construct (GOComboBox *combo,
+ GtkWidget *display_widget,
+ GtkWidget *popdown_container,
+ GtkWidget *popdown_focus)
+{
+ GtkWidget *tearable;
+ GtkWidget *vbox;
+
+ g_return_if_fail (IS_GO_COMBO_BOX (combo));
+ g_return_if_fail (GTK_IS_WIDGET (display_widget));
+
+ GTK_BOX (combo)->spacing = 0;
+ GTK_BOX (combo)->homogeneous = FALSE;
+
+ combo->priv->popdown_container = popdown_container;
+ combo->priv->display_widget = NULL;
+
+ vbox = gtk_vbox_new (FALSE, 5);
+ tearable = gtk_tearoff_menu_item_new ();
+ g_signal_connect (tearable, "enter-notify-event",
+ G_CALLBACK (cb_tearable_enter_leave),
+ GINT_TO_POINTER (TRUE));
+ g_signal_connect (tearable, "leave-notify-event",
+ G_CALLBACK (cb_tearable_enter_leave),
+ GINT_TO_POINTER (FALSE));
+ g_signal_connect (tearable, "button-release-event",
+ G_CALLBACK (cb_tearable_button_release),
+ (gpointer) combo);
+ gtk_box_pack_start (GTK_BOX (vbox), tearable, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), popdown_container, TRUE, TRUE, 0);
+ combo->priv->tearable = tearable;
+ go_combo_box_set_tearable (combo, FALSE);
+ go_combo_box_set_relief (combo, GTK_RELIEF_NORMAL);
+
+ go_combo_box_set_display (combo, display_widget);
+ gtk_container_add (GTK_CONTAINER (combo->priv->frame), vbox);
+ gtk_widget_show_all (combo->priv->frame);
+}
+
+/**
+ * go_combo_box_set_title
+ * @combo: Combo box
+ * @title: Title
+ *
+ * Set a title to display over the tearoff window.
+ *
+ * FIXME:
+ *
+ * This should really change the title even when the popup is already torn off.
+ * I guess the tearoff window could attach a listener to title change or
+ * something. But I don't think we need the functionality, so I didn't bother
+ * to investigate.
+ */
+void
+go_combo_box_set_title (GOComboBox *combo, char const *title)
+{
+ GOComboBoxClass *klass = G_TYPE_INSTANCE_GET_CLASS (combo,
+ GO_COMBO_BOX_TYPE, GOComboBoxClass);
+
+ g_return_if_fail (klass != NULL);
+
+ g_object_set_data_full (G_OBJECT (combo), "gnm-combo-title",
+ g_strdup (title), (GDestroyNotify) g_free);
+
+ if (klass->set_title)
+ (klass->set_title) (combo, title);
+}
+
+char const *
+go_combo_box_get_title (GOComboBox *combo)
+{
+ return g_object_get_data (G_OBJECT (combo), "gnm-combo-title");
+}
+
+/**
+ * go_combo_box_set_tearable:
+ * @combo: Combo box
+ * @tearable: whether to allow the @combo to be tearable
+ *
+ * controls whether the combo box's pop up widget can be torn off.
+ */
+void
+go_combo_box_set_tearable (GOComboBox *combo, gboolean tearable)
+{
+ g_return_if_fail (IS_GO_COMBO_BOX (combo));
+
+ if (tearable){
+ gtk_widget_show (combo->priv->tearable);
+ } else {
+ go_combo_set_tearoff_state (combo, FALSE);
+ gtk_widget_hide (combo->priv->tearable);
+ }
+}
+
+void
+go_combo_box_set_relief (GOComboBox *combo, GtkReliefStyle relief)
+{
+ g_return_if_fail (IS_GO_COMBO_BOX (combo));
+
+ gtk_button_set_relief (GTK_BUTTON (combo->priv->arrow_button), relief);
+ if (GTK_IS_BUTTON (combo->priv->display_widget))
+ gtk_button_set_relief (GTK_BUTTON (combo->priv->display_widget), relief);
+}
+
+void
+go_combo_box_set_tooltip (GOComboBox *c, GtkTooltips *tips,
+ char const *text, char const *priv_text)
+{
+#warning this is ugly the tip moves as we jump from preview to arrow
+ gtk_tooltips_set_tip (tips, c->priv->display_widget, text, priv_text);
+ gtk_tooltips_set_tip (tips, c->priv->arrow_button, text, priv_text);
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-action-combo-color.h
@@ -0,0 +1,49 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-action-combo-color.h: A custom GtkAction to handle color selection
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **/
+
+#ifndef __GO_ACTION_COMBO_COLOR_H__
+#define __GO_ACTION_COMBO_COLOR_H__
+
+#include <glib-object.h>
+#include <goffice/utils/go-color.h>
+
+G_BEGIN_DECLS
+
+#define GO_ACTION_COMBO_COLOR_TYPE (go_action_combo_color ())
+#define GO_ACTION_COMBO_COLOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_ACTION_COMBO_COLOR_TYPE, GOActionComboColor))
+#define IS_GO_ACTION_COMBO_COLOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_ACTION_COMBO_COLOR_TYPE))
+
+typedef struct _GOActionComboColor GOActionComboColor;
+
+GType go_action_combo_color_get_type (void);
+GOActionComboColor *
+ go_action_combo_color_new (char const *action_name,
+ char const *stock_id,
+ char const *default_color_label,
+ GOColor default_color,
+ gpointer group_key);
+void go_action_combo_color_set_group (GOActionComboColor *a, gpointer group_key);
+GOColor go_action_combo_color_get_color (GOActionComboColor *a, gboolean *is_default);
+void go_action_combo_color_set_color (GOActionComboColor *a, GOColor color);
+
+G_END_DECLS
+
+#endif /* __GO_ACTION_COMBO_COLOR_H__ */
--- /dev/null
+++ lib/goffice/gui-utils/go-font-sel.c
@@ -0,0 +1,529 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * A font selector widget. This is a simplified version of the
+ * GnomePrint font selector widget.
+ *
+ * Authors:
+ * Jody Goldberg (jody at gnome.org)
+ * Miguel de Icaza (miguel at gnu.org)
+ * Almer S. Tigelaar (almer at gnome.org)
+ */
+#include <goffice/goffice-config.h>
+#include "go-font-sel.h"
+#include <goffice/utils/go-font.h>
+#include <goffice/gui-utils/go-gui-utils.h>
+#include <goffice/utils/go-color.h>
+
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkentry.h>
+
+#include <libfoocanvas/foo-canvas.h>
+#include <libfoocanvas/foo-canvas-text.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <errno.h>
+#include <math.h>
+#include <stdlib.h>
+
+struct _GOFontSel {
+ GtkHBox box;
+ GladeXML *gui;
+
+ GtkWidget *font_name_entry;
+ GtkWidget *font_style_entry;
+ GtkWidget *font_size_entry;
+ GtkTreeView *font_name_list;
+ GtkTreeView *font_style_list;
+ GtkTreeView *font_size_list;
+
+ FooCanvas *font_preview_canvas;
+ FooCanvasItem *font_preview_text;
+
+ GOFont *base, *current;
+ PangoAttrList *modifications;
+};
+
+typedef struct {
+ GtkHBoxClass parent_class;
+
+ void (* font_changed) (GOFontSel *gfs, PangoAttrList *modfications);
+} GOFontSelClass;
+
+enum {
+ FONT_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint gfs_signals[LAST_SIGNAL] = { 0 };
+static GtkObjectClass *gfs_parent_class;
+
+static void
+go_font_sel_add_attr (GOFontSel *gfs, PangoAttribute *attr0, PangoAttribute *attr1)
+{
+ attr0->start_index = 0;
+ attr0->end_index = -1;
+ pango_attr_list_change (gfs->modifications, attr0);
+ if (attr1 != NULL) {
+ attr1->start_index = 0;
+ attr1->end_index = -1;
+ pango_attr_list_change (gfs->modifications, attr1);
+ }
+
+}
+static void
+go_dont_sel_emit_changed (GOFontSel *gfs)
+{
+ g_signal_emit (G_OBJECT (gfs),
+ gfs_signals [FONT_CHANGED], 0, gfs->modifications);
+ foo_canvas_item_set (gfs->font_preview_text,
+ "attributes", gfs->modifications,
+ NULL);
+}
+
+static void
+cb_list_adjust (GtkTreeView* view)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GtkScrolledWindow* scroll;
+ GdkRectangle rect;
+ GtkAdjustment *adj;
+ int pos, height, child_height;
+
+ if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (view), &model, &iter)) {
+ path = gtk_tree_model_get_path (model, &iter);
+ scroll = GTK_SCROLLED_WINDOW (gtk_widget_get_parent (GTK_WIDGET (view)));
+ height = GTK_WIDGET (view)->allocation.height;
+ child_height = GTK_WIDGET (view)->requisition.height;
+ if (height < child_height) {
+ gtk_tree_view_get_cell_area (view, path, NULL, &rect);
+ adj = gtk_scrolled_window_get_vadjustment (scroll);
+ pos = gtk_adjustment_get_value (adj);
+ if (rect.y < 0)
+ pos += rect.y;
+ else if (rect.y + rect.height > height)
+ pos += rect.y + rect.height - height;
+ gtk_adjustment_set_value (adj, pos);
+ gtk_scrolled_window_set_vadjustment (scroll, adj);
+ }
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+list_init (GtkTreeView* view)
+{
+ GtkCellRenderer *renderer;
+ GtkListStore *store;
+ GtkTreeViewColumn *column;
+
+ gtk_tree_view_set_headers_visible (view, FALSE);
+ store = gtk_list_store_new (1, G_TYPE_STRING);
+ gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (
+ NULL, renderer, "text", 0, NULL);
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (view, column);
+ g_signal_connect (view, "realize", G_CALLBACK (cb_list_adjust), NULL);
+}
+
+static void
+font_selected (GtkTreeSelection *selection, GOFontSel *gfs)
+{
+ gchar *text;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gtk_tree_model_get (model, &iter, 0, &text, -1);
+ gtk_entry_set_text (GTK_ENTRY (gfs->font_name_entry), text);
+ go_font_sel_add_attr (gfs, pango_attr_family_new (text), NULL);
+ go_dont_sel_emit_changed (gfs);
+ g_free (text);
+ }
+}
+
+static void
+gfs_fill_font_name_list (GOFontSel *gfs)
+{
+ GSList *ptr;
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ list_init (gfs->font_name_list);
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (gfs->font_name_list));
+ for (ptr = go_font_family_list; ptr != NULL; ptr = ptr->next) {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, ptr->data, -1);
+ }
+
+ g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (gfs->font_name_list)),
+ "changed",
+ G_CALLBACK (font_selected), gfs);
+}
+
+static char const *styles[] = {
+ N_("Normal"),
+ N_("Bold"),
+ N_("Bold italic"),
+ N_("Italic"),
+ NULL
+};
+
+static void
+style_selected (GtkTreeSelection *selection,
+ GOFontSel *gfs)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ int row;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ path = gtk_tree_model_get_path (model, &iter);
+ row = *gtk_tree_path_get_indices (path);
+ gtk_tree_path_free (path);
+ gtk_entry_set_text (GTK_ENTRY (gfs->font_style_entry), _(styles[row]));
+ go_font_sel_add_attr (gfs,
+ pango_attr_weight_new ((row == 0 || row == 3)
+ ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL),
+ pango_attr_style_new ((row == 1 || row == 3)
+ ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
+ go_dont_sel_emit_changed (gfs);
+ }
+}
+
+static void
+gfs_fill_font_style_list (GOFontSel *gfs)
+{
+ int i;
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ list_init (gfs->font_style_list);
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (gfs->font_style_list));
+ for (i = 0; styles[i] != NULL; i++) {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, _(styles[i]), -1);
+ }
+ g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (gfs->font_style_list)),
+ "changed",
+ G_CALLBACK (style_selected), gfs);
+}
+
+static void
+select_row (GtkTreeView *list, int row)
+{
+ GtkTreePath *path;
+
+ if (row < 0)
+ gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (list));
+ else {
+ path = gtk_tree_path_new_from_indices (row, -1);
+
+ gtk_tree_selection_select_path (gtk_tree_view_get_selection (list), path);
+ if (GTK_WIDGET_REALIZED (list))
+ cb_list_adjust (list);
+ gtk_tree_path_free (path);
+ }
+}
+
+static float
+size_set_text (GOFontSel *gfs, char const *size_text)
+{
+ char *end;
+ float size;
+ errno = 0; /* strtol sets errno, but does not clear it. */
+ size = strtod (size_text, &end);
+ size = ((int)floor ((size * 20.) + .5)) / 20.; /* round .05 */
+
+ if (size_text != end && errno != ERANGE && 1. <= size && size <= 400.) {
+ gtk_entry_set_text (GTK_ENTRY (gfs->font_size_entry), size_text);
+ go_font_sel_add_attr (gfs,
+ pango_attr_size_new (size * PANGO_SCALE), NULL);
+ go_dont_sel_emit_changed (gfs);
+ return size;
+ }
+ return -1;
+}
+
+static void
+size_selected (GtkTreeSelection *selection,
+ GOFontSel *gfs)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ char *size_text;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ gtk_tree_model_get (model, &iter, 0, &size_text, -1);
+ size_set_text (gfs, size_text);
+ g_free (size_text);
+ }
+}
+
+static void
+size_changed (GtkEntry *entry, GOFontSel *gfs)
+{
+ int i;
+ float size = size_set_text (gfs, gtk_entry_get_text (entry));
+
+ if (size > 0) {
+ for (i = 0; go_font_sizes [i] != 0; i++)
+ if (go_font_sizes [i] == size)
+ break;
+ g_signal_handlers_block_by_func (
+ gtk_tree_view_get_selection (gfs->font_size_list),
+ size_selected, gfs);
+ select_row (gfs->font_size_list, (go_font_sizes [i] != 0) ? i : -1);
+ g_signal_handlers_unblock_by_func (
+ gtk_tree_view_get_selection (gfs->font_size_list),
+ size_selected, gfs);
+ }
+}
+
+static void
+gfs_fill_font_size_list (GOFontSel *gfs)
+{
+ int i;
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ list_init (gfs->font_size_list);
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (gfs->font_size_list));
+ for (i = 0; go_font_sizes [i] != 0; i++) {
+ char buffer[4 * sizeof (int)];
+ sprintf (buffer, "%d", go_font_sizes [i]);
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, buffer, -1);
+ }
+ g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (gfs->font_size_list)),
+ "changed",
+ G_CALLBACK (size_selected), gfs);
+ g_signal_connect (G_OBJECT (gfs->font_size_entry),
+ "changed",
+ G_CALLBACK (size_changed), gfs);
+}
+
+static void
+canvas_size_changed (G_GNUC_UNUSED GtkWidget *widget,
+ GtkAllocation *allocation, GOFontSel *gfs)
+{
+ int width = allocation->width - 1;
+ int height = allocation->height - 1;
+
+ foo_canvas_item_set (gfs->font_preview_text,
+ "default-col-width", width,
+ "default-row-height", height,
+ NULL);
+
+ foo_canvas_set_scroll_region (gfs->font_preview_canvas, 0, 0,
+ width, height);
+}
+
+static void
+gfs_init (GOFontSel *gfs)
+{
+ GtkWidget *w;
+
+ gfs->gui = go_libglade_new ("go-font-sel.glade", "toplevel-table", NULL, NULL);
+ if (gfs->gui == NULL)
+ return;
+
+ gfs->modifications = pango_attr_list_new ();
+
+ gtk_box_pack_start_defaults (GTK_BOX (gfs),
+ glade_xml_get_widget (gfs->gui, "toplevel-table"));
+ gfs->font_name_entry = glade_xml_get_widget (gfs->gui, "font-name-entry");
+ gfs->font_style_entry = glade_xml_get_widget (gfs->gui, "font-style-entry");
+ gfs->font_size_entry = glade_xml_get_widget (gfs->gui, "font-size-entry");
+ gfs->font_name_list = GTK_TREE_VIEW (glade_xml_get_widget (gfs->gui, "font-name-list"));
+ gfs->font_style_list = GTK_TREE_VIEW (glade_xml_get_widget (gfs->gui, "font-style-list"));
+ gfs->font_size_list = GTK_TREE_VIEW (glade_xml_get_widget (gfs->gui, "font-size-list"));
+
+ w = foo_canvas_new ();
+ gfs->font_preview_canvas = FOO_CANVAS (w);
+ foo_canvas_set_scroll_region (gfs->font_preview_canvas, -1, -1, INT_MAX/2, INT_MAX/2);
+ foo_canvas_scroll_to (gfs->font_preview_canvas, 0, 0);
+ gtk_widget_show_all (w);
+ w = glade_xml_get_widget (gfs->gui, "font-preview-frame");
+ gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (gfs->font_preview_canvas));
+
+ gfs->font_preview_text = FOO_CANVAS_ITEM (foo_canvas_item_new (
+ foo_canvas_root (gfs->font_preview_canvas),
+ FOO_TYPE_CANVAS_TEXT,
+ NULL));
+ go_font_sel_set_sample_text (gfs, NULL); /* init to default */
+
+ g_signal_connect (G_OBJECT (gfs->font_preview_canvas),
+ "size-allocate",
+ G_CALLBACK (canvas_size_changed), gfs);
+
+ gfs_fill_font_name_list (gfs);
+ gfs_fill_font_style_list (gfs);
+ gfs_fill_font_size_list (gfs);
+}
+
+static void
+gfs_destroy (GtkObject *object)
+{
+ GOFontSel *gfs = GO_FONT_SEL (object);
+
+ if (gfs->gui) {
+ g_object_unref (G_OBJECT (gfs->gui));
+ gfs->gui = NULL;
+ }
+ if (gfs->base != NULL) {
+ go_font_unref (gfs->base);
+ gfs->base = NULL;
+ }
+ if (gfs->current != NULL) {
+ go_font_unref (gfs->current);
+ gfs->current = NULL;
+ }
+ if (gfs->modifications != NULL) {
+ pango_attr_list_unref (gfs->modifications);
+ gfs->modifications = NULL;
+ }
+
+ gfs_parent_class->destroy (object);
+}
+
+static void
+gfs_class_init (GObjectClass *klass)
+{
+ GtkObjectClass *gto_class = (GtkObjectClass *) klass;
+
+ gto_class->destroy = gfs_destroy;
+
+ gfs_parent_class = g_type_class_peek (gtk_hbox_get_type ());
+
+ gfs_signals [FONT_CHANGED] =
+ g_signal_new (
+ "font_changed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOFontSelClass, font_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
+
+GSF_CLASS (GOFontSel, go_font_sel,
+ gfs_class_init, gfs_init, GTK_TYPE_HBOX)
+
+GtkWidget *
+go_font_sel_new (void)
+{
+ return g_object_new (GO_FONT_SEL_TYPE, NULL);
+}
+
+void
+go_font_sel_editable_enters (GOFontSel *gfs, GtkWindow *dialog)
+{
+ go_editable_enters (dialog,
+ GTK_WIDGET (gfs->font_name_entry));
+ go_editable_enters (dialog,
+ GTK_WIDGET (gfs->font_style_entry));
+ go_editable_enters (dialog,
+ GTK_WIDGET (gfs->font_size_entry));
+}
+
+void
+go_font_sel_set_sample_text (GOFontSel *gfs, char const *text)
+{
+ g_return_if_fail (IS_GO_FONT_SEL (gfs));
+ foo_canvas_item_set (gfs->font_preview_text,
+ /* xgettext: This text is used as a sample when selecting a font
+ * please choose a translation that would produce common
+ * characters specific to the target alphabet. */
+ "text", ((text == NULL) ? _("AaBbCcDdEe12345") : text),
+ NULL);
+}
+
+GOFont const *
+go_font_sel_get_font (GOFontSel const *gfs)
+{
+ g_return_val_if_fail (IS_GO_FONT_SEL (gfs), NULL);
+ return gfs->current;
+}
+
+static void
+go_font_sel_set_name (GOFontSel *gfs, char const *font_name)
+{
+ GSList *ptr;
+ int row;
+
+ for (row = 0, ptr = go_font_family_list; ptr != NULL; ptr = ptr->next, row++)
+ if (g_ascii_strcasecmp (font_name, ptr->data) == 0)
+ break;
+ select_row (gfs->font_name_list, (ptr != NULL) ? row : -1);
+}
+
+static void
+go_font_sel_set_style (GOFontSel *gfs, gboolean is_bold, gboolean is_italic)
+{
+ int n;
+
+ if (is_bold) {
+ if (is_italic)
+ n = 2;
+ else
+ n = 1;
+ } else {
+ if (is_italic)
+ n = 3;
+ else
+ n = 0;
+ }
+ select_row (gfs->font_style_list, n);
+
+ go_font_sel_add_attr (gfs,
+ pango_attr_weight_new (is_bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL),
+ pango_attr_style_new (is_italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
+ go_dont_sel_emit_changed (gfs);
+}
+
+static void
+go_font_sel_set_points (GOFontSel *gfs,
+ double point_size)
+{
+ int i;
+
+ for (i = 0; go_font_sizes [i] != 0; i++)
+ if (go_font_sizes [i] == point_size) {
+ select_row (gfs->font_size_list, i);
+ break;
+ }
+
+ if (go_font_sizes [i] == 0) {
+ char *buffer;
+ buffer = g_strdup_printf ("%g", point_size);
+ gtk_entry_set_text (GTK_ENTRY (gfs->font_size_entry), buffer);
+ g_free (buffer);
+ }
+}
+
+void
+go_font_sel_set_font (GOFontSel *gfs, GOFont const *font)
+{
+ g_return_if_fail (IS_GO_FONT_SEL (gfs));
+
+ go_font_sel_set_name (gfs, pango_font_description_get_family (font->desc));
+ go_font_sel_set_style (gfs,
+ pango_font_description_get_weight (font->desc) >= PANGO_WEIGHT_BOLD,
+ pango_font_description_get_style (font->desc) != PANGO_STYLE_NORMAL);
+ go_font_sel_set_points (gfs,
+ pango_font_description_get_size (font->desc) / PANGO_SCALE);
+ go_font_sel_set_strike (gfs, font->has_strike);
+ go_font_sel_set_uline (gfs, font->underline);
+ go_font_sel_set_color (gfs, font->color);
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-dock-layout.h
@@ -0,0 +1,135 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* go-dock-layout.c
+
+ Copyright (C) 1998 Free Software Foundation
+
+ All rights reserved.
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Ettore Perazzoli <ettore at comm2000.it>
+*/
+/*
+ @NOTATION@
+*/
+
+#ifndef _GO_DOCK_LAYOUT_H
+#define _GO_DOCK_LAYOUT_H
+
+
+
+G_BEGIN_DECLS
+
+#define GO_TYPE_DOCK_LAYOUT (go_dock_layout_get_type ())
+#define GO_DOCK_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GO_TYPE_DOCK_LAYOUT, GoDockLayout))
+#define GO_DOCK_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GO_TYPE_DOCK_LAYOUT, GoDockLayoutClass))
+#define GO_IS_DOCK_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GO_TYPE_DOCK_LAYOUT))
+#define GO_IS_DOCK_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GO_TYPE_DOCK_LAYOUT))
+#define GO_DOCK_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GO_TYPE_DOCK_LAYOUT, GoDockLayoutClass))
+
+typedef struct _GoDockLayoutItem GoDockLayoutItem;
+typedef struct _GoDockLayoutClass GoDockLayoutClass;
+typedef struct _GoDockLayout GoDockLayout;
+typedef struct _GoDockLayoutPrivate GoDockLayoutPrivate;
+
+#include <goffice/gui-utils/go-dock.h>
+#include <goffice/gui-utils/go-dock-item.h>
+
+struct _GoDockLayoutItem
+{
+ GoDockItem *item;
+
+ GoDockPlacement placement;
+
+ union
+ {
+ struct
+ {
+ gint x;
+ gint y;
+ GtkOrientation orientation;
+ } floating;
+
+ struct
+ {
+ gint band_num;
+ gint band_position;
+ gint offset;
+ } docked;
+
+ } position;
+};
+
+struct _GoDockLayout
+{
+ GObject object;
+
+ GList *items; /* GoDockLayoutItem */
+
+ /*< private >*/
+ GoDockLayoutPrivate *_priv;
+};
+
+struct _GoDockLayoutClass
+{
+ GObjectClass parent_class;
+
+ gpointer dummy[4];
+};
+
+GoDockLayout *go_dock_layout_new (void);
+GType go_dock_layout_get_type (void) G_GNUC_CONST;
+
+gboolean go_dock_layout_add_item (GoDockLayout *layout,
+ GoDockItem *item,
+ GoDockPlacement placement,
+ gint band_num,
+ gint band_position,
+ gint offset);
+
+gboolean go_dock_layout_add_floating_item
+ (GoDockLayout *layout,
+ GoDockItem *item,
+ gint x, gint y,
+ GtkOrientation orientation);
+
+GoDockLayoutItem *go_dock_layout_get_item (GoDockLayout *layout,
+ GoDockItem *item);
+GoDockLayoutItem *go_dock_layout_get_item_by_name
+ (GoDockLayout *layout,
+ const gchar *name);
+
+gboolean go_dock_layout_remove_item
+ (GoDockLayout *layout,
+ GoDockItem *item);
+gboolean go_dock_layout_remove_item_by_name
+ (GoDockLayout *layout,
+ const gchar *name);
+
+gchar *go_dock_layout_create_string
+ (GoDockLayout *layout);
+gboolean go_dock_layout_parse_string
+ (GoDockLayout *layout,
+ const gchar *string);
+
+gboolean go_dock_layout_add_to_dock
+ (GoDockLayout *layout,
+ GoDock *dock);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+++ lib/goffice/gui-utils/go-dock.c
@@ -0,0 +1,1816 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* go-dock.c
+
+ Copyright (C) 1998 Free Software Foundation
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Ettore Perazzoli <ettore at comm2000.it>
+*/
+/*
+ @NOTATION@
+*/
+
+#include "gnumeric-config.h"
+#include <glib/gi18n.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "go-dock.h"
+#include "go-dock-band.h"
+#include "go-dock-item.h"
+#include <libgnome/gnome-macros.h>
+
+GNOME_CLASS_BOILERPLATE (GoDock, go_dock,
+ GtkContainer, GTK_TYPE_CONTAINER);
+
+#define noBONOBO_DOCK_DEBUG
+
+/* FIXME: To be removed. */
+#if defined GO_DOCK_DEBUG && defined __GNUC__
+#define DEBUG(x) \
+ do \
+ { \
+ printf ("%s.%d: ", __FUNCTION__, __LINE__); \
+ printf x; \
+ putchar ('\n'); \
+ } \
+ while (0)
+#else
+#define DEBUG(x)
+#endif
+
+
+
+struct _GoDockPrivate
+{
+ GdkDragContext *current_drag;
+};
+
+enum {
+ LAYOUT_CHANGED,
+ LAST_SIGNAL
+};
+
+
+
+static void go_dock_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void go_dock_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void go_dock_map (GtkWidget *widget);
+static void go_dock_unmap (GtkWidget *widget);
+static void go_dock_add (GtkContainer *container,
+ GtkWidget *child);
+static void go_dock_remove (GtkContainer *container,
+ GtkWidget *widget);
+static void go_dock_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data);
+static void go_dock_finalize (GObject *object);
+
+static void size_request_v (GList *list,
+ GtkRequisition *requisition);
+static void size_request_h (GList *list,
+ GtkRequisition *requisition);
+static gint size_allocate_v (GList *list,
+ gint start_x, gint start_y,
+ guint width, gint direction);
+static gint size_allocate_h (GList *list,
+ gint start_x, gint start_y,
+ guint width, gint direction);
+static void map_widget (GtkWidget *w);
+static void map_widget_foreach (gpointer data,
+ gpointer user_data);
+static void map_band_list (GList *list);
+static void unmap_widget (GtkWidget *w);
+static void unmap_widget_foreach (gpointer data,
+ gpointer user_data);
+static void unmap_band_list (GList *list);
+static gboolean remove_from_band_list (GList **list,
+ GoDockBand *child);
+static void forall_helper (GList *list,
+ GtkCallback callback,
+ gpointer callback_data);
+
+static void drag_begin (GtkWidget *widget,
+ gpointer data);
+static void drag_end_bands (GList **list,
+ GoDockItem *item);
+static void drag_end (GtkWidget *widget,
+ gpointer data);
+static gboolean drag_new (GoDock *dock,
+ GoDockItem *item,
+ GList **area,
+ GList *where,
+ gint x, gint y,
+ gboolean is_vertical);
+static gboolean drag_to (GoDock *dock,
+ GoDockItem *item,
+ GList *where,
+ gint x, gint y,
+ gboolean is_vertical);
+static gboolean drag_floating (GoDock *dock,
+ GoDockItem *item,
+ gint x, gint y,
+ gint rel_x, gint rel_y);
+static gboolean drag_check (GoDock *dock,
+ GoDockItem *item,
+ GList **area,
+ gint x, gint y,
+ gboolean is_vertical);
+static void drag_snap (GoDock *dock,
+ GtkWidget *widget,
+ gint x, gint y);
+static void drag_motion (GtkWidget *widget,
+ gint x, gint y,
+ gpointer data);
+
+static GoDockItem *get_docked_item_by_name (GoDock *dock,
+ const gchar *name,
+ GoDockPlacement *placement_return,
+ guint *num_band_return,
+ guint *band_position_return,
+ guint *offset_return);
+static GoDockItem *get_floating_item_by_name (GoDock *dock,
+ const gchar *name);
+
+static void connect_drag_signals (GoDock *dock,
+ GtkWidget *item);
+
+
+static guint dock_signals[LAST_SIGNAL] = { 0 };
+
+
+
+static void
+go_dock_class_init (GoDockClass *class)
+{
+ GtkObjectClass *object_class;
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
+
+ object_class = (GtkObjectClass *) class;
+ gobject_class = (GObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+ container_class = (GtkContainerClass *) class;
+
+ gobject_class->finalize = go_dock_finalize;
+
+ widget_class->size_request = go_dock_size_request;
+ widget_class->size_allocate = go_dock_size_allocate;
+ widget_class->map = go_dock_map;
+ widget_class->unmap = go_dock_unmap;
+
+ container_class->add = go_dock_add;
+ container_class->remove = go_dock_remove;
+ container_class->forall = go_dock_forall;
+
+ dock_signals[LAYOUT_CHANGED] =
+ g_signal_new ("layout_changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GoDockClass,
+ layout_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+static void
+go_dock_instance_init (GoDock *dock)
+{
+ GTK_WIDGET_SET_FLAGS (GTK_WIDGET (dock), GTK_NO_WINDOW);
+
+ dock->_priv = NULL;
+ /* XXX: when there is some private stuff enable this
+ dock->_priv = g_new0(GoDockPrivate, 1);
+ */
+
+ dock->client_area = NULL;
+
+ dock->top_bands = NULL;
+ dock->bottom_bands = NULL;
+ dock->right_bands = NULL;
+ dock->left_bands = NULL;
+
+ dock->floating_children = NULL;
+
+ dock->floating_items_allowed = TRUE;
+}
+
+
+
+static void
+size_request_v (GList *list, GtkRequisition *requisition)
+{
+ for (; list != NULL; list = list->next)
+ {
+ GtkWidget *w;
+ GtkRequisition req;
+
+ w = GTK_WIDGET (list->data);
+ gtk_widget_size_request (w, &req);
+ requisition->width += req.width;
+ requisition->height = MAX (requisition->height, req.height);
+ }
+}
+
+static void
+size_request_h (GList *list, GtkRequisition *requisition)
+{
+ for (list = list; list != NULL; list = list->next)
+ {
+ GtkWidget *w;
+ GtkRequisition req;
+
+ w = GTK_WIDGET (list->data);
+ gtk_widget_size_request (w, &req);
+ requisition->height += req.height;
+ requisition->width = MAX (requisition->width, req.width);
+ }
+}
+
+static void
+go_dock_size_request (GtkWidget *widget, GtkRequisition *requisition)
+{
+ GoDock *dock;
+ GList *lp;
+
+ dock = GO_DOCK (widget);
+
+ if (dock->client_area != NULL && GTK_WIDGET_VISIBLE (dock->client_area))
+ gtk_widget_size_request (dock->client_area, requisition);
+ else
+ {
+ requisition->width = 0;
+ requisition->height = 0;
+ }
+
+ size_request_v (dock->left_bands, requisition);
+ size_request_v (dock->right_bands, requisition);
+ size_request_h (dock->top_bands, requisition);
+ size_request_h (dock->bottom_bands, requisition);
+
+ lp = dock->floating_children;
+ while (lp != NULL)
+ {
+ GtkWidget *w;
+ GtkRequisition float_item_requisition;
+
+ w = lp->data;
+ lp = lp->next;
+ gtk_widget_size_request (w, &float_item_requisition);
+ }
+}
+
+
+
+static gint
+size_allocate_h (GList *list, gint start_x, gint start_y, guint width,
+ gint direction)
+{
+ GtkAllocation allocation;
+
+ allocation.x = start_x;
+ allocation.y = start_y;
+ allocation.width = width;
+
+ if (direction < 0)
+ list = g_list_last (list);
+ while (list != NULL)
+ {
+ GtkWidget *w;
+
+ w = GTK_WIDGET (list->data);
+ allocation.height = w->requisition.height;
+
+ if (direction > 0)
+ {
+ gtk_widget_size_allocate (w, &allocation);
+ allocation.y += allocation.height;
+ list = list->next;
+ }
+ else
+ {
+ allocation.y -= allocation.height;
+ gtk_widget_size_allocate (w, &allocation);
+ list = list->prev;
+ }
+ }
+
+ return allocation.y;
+}
+
+static gint
+size_allocate_v (GList *list, gint start_x, gint start_y, guint height,
+ gint direction)
+{
+ GtkAllocation allocation;
+
+ allocation.x = start_x;
+ allocation.y = start_y;
+ allocation.height = height;
+
+ if (direction < 0)
+ list = g_list_last (list);
+
+ while (list != NULL)
+ {
+ GtkWidget *w;
+
+ w = GTK_WIDGET (list->data);
+ allocation.width = w->requisition.width;
+
+ if (direction > 0)
+ {
+ gtk_widget_size_allocate (w, &allocation);
+ allocation.x += allocation.width;
+ list = list->next;
+ }
+ else
+ {
+ allocation.x -= allocation.width;
+ gtk_widget_size_allocate (w, &allocation);
+ list = list->prev;
+ }
+ }
+
+ return allocation.x;
+}
+
+static void
+go_dock_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ GoDock *dock;
+ gint top_bands_y, bottom_bands_y;
+ gint left_bands_x, right_bands_x;
+ GtkAllocation child_allocation;
+ GList *lp;
+
+ dock = GO_DOCK (widget);
+
+ widget->allocation = *allocation;
+
+ top_bands_y = size_allocate_h (dock->top_bands,
+ allocation->x,
+ allocation->y,
+ allocation->width,
+ +1);
+
+ bottom_bands_y = size_allocate_h (dock->bottom_bands,
+ allocation->x,
+ allocation->y + allocation->height,
+ allocation->width,
+ -1);
+
+ child_allocation.height = MAX (bottom_bands_y - top_bands_y, 1);
+
+ left_bands_x = size_allocate_v (dock->left_bands,
+ allocation->x,
+ top_bands_y,
+ child_allocation.height,
+ +1);
+
+ right_bands_x = size_allocate_v (dock->right_bands,
+ allocation->x + allocation->width,
+ top_bands_y,
+ child_allocation.height,
+ -1);
+
+ child_allocation.width = MAX (right_bands_x - left_bands_x, 1);
+
+ child_allocation.x = left_bands_x;
+ child_allocation.y = top_bands_y;
+
+ dock->client_rect = child_allocation;
+
+ if (dock->client_area != NULL && GTK_WIDGET_VISIBLE (dock->client_area))
+ gtk_widget_size_allocate (dock->client_area, &child_allocation);
+
+ lp = dock->floating_children;
+ while (lp != NULL)
+ {
+ GtkWidget *w;
+ GtkAllocation float_item_allocation;
+
+ w = lp->data;
+ lp = lp->next;
+ float_item_allocation.x = 0;
+ float_item_allocation.y = 0;
+ float_item_allocation.width = w->requisition.width;
+ float_item_allocation.height = w->requisition.height;
+ gtk_widget_size_allocate (w, &float_item_allocation);
+ }
+}
+
+
+
+static void
+map_widget (GtkWidget *w)
+{
+ if (w != NULL && GTK_WIDGET_VISIBLE (w) && ! GTK_WIDGET_MAPPED (w))
+ gtk_widget_map (w);
+}
+
+static void
+unmap_widget (GtkWidget *w)
+{
+ if (w != NULL && GTK_WIDGET_VISIBLE (w) && GTK_WIDGET_MAPPED (w))
+ gtk_widget_unmap (w);
+}
+
+static void
+map_widget_foreach (gpointer data,
+ gpointer user_data)
+{
+ map_widget (GTK_WIDGET (data));
+}
+
+static void
+unmap_widget_foreach (gpointer data,
+ gpointer user_data)
+{
+ unmap_widget (GTK_WIDGET (data));
+}
+
+static void
+map_band_list (GList *list)
+{
+ while (list != NULL)
+ {
+ GtkWidget *w;
+
+ w = GTK_WIDGET (list->data);
+ map_widget (w);
+
+ list = list->next;
+ }
+}
+
+static void
+unmap_band_list (GList *list)
+{
+ while (list != NULL)
+ {
+ GtkWidget *w;
+
+ w = GTK_WIDGET (list->data);
+ unmap_widget (w);
+
+ list = list->next;
+ }
+}
+
+static void
+go_dock_map (GtkWidget *widget)
+{
+ GoDock *dock;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GO_IS_DOCK(widget));
+
+ GNOME_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
+
+ dock = GO_DOCK (widget);
+
+ map_widget (dock->client_area);
+
+ map_band_list (dock->top_bands);
+ map_band_list (dock->bottom_bands);
+ map_band_list (dock->left_bands);
+ map_band_list (dock->right_bands);
+
+ g_list_foreach (dock->floating_children, map_widget_foreach, NULL);
+}
+
+static void
+go_dock_unmap (GtkWidget *widget)
+{
+ GoDock *dock;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GO_IS_DOCK(widget));
+
+ dock = GO_DOCK (widget);
+
+ unmap_widget (dock->client_area);
+
+ unmap_band_list (dock->top_bands);
+ unmap_band_list (dock->bottom_bands);
+ unmap_band_list (dock->left_bands);
+ unmap_band_list (dock->right_bands);
+
+ g_list_foreach (dock->floating_children, unmap_widget_foreach, NULL);
+
+ GNOME_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget));
+}
+
+
+
+/* GtkContainer methods. */
+
+static void
+go_dock_add (GtkContainer *container, GtkWidget *child)
+{
+ GoDock *dock;
+
+ dock = GO_DOCK (container);
+ go_dock_add_item (dock, GO_DOCK_ITEM(child), GO_DOCK_TOP, 0, 0, 0, TRUE);
+}
+
+static gboolean
+remove_from_band_list (GList **list, GoDockBand *child)
+{
+ GList *lp;
+
+ for (lp = *list; lp != NULL; lp = lp->next)
+ {
+ if (lp->data == child)
+ {
+ gtk_widget_unparent (GTK_WIDGET (child));
+ *list = g_list_remove_link (*list, lp);
+ g_list_free (lp);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+go_dock_remove (GtkContainer *container, GtkWidget *widget)
+{
+ GoDock *dock;
+
+ dock = GO_DOCK (container);
+
+ if (dock->client_area == widget)
+ {
+ gtk_widget_unparent (widget);
+ dock->client_area = NULL;
+ gtk_widget_queue_resize (GTK_WIDGET (dock));
+ }
+ else
+ {
+ /* Check if it's a floating child. */
+ {
+ GList *lp;
+
+ lp = dock->floating_children;
+ while (lp != NULL)
+ {
+ GtkWidget *w;
+
+ w = lp->data;
+ if (w == widget)
+ {
+ gtk_widget_unparent (w);
+ dock->floating_children
+ = g_list_remove_link (dock->floating_children, lp);
+ g_list_free (lp);
+ return;
+ }
+
+ lp = lp->next;
+ }
+ }
+
+ /* Then it must be one of the bands. */
+ {
+ GoDockBand *band;
+
+ g_return_if_fail (GO_IS_DOCK_BAND (widget));
+
+ band = GO_DOCK_BAND (widget);
+ if (remove_from_band_list (&dock->top_bands, band)
+ || remove_from_band_list (&dock->bottom_bands, band)
+ || remove_from_band_list (&dock->left_bands, band)
+ || remove_from_band_list (&dock->right_bands, band))
+ {
+ gtk_widget_queue_resize (GTK_WIDGET (dock));
+ return;
+ }
+ }
+ }
+}
+
+static void
+forall_helper (GList *list,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ while (list != NULL)
+ {
+ GtkWidget *w;
+
+ w = GTK_WIDGET(list->data);
+ list = list->next;
+ (* callback) (w, callback_data);
+ }
+}
+
+static void
+go_dock_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ GoDock *dock;
+ GList *lp;
+
+ g_return_if_fail (container != NULL);
+ g_return_if_fail (GO_IS_DOCK (container));
+ g_return_if_fail (callback != NULL);
+
+ dock = GO_DOCK (container);
+
+ forall_helper (dock->top_bands, callback, callback_data);
+ forall_helper (dock->bottom_bands, callback, callback_data);
+ forall_helper (dock->left_bands, callback, callback_data);
+ forall_helper (dock->right_bands, callback, callback_data);
+
+ lp = dock->floating_children;
+ while (lp != NULL)
+ {
+ GtkWidget *w;
+
+ w = lp->data;
+ lp = lp->next;
+ (* callback) (w, callback_data);
+ }
+
+ if (dock->client_area != NULL)
+ (* callback) (dock->client_area, callback_data);
+}
+
+static void
+go_dock_finalize (GObject *object)
+{
+ GoDock *self = GO_DOCK (object);
+
+ g_free (self->_priv);
+ self->_priv = NULL;
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+
+
+static void
+new_band_setup (GoDock *dock,
+ GtkWidget *new_band,
+ GtkOrientation orientation)
+{
+ go_dock_band_set_orientation (
+ GO_DOCK_BAND (new_band), orientation);
+ gtk_widget_set_parent (GTK_WIDGET (new_band), GTK_WIDGET (dock));
+ gtk_widget_queue_resize (GTK_WIDGET (new_band));
+ gtk_widget_show (GTK_WIDGET (new_band));
+}
+
+
+/* When an item is being dragged, there can be 3 situations:
+
+ (I) A new band is created and the item is docked to it.
+
+ (II) The item is docked to an existing band.
+
+ (III) The item must be floating, so it has to be detached if
+ currently not floating, and moved around in its own window. */
+
+/* Case (I): Dock `item' into a new band next to `where' in the
+ docking area `area'. If `where' is NULL, the band becomes the
+ first one in `area'. */
+static gboolean
+drag_new (GoDock *dock,
+ GoDockItem *item,
+ GList **area,
+ GList *where,
+ gint x, gint y,
+ gboolean is_vertical)
+{
+ GoDockBand *new_band;
+ GList *next;
+
+ DEBUG (("entering function"));
+
+ new_band = NULL;
+
+ /* We need a new band next to `where', but we try to re-use the band
+ next to it if either it contains no children, or it only contains
+ `item'. */
+
+ next = NULL;
+ if (where == NULL && area != NULL)
+ next = *area;
+ else
+ next = where->next;
+ if (next != NULL)
+ {
+ GoDockBand *band;
+ guint num_children;
+
+ band = GO_DOCK_BAND (next->data);
+
+ num_children = go_dock_band_get_num_children (band);
+
+ if (num_children == 0
+ || (num_children == 1
+ && GTK_WIDGET (band) == GTK_WIDGET (item)->parent))
+ new_band = GO_DOCK_BAND (next->data);
+ }
+
+ /* Create the new band and make it our child if we cannot re-use an
+ existing one. */
+ if (new_band == NULL)
+ {
+ new_band = GO_DOCK_BAND (go_dock_band_new ());
+
+ /* This is mostly to remember that `drag_allocation' for this
+ child is bogus, as it was not previously allocated. */
+ new_band->new_for_drag = TRUE;
+
+ if (where == NULL)
+ *area = where = g_list_prepend (*area, new_band);
+ else if (where->next == NULL)
+ g_list_append (where, new_band);
+ else
+ g_list_prepend (where->next, new_band);
+
+ new_band_setup (dock, GTK_WIDGET (new_band),
+ is_vertical ? GTK_ORIENTATION_VERTICAL
+ : GTK_ORIENTATION_HORIZONTAL);
+ }
+
+ /* Move the item to the new band. (This is a no-op if we are using
+ `where->next' and it already contains `item'.) */
+ go_dock_item_attach (item, GTK_WIDGET (new_band), x, y);
+
+ /* Prepare the band for dragging of `item'. */
+ go_dock_band_drag_begin (new_band, item);
+
+ /* Set the offset of `item' in the band. */
+ if (is_vertical)
+ go_dock_band_set_child_offset (new_band, GTK_WIDGET (item),
+ MAX (y - dock->client_rect.y, 0));
+ else
+ go_dock_band_set_child_offset (new_band, GTK_WIDGET (item),
+ MAX (x - GTK_WIDGET (dock)->allocation.x, 0));
+
+ return TRUE;
+}
+
+/* Case (II): Drag into an existing band. */
+static gboolean
+drag_to (GoDock *dock,
+ GoDockItem *item,
+ GList *where,
+ gint x, gint y,
+ gboolean is_vertical)
+{
+ DEBUG (("x %d y %d", x, y));
+
+ return go_dock_band_drag_to (GO_DOCK_BAND (where->data), item, x, y);
+}
+
+/* Case (III): Move a floating (i.e. floating) item. */
+static gboolean
+drag_floating (GoDock *dock,
+ GoDockItem *item,
+ gint x,
+ gint y,
+ gint rel_x,
+ gint rel_y)
+{
+ GtkWidget *item_widget, *dock_widget;
+
+ item_widget = GTK_WIDGET (item);
+ dock_widget = GTK_WIDGET (dock);
+
+ if (!item->is_floating && item_widget->parent != dock_widget)
+ {
+ GtkAllocation *dock_allocation, *client_allocation;
+
+ /* The item is currently not floating (so it is not our child).
+ Make it so if we are outside the docking areas. */
+
+ dock_allocation = &dock_widget->allocation;
+ if (dock->client_area)
+ client_allocation = &dock->client_area->allocation;
+ else
+ client_allocation = NULL;
+
+ if (rel_x < 0
+ || rel_x >= dock_allocation->width
+ || rel_y < 0
+ || rel_y >= dock_allocation->height
+ || (client_allocation != NULL
+ && rel_x >= client_allocation->x
+ && rel_x < client_allocation->x + client_allocation->width
+ && rel_y >= client_allocation->y
+ && rel_y < client_allocation->y + client_allocation->height))
+ {
+ gtk_widget_ref (item_widget);
+
+ gtk_container_remove (GTK_CONTAINER (item_widget->parent),
+ item_widget);
+ gtk_widget_set_parent (item_widget, dock_widget);
+
+ dock->floating_children = g_list_prepend (dock->floating_children,
+ item);
+
+ gtk_widget_realize (item_widget);
+ gtk_widget_map (item_widget);
+ gtk_widget_queue_resize (item_widget);
+
+ go_dock_item_detach (item, x, y);
+ if (item->in_drag)
+ go_dock_item_grab_pointer (item);
+
+ gtk_widget_unref (item_widget);
+ }
+ }
+ else
+ {
+ /* The item is already floating; all we have to do is move it to
+ the current dragging position. */
+ go_dock_item_drag_floating (item, x, y);
+ }
+
+ return TRUE;
+}
+
+
+
+/* Check if `item' can be docked to any of the DockBands of the dock
+ area `area'. If so, dock it and return TRUE; otherwise, return
+ FALSE. */
+static gboolean
+drag_check (GoDock *dock,
+ GoDockItem *item,
+ GList **area,
+ gint x, gint y,
+ gboolean is_vertical)
+{
+ GList *lp;
+ GtkAllocation *alloc;
+
+ for (lp = *area; lp != NULL; lp = lp->next)
+ {
+ GoDockBand *band;
+
+ band = GO_DOCK_BAND (lp->data);
+
+ if (! band->new_for_drag)
+ {
+ alloc = &band->drag_allocation;
+
+ if (x >= alloc->x - 10 && x < alloc->x + alloc->width
+ && y >= alloc->y && y < alloc->y + alloc->height)
+ {
+ if (is_vertical)
+ {
+ if (x < alloc->x + alloc->width / 2
+ && drag_to (dock, item, lp, x, y, TRUE))
+ return TRUE;
+ else
+ return drag_new (dock, item, area, lp, x, y, TRUE);
+ }
+ else
+ {
+ if (y < alloc->y + alloc->height / 2
+ && drag_to (dock, item, lp, x, y, FALSE))
+ return TRUE;
+ else
+ return drag_new (dock, item, area, lp, x, y, FALSE);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/* Snap the GoDockItem `widget' to `dock' at the specified
+ position. */
+static void
+drag_snap (GoDock *dock,
+ GtkWidget *widget,
+ gint x, gint y)
+{
+#define SNAP 50
+ GoDockItem *item;
+ GoDockItemBehavior item_behavior;
+ gint win_x, win_y;
+ gint rel_x, rel_y;
+ gboolean item_allows_horizontal, item_allows_vertical;
+
+ item = GO_DOCK_ITEM (widget);
+
+ item_behavior = go_dock_item_get_behavior (item);
+ item_allows_horizontal = ! (item_behavior
+ & GO_DOCK_ITEM_BEH_NEVER_HORIZONTAL);
+ item_allows_vertical = ! (item_behavior
+ & GO_DOCK_ITEM_BEH_NEVER_VERTICAL);
+
+ gdk_window_get_origin (GTK_WIDGET (dock)->window, &win_x, &win_y);
+ rel_x = x - win_x;
+ rel_y = y - win_y;
+
+ DEBUG (("(%d,%d)", x, y));
+ DEBUG (("relative (%d,%d)", rel_x, rel_y));
+
+ if (item_allows_horizontal
+ && rel_x >= 0 && rel_x < GTK_WIDGET (dock)->allocation.width)
+ {
+ /* Check prepending to top/bottom bands. */
+ if (rel_y < 0 && rel_y >= -SNAP
+ && drag_new (dock, item, &dock->top_bands, NULL,
+ rel_x, rel_y, FALSE))
+ return;
+ else if (rel_y >= dock->client_rect.y + dock->client_rect.height - SNAP
+ && rel_y < dock->client_rect.y + dock->client_rect.height
+ && drag_new (dock, item, &dock->bottom_bands, NULL,
+ rel_x, rel_y, FALSE))
+ return;
+ }
+
+ if (item_allows_vertical
+ && rel_y >= dock->client_rect.y
+ && rel_y < dock->client_rect.y + dock->client_rect.height)
+ {
+ /* Check prepending to left/right bands. */
+ if (rel_x < 0 && rel_x >= -SNAP
+ && drag_new (dock, item, &dock->left_bands, NULL,
+ rel_x, rel_y, TRUE))
+ return;
+ else if (rel_x >= dock->client_rect.x + dock->client_rect.width - SNAP
+ && rel_x < dock->client_rect.x + dock->client_rect.width
+ && drag_new (dock, item, &dock->right_bands, NULL,
+ rel_x, rel_y, TRUE))
+ return;
+ }
+
+ /* Check dragging into bands. */
+ if (item_allows_horizontal
+ && drag_check (dock, item, &dock->top_bands, rel_x, rel_y, FALSE))
+ return;
+ else if (item_allows_horizontal
+ && drag_check (dock, item, &dock->bottom_bands, rel_x, rel_y, FALSE))
+ return;
+ else if (item_allows_vertical
+ && drag_check (dock, item, &dock->left_bands, rel_x, rel_y, TRUE))
+ return;
+ else if (item_allows_vertical
+ && drag_check (dock, item, &dock->right_bands, rel_x, rel_y, TRUE))
+ return;
+
+ /* We are not in any "interesting" area: the item must be floating
+ if allowed to. */
+ if (dock->floating_items_allowed
+ && ! (item_behavior & GO_DOCK_ITEM_BEH_NEVER_DETACH))
+ drag_floating (dock, item, x, y, rel_x, rel_y);
+
+ /* If still not floating, fall back to moving the item in its own
+ band. */
+ if (! item->is_floating)
+ go_dock_band_drag_to (GO_DOCK_BAND (GTK_WIDGET (item)->parent),
+ item, rel_x, rel_y);
+}
+
+
+
+/* "drag_begin" signal handling. */
+static void
+drag_begin (GtkWidget *widget, gpointer data)
+{
+ GoDock *dock;
+ GoDockItem *item;
+
+ DEBUG (("entering function"));
+
+ dock = GO_DOCK (data);
+ item = GO_DOCK_ITEM (widget);
+
+ /* Communicate all the bands that `widget' is currently being
+ dragged. */
+ g_list_foreach (dock->top_bands, (GFunc) go_dock_band_drag_begin, item);
+ g_list_foreach (dock->bottom_bands, (GFunc) go_dock_band_drag_begin, item);
+ g_list_foreach (dock->right_bands, (GFunc) go_dock_band_drag_begin, item);
+ g_list_foreach (dock->left_bands, (GFunc) go_dock_band_drag_begin, item);
+}
+
+
+
+/* "drag_end" signal handling. */
+
+static void
+drag_end_bands (GList **list, GoDockItem *item)
+{
+ GList *lp;
+ GoDockBand *band;
+
+ lp = *list;
+ while (lp != NULL)
+ {
+ band = GO_DOCK_BAND(lp->data);
+ go_dock_band_drag_end (band, item);
+
+ if (go_dock_band_get_num_children (band) == 0)
+ {
+ GList *next;
+
+ next = lp->next;
+
+ /* This will remove this link, too. */
+ gtk_widget_destroy (GTK_WIDGET (band));
+
+ lp = next;
+ }
+ else
+ lp = lp->next;
+ }
+}
+
+static void
+drag_end (GtkWidget *widget, gpointer data)
+{
+ GoDockItem *item;
+ GoDock *dock;
+
+ DEBUG (("entering function"));
+
+ item = GO_DOCK_ITEM (widget);
+ dock = GO_DOCK (data);
+
+ /* Communicate to all the bands that `item' is no longer being
+ dragged. */
+ drag_end_bands (&dock->top_bands, item);
+ drag_end_bands (&dock->bottom_bands, item);
+ drag_end_bands (&dock->left_bands, item);
+ drag_end_bands (&dock->right_bands, item);
+
+ g_signal_emit (data, dock_signals[LAYOUT_CHANGED], 0);
+}
+
+
+
+/* "drag_motion" signal handling. */
+
+/* Handle a drag motion on the GoDockItem `widget'. This is
+ connected to the "drag_motion" of all the children being added to
+ the GoDock, and tries to dock the dragged item at the current
+ (`x', `y') position of the pointer. */
+static void
+drag_motion (GtkWidget *widget,
+ gint x, gint y,
+ gpointer data)
+{
+ drag_snap (GO_DOCK (data), widget, x, y);
+}
+
+
+
+static GoDockItem *
+get_docked_item_by_name (GoDock *dock,
+ const gchar *name,
+ GoDockPlacement *placement_return,
+ guint *num_band_return,
+ guint *band_position_return,
+ guint *offset_return)
+{
+ {
+ struct
+ {
+ GList *band_list;
+ GoDockPlacement placement;
+ }
+ areas[] =
+ {
+ { NULL, GO_DOCK_TOP },
+ { NULL, GO_DOCK_BOTTOM },
+ { NULL, GO_DOCK_LEFT },
+ { NULL, GO_DOCK_RIGHT },
+ { NULL, GO_DOCK_FLOATING },
+ };
+ GList *lp;
+ guint i;
+
+ areas[0].band_list = dock->top_bands;
+ areas[1].band_list = dock->bottom_bands;
+ areas[2].band_list = dock->left_bands;
+ areas[3].band_list = dock->right_bands;
+
+ for (i = 0; i < 4; i++)
+ {
+ guint num_band;
+
+ for (lp = areas[i].band_list, num_band = 0;
+ lp != NULL;
+ lp = lp->next, num_band++)
+ {
+ GoDockBand *band;
+ GoDockItem *item;
+
+ band = GO_DOCK_BAND(lp->data);
+ item = go_dock_band_get_item_by_name (band,
+ name,
+ band_position_return,
+ offset_return);
+ if (item != NULL)
+ {
+ if (num_band_return != NULL)
+ *num_band_return = num_band;
+ if (placement_return != NULL)
+ *placement_return = areas[i].placement;
+
+ return item;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static GoDockItem *
+get_floating_item_by_name (GoDock *dock,
+ const gchar *name)
+{
+ GList *lp;
+ GoDockItem *item;
+
+ for (lp = dock->floating_children; lp != NULL; lp = lp->next)
+ {
+ item = lp->data;
+ if (strcmp (item->name, name) == 0)
+ return item;
+ }
+
+ return NULL;
+}
+
+static void
+connect_drag_signals (GoDock *dock,
+ GtkWidget *item)
+{
+ if (GO_IS_DOCK_ITEM (item))
+ {
+ DEBUG (("here"));
+ g_signal_connect (item, "dock_drag_begin",
+ G_CALLBACK (drag_begin), dock);
+ g_signal_connect (item, "dock_drag_motion",
+ G_CALLBACK (drag_motion), dock);
+ g_signal_connect (item, "dock_drag_end",
+ G_CALLBACK (drag_end), dock);
+ }
+}
+
+/**
+ * go_dock_new:
+ *
+ * Description: Creates a new #GoDock widget.
+ *
+ * Return value: The new widget.
+ **/
+GtkWidget *
+go_dock_new (void)
+{
+ GoDock *dock;
+ GtkWidget *widget;
+
+ dock = g_object_new (go_dock_get_type (), NULL);
+ widget = GTK_WIDGET (dock);
+
+#if 0 /* FIXME: should I? */
+ if (GTK_WIDGET_VISIBLE (widget))
+ gtk_widget_queue_resize (widget);
+#endif
+
+ return widget;
+}
+
+/**
+ * go_dock_allow_floating_items:
+ * @dock: A pointer to a #GoDock widget
+ * @enable: Specifies whether floating items are allowed in this dock
+ *
+ * Description: Enable or disable floating items on @dock, according
+ * to @enable.
+ **/
+void
+go_dock_allow_floating_items (GoDock *dock,
+ gboolean enable)
+{
+ dock->floating_items_allowed = enable;
+}
+
+static GList **
+get_band_list (GoDock *dock, GoDockPlacement placement)
+{
+ GList **band_ptr = NULL;
+
+ switch (placement)
+ {
+ case GO_DOCK_TOP:
+ band_ptr = &dock->top_bands;
+ break;
+ case GO_DOCK_BOTTOM:
+ band_ptr = &dock->bottom_bands;
+ break;
+ case GO_DOCK_LEFT:
+ band_ptr = &dock->left_bands;
+ break;
+ case GO_DOCK_RIGHT:
+ band_ptr = &dock->right_bands;
+ break;
+ default:
+ break;
+ }
+ return band_ptr;
+}
+
+/**
+ * go_dock_add_item:
+ * @dock: A pointer to a #GoDock widget
+ * @item: The item to add
+ * @placement: Placement for the new item
+ * @band_num: Number of the band the new item must be added to
+ * @position: Position of the item in the specified band
+ * @offset: Offset (in pixels) from the previous item in the same band
+ * @in_new_band: Specifies whether a new band must be created for this item
+ *
+ * Description: Add @item to @dock. @placement can be either
+ * %GO_DOCK_TOP, %GO_DOCK_RIGHT, %GO_DOCK_BOTTOM or
+ * %GO_DOCK_LEFT, and specifies what area of the dock should
+ * contain the item. If @in_new_band is %TRUE, a new dock band is
+ * created at the position specified by @band_num; otherwise, the item
+ * is added to the @band_num'th band.
+ **/
+void
+go_dock_add_item (GoDock *dock,
+ GoDockItem *item,
+ GoDockPlacement placement,
+ guint band_num,
+ gint position,
+ guint offset,
+ gboolean in_new_band)
+{
+ GoDockBand *band;
+ GList **band_ptr;
+ GList *p;
+
+ DEBUG (("band_num %d offset %d position %d in_new_band %d",
+ band_num, offset, position, in_new_band));
+
+ if (placement == GO_DOCK_FLOATING)
+ {
+ g_warning ("Floating dock items not supported by `go_dock_add_item'.");
+ return;
+ }
+ band_ptr = get_band_list (dock, placement);
+ g_return_if_fail (band_ptr != NULL);
+
+ p = g_list_nth (*band_ptr, band_num);
+ if (in_new_band || p == NULL)
+ {
+ GtkWidget *new_band;
+
+ new_band = go_dock_band_new ();
+
+ /* FIXME: slow. */
+ if (in_new_band)
+ {
+ *band_ptr = g_list_insert (*band_ptr, new_band, band_num);
+ p = g_list_nth (*band_ptr, band_num);
+ if (p == NULL)
+ p = g_list_last (*band_ptr);
+ }
+ else
+ {
+ *band_ptr = g_list_append (*band_ptr, new_band);
+ p = g_list_last (*band_ptr);
+ }
+
+ if (placement == GO_DOCK_TOP || placement == GO_DOCK_BOTTOM)
+ go_dock_band_set_orientation (GO_DOCK_BAND (new_band),
+ GTK_ORIENTATION_HORIZONTAL);
+ else
+ go_dock_band_set_orientation (GO_DOCK_BAND (new_band),
+ GTK_ORIENTATION_VERTICAL);
+
+ gtk_widget_set_parent (new_band, GTK_WIDGET (dock));
+ gtk_widget_show (new_band);
+ gtk_widget_queue_resize (GTK_WIDGET (dock));
+ }
+
+ band = GO_DOCK_BAND (p->data);
+ go_dock_band_insert (band, GTK_WIDGET(item), offset, position);
+
+ connect_drag_signals (dock, GTK_WIDGET(item));
+
+ g_signal_emit (dock, dock_signals[LAYOUT_CHANGED], 0);
+}
+
+/**
+ * go_dock_add_floating_item:
+ * @dock: A #GoDock widget
+ * @item: The item to be added
+ * @x: X-coordinate for the floating item
+ * @y: Y-coordinate for the floating item
+ * @orientation: Orientation for the new item.
+ *
+ * Description: Add @item to @dock and make it floating at the
+ * specified (@x, @y) coordinates (relative to the root window of the
+ * screen).
+ **/
+void
+go_dock_add_floating_item (GoDock *dock,
+ GoDockItem *item,
+ gint x, gint y,
+ GtkOrientation orientation)
+{
+ GtkWidget *widget;
+
+ g_return_if_fail (GO_IS_DOCK_ITEM (item));
+
+ go_dock_item_set_orientation (item, orientation);
+
+ widget = GTK_WIDGET(item);
+ gtk_widget_ref (widget);
+
+#if 0
+ if (widget->parent != NULL)
+ gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
+#endif
+
+ gtk_widget_set_parent (widget, GTK_WIDGET (dock));
+
+ if (GTK_WIDGET_REALIZED (widget->parent))
+ gtk_widget_realize (widget);
+
+ if (GTK_WIDGET_VISIBLE (widget->parent) && GTK_WIDGET_VISIBLE (widget))
+ {
+ if (GTK_WIDGET_MAPPED (widget->parent))
+ gtk_widget_map (widget);
+
+ gtk_widget_queue_resize (widget);
+ }
+
+ go_dock_item_detach (item, x, y);
+ dock->floating_children = g_list_prepend (dock->floating_children, widget);
+
+ connect_drag_signals (dock, widget);
+
+ gtk_widget_unref (widget);
+
+ g_signal_emit (dock, dock_signals[LAYOUT_CHANGED], 0);
+}
+
+/**
+ * go_dock_set_client_area:
+ * @dock: A #GoDock widget
+ * @widget: The widget to be used for the client area.
+ *
+ * Description: Specify a widget for the dock's client area.
+ **/
+void
+go_dock_set_client_area (GoDock *dock, GtkWidget *widget)
+{
+ g_return_if_fail (dock != NULL);
+
+ if (widget != NULL)
+ gtk_widget_ref (widget);
+
+ if (dock->client_area != NULL)
+ gtk_widget_unparent (dock->client_area);
+
+ if (widget != NULL)
+ {
+ gtk_widget_set_parent (widget, GTK_WIDGET (dock));
+ dock->client_area = widget;
+
+ if (GTK_WIDGET_REALIZED (widget->parent))
+ gtk_widget_realize (widget);
+
+ if (GTK_WIDGET_VISIBLE (widget->parent) && GTK_WIDGET_VISIBLE (widget))
+ {
+ if (GTK_WIDGET_MAPPED (widget->parent))
+ gtk_widget_map (widget);
+
+ gtk_widget_queue_resize (widget);
+ }
+ }
+ else
+ {
+ if (dock->client_area != NULL && GTK_WIDGET_VISIBLE (dock))
+ gtk_widget_queue_resize (GTK_WIDGET (dock));
+ dock->client_area = NULL;
+ }
+
+ if (widget != NULL)
+ gtk_widget_unref (widget);
+}
+
+/**
+ * go_dock_get_client_area:
+ * @dock: A #GoDock widget.
+ *
+ * Description: Retrieve the widget being used as the client area in
+ * @dock.
+ *
+ * Returns: The client area widget.
+ **/
+GtkWidget *
+go_dock_get_client_area (GoDock *dock)
+{
+ return dock->client_area;
+}
+
+/**
+ * go_dock_get_item_by_name:
+ * @dock: A #GoDock widget.
+ * @name: The name of the dock item to retrieve
+ * @placement_return: A pointer to a variable holding the item's placement
+ * @num_band_return: A pointer to a variable holding the band number
+ * @band_position_return: A pointer to a variable holding the position
+ * of the item within the band
+ * @offset_return: A pointer to a variable holding the offset of the item
+ * from the previous item in the same band
+ *
+ * Description: Retrieve the dock item named @name; information about
+ * its position in the dock is returned via @placement_return,
+ * @num_band_return, @band_position_return and @offset_return. If
+ * the placement is %GO_DOCK_FLOATING *@num_band_return,
+ * *@band_position_return and *@offset_return are not set.
+ *
+ * Returns: The named #GoDockItem widget, or %NULL if no item with
+ * such name exists.
+ **/
+GoDockItem *
+go_dock_get_item_by_name (GoDock *dock,
+ const gchar *name,
+ GoDockPlacement *placement_return,
+ guint *num_band_return,
+ guint *band_position_return,
+ guint *offset_return)
+{
+ GoDockItem *item;
+
+ item = get_docked_item_by_name (dock,
+ name,
+ placement_return,
+ num_band_return,
+ band_position_return,
+ offset_return);
+ if (item != NULL)
+ return item;
+
+ item = get_floating_item_by_name (dock, name);
+ if (item != NULL)
+ {
+ if (placement_return != NULL)
+ *placement_return = GO_DOCK_FLOATING;
+ return item;
+ }
+
+ return NULL;
+}
+
+
+
+/* Layout functions. */
+
+static void
+layout_add_floating (GoDock *dock,
+ GoDockLayout *layout)
+{
+ GList *lp;
+
+ for (lp = dock->floating_children; lp != NULL; lp = lp->next)
+ {
+ GtkOrientation orientation;
+ gint x, y;
+ GoDockItem *item;
+
+ item = GO_DOCK_ITEM (lp->data);
+
+ orientation = go_dock_item_get_orientation (item);
+ go_dock_item_get_floating_position (item, &x, &y);
+
+ go_dock_layout_add_floating_item (layout, item,
+ x, y,
+ orientation);
+ }
+}
+
+static void
+layout_add_bands (GoDock *dock,
+ GoDockLayout *layout,
+ GoDockPlacement placement,
+ GList *band_list)
+{
+ guint band_num;
+ GList *lp;
+
+ for (lp = band_list, band_num = 0;
+ lp != NULL;
+ lp = lp->next, band_num++)
+ {
+ GoDockBand *band;
+
+ band = GO_DOCK_BAND(lp->data);
+ go_dock_band_layout_add (band, layout, placement, band_num);
+ }
+}
+
+/**
+ * go_dock_get_layout:
+ * @dock: A #GoDock widget
+ *
+ * Description: Retrieve the layout of @dock.
+ *
+ * Returns: @dock's layout as a #GoDockLayout object.
+ **/
+GoDockLayout *
+go_dock_get_layout (GoDock *dock)
+{
+ GoDockLayout *layout;
+
+ layout = go_dock_layout_new ();
+
+ layout_add_bands (dock, layout, GO_DOCK_TOP, dock->top_bands);
+ layout_add_bands (dock, layout, GO_DOCK_BOTTOM, dock->bottom_bands);
+ layout_add_bands (dock, layout, GO_DOCK_LEFT, dock->left_bands);
+ layout_add_bands (dock, layout, GO_DOCK_RIGHT, dock->right_bands);
+
+ layout_add_floating (dock, layout);
+
+ return layout;
+}
+
+/**
+ * go_dock_add_from_layout:
+ * @dock: The #GoDock widget
+ * @layout: A #GoDockLayout widget
+ *
+ * Description: Add all the items in @layout to the specified @dock.
+ *
+ * Returns: %TRUE if the operation succeeds, %FALSE if it fails.
+ **/
+gboolean
+go_dock_add_from_layout (GoDock *dock,
+ GoDockLayout *layout)
+{
+ return go_dock_layout_add_to_dock (layout, dock);
+}
+
+static GList **
+find_band_list (GoDock *dock,
+ GoDockBand *band,
+ GoDockPlacement *placement)
+{
+ GList **band_list = NULL;
+
+ if (g_list_find (dock->top_bands, band))
+ {
+ *placement = GO_DOCK_TOP;
+ band_list = &dock->top_bands;
+ }
+
+ if (g_list_find (dock->bottom_bands, band))
+ {
+ *placement = GO_DOCK_BOTTOM;
+ band_list = &dock->bottom_bands;
+ }
+
+ if (g_list_find (dock->left_bands, band))
+ {
+ *placement = GO_DOCK_LEFT;
+ band_list = &dock->left_bands;
+ }
+
+ if (g_list_find (dock->right_bands, band))
+ {
+ *placement = GO_DOCK_RIGHT;
+ band_list = &dock->right_bands;
+ }
+
+ return band_list;
+}
+
+static gboolean
+insert_into_band_list (GoDock *dock,
+ GList **band_list,
+ GtkOrientation orientation,
+ GoDockItem *item,
+ gboolean prepend)
+{
+ GtkWidget *new_band;
+
+ new_band = go_dock_band_new ();
+
+ if (item->behavior & GO_DOCK_ITEM_BEH_NEVER_VERTICAL)
+ orientation = GTK_ORIENTATION_HORIZONTAL;
+
+ if (item->behavior & GO_DOCK_ITEM_BEH_NEVER_HORIZONTAL)
+ orientation = GTK_ORIENTATION_VERTICAL;
+
+ if (!go_dock_band_append (
+ GO_DOCK_BAND (new_band), GTK_WIDGET (item), 0))
+ return FALSE;
+
+ if (prepend)
+ *band_list = g_list_prepend (*band_list, new_band);
+ else
+ *band_list = g_list_append (*band_list, new_band);
+
+ new_band_setup (dock, new_band, orientation);
+
+ return TRUE;
+}
+
+gint
+_bonobo_dock_handle_key_nav (GoDock *dock,
+ GoDockBand *band,
+ GoDockItem *item,
+ GdkEventKey *event)
+{
+ GList *entry;
+ GList **band_list;
+ int cross_band_dir = 0;
+ int switch_side_dir = 0;
+ gboolean end_stop = FALSE;
+ gboolean was_inserted = FALSE;
+ GtkOrientation orientation;
+ GoDockPlacement placement;
+
+ if (!(event->state & GDK_CONTROL_MASK))
+ return FALSE;
+
+ switch (event->keyval)
+ {
+ case GDK_Up:
+ cross_band_dir = -1;
+ break;
+ case GDK_Down:
+ cross_band_dir = +1;
+ break;
+ case GDK_Left:
+ switch_side_dir = -1;
+ break;
+ case GDK_Right:
+ switch_side_dir = +1;
+ break;
+ default:
+ return FALSE;
+ }
+
+ band_list = find_band_list (dock, band, &placement);
+ g_return_val_if_fail (band_list != NULL, FALSE);
+
+ if (placement == GO_DOCK_LEFT ||
+ placement == GO_DOCK_RIGHT)
+ {
+ int tmp = switch_side_dir;
+ switch_side_dir = cross_band_dir;
+ cross_band_dir = tmp;
+ orientation = GTK_ORIENTATION_VERTICAL;
+ }
+ else
+ {
+ orientation = GTK_ORIENTATION_HORIZONTAL;
+ }
+
+ g_object_ref (G_OBJECT (item));
+
+ gtk_container_remove (GTK_CONTAINER (band), GTK_WIDGET (item));
+
+ /*
+ * Find somewhere new for it ...
+ */
+ entry = g_list_find (*band_list, band);
+ g_return_val_if_fail (entry != NULL, FALSE);
+
+ if (cross_band_dir == -1)
+ {
+ for (entry = entry->prev; !was_inserted && entry;
+ entry = entry->prev)
+ was_inserted = go_dock_band_append (
+ entry->data, GTK_WIDGET (item), 0);
+
+ if (!was_inserted &&
+ ((*band_list)->data != band ||
+ go_dock_band_get_num_children (band) > 0))
+ {
+ was_inserted = insert_into_band_list (
+ dock, band_list, orientation, item, TRUE);
+ }
+
+ if (!was_inserted)
+ {
+ if (placement == GO_DOCK_BOTTOM)
+ {
+ was_inserted = insert_into_band_list (
+ dock, &dock->top_bands, orientation, item, FALSE);
+ }
+ else if (placement == GO_DOCK_RIGHT)
+ {
+ was_inserted = insert_into_band_list (
+ dock, &dock->left_bands, orientation, item, FALSE);
+ }
+ else
+ end_stop = TRUE;
+ }
+ }
+
+ if (cross_band_dir == +1)
+ {
+ for (entry = entry->next; !was_inserted && entry;
+ entry = entry->next)
+ was_inserted = go_dock_band_append (
+ entry->data, GTK_WIDGET (item), 0);
+
+ if (!was_inserted &&
+ (g_list_last (*band_list)->data != band ||
+ go_dock_band_get_num_children (band) > 0))
+ {
+ was_inserted = insert_into_band_list (
+ dock, band_list, orientation, item, FALSE);
+ }
+
+ if (!was_inserted)
+ {
+ if (placement == GO_DOCK_TOP)
+ {
+ was_inserted = insert_into_band_list (
+ dock, &dock->bottom_bands, orientation, item, TRUE);
+ }
+ else if (placement == GO_DOCK_LEFT)
+ {
+ was_inserted = insert_into_band_list (
+ dock, &dock->right_bands, orientation, item, TRUE);
+ }
+ else
+ end_stop = TRUE;
+ }
+ }
+
+ if (!end_stop && !was_inserted)
+ {
+ orientation = (orientation == GTK_ORIENTATION_HORIZONTAL) ?
+ GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL;
+ if (placement == GO_DOCK_TOP ||
+ placement == GO_DOCK_BOTTOM)
+ {
+ if (switch_side_dir == -1)
+ was_inserted = insert_into_band_list (
+ dock, &dock->left_bands, orientation, item, FALSE);
+ else
+ was_inserted = insert_into_band_list (
+ dock, &dock->right_bands, orientation, item, TRUE);
+ }
+ else
+ {
+ if (switch_side_dir == -1)
+ was_inserted = insert_into_band_list (
+ dock, &dock->top_bands, orientation, item, FALSE);
+ else
+ was_inserted = insert_into_band_list (
+ dock, &dock->bottom_bands, orientation, item, TRUE);
+ }
+ }
+
+ if (!was_inserted)
+ { /* geometry issue */
+ if (!go_dock_band_append (band, GTK_WIDGET (item), 0))
+ g_error ("no space in fallback original band");
+ }
+
+ if (go_dock_band_get_num_children (band) == 0)
+ gtk_widget_destroy (GTK_WIDGET (band));
+
+ g_object_unref (G_OBJECT (item));
+
+ return TRUE;
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-combo-pixmaps.h
@@ -0,0 +1,54 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * widget-pixmap-combo.h - A pixmap selector combo box
+ * Copyright 2000-2003, Ximian, Inc.
+ *
+ * Authors:
+ * Jody Goldberg <jody at gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef GO_COMBO_PIXMAPS_H
+#define GO_COMBO_PIXMAPS_H
+
+#include <gtk/gtktooltips.h>
+
+G_BEGIN_DECLS
+
+#define GO_COMBO_PIXMAPS_TYPE (go_combo_pixmaps_get_type ())
+#define GO_COMBO_PIXMAPS(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GO_COMBO_PIXMAPS_TYPE, GOComboPixmaps))
+#define IS_GO_COMBO_PIXMAPS(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GO_COMBO_PIXMAPS_TYPE))
+
+typedef struct _GOComboPixmaps GOComboPixmaps;
+typedef struct _GOMenuPixmaps GOMenuPixmaps;
+
+GType go_combo_pixmaps_get_type (void);
+GOComboPixmaps *go_combo_pixmaps_new (int ncols);
+void go_combo_pixmaps_add_element (GOComboPixmaps *combo,
+ GdkPixbuf const *pixbuf, int id,
+ char const *tooltip);
+gboolean go_combo_pixmaps_select_index (GOComboPixmaps *combo, int index);
+gboolean go_combo_pixmaps_select_id (GOComboPixmaps *combo, int id);
+int go_combo_pixmaps_get_selected (GOComboPixmaps const *combo, int *index);
+GtkWidget *go_combo_pixmaps_get_preview (GOComboPixmaps const *combo);
+
+GOMenuPixmaps *go_menu_pixmaps_new (int ncols);
+void go_menu_pixmaps_add_element (GOMenuPixmaps *menu,
+ GdkPixbuf const *pixbuf, int id);
+
+G_END_DECLS
+
+#endif /* GO_COMBO_PIXMAPS_H */
--- /dev/null
+++ lib/goffice/gui-utils/go-gui-utils.c
@@ -0,0 +1,163 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-gui-utils.c: Misc gtk utilities
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <goffice/goffice-config.h>
+#include "go-gui-utils.h"
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * go_gtk_button_new_with_stock_image
+ *
+ * Code from gedit
+ *
+ * Creates a new GtkButton with custom label and stock image.
+ *
+ * text : button label
+ * sotck_id : id for stock icon
+ *
+ * return : newly created button
+ *
+ **/
+
+GtkWidget*
+go_gtk_button_new_with_stock_image (char const *text, char const* stock_id)
+{
+ GtkWidget *button;
+ GtkStockItem item;
+ GtkWidget *label;
+ GtkWidget *image;
+ GtkWidget *hbox;
+ GtkWidget *align;
+
+ button = gtk_button_new ();
+
+ if (GTK_BIN (button)->child)
+ gtk_container_remove (GTK_CONTAINER (button),
+ GTK_BIN (button)->child);
+
+ if (gtk_stock_lookup (stock_id, &item)) {
+ label = gtk_label_new_with_mnemonic (text);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+ hbox = gtk_hbox_new (FALSE, 2);
+
+ align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+
+ gtk_container_add (GTK_CONTAINER (button), align);
+ gtk_container_add (GTK_CONTAINER (align), hbox);
+ gtk_widget_show_all (align);
+
+ return button;
+ }
+
+ label = gtk_label_new_with_mnemonic (text);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (button));
+
+ gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
+
+ gtk_widget_show (label);
+ gtk_container_add (GTK_CONTAINER (button), label);
+
+ return button;
+}
+
+/**
+ * go_libglade_new :
+ * @gcc : #GOCmdContext
+ * @gladefile :
+ *
+ * Simple utility to open glade files
+ **/
+GladeXML *
+go_libglade_new (char const *gladefile, char const *root,
+ char const *domain, GOCmdContext *gcc)
+{
+ GladeXML *gui;
+ char *f;
+
+ g_return_val_if_fail (gladefile != NULL, NULL);
+
+ if (!g_path_is_absolute (gladefile)) {
+ char *d = gnm_sys_glade_dir ();
+ f = g_build_filename (d, gladefile, NULL);
+ g_free (d);
+ } else
+ f = g_strdup (gladefile);
+
+ gui = glade_xml_new (f, root, domain);
+ if (gui == NULL && gcc != NULL) {
+ char *msg = g_strdup_printf (_("Unable to open file '%s'"), f);
+ go_cmd_context_error_system (gcc, msg);
+ g_free (msg);
+ }
+ g_free (f);
+
+ return gui;
+}
+
+/**
+ * go_editable_enters:
+ * @window: dialog to affect.
+ * @editable: Editable to affect.
+ *
+ * Normally if there's an editable widget (such as #GtkEntry) in your
+ * dialog, pressing Enter will activate the editable rather than the
+ * default dialog button. However, in most cases, the user expects to
+ * type something in and then press enter to close the dialog. This
+ * function enables that behavior.
+ **/
+void
+go_editable_enters (GtkWindow *window, GtkWidget *w)
+{
+ g_return_if_fail (GTK_IS_WINDOW (window));
+ g_signal_connect_swapped (G_OBJECT (w),
+ "activate",
+ G_CALLBACK (gtk_window_activate_default), window);
+}
+
+GdkPixbuf *
+go_pixbuf_intelligent_scale (GdkPixbuf *buf, guint width, guint height)
+{
+ GdkPixbuf *scaled;
+ int w, h;
+ unsigned long int ow = gdk_pixbuf_get_width (buf);
+ unsigned long int oh = gdk_pixbuf_get_height (buf);
+
+ if (ow > width || oh > height) {
+ if (ow * height > oh * width) {
+ w = width;
+ h = width * (((double)oh)/(double)ow);
+ } else {
+ h = height;
+ w = height * (((double)ow)/(double)oh);
+ }
+
+ scaled = gdk_pixbuf_scale_simple (buf, w, h, GDK_INTERP_BILINEAR);
+ } else
+ scaled = g_object_ref (buf);
+
+ return scaled;
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-dock-item-grip.h
@@ -0,0 +1,45 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/**
+ * go-dock-item-grip.h
+ *
+ * Author:
+ * Michael Meeks
+ *
+ * Copyright (C) 2002 Sun Microsystems, Inc.
+ */
+
+#ifndef _GO_DOCK_ITEM_GRIP_H_
+#define _GO_DOCK_ITEM_GRIP_H_
+
+#include <gtk/gtkwidget.h>
+#include <goffice/gui-utils/go-dock-item.h>
+
+G_BEGIN_DECLS
+
+#define GO_TYPE_DOCK_ITEM_GRIP (go_dock_item_grip_get_type())
+#define GO_DOCK_ITEM_GRIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GO_TYPE_DOCK_ITEM_GRIP, GoDockItemGrip))
+#define GO_DOCK_ITEM_GRIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GO_TYPE_DOCK_ITEM_GRIP, GoDockItemGripClass))
+#define GO_IS_DOCK_ITEM_GRIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GO_TYPE_DOCK_ITEM_GRIP))
+#define GO_IS_DOCK_ITEM_GRIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GO_TYPE_DOCK_ITEM_GRIP))
+#define GO_DOCK_ITEM_GRIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GO_TYPE_DOCK_ITEM_GRIP, GoDockItemGripClass))
+
+typedef struct {
+ GtkWidget parent;
+
+ GoDockItem *item;
+} GoDockItemGrip;
+
+typedef struct {
+ GtkWidgetClass parent_class;
+
+ void (*activate) (GoDockItemGrip *grip);
+} GoDockItemGripClass;
+
+GType go_dock_item_grip_get_type (void);
+GtkWidget *go_dock_item_grip_new (GoDockItem *item);
+
+G_END_DECLS
+
+#endif /* _GO_DOCK_ITEM_GRIP_H_ */
--- /dev/null
+++ lib/goffice/gui-utils/go-action-combo-color.c
@@ -0,0 +1,266 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-action-combo-color.c: A custom GtkAction to handle color selection
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <goffice/goffice-config.h>
+#include "go-action-combo-color.h"
+#include "go-combo-color.h"
+#include "go-combo-box.h"
+#include "go-color-palette.h"
+
+// #include <src/gui-util.h>
+#include <gui-util.h>
+#include <application.h>
+
+#include <gtk/gtkaction.h>
+#include <gtk/gtktoolitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkimage.h>
+#include <gsf/gsf-impl-utils.h>
+
+#include <glib/gi18n.h>
+
+typedef struct {
+ GtkToolItem base;
+ GOComboColor *combo; /* container has a ref, not us */
+} GOToolComboColor;
+typedef GtkToolItemClass GOToolComboColorClass;
+
+#define GO_TOOL_COMBO_COLOR_TYPE (go_tool_combo_color_get_type ())
+#define GO_TOOL_COMBO_COLOR(o) (G_TYPE_CHECK_INSTANCE_CAST (o, GO_TOOL_COMBO_COLOR_TYPE, GOToolComboColor))
+#define IS_GO_TOOL_COMBO_COLOR(o) (G_TYPE_CHECK_INSTANCE_TYPE (o, GO_TOOL_COMBO_COLOR_TYPE))
+
+static GType go_tool_combo_color_get_type (void);
+static gboolean
+go_tool_combo_color_set_tooltip (GtkToolItem *tool_item, GtkTooltips *tooltips,
+ char const *tip_text,
+ char const *tip_private)
+{
+ GOToolComboColor *self = (GOToolComboColor *)tool_item;
+ go_combo_box_set_tooltip (GO_COMBO_BOX (self->combo), tooltips,
+ tip_text, tip_private);
+ return TRUE;
+}
+static void
+go_tool_combo_color_class_init (GtkToolItemClass *tool_item_class)
+{
+ tool_item_class->set_tooltip = go_tool_combo_color_set_tooltip;
+}
+
+static GSF_CLASS (GOToolComboColor, go_tool_combo_color,
+ go_tool_combo_color_class_init, NULL,
+ GTK_TYPE_TOOL_ITEM)
+
+/*****************************************************************************/
+
+struct _GOActionComboColor {
+ GtkAction base;
+ GdkPixbuf *icon;
+ GOColorGroup *color_group;
+ char const *default_val_label;
+ GOColor default_val, current_color;
+};
+typedef struct {
+ GtkActionClass base;
+ void (*display_custom_dialog) (GOActionComboColor *caction, GtkWidget *dialog);
+} GOActionComboColorClass;
+
+enum {
+ DISPLAY_CUSTOM_DIALOG,
+ LAST_SIGNAL
+};
+
+static guint go_action_combo_color_signals [LAST_SIGNAL] = { 0, };
+static GObjectClass *combo_color_parent;
+
+static void
+go_action_combo_color_connect_proxy (GtkAction *a, GtkWidget *proxy)
+{
+ GTK_ACTION_CLASS (combo_color_parent)->connect_proxy (a, proxy);
+
+ if (GTK_IS_IMAGE_MENU_ITEM (proxy)) { /* set the icon */
+ GOActionComboColor *caction = (GOActionComboColor *)a;
+ GtkWidget *image = gtk_image_new_from_pixbuf (caction->icon);
+ gtk_widget_show (image);
+ gtk_image_menu_item_set_image (
+ GTK_IMAGE_MENU_ITEM (proxy), image);
+ }
+}
+
+static void
+cb_color_changed (GtkWidget *cc, GOColor color,
+ gboolean is_custom, gboolean by_user, gboolean is_default,
+ GOActionComboColor *caction)
+{
+ if (!by_user)
+ return;
+ caction->current_color = is_default ? caction->default_val : color;
+ gtk_action_activate (GTK_ACTION (caction));
+}
+
+static char *
+get_title (GtkAction *a)
+{
+ char *res;
+ g_object_get (G_OBJECT (a), "label", &res, NULL);
+ return res;
+}
+
+static void
+cb_proxy_custom_dialog (G_GNUC_UNUSED GObject *ignored,
+ GtkWidget *dialog, GOActionComboColor *caction)
+{
+ g_signal_emit (caction,
+ go_action_combo_color_signals [DISPLAY_CUSTOM_DIALOG], 0,
+ dialog);
+}
+
+static GtkWidget *
+go_action_combo_color_create_tool_item (GtkAction *a)
+{
+ GOActionComboColor *caction = (GOActionComboColor *)a;
+ GOToolComboColor *tool = g_object_new (GO_TOOL_COMBO_COLOR_TYPE, NULL);
+ char *title;
+
+ tool->combo = (GOComboColor *)go_combo_color_new (caction->icon,
+ caction->default_val_label, caction->default_val,
+ caction->color_group);
+
+ go_combo_color_set_instant_apply (GO_COMBO_COLOR (tool->combo), TRUE);
+ go_combo_box_set_relief (GO_COMBO_BOX (tool->combo), GTK_RELIEF_NONE);
+ go_combo_box_set_tearable (GO_COMBO_BOX (tool->combo), TRUE);
+ title = get_title (a);
+ go_combo_box_set_title (GO_COMBO_BOX (tool->combo), title);
+ g_free (title);
+
+ gnm_widget_disable_focus (GTK_WIDGET (tool->combo));
+ gtk_container_add (GTK_CONTAINER (tool), GTK_WIDGET (tool->combo));
+ gtk_widget_show (GTK_WIDGET (tool->combo));
+ gtk_widget_show (GTK_WIDGET (tool));
+
+ g_object_connect (G_OBJECT (tool->combo),
+ "signal::color_changed", G_CALLBACK (cb_color_changed), a,
+ "signal::display-custom-dialog", G_CALLBACK (cb_proxy_custom_dialog), a,
+ NULL);
+ return GTK_WIDGET (tool);
+}
+
+static GtkWidget *
+go_action_combo_color_create_menu_item (GtkAction *a)
+{
+ GOActionComboColor *caction = (GOActionComboColor *)a;
+ char * title = get_title (a);
+ GtkWidget *submenu = go_color_palette_make_menu (
+ caction->default_val_label,
+ caction->default_val,
+ caction->color_group, title, caction->current_color);
+ GtkWidget *item = gtk_image_menu_item_new ();
+
+ g_free (title);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
+ gtk_widget_show (submenu);
+
+ g_object_connect (G_OBJECT (submenu),
+ "signal::color_changed", G_CALLBACK (cb_color_changed), a,
+ "signal::display-custom-dialog", G_CALLBACK (cb_proxy_custom_dialog), a,
+ NULL);
+ return item;
+}
+
+static void
+go_action_combo_color_finalize (GObject *obj)
+{
+ GOActionComboColor *color = (GOActionComboColor *)obj;
+ if (color->icon != NULL)
+ g_object_unref (color->icon);
+ if (color->color_group != NULL)
+ g_object_unref (color->color_group);
+
+ combo_color_parent->finalize (obj);
+}
+
+static void
+go_action_combo_color_class_init (GtkActionClass *gtk_act_class)
+{
+ GObjectClass *gobject_class = (GObjectClass *)gtk_act_class;
+
+ combo_color_parent = g_type_class_peek_parent (gobject_class);
+ gobject_class->finalize = go_action_combo_color_finalize;
+
+ gtk_act_class->create_tool_item = go_action_combo_color_create_tool_item;
+ gtk_act_class->create_menu_item = go_action_combo_color_create_menu_item;
+ gtk_act_class->connect_proxy = go_action_combo_color_connect_proxy;
+
+ go_action_combo_color_signals [DISPLAY_CUSTOM_DIALOG] =
+ g_signal_new ("display-custom-dialog",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOActionComboColorClass, display_custom_dialog),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+}
+
+GSF_CLASS (GOActionComboColor, go_action_combo_color,
+ go_action_combo_color_class_init, NULL,
+ GTK_TYPE_ACTION)
+
+GOActionComboColor *
+go_action_combo_color_new (char const *action_name,
+ char const *stock_id,
+ char const *default_color_label,
+ GOColor default_color,
+ gpointer group_key)
+{
+ GOActionComboColor *res = g_object_new (go_action_combo_color_get_type (),
+ "name", action_name,
+ "stock_id", stock_id,
+ NULL);
+ res->icon = gnm_app_get_pixbuf (stock_id);
+ res->color_group = go_color_group_fetch (action_name, group_key);
+ res->default_val_label = g_strdup (default_color_label);
+ res->current_color = res->default_val = default_color;
+
+ return res;
+}
+
+void
+go_action_combo_color_set_group (GOActionComboColor *action, gpointer group_key)
+{
+#warning TODO
+}
+
+GOColor
+go_action_combo_color_get_color (GOActionComboColor *a, gboolean *is_default)
+{
+ if (is_default != NULL)
+ *is_default = (a->current_color == a->default_val);
+ return a->current_color;
+}
+
+void
+go_action_combo_color_set_color (GOActionComboColor *a, GOColor color)
+{
+ GSList *ptr = gtk_action_get_proxies (GTK_ACTION (a));
+
+ a->current_color = color;
+ for ( ; ptr != NULL ; ptr = ptr->next)
+ if (IS_GO_TOOL_COMBO_COLOR (ptr->data))
+ go_combo_color_set_color (GO_TOOL_COMBO_COLOR (ptr->data)->combo, color);
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-dock-item.h
@@ -0,0 +1,164 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* WARNING ____ IMMATURE API ____ liable to change */
+
+/* go-dock-item.h
+ *
+ * Copyright (C) 1998 Ettore Perazzoli
+ * Copyright (C) 1998 Elliot Lee
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ * All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+*/
+
+#ifndef _GO_DOCK_ITEM_H
+#define _GO_DOCK_ITEM_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GO_TYPE_DOCK_ITEM (go_dock_item_get_type())
+#define GO_DOCK_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GO_TYPE_DOCK_ITEM, GoDockItem))
+#define GO_DOCK_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GO_TYPE_DOCK_ITEM, GoDockItemClass))
+#define GO_IS_DOCK_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GO_TYPE_DOCK_ITEM))
+#define GO_IS_DOCK_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GO_TYPE_DOCK_ITEM))
+#define GO_DOCK_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GO_TYPE_DOCK_ITEM, GoDockItemClass))
+
+typedef enum
+{
+ GO_DOCK_ITEM_BEH_NORMAL = 0,
+ GO_DOCK_ITEM_BEH_EXCLUSIVE = 1 << 0,
+ GO_DOCK_ITEM_BEH_NEVER_FLOATING = 1 << 1,
+ GO_DOCK_ITEM_BEH_NEVER_VERTICAL = 1 << 2,
+ GO_DOCK_ITEM_BEH_NEVER_HORIZONTAL = 1 << 3,
+ GO_DOCK_ITEM_BEH_LOCKED = 1 << 4
+ /* MAINT: Update the size of the bit field in the GoDockItem structure if you add items to this */
+} GoDockItemBehavior;
+
+/* obsolete, for compatibility; don't use */
+#define GO_DOCK_ITEM_BEH_NEVER_DETACH GO_DOCK_ITEM_BEH_NEVER_FLOATING
+
+#define GO_DOCK_ITEM_NOT_LOCKED(x) (! (GO_DOCK_ITEM(x)->behavior \
+ & GO_DOCK_ITEM_BEH_LOCKED))
+
+typedef struct _GoDockItem GoDockItem;
+typedef struct _GoDockItemPrivate GoDockItemPrivate;
+typedef struct _GoDockItemClass GoDockItemClass;
+
+struct _GoDockItem
+{
+ GtkBin bin;
+
+ gchar *name;
+
+ /* <private> */
+ GdkWindow *bin_window; /* parent window for children */
+ GdkWindow *float_window; /* always NULL */
+ GtkShadowType shadow_type;
+
+ /* Start drag position (wrt widget->window). */
+ gint16 dragoff_x, dragoff_y;
+
+ /* Position of the floating window. */
+ gint16 float_x, float_y;
+
+ guint behavior : 5;
+ guint orientation : 1;
+
+ guint float_window_mapped : 1;
+ guint is_floating : 1;
+ guint in_drag : 1;
+ /* If TRUE, the pointer must be grabbed on "map_event". */
+ guint grab_on_map_event : 1;
+
+ /*< private >*/
+ GoDockItemPrivate *_priv;
+};
+
+struct _GoDockItemClass
+{
+ GtkBinClass parent_class;
+
+ void (* dock_drag_begin) (GoDockItem *item);
+ void (* dock_drag_motion) (GoDockItem *item, gint x, gint y);
+ void (* dock_drag_end) (GoDockItem *item);
+ void (* dock_detach) (GoDockItem *item);
+ void (* orientation_changed) (GoDockItem *item, GtkOrientation new_orientation);
+
+ gpointer dummy[4];
+};
+
+/* Public methods. */
+GtkType go_dock_item_get_type (void) G_GNUC_CONST;
+GtkWidget *go_dock_item_new (const gchar *name,
+ GoDockItemBehavior behavior);
+void go_dock_item_construct (GoDockItem *new_dock_item,
+ const gchar *name,
+ GoDockItemBehavior behavior);
+
+GtkWidget *go_dock_item_get_child (GoDockItem *dock_item);
+
+char *go_dock_item_get_name (GoDockItem *dock_item);
+
+void go_dock_item_set_shadow_type (GoDockItem *dock_item,
+ GtkShadowType type);
+
+GtkShadowType go_dock_item_get_shadow_type (GoDockItem *dock_item);
+
+gboolean go_dock_item_set_orientation (GoDockItem *dock_item,
+ GtkOrientation orientation);
+
+GtkOrientation go_dock_item_get_orientation (GoDockItem *dock_item);
+
+GoDockItemBehavior
+ go_dock_item_get_behavior (GoDockItem *dock_item);
+
+/* Private methods. */
+#if 1 /* defined(GO_UI_INTERNAL) */
+void go_dock_item_set_locked (GoDockItem *dock_item,
+ gboolean locked);
+gboolean go_dock_item_detach (GoDockItem *item,
+ gint x, gint y);
+
+void go_dock_item_attach (GoDockItem *item,
+ GtkWidget *parent,
+ gint x, gint y);
+void go_dock_item_unfloat (GoDockItem *item);
+
+void go_dock_item_grab_pointer (GoDockItem *item);
+
+void go_dock_item_drag_floating (GoDockItem *item,
+ gint x, gint y);
+
+void go_dock_item_handle_size_request
+ (GoDockItem *item,
+ GtkRequisition *requisition);
+
+void go_dock_item_get_floating_position
+ (GoDockItem *item,
+ gint *x, gint *y);
+GtkWidget *go_dock_item_get_grip (GoDockItem *item);
+
+#endif /* GO_UI_INTERNAL */
+
+G_END_DECLS
+
+#endif /* _GO_DOCK_ITEM_H */
--- /dev/null
+++ lib/goffice/gui-utils/go-combo-color.c
@@ -0,0 +1,371 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * widget-color-combo.c - A color selector combo box
+ * Copyright 2000-2004, Ximian, Inc.
+ *
+ * Authors:
+ * Miguel de Icaza (miguel at kernel.org)
+ * Dom Lachowicz (dominicl at seas.upenn.edu)
+ *
+ * Reworked and split up into a separate GOColorPalette object:
+ * Michael Levy (mlevy at genoscope.cns.fr)
+ *
+ * And later revised and polished by:
+ * Almer S. Tigelaar (almer at gnome.org)
+ * Jody Goldberg (jody at gnome.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License, version 2, as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <goffice/goffice-config.h>
+
+#include "go-combo-color.h"
+#include "go-marshalers.h"
+#include "go-combo-box.h"
+#include "go-color-palette.h"
+
+#include <gsf/gsf-impl-utils.h>
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkwindow.h>
+#include <gdk/gdkcolor.h>
+
+struct _GOComboColor {
+ GOComboBox combo_box;
+
+ GOColorPalette *palette;
+ GtkWidget *preview_button;
+ GtkWidget *preview_image;
+ gboolean preview_is_icon;
+ gboolean instant_apply;
+
+ GOColor default_color;
+};
+
+typedef struct {
+ GOComboBoxClass base;
+ void (*color_changed) (GOComboColor *cc, GOColor color,
+ gboolean custom, gboolean by_user, gboolean is_default);
+ void (*display_custom_dialog) (GOComboColor *cc, GtkWidget *dialog);
+} GOComboColorClass;
+
+enum {
+ COLOR_CHANGED,
+ DISPLAY_CUSTOM_DIALOG,
+ LAST_SIGNAL
+};
+
+static guint go_combo_color_signals [LAST_SIGNAL] = { 0, };
+
+static GObjectClass *go_combo_color_parent_class;
+
+#define PREVIEW_SIZE 20
+
+static void
+go_combo_color_set_color_internal (GOComboColor *cc, GOColor color, gboolean is_default)
+{
+ guint color_y, color_height;
+ guint height, width;
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *color_pixbuf;
+ gboolean add_an_outline;
+
+ pixbuf = gtk_image_get_pixbuf (GTK_IMAGE (cc->preview_image));
+ if (!pixbuf)
+ return;
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ if (cc->preview_is_icon) {
+ color_y = height - 4;
+ color_height = 4;
+ } else {
+ color_y = 0;
+ color_height = height;
+ }
+
+ color_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ width, color_height);
+
+ /* mostly transparent things should have an outline */
+ add_an_outline = (UINT_RGBA_A (color) < 0x80);
+ gdk_pixbuf_fill (color_pixbuf, add_an_outline ? RGBA_GREY (0x33) : color);
+ gdk_pixbuf_copy_area (color_pixbuf, 0, 0, width, color_height,
+ pixbuf, 0, color_y);
+ if (add_an_outline) {
+ gdk_pixbuf_fill (color_pixbuf, color);
+ gdk_pixbuf_copy_area (color_pixbuf, 0, 0, width - 2, color_height -2,
+ pixbuf, 1, color_y + 1);
+ }
+
+ g_object_unref (color_pixbuf);
+ gtk_widget_queue_draw (cc->preview_image);
+}
+
+static void
+cb_screen_changed (GOComboColor *cc, GdkScreen *previous_screen)
+{
+ GtkWidget *w = GTK_WIDGET (cc);
+ GdkScreen *screen = gtk_widget_has_screen (w)
+ ? gtk_widget_get_screen (w)
+ : NULL;
+
+ if (screen) {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (cc->palette));
+ gtk_window_set_screen (GTK_WINDOW (toplevel), screen);
+ }
+}
+
+static void
+emit_color_changed (GOComboColor *cc, GOColor color,
+ gboolean is_custom, gboolean by_user, gboolean is_default)
+{
+ g_signal_emit (cc,
+ go_combo_color_signals [COLOR_CHANGED], 0,
+ color, is_custom, by_user, is_default);
+ go_combo_box_popup_hide (GO_COMBO_BOX (cc));
+}
+
+static void
+cb_preview_clicked (GtkWidget *button, GOComboColor *cc)
+{
+ if (_go_combo_is_updating (GO_COMBO_BOX (cc)))
+ return;
+ if (cc->instant_apply) {
+ gboolean is_default, is_custom;
+ GOColor color = go_color_palette_get_current_color (cc->palette,
+ &is_default, &is_custom);
+ emit_color_changed (cc, color, is_custom, TRUE, is_default);
+ } else
+ go_combo_box_popup_display (GO_COMBO_BOX (cc));
+}
+
+static void
+go_combo_color_init (GOComboColor *cc)
+{
+ cc->instant_apply = FALSE;
+ cc->preview_is_icon = FALSE;
+ cc->preview_button = gtk_toggle_button_new ();
+
+ g_signal_connect (G_OBJECT (cc),
+ "screen-changed",
+ G_CALLBACK (cb_screen_changed), NULL);
+ g_signal_connect (cc->preview_button,
+ "clicked",
+ G_CALLBACK (cb_preview_clicked), cc);
+}
+
+static void
+go_combo_color_set_title (GOComboBox *combo, char const *title)
+{
+ go_color_palette_set_title (GO_COMBO_COLOR (combo)->palette, title);
+}
+
+static void
+go_combo_color_class_init (GObjectClass *gobject_class)
+{
+ go_combo_color_parent_class = g_type_class_ref (GO_COMBO_BOX_TYPE);
+
+ GO_COMBO_BOX_CLASS (gobject_class)->set_title = go_combo_color_set_title;
+
+ go_combo_color_signals [COLOR_CHANGED] =
+ g_signal_new ("color_changed",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOComboColorClass, color_changed),
+ NULL, NULL,
+ go__VOID__INT_BOOLEAN_BOOLEAN_BOOLEAN,
+ G_TYPE_NONE, 4, G_TYPE_POINTER,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+ go_combo_color_signals [DISPLAY_CUSTOM_DIALOG] =
+ g_signal_new ("display-custom-dialog",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOComboColorClass, display_custom_dialog),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+}
+
+GSF_CLASS (GOComboColor, go_combo_color,
+ go_combo_color_class_init, go_combo_color_init,
+ GO_COMBO_BOX_TYPE)
+
+static void
+cb_palette_color_changed (GOColorPalette *P, GOColor color,
+ gboolean custom, gboolean by_user, gboolean is_default,
+ GOComboColor *cc)
+{
+ go_combo_color_set_color_internal (cc, color, is_default);
+ emit_color_changed (cc, color, custom, by_user, is_default);
+}
+
+static void
+cb_proxy_custom_dialog (GOColorPalette *pal, GtkWidget *dialog, GOComboColor *cc)
+{
+ go_combo_box_popup_hide (GO_COMBO_BOX (cc));
+ g_signal_emit (cc, go_combo_color_signals [DISPLAY_CUSTOM_DIALOG], 0,
+ dialog);
+}
+
+static void
+color_table_setup (GOComboColor *cc,
+ char const *no_color_label, GOColorGroup *color_group)
+{
+ g_return_if_fail (cc != NULL);
+
+ /* Tell the palette that we will be changing it's custom colors */
+ cc->palette = (GOColorPalette *)go_color_palette_new (no_color_label,
+ cc->default_color, color_group);
+ g_signal_connect (cc->palette,
+ "color_changed",
+ G_CALLBACK (cb_palette_color_changed), cc);
+ g_signal_connect (cc->palette,
+ "display-custom-dialog",
+ G_CALLBACK (cb_proxy_custom_dialog), cc);
+ gtk_widget_show_all (GTK_WIDGET (cc->palette));
+}
+
+/* go_combo_color_get_color:
+ *
+ * Return current color
+ */
+GOColor
+go_combo_color_get_color (GOComboColor *cc, gboolean *is_default)
+{
+ g_return_val_if_fail (IS_GO_COMBO_COLOR (cc), RGBA_BLACK);
+ return go_color_palette_get_current_color (cc->palette, is_default, NULL);
+}
+
+/**
+ * go_combo_color_set_color_gdk
+ * @cc The combo
+ * @color The color
+ *
+ * Set the color of the combo to the given color. Causes the color_changed
+ * signal to be emitted.
+ */
+void
+go_combo_color_set_color_gdk (GOComboColor *cc, GdkColor *color)
+{
+#warning convert to GOColor
+ g_return_if_fail (IS_GO_COMBO_COLOR (cc));
+
+ if (color != NULL)
+ go_color_palette_set_current_color (cc->palette, GDK_TO_UINT (*color));
+ else
+ go_color_palette_set_color_to_default (cc->palette);
+}
+
+/**
+ * go_combo_color_set_color :
+ * @cc : #GOComboColor
+ * @c : #GOColor
+ */
+void
+go_combo_color_set_color (GOComboColor *cc, GOColor c)
+{
+ go_color_palette_set_current_color (cc->palette, c);
+}
+
+/**
+ * go_combo_color_set_instant_apply
+ * @cc The combo
+ * @active Whether instant apply should be active or not
+ *
+ * Turn instant apply behaviour on or off. Instant apply means that pressing
+ * the button applies the current color. When off, pressing the button opens
+ * the combo.
+ */
+void
+go_combo_color_set_instant_apply (GOComboColor *cc, gboolean active)
+{
+ g_return_if_fail (IS_GO_COMBO_COLOR (cc));
+
+ cc->instant_apply = active;
+}
+
+/**
+ * go_combo_color_set_allow_alpha :
+ * @cc : #GOComboColor
+ * @allow_alpha :
+ *
+ * Should the custom colour selector allow the use of opacity.
+ **/
+void
+go_combo_color_set_allow_alpha (GOComboColor *cc, gboolean allow_alpha)
+{
+ go_color_palette_set_allow_alpha (cc->palette, allow_alpha);
+}
+
+/**
+ * go_combo_color_set_color_to_default
+ * @cc The combo
+ *
+ * Set the color of the combo to the default color. Causes the color_changed
+ * signal to be emitted.
+ */
+void
+go_combo_color_set_color_to_default (GOComboColor *cc)
+{
+ g_return_if_fail (IS_GO_COMBO_COLOR (cc));
+
+ go_color_palette_set_color_to_default (cc->palette);
+}
+
+/**
+ * go_combo_color_new :
+ * icon : optionally NULL.
+ * no_color_label :
+ *
+ * Default constructor. Pass an optional icon and an optional label for the
+ * no/auto color button.
+ */
+GtkWidget *
+go_combo_color_new (GdkPixbuf *icon, char const *no_color_label,
+ GOColor default_color,
+ GOColorGroup *color_group)
+{
+ GOColor color;
+ gboolean is_default;
+ GdkPixbuf *pixbuf = NULL;
+ GOComboColor *cc = g_object_new (GO_COMBO_COLOR_TYPE, NULL);
+
+ cc->default_color = default_color;
+ if (icon != NULL &&
+ gdk_pixbuf_get_width (icon) > 4 &&
+ gdk_pixbuf_get_height (icon) > 4) {
+ cc->preview_is_icon = TRUE;
+ pixbuf = gdk_pixbuf_copy (icon);
+ } else
+ pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+ PREVIEW_SIZE, PREVIEW_SIZE);
+
+ cc->preview_image = gtk_image_new_from_pixbuf (pixbuf);
+ g_object_unref (pixbuf);
+ gtk_widget_show (cc->preview_image);
+ gtk_container_add (GTK_CONTAINER (cc->preview_button), cc->preview_image);
+
+ color_table_setup (cc, no_color_label, color_group);
+ gtk_widget_show_all (cc->preview_button);
+
+ go_combo_box_construct (GO_COMBO_BOX (cc),
+ cc->preview_button, GTK_WIDGET (cc->palette), GTK_WIDGET (cc->palette));
+
+ color = go_color_palette_get_current_color (cc->palette, &is_default, NULL);
+ go_combo_color_set_color_internal (cc, color, is_default);
+
+ return GTK_WIDGET (cc);
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-action-combo-stack.c
@@ -0,0 +1,490 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-action-combo-stack.c: A custom GtkAction to handle undo/redo menus/toolbars
+ *
+ * Copyright (C) 2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <goffice/goffice-config.h>
+#include "go-action-combo-stack.h"
+#include "go-combo-box.h"
+//#include <src/gui-util.h>
+#include <gui-util.h>
+
+#include <gtk/gtkaction.h>
+#include <gtk/gtktoolitem.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkwindow.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtkscrolledwindow.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <stdio.h>
+
+////////////////////////////////////////////////////////////////////////////
+
+typedef struct {
+ GOComboBox base;
+
+ GtkWidget *button;
+ GtkTreeView *list;
+ GtkWidget *scrolled;
+
+ gpointer last_key;
+} GOComboStack;
+
+typedef struct {
+ GOComboBoxClass base;
+ void (*pop) (GOComboStack *cbox, gpointer key);
+} GOComboStackClass;
+
+enum {
+ POP,
+ LAST_SIGNAL
+};
+enum {
+ LABEL_COL,
+ INDEX_COL,
+ KEY_COL
+};
+
+#define GO_COMBO_STACK_TYPE (go_combo_stack_get_type ())
+#define GO_COMBO_STACK(o) G_TYPE_CHECK_INSTANCE_CAST (o, GO_COMBO_STACK_TYPE, GOComboStack)
+#define IS_GO_COMBO_STACK(o) G_TYPE_CHECK_INSTANCE_TYPE (o, GO_COMBO_STACK_TYPE)
+
+static GtkType go_combo_stack_get_type (void);
+static guint go_combo_stack_signals [LAST_SIGNAL] = { 0, };
+
+static void
+cb_screen_changed (GOComboStack *cs, GdkScreen *previous_screen)
+{
+ GtkWidget *w = GTK_WIDGET (cs);
+ GdkScreen *screen = gtk_widget_has_screen (w)
+ ? gtk_widget_get_screen (w)
+ : NULL;
+
+ if (screen) {
+ GtkWidget *toplevel = gtk_widget_get_toplevel (cs->scrolled
+ ? cs->scrolled : GTK_WIDGET (cs->list));
+ gtk_window_set_screen (GTK_WINDOW (toplevel), screen);
+ }
+}
+
+static gpointer
+get_key_at_path (GtkTreeView *view, GtkTreePath *pos)
+{
+ gpointer res = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_tree_view_get_model (view);
+ if (gtk_tree_model_get_iter (model, &iter, pos))
+ gtk_tree_model_get (model, &iter, KEY_COL, &res, -1);
+ return res;
+}
+
+static void
+cb_button_clicked (GOComboStack *stack)
+{
+ if (!_go_combo_is_updating (GO_COMBO_BOX (stack))) {
+ GtkTreePath *pos = gtk_tree_path_new_first ();
+ gpointer top = get_key_at_path (stack->list, pos);
+ gtk_tree_path_free (pos);
+ g_signal_emit (stack, go_combo_stack_signals [POP], 0, top);
+ go_combo_box_popup_hide (GO_COMBO_BOX (stack));
+ }
+}
+
+static gboolean
+cb_button_release_event (GtkWidget *list, GdkEventButton *e, gpointer data)
+{
+ GOComboStack *stack = GO_COMBO_STACK (data);
+
+ go_combo_box_popup_hide (GO_COMBO_BOX (stack));
+
+ if (stack->last_key != NULL) {
+ gint dummy, w, h;
+ gdk_window_get_geometry (e->window, &dummy, &dummy, &w, &h, &dummy);
+ if (0 <= e->x && e->x < w && 0 <= e->y && e->y < h)
+ g_signal_emit (stack, go_combo_stack_signals [POP], 0,
+ stack->last_key);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+cb_motion_notify_event (GtkWidget *widget, GdkEventMotion *event,
+ GOComboStack *stack)
+{
+ GtkTreePath *start, *pos;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model = gtk_tree_view_get_model (stack->list);
+
+ stack->last_key = NULL;
+ sel = gtk_tree_view_get_selection (stack->list);
+ gtk_tree_selection_unselect_all (sel);
+
+ if (!gtk_tree_view_get_path_at_pos
+ (stack->list, event->x, event->y, &pos, NULL, NULL, NULL)) {
+ int n = gtk_tree_model_iter_n_children (model, NULL);
+ if (n == 0)
+ return TRUE;
+ pos = gtk_tree_path_new_from_indices (n - 1, -1);
+ }
+
+ stack->last_key = get_key_at_path (stack->list, pos);
+ start = gtk_tree_path_new_first ();
+ gtk_tree_selection_select_range (sel, start, pos);
+ gtk_tree_path_free (start);
+ gtk_tree_path_free (pos);
+
+ return TRUE;
+}
+
+static gboolean
+cb_leave_notify_event (GOComboStack *stack)
+{
+ stack->last_key = NULL;
+ gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (stack->list));
+ return FALSE;
+}
+
+static void
+go_combo_stack_init (GOComboStack *stack)
+{
+ GtkScrolledWindow *scrolled;
+ GtkTreeSelection *selection;
+
+ stack->button = gtk_toggle_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (stack->button), GTK_RELIEF_NONE);
+ GTK_WIDGET_UNSET_FLAGS (stack->button, GTK_CAN_FOCUS);
+
+ stack->list = (GtkTreeView *)gtk_tree_view_new ();
+ selection = gtk_tree_view_get_selection (stack->list);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+
+ stack->scrolled = gtk_scrolled_window_new (
+ gtk_tree_view_get_hadjustment (stack->list),
+ gtk_tree_view_get_vadjustment (stack->list));
+ scrolled = GTK_SCROLLED_WINDOW (stack->scrolled);
+ gtk_scrolled_window_set_policy (scrolled,
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_add_with_viewport (scrolled, GTK_WIDGET (stack->list));
+ gtk_widget_set_size_request (stack->scrolled, -1, 200); /* MAGIC NUMBER */
+
+ /* Set up the dropdown list */
+ g_signal_connect (G_OBJECT (stack), "screen-changed", G_CALLBACK (cb_screen_changed), NULL);
+ g_signal_connect (G_OBJECT (stack->list),
+ "button_release_event",
+ G_CALLBACK (cb_button_release_event), stack);
+ g_signal_connect (G_OBJECT (stack->list),
+ "motion_notify_event",
+ G_CALLBACK (cb_motion_notify_event), stack);
+ g_signal_connect_swapped (G_OBJECT (stack->list),
+ "leave_notify_event",
+ G_CALLBACK (cb_leave_notify_event), stack);
+ g_signal_connect_swapped (stack->button, "clicked",
+ G_CALLBACK (cb_button_clicked),
+ (gpointer) stack);
+
+ gtk_widget_show (GTK_WIDGET (stack->list));
+ gtk_widget_show (stack->scrolled);
+ gtk_widget_show (stack->button);
+ go_combo_box_construct (GO_COMBO_BOX (stack),
+ stack->button, stack->scrolled, GTK_WIDGET (stack->list));
+}
+
+static void
+go_combo_stack_class_init (GObjectClass *klass)
+{
+ go_combo_stack_signals [POP] = g_signal_new ("pop",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GOComboStackClass, pop),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1, G_TYPE_POINTER);
+}
+
+GSF_CLASS (GOComboStack, go_combo_stack,
+ go_combo_stack_class_init, go_combo_stack_init,
+ GO_COMBO_BOX_TYPE)
+
+////////////////////////////////////////////////////////////////////////////
+
+typedef struct {
+ GtkToolItem base;
+ GOComboStack *combo; /* container has a ref, not us */
+} GOToolComboStack;
+typedef GtkToolItemClass GOToolComboStackClass;
+
+#define GO_TOOL_COMBO_STACK_TYPE (go_tool_combo_stack_get_type ())
+#define GO_TOOL_COMBO_STACK(o) (G_TYPE_CHECK_INSTANCE_CAST (o, GO_TOOL_COMBO_STACK_TYPE, GOToolComboStack))
+#define IS_GO_TOOL_COMBO_STACK(o) (G_TYPE_CHECK_INSTANCE_TYPE (o, GO_TOOL_COMBO_STACK_TYPE))
+
+static GType go_tool_combo_stack_get_type (void);
+static gboolean
+go_tool_combo_stack_set_tooltip (GtkToolItem *tool_item, GtkTooltips *tooltips,
+ char const *tip_text,
+ char const *tip_private)
+{
+ GOToolComboStack *self = (GOToolComboStack *)tool_item;
+ go_combo_box_set_tooltip (GO_COMBO_BOX (self->combo), tooltips,
+ tip_text, tip_private);
+ return TRUE;
+}
+
+static void
+go_tool_combo_stack_class_init (GtkToolItemClass *tool_item_klass)
+{
+ tool_item_klass->set_tooltip = go_tool_combo_stack_set_tooltip;
+}
+
+static GSF_CLASS (GOToolComboStack, go_tool_combo_stack,
+ go_tool_combo_stack_class_init, NULL,
+ GTK_TYPE_TOOL_ITEM)
+
+/*****************************************************************************/
+
+struct _GOActionComboStack {
+ GtkAction base;
+ GtkTreeModel *model;
+
+ gpointer last_selection;
+};
+typedef GtkActionClass GOActionComboStackClass;
+
+static GObjectClass *combo_stack_parent;
+
+static void
+cb_tool_popped (GOToolComboStack *tool, gpointer key, GOActionComboStack *a)
+{
+ /* YUCK
+ * YUCK
+ * YUCK
+ * We really need to return the key in "activate" but can not for now.
+ * as a result people had better call
+ * go_action_combo_stack_selection
+ * from with the handler or they will lose the selection from toolitems.
+ * We can not tell whether the activation was a menu or accelerator
+ * which just use the top. */
+ a->last_selection = key;
+ gtk_action_activate (GTK_ACTION (a));
+ a->last_selection = NULL;
+}
+
+static GtkWidget *
+go_action_combo_stack_create_tool_item (GtkAction *a)
+{
+ GOActionComboStack *saction = (GOActionComboStack *)a;
+ GtkWidget *image;
+ GtkTreeView *tree_view;
+ GOToolComboStack *tool = g_object_new (GO_TOOL_COMBO_STACK_TYPE, NULL);
+ char *stock_id;
+ gboolean is_sensitive = gtk_tree_model_iter_n_children (saction->model, NULL) > 0;
+
+ tool->combo = g_object_new (GO_COMBO_STACK_TYPE, NULL);
+ tree_view = GTK_TREE_VIEW (tool->combo->list);
+ gtk_tree_view_set_model (tree_view, saction->model);
+ gtk_tree_view_set_headers_visible (tree_view, FALSE);
+ gtk_tree_view_append_column (tree_view,
+ gtk_tree_view_column_new_with_attributes (NULL,
+ gtk_cell_renderer_text_new (),
+ "text", 0,
+ NULL));
+
+ g_object_get (G_OBJECT (a), "stock_id", &stock_id, NULL);
+ image = gtk_image_new_from_stock (
+ stock_id, GTK_ICON_SIZE_LARGE_TOOLBAR);
+ g_free (stock_id);
+ gtk_widget_show (image);
+ gtk_container_add (GTK_CONTAINER (tool->combo->button), image);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (tool), is_sensitive);
+
+ go_combo_box_set_relief (GO_COMBO_BOX (tool->combo), GTK_RELIEF_NONE);
+ go_combo_box_set_tearable (GO_COMBO_BOX (tool->combo), TRUE);
+ gnm_widget_disable_focus (GTK_WIDGET (tool->combo));
+ gtk_container_add (GTK_CONTAINER (tool), GTK_WIDGET (tool->combo));
+ gtk_widget_show (GTK_WIDGET (tool->combo));
+ gtk_widget_show (GTK_WIDGET (tool));
+
+ g_signal_connect (G_OBJECT (tool->combo),
+ "pop",
+ G_CALLBACK (cb_tool_popped), saction);
+
+ return GTK_WIDGET (tool);
+}
+
+static GtkWidget *
+go_action_combo_stack_create_menu_item (GtkAction *a)
+{
+ GOActionComboStack *saction = (GOActionComboStack *)a;
+ GtkWidget *item = gtk_image_menu_item_new ();
+ gboolean is_sensitive = gtk_tree_model_iter_n_children (saction->model, NULL) > 0;
+ gtk_widget_set_sensitive (GTK_WIDGET (item), is_sensitive);
+ return item;
+}
+
+static void
+go_action_combo_stack_finalize (GObject *obj)
+{
+ GOActionComboStack *saction = (GOActionComboStack *)obj;
+ g_object_unref (saction->model);
+ saction->model = NULL;
+ combo_stack_parent->finalize (obj);
+}
+
+static void
+go_action_combo_stack_class_init (GtkActionClass *gtk_act_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *)gtk_act_klass;
+
+ combo_stack_parent = g_type_class_peek_parent (gobject_klass);
+
+ gobject_klass->finalize = go_action_combo_stack_finalize;
+ gtk_act_klass->create_tool_item = go_action_combo_stack_create_tool_item;
+ gtk_act_klass->create_menu_item = go_action_combo_stack_create_menu_item;
+}
+
+static void
+go_action_combo_stack_init (GOActionComboStack *saction)
+{
+ saction->model = (GtkTreeModel *)
+ gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER);
+ saction->last_selection = NULL;
+}
+
+GSF_CLASS (GOActionComboStack, go_action_combo_stack,
+ go_action_combo_stack_class_init, go_action_combo_stack_init,
+ GTK_TYPE_ACTION)
+
+static void
+check_sensitivity (GOActionComboStack *saction, unsigned old_count)
+{
+ unsigned new_count = gtk_tree_model_iter_n_children (saction->model, NULL);
+
+ if ((old_count > 0) ^ (new_count > 0)) {
+ GSList *ptr = gtk_action_get_proxies (GTK_ACTION (saction));
+ gboolean is_sensitive = (new_count > 0);
+ for ( ; ptr != NULL ; ptr = ptr->next)
+ gtk_widget_set_sensitive (ptr->data, is_sensitive);
+ }
+}
+
+/**
+ * go_action_combo_stack_push :
+ * @act : #GOActionComboStack
+ * @str : The label to push
+ * @key : a key value to id the pushe item
+ **/
+void
+go_action_combo_stack_push (GOActionComboStack *a,
+ char const *label, gpointer key)
+{
+ GOActionComboStack *saction = GO_ACTION_COMBO_STACK (a);
+ GtkTreeIter iter;
+ unsigned old_count = gtk_tree_model_iter_n_children (saction->model, NULL);
+
+ g_return_if_fail (saction != NULL);
+
+ gtk_list_store_insert (GTK_LIST_STORE (saction->model), &iter, 0);
+ gtk_list_store_set (GTK_LIST_STORE (saction->model), &iter,
+ LABEL_COL, label,
+ KEY_COL, key,
+ -1);
+ check_sensitivity (saction, old_count);
+}
+
+/**
+ * go_action_combo_stack_pop :
+ * @act : #GOActionComboStack
+ * @n :
+ *
+ * Shorten list @act by removing @n off the top (or fewer if the list is
+ * shorter)
+ **/
+void
+go_action_combo_stack_pop (GOActionComboStack *a, unsigned n)
+{
+ GOActionComboStack *saction = GO_ACTION_COMBO_STACK (a);
+ GtkTreeIter iter;
+ unsigned old_count = gtk_tree_model_iter_n_children (saction->model, NULL);
+
+ g_return_if_fail (saction != NULL);
+
+ if (gtk_tree_model_iter_nth_child (saction->model, &iter, NULL, 0))
+ while (n-- > 0 &&
+ gtk_list_store_remove (GTK_LIST_STORE (saction->model), &iter))
+ ;
+ check_sensitivity (saction, old_count);
+}
+
+/**
+ * go_action_combo_stack_truncate :
+ * @act : #GOActionComboStack
+ * @n :
+ *
+ * Ensure that list @act is no longer than @n, dropping any extra off the
+ * bottom.
+ **/
+void
+go_action_combo_stack_truncate (GOActionComboStack *a, unsigned n)
+{
+ GOActionComboStack *saction = GO_ACTION_COMBO_STACK (a);
+ GtkTreeIter iter;
+ unsigned old_count = gtk_tree_model_iter_n_children (saction->model, NULL);
+
+ g_return_if_fail (saction != NULL);
+
+ if (gtk_tree_model_iter_nth_child (saction->model, &iter, NULL, n))
+ while (gtk_list_store_remove (GTK_LIST_STORE (saction->model), &iter))
+ ;
+ check_sensitivity (saction, old_count);
+}
+
+/**
+ * go_action_combo_stack_selection :
+ * @a : #GOActionComboStack
+ *
+ * Returns the key of the item last selected in one of the proxies.
+ * Yes this interface is terrible, but we can't return the key in the activate
+ * signal.
+ *
+ * NOTE : see writeup in cb_tool_popped.
+ **/
+gpointer
+go_action_combo_stack_selection (GOActionComboStack const *a)
+{
+ gpointer res = NULL;
+ GtkTreeIter iter;
+
+ if (a->last_selection != NULL)
+ return a->last_selection;
+ if (gtk_tree_model_get_iter_first (a->model, &iter))
+ gtk_tree_model_get (a->model, &iter,
+ KEY_COL, &res,
+ -1);
+ return res;
+}
--- /dev/null
+++ lib/goffice/gui-utils/go-dock-band.c
@@ -0,0 +1,1986 @@
+/* File import from bonoboui to gnumeric by import-bonobo. Do not edit. */
+
+/* go-dock-band.c
+
+ Copyright (C) 1998 Free Software Foundation
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Ettore Perazzoli <ettore at comm2000.it>
+*/
+
+#include <gnumeric-config.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <libgnome/gnome-macros.h>
+#include "go-dock.h"
+#include "go-dock-band.h"
+#include "go-dock-item.h"
+
+GNOME_CLASS_BOILERPLATE (GoDockBand, go_dock_band,
+ GtkContainer, GTK_TYPE_CONTAINER);
+
+#define noBONOBO_DOCK_BAND_DEBUG
+
+/* FIXME: To be removed. */
+#if defined GO_DOCK_BAND_DEBUG && defined __GNUC__
+#define DEBUG(x) \
+ do \
+ { \
+ printf ("%s.%d: ", __FUNCTION__, __LINE__); \
+ printf x; \
+ putchar ('\n'); \
+ } \
+ while (0)
+#else
+#define DEBUG(x)
+#endif
+
+static void go_dock_band_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+
+static void go_dock_band_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+
+static void go_dock_band_map (GtkWidget *widget);
+static void go_dock_band_unmap (GtkWidget *widget);
+
+static void go_dock_band_add (GtkContainer *container,
+ GtkWidget *child);
+
+static void go_dock_band_remove (GtkContainer *container,
+ GtkWidget *widget);
+
+static void go_dock_band_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data);
+
+static void go_dock_band_finalize (GObject *object);
+
+static void size_allocate_child (GoDockBand *band,
+ GoDockBandChild *child,
+ guint space,
+ GtkAllocation *child_allocation);
+
+static void size_allocate_small (GoDockBand *band,
+ GtkAllocation *allocation,
+ guint space,
+ guint requested_space);
+
+static gboolean docking_allowed (GoDockBand *band,
+ GoDockItem *item);
+
+static GList *find_child (GoDockBand *band,
+ GtkWidget *child);
+
+static GList *prev_if_floating (GoDockBand *band,
+ GList *c);
+
+static GList *next_if_floating (GoDockBand *band,
+ GList *c);
+
+static GList *prev_not_floating (GoDockBand *band,
+ GList *c);
+
+static GList *next_not_floating (GoDockBand *band,
+ GList *c);
+
+static void calc_prev_and_foll_space (GoDockBand *band);
+
+static guint attempt_move_backward (GoDockBand *band,
+ GList *child,
+ guint amount);
+
+static guint attempt_move_forward (GoDockBand *band,
+ GList *child,
+ guint amount);
+
+static gboolean dock_nonempty (GoDockBand *band,
+ GoDockItem *item,
+ GList *where,
+ gint x, gint y);
+
+static gboolean dock_empty (GoDockBand *band,
+ GoDockItem *item,
+ GList *where,
+ gint x, gint y);
+
+static gboolean dock_empty_right (GoDockBand *band,
+ GoDockItem *item,
+ GList *where,
+ gint x, gint y);
+
+static gboolean check_guint_arg (GObject *object,
+ const gchar *name,
+ guint *value_return);
+
+static void
+go_dock_band_class_init (GoDockBandClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
+
+ gobject_class = (GObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+ container_class = (GtkContainerClass *) klass;
+
+ gobject_class->finalize = go_dock_band_finalize;
+
+ widget_class->map = go_dock_band_map;
+ widget_class->unmap = go_dock_band_unmap;
+ widget_class->size_request = go_dock_band_size_request;
+ widget_class->size_allocate = go_dock_band_size_allocate;
+
+ container_class->add = go_dock_band_add;
+ container_class->remove = go_dock_band_remove;
+ container_class->forall = go_dock_band_forall;
+}
+
+static void
+go_dock_band_instance_init (GoDockBand *band)
+{
+ GtkWidget *widget = GTK_WIDGET (band);
+
+ GTK_WIDGET_SET_FLAGS (band, GTK_NO_WINDOW);
+
+ band->_priv = NULL;
+ band->orientation = GTK_ORIENTATION_HORIZONTAL;
+
+ band->children = NULL;
+ band->num_children = 0;
+
+ band->floating_child = NULL;
+
+ band->doing_drag = FALSE;
+
+ band->max_space_requisition = 0;
+ band->tot_offsets = 0;
+
+ band->drag_allocation.x = band->drag_allocation.y = -1;
+ band->drag_allocation.width = band->drag_allocation.height = 0;
+
+ band->new_for_drag = FALSE;
+
+ if (GTK_WIDGET_VISIBLE (widget))
+ gtk_widget_queue_resize (widget);
+}
+
+
+
+static void
+go_dock_band_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GoDockBand *band;
+ GList *lp;
+
+ DEBUG (("entering function"));
+
+ band = GO_DOCK_BAND (widget);
+
+ band->max_space_requisition = 0;
+ band->tot_offsets = 0;
+
+ requisition->width = 0;
+ requisition->height = 0;
+
+ for (lp = band->children; lp != NULL; lp = lp->next)
+ {
+ GoDockBandChild *c = lp->data;
+
+ if (GTK_WIDGET_VISIBLE (c->widget))
+ {
+ GtkRequisition req;
+
+ req.width = req.height = 0;
+
+ if (GO_IS_DOCK_ITEM (c->widget))
+ go_dock_item_handle_size_request(GO_DOCK_ITEM (c->widget),
+ &req);
+ else
+ gtk_widget_size_request (c->widget, &req);
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ gboolean has_preferred_width;
+ guint preferred_width;
+
+ has_preferred_width = check_guint_arg (G_OBJECT (c->widget),
+ "preferred_width",
+ &preferred_width);
+
+ if (has_preferred_width)
+ c->max_space_requisition = MAX ((int)preferred_width, req.width);
+ else
+ c->max_space_requisition = req.width;
+ }
+ else
+ {
+ gboolean has_preferred_height;
+ guint preferred_height;
+
+ has_preferred_height = check_guint_arg (G_OBJECT (c->widget),
+ "preferred_height",
+ &preferred_height);
+
+ if (has_preferred_height)
+ c->max_space_requisition = MAX ((int)preferred_height, req.height);
+ else
+ c->max_space_requisition = req.height;
+ }
+
+ band->max_space_requisition += c->max_space_requisition;
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ requisition->height = MAX (requisition->height, req.height);
+ requisition->width += req.width;
+ }
+ else
+ {
+ requisition->width = MAX (requisition->width, req.width);
+ requisition->height += req.height;
+ }
+
+ c->widget->requisition = req;
+ band->tot_offsets += c->offset;
+ }
+ }
+
+ widget->requisition = *requisition;
+}
+
+
+
+static void
+size_allocate_child (GoDockBand *band,
+ GoDockBandChild *child,
+ guint space,
+ GtkAllocation *child_allocation)
+{
+ GtkWidget *band_widget;
+
+ band_widget = GTK_WIDGET (band);
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ child_allocation->x += child->real_offset;
+ child_allocation->width = space;
+ child_allocation->height = band_widget->allocation.height;
+ DEBUG (("horizontal %d %d %d %d real_offset %d",
+ child_allocation->x, child_allocation->y,
+ child_allocation->width, child_allocation->height,
+ child->real_offset));
+ gtk_widget_size_allocate (child->widget, child_allocation);
+ child_allocation->x += child_allocation->width;
+ }
+ else
+ {
+ child_allocation->y += child->real_offset;
+ child_allocation->width = band_widget->allocation.width;
+ child_allocation->height = space;
+ DEBUG (("vertical %d %d %d %d real_offset %d",
+ child_allocation->x, child_allocation->y,
+ child_allocation->width, child_allocation->height,
+ child->real_offset));
+ gtk_widget_size_allocate (child->widget, child_allocation);
+ child_allocation->y += child_allocation->height;
+ }
+}
+
+/* The allocated space is smaller than the space needed to show all
+ the items completely. */
+static void
+size_allocate_small (GoDockBand *band,
+ GtkAllocation *allocation,
+ guint space,
+ guint requested_space)
+{
+ GtkAllocation child_allocation;
+ GList *lp;
+ guint max_space_requisition;
+
+ DEBUG (("entering function"));
+
+ child_allocation.x = allocation->x;
+ child_allocation.y = allocation->y;
+
+ max_space_requisition = band->max_space_requisition;
+
+ for (lp = band->children; lp != NULL; lp = lp->next)
+ {
+ GoDockBandChild *child;
+
+ child = lp->data;
+
+ if (GTK_WIDGET_VISIBLE (child->widget))
+ {
+ guint child_requested_space;
+
+ child->real_offset = 0;
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ child_requested_space = child->widget->requisition.width;
+ else
+ child_requested_space = child->widget->requisition.height;
+
+ if (space < child->max_space_requisition
+ || (space - child->max_space_requisition
+ < requested_space - child_requested_space))
+ break;
+
+ space -= child->max_space_requisition;
+ requested_space -= child_requested_space;
+ max_space_requisition -= child->max_space_requisition;
+
+ size_allocate_child (band, child,
+ child->max_space_requisition,
+ &child_allocation);
+ }
+ }
+
+ if (lp != NULL)
+ {
+ GoDockBandChild *child;
+ guint child_space, child_requested_space;
+
+ child = lp->data;
+
+ if (GTK_WIDGET_VISIBLE (child->widget))
+ {
+ child->real_offset = 0;
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ child_requested_space = child->widget->requisition.width;
+ else
+ child_requested_space = child->widget->requisition.height;
+
+ requested_space -= child_requested_space;
+ child_space = space - requested_space;
+ space -= child_space;
+
+ size_allocate_child (band, child,
+ child_space,
+ &child_allocation);
+ }
+
+ lp = lp->next;
+ }
+
+ for (; lp != NULL; lp = lp->next)
+ {
+ GoDockBandChild *child;
+
+ child = lp->data;
+
+ if (GTK_WIDGET_VISIBLE (child->widget))
+ {
+ child->real_offset = 0;
+ child->real_offset = 0;
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ size_allocate_child (band, child,
+ child->widget->requisition.width,
+ &child_allocation);
+ else
+ size_allocate_child (band, child,
+ child->widget->requisition.height,
+ &child_allocation);
+ }
+ }
+}
+
+/* The allocation is enough to show all the items completely, but not
+ to satisfy all the requested offsets. */
+static void
+size_allocate_medium (GoDockBand *band,
+ GtkAllocation *allocation,
+ guint space,
+ guint requested_space)
+{
+ GtkAllocation child_allocation;
+ GList *lp;
+ gfloat factor;
+
+ DEBUG (("entering function"));
+
+ child_allocation.x = allocation->x;
+ child_allocation.y = allocation->y;
+
+ factor = (1.0 - ((float) (band->max_space_requisition + band->tot_offsets
+ - space)
+ / (float) band->tot_offsets));
+
+ /* Shrink the offsets proportionally. */
+ for (lp = band->children; lp != NULL; lp = lp->next)
+ {
+ GoDockBandChild *child;
+
+ child = lp->data;
+
+ if (GTK_WIDGET_VISIBLE (child->widget))
+ {
+ child->real_offset = (guint) ((float) child->offset * factor + .5);
+
+ size_allocate_child (band, child,
+ child->max_space_requisition,
+ &child_allocation);
+ }
+ }
+}
+
+/* The allocation is enough to show all the items completely, with the
+ requested offsets. */
+static void
+size_allocate_large (GoDockBand *band,
+ GtkAllocation *allocation,
+ guint space,
+ guint requested_space)
+{
+ GtkAllocation child_allocation;
+ GList *lp;
+
+ DEBUG (("entering function"));
+
+ child_allocation.x = allocation->x;
+ child_allocation.y = allocation->y;
+
+ for (lp = band->children; lp != NULL; lp = lp->next)
+ {
+ GoDockBandChild *child;
+
+ child = lp->data;
+
+ if (GTK_WIDGET_VISIBLE (child->widget))
+ {
+ child->real_offset = child->offset;
+
+ size_allocate_child (band, child,
+ child->max_space_requisition,
+ &child_allocation);
+ }
+ }
+}
+
+static void
+go_dock_band_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GoDockBand *band;
+ guint space, requested_space;
+
+ band = GO_DOCK_BAND (widget);
+
+ widget->allocation = *allocation;
+
+ /* Check if we have a single exclusive item. If so, allocate the
+ whole space to it. */
+ if (band->num_children == 1)
+ {
+ GoDockBandChild *c;
+
+ c = (GoDockBandChild *) band->children->data;
+ if (GO_IS_DOCK_ITEM (c->widget) && GTK_WIDGET_VISIBLE (c->widget))
+ {
+ GoDockItemBehavior behavior;
+ GoDockItem *item;
+
+ item = GO_DOCK_ITEM (c->widget);
+ behavior = go_dock_item_get_behavior (item);
+ if (behavior & GO_DOCK_ITEM_BEH_EXCLUSIVE)
+ {
+ gtk_widget_size_allocate (c->widget, allocation);
+ return;
+ }
+ }
+ }
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ space = allocation->width;
+ requested_space = widget->requisition.width;
+ }
+ else
+ {
+ space = allocation->height;
+ requested_space = widget->requisition.height;
+ }
+
+ if (space <= band->max_space_requisition)
+ size_allocate_small (band, allocation, space, requested_space);
+ else if (space < band->max_space_requisition + band->tot_offsets)
+ size_allocate_medium (band, allocation, space, requested_space);
+ else
+ size_allocate_large (band, allocation, space, requested_space);
+
+ calc_prev_and_foll_space (band);
+}
+
+
+
+static void
+go_dock_band_map (GtkWidget *widget)
+{
+ GoDockBand *band = GO_DOCK_BAND (widget);
+ GList *lp;
+
+ g_return_if_fail(widget != NULL);
+ g_return_if_fail(GO_IS_DOCK_BAND(widget));
+
+ GNOME_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
+
+ for (lp = band->children; lp != NULL; lp = lp->next)
+ {
+ GoDockBandChild *c;
+
+ c = lp->data;
+ if (GTK_WIDGET_VISIBLE (c->widget) && ! GTK_WIDGET_MAPPED (c->widget))
+ gtk_widget_map (c->widget);
+ }
+}
+
+static void
+go_dock_band_unmap (GtkWidget *widget)
+{
+ GoDockBand *band = GO_DOCK_BAND (widget);
+ GList *lp;
+
+ g_return_if_fail(widget != NULL);
+ g_return_if_fail(GO_IS_DOCK_BAND(widget));
+
+ GNOME_CALL_PARENT (GTK_WIDGET_CLASS, unmap, (widget));
+
+ for (lp = band->children; lp != NULL; lp = lp->next)
+ {
+ GoDockBandChild *c;
+
+ c = lp->data;
+ if (GTK_WIDGET_VISIBLE (c->widget) && GTK_WIDGET_MAPPED (c->widget))
+ gtk_widget_unmap (c->widget);
+ }
+}
+
+
+/* GtkContainer methods. */
+
+static void
+go_dock_band_add (GtkContainer *container, GtkWidget *child)
+{
+ GoDockBand *band = GO_DOCK_BAND (container);
+
+ g_return_if_fail (go_dock_band_prepend (band, child, 0));
+}
+
+static void
+go_dock_band_remove (GtkContainer *container, GtkWidget *widget)
+{
+ GoDockBand *band;
+ GList *child;
+
+ band = GO_DOCK_BAND (container);
+ if (band->num_children == 0)
+ return;
+
+ child = find_child (band, widget);
+ if (child != NULL)
+ {
+ gboolean was_visible;
+
+ if (child == band->floating_child)
+ band->floating_child = NULL;
+
+ was_visible = GTK_WIDGET_VISIBLE (widget);
+ gtk_widget_unparent (widget);
+
+ band->children = g_list_remove_link (band->children, child);
+ g_free (child->data);
+ g_list_free (child);
+
+ if (band->doing_drag)
+ {
+ GList *p;
+
+ for (p = band->children; p != NULL; p = p->next)
+ {
+ GoDockBandChild *c;
+
+ c = (GoDockBandChild *) p->data;
+ c->offset = c->real_offset = c->drag_offset;
+ }
+ }
+
+ gtk_widget_queue_resize (GTK_WIDGET (band));
+
+ band->num_children--;
+ DEBUG (("now num_children = %d", band->num_children));
+ }
+}
+
+static void
+go_dock_band_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ GoDockBand *band;
+ GoDockBandChild *child;
+ GList *children;
+
+ band = GO_DOCK_BAND (container);
+
+ children = band->children;
+ while (children)
+ {
+ child = children->data;
+ children = children->next;
+ (* callback) (child->widget, callback_data);
+ }
+}
+
+static void
+go_dock_band_finalize (GObject *object)
+{
+ GoDockBand *self = GO_DOCK_BAND (object);
+
+ g_free (self->_priv);
+ self->_priv = NULL;
+
+ GNOME_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+
+/* Utility functions. */
+
+static gboolean
+docking_allowed (GoDockBand *band, GoDockItem *item)
+{
+ GoDockItemBehavior behavior;
+ GoDockBandChild *c;
+
+ if (band->num_children == 0)
+ return TRUE;
+
+ behavior = go_dock_item_get_behavior (item);
+
+ if (behavior & GO_DOCK_ITEM_BEH_EXCLUSIVE)
+ return FALSE;
+
+ c = (GoDockBandChild *) band->children->data;
+ if (GO_IS_DOCK_ITEM (c->widget))
+ {
+ behavior = go_dock_item_get_behavior (GO_DOCK_ITEM (c->widget));
+ if (behavior & GO_DOCK_ITEM_BEH_EXCLUSIVE)
+ return c->widget == GTK_WIDGET (item);
+ }
+
+ return TRUE;
+}
+
+static GList *
+find_child (GoDockBand *band, GtkWidget *child)
+{
+ GList *children;
+
+ children = band->children;
+
+ while (children != NULL)
+ {
+ GoDockBandChild *c;
+
+ c = (GoDockBandChild *) children->data;
+ if (c->widget == child)
+ return children;
+
+ children = children->next;
+ }
+
+ return NULL;
+}
+
+static GList *
+next_if_floating (GoDockBand *band, GList *c)
+{
+ if (c != NULL && c == band->floating_child)
+ return c->next;
+ else
+ return c;
+}
+
+static GList *
+prev_if_floating (GoDockBand *band, GList *c)
+{
+ if (c != NULL && c == band->floating_child)
+ return c->prev;
+ else
+ return c;
+}
+
+static GList *
+next_not_floating (GoDockBand *band, GList *c)
+{
+ if (c == NULL)
+ return NULL;
+ else
+ return next_if_floating (band, c->next);
+}
+
+static GList *
+prev_not_floating (GoDockBand *band, GList *c)
+{
+ if (c == NULL)
+ return NULL;
+ else
+ return prev_if_floating (band, c->prev);
+}
+
+
+
+static GList *
+find_where (GoDockBand *band, gint offset, gboolean *is_empty)
+{
+ guint count; /* FIXME: used for debugging only */
+ gint offs;
+ GList *lp;
+
+ if (offset < 0)
+ offset = 0;
+
+ offs = 0;
+ count = 0; /* FIXME */
+ for (lp = band->children; lp != NULL; lp = lp->next)
+ {
+ GoDockBandChild *child;
+
+ child = lp->data;
+
+ if (lp == band->floating_child)
+ {
+ if (lp->next == NULL)
+ {
+ DEBUG (("empty last %d", count));
+ *is_empty = TRUE;
+
+ return lp == band->floating_child ? lp->prev : lp;
+ }
+ DEBUG (("%d: is floating or dragged.", count++));
+ continue;
+ }
+
+ DEBUG (("%d: Checking for x %d, width %d, offs %d (%d)",
+ count, child->drag_allocation.x,
+ child->drag_allocation.width, offs, offset));
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (offset >= offs && offset <= child->drag_allocation.x)
+ {
+ *is_empty = TRUE;
+ DEBUG (("empty %d (allocation.x %d)",
+ count, child->drag_allocation.x));
+
+ return prev_if_floating (band, lp->prev);
+ }
+
+ offs = child->drag_allocation.x + child->drag_allocation.width;
+ if (offset > child->drag_allocation.x && offset < offs)
+ {
+ *is_empty = FALSE;
+ DEBUG (("%d", count));
+ return lp->prev;
+ }
+ }
+ else
+ {
+ if (offset >= offs && offset <= child->drag_allocation.y)
+ {
+ *is_empty = TRUE;
+ DEBUG (("empty %d (allocation.y %d)",
+ count, child->drag_allocation.y));
+
+ return prev_if_floating (band, lp->prev);
+ }
+
+ offs = child->drag_allocation.y + child->drag_allocation.height;
+ if (offset > child->drag_allocation.y && offset < offs)
+ {
+ *is_empty = FALSE;
+ DEBUG (("%d", count));
+ return lp->prev;
+ }
+ }
+
+ if (lp->next == NULL)
+ {
+ DEBUG (("empty last %d", count));
+ *is_empty = TRUE;
+ return lp;
+ }
+
+ count++; /* FIXME */
+ }
+
+ DEBUG (("nothing done."));
+
+ /* Make compiler happy. */
+ *is_empty = TRUE;
+ return lp;
+}
+
+
+
+static void
+calc_prev_and_foll_space (GoDockBand *band)
+{
+ GtkWidget *widget;
+ GList *lp;
+
+ if (band->children == NULL)
+ return;
+
+ widget = GTK_WIDGET (band);
+
+ lp = next_if_floating (band, band->children);
+ if (lp != NULL)
+ {
+ GoDockBandChild *c;
+ guint prev_space, foll_space;
+
+ prev_space = 0;
+
+ while (1)
+ {
+ GList *next;
+
+ c = lp->data;
+ prev_space += c->real_offset;
+ c->prev_space = prev_space;
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ prev_space += (c->widget->allocation.width
+ - c->widget->requisition.width);
+ else
+ prev_space += (c->widget->allocation.height
+ - c->widget->requisition.height);
+
+ next = next_not_floating (band, lp);
+ if (next == NULL)
+ break;
+
+ lp = next;
+ }
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ foll_space = (widget->allocation.x + widget->allocation.width
+ - (c->widget->allocation.x
+ + c->widget->requisition.width));
+ else
+ foll_space = (widget->allocation.y + widget->allocation.height
+ - (c->widget->allocation.y
+ + c->widget->requisition.height));
+
+ DEBUG(("foll_space %d", foll_space));
+
+ for (; lp != NULL; lp = prev_not_floating (band, lp))
+ {
+ c = lp->data;
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ foll_space += (c->widget->allocation.width
+ - c->widget->requisition.width);
+ else
+ foll_space += (c->widget->allocation.height
+ - c->widget->requisition.height);
+ c->foll_space = foll_space;
+
+ foll_space += c->real_offset;
+
+ }
+ }
+}
+
+
+
+static guint
+attempt_move_backward (GoDockBand *band, GList *child, guint amount)
+{
+ GList *lp;
+ guint effective_amount;
+
+ effective_amount = 0;
+
+ for (lp = prev_if_floating (band, child);
+ lp != NULL && amount > 0;
+ lp = prev_not_floating (band, lp))
+ {
+ GoDockBandChild *c;
+
+ c = lp->data;
+
+ if (c->drag_offset > amount)
+ {
+ c->real_offset = c->drag_offset - amount;
+ effective_amount += amount;
+ amount = 0;
+ }
+ else
+ {
+ c->real_offset = 0;
+ effective_amount += c->drag_offset;
+ amount -= c->drag_offset;
+ }
+ c->offset = c->real_offset;
+ }
+
+ return effective_amount;
+}
+
+static guint
+attempt_move_forward (GoDockBand *band, GList *child, guint requirement)
+{
+ GList *lp;
+ guint effective_amount;
+
+ effective_amount = 0;
+ for (lp = next_if_floating (band, child);
+ lp != NULL && requirement > 0;
+ lp = next_not_floating (band, lp))
+ {
+ GoDockBandChild *c;
+
+ c = lp->data;
+
+ DEBUG (("requirement = %d", requirement));
+ if (c->drag_offset > requirement)
+ {
+ c->real_offset = c->drag_offset - requirement;
+ effective_amount += requirement;
+ requirement = 0;
+ }
+ else
+ {
+ c->real_offset = 0;
+ effective_amount += c->drag_offset;
+ requirement -= c->drag_offset;
+ }
+ c->offset = c->real_offset;
+ }
+
+ return effective_amount;
+}
+
+
+
+static void
+reparent_if_needed (GoDockBand *band,
+ GoDockItem *item,
+ gint x, gint y)
+{
+ if (GTK_WIDGET (item)->parent != GTK_WIDGET (band))
+ {
+ go_dock_item_attach (item, GTK_WIDGET (band), x, y);
+
+ /* Reparenting causes the new floating child to be the first
+ item on the child list (see the `remove' method). */
+ band->floating_child = band->children;
+
+ /* Reparenting will remove the grab, so we need to redo it. */
+ go_dock_item_grab_pointer (item);
+ }
+}
+
+static gboolean
+dock_nonempty (GoDockBand *band,
+ GoDockItem *item,
+ GList *where,
+ gint x, gint y)
+{
+ GoDockBandChild *c, *floating_child;
+ GtkOrientation orig_item_orientation;
+ GtkRequisition item_requisition;
+ GList *lp, *next;
+ gint amount, requirement;
+
+ DEBUG (("entering function"));
+
+ if (! docking_allowed (band, item))
+ return FALSE;
+
+ if (where == NULL)
+ lp = band->children;
+ else
+ lp = next_not_floating (band, where);
+
+ c = lp->data;
+
+ orig_item_orientation = go_dock_item_get_orientation (item);
+ if (orig_item_orientation != band->orientation
+ && ! go_dock_item_set_orientation (item, band->orientation))
+ return FALSE;
+
+ go_dock_item_handle_size_request (item, &item_requisition);
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ requirement = item_requisition.width;
+ else
+ requirement = item_requisition.height;
+
+ if ((c->drag_prev_space + c->drag_foll_space) < requirement)
+ {
+ DEBUG (("not enough space %d %d",
+ c->drag_prev_space + c->drag_foll_space,
+ requirement));
+
+ /* Restore original orientation. */
+ if (orig_item_orientation != band->orientation)
+ go_dock_item_set_orientation (item, orig_item_orientation);
+
+ return FALSE;
+ }
+
+ gtk_widget_size_request (GTK_WIDGET (item), &item_requisition);
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ requirement = item_requisition.width;
+ else
+ requirement = item_requisition.height;
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ amount = c->drag_allocation.x + c->drag_allocation.width - x;
+ else
+ amount = c->drag_allocation.y + c->drag_allocation.height - y;
+
+ DEBUG (("amount %d requirement %d", amount, requirement));
+ amount = attempt_move_backward (band, lp, amount);
+
+ if (requirement < amount)
+ requirement = 0;
+ else
+ {
+ requirement -= amount;
+ next = next_not_floating (band, lp);
+ if (next != NULL)
+ attempt_move_forward (band, next, requirement);
+ }
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ reparent_if_needed (band, item, x, GTK_WIDGET (band)->allocation.y);
+ else
+ reparent_if_needed (band, item, GTK_WIDGET (band)->allocation.x, y);
+
+ floating_child = band->floating_child->data;
+ floating_child->offset = floating_child->real_offset = 0;
+
+ if (band->floating_child->prev != lp)
+ {
+ DEBUG (("moving"));
+ band->children = g_list_remove_link (band->children,
+ band->floating_child);
+ band->floating_child->next = lp->next;
+ if (band->floating_child->next != NULL)
+ band->floating_child->next->prev = band->floating_child;
+ band->floating_child->prev = lp;
+ lp->next = band->floating_child;
+ }
+
+ gtk_widget_queue_resize (floating_child->widget);
+
+ return TRUE;
+}
+
+static gboolean
+dock_empty (GoDockBand *band,
+ GoDockItem *item,
+ GList *where,
+ gint x, gint y)
+{
+ GoDockBandChild *floating_child;
+ GoDockBandChild *c1, *c2;
+ GtkOrientation orig_item_orientation;
+ GtkRequisition item_requisition;
+ GList *lp;
+ guint new_offset;
+ GtkWidget *item_widget;
+
+ DEBUG (("entering function"));
+
+ if (! docking_allowed (band, item))
+ return FALSE;
+
+ if (where != NULL)
+ {
+ lp = next_not_floating (band, where);
+
+ if (lp == NULL)
+ /* Extreme right is a special case. */
+ return dock_empty_right (band, item, where, x, y);
+
+ c1 = where->data;
+ }
+ else
+ {
+ c1 = NULL;
+ lp = next_if_floating (band, band->children);
+
+ if (lp == NULL)
+ {
+ /* Only one floating element. Easy. */
+ GoDockBandChild *c;
+
+ if (! go_dock_item_set_orientation (item, band->orientation))
+ return FALSE;
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ reparent_if_needed (band, item,
+ x, GTK_WIDGET (band)->allocation.y);
+ else
+ reparent_if_needed (band, item,
+ GTK_WIDGET (band)->allocation.x, y);
+
+ c = band->floating_child->data;
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ c->real_offset = x - GTK_WIDGET (band)->allocation.x;
+ else
+ c->real_offset = y - GTK_WIDGET (band)->allocation.y;
+ c->offset = c->real_offset;
+
+ DEBUG (("simple case offset %d", c->offset));
+
+ gtk_widget_queue_resize (c->widget);
+
+ return TRUE;
+ }
+ }
+
+ c2 = lp->data;
+
+ item_widget = GTK_WIDGET (item);
+
+ orig_item_orientation = go_dock_item_get_orientation (item);
+ if (! go_dock_item_set_orientation (item, band->orientation))
+ return FALSE;
+
+ /* Check whether there is enough space for the widget. */
+ {
+ gint space;
+
+ if (c1 != NULL)
+ space = c1->drag_foll_space;
+ else
+ {
+ space = c2->real_offset + c2->drag_foll_space;
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ space += c2->widget->allocation.width - c2->widget->requisition.width;
+ else
+ space += c2->widget->allocation.height - c2->widget->requisition.height;
+ }
+
+ go_dock_item_handle_size_request (item, &item_requisition);
+ if (space < (band->orientation == GTK_ORIENTATION_HORIZONTAL
+ ? item_requisition.width
+ : item_requisition.height))
+ {
+ DEBUG (("not enough space %d", space));
+
+ /* Restore original orientation. */
+ if (orig_item_orientation != band->orientation)
+ go_dock_item_set_orientation (item, orig_item_orientation);
+
+ return FALSE;
+ }
+
+ }
+
+ gtk_widget_size_request (item_widget, &item_requisition);
+
+ if (c1 == NULL)
+ {
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ new_offset = x - GTK_WIDGET (band)->allocation.x;
+ else
+ new_offset = y - GTK_WIDGET (band)->allocation.y;
+ }
+ else
+ {
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ new_offset = x - (c1->drag_allocation.x + c1->drag_allocation.width);
+ else
+ new_offset = y - (c1->drag_allocation.y + c1->drag_allocation.height);
+ }
+
+ DEBUG (("new_offset %d", new_offset));
+
+ if (c2->drag_offset >= (new_offset
+ + (band->orientation == GTK_ORIENTATION_HORIZONTAL
+ ? item_requisition.width
+ : item_requisition.height)))
+ {
+ c2->real_offset = (c2->drag_offset
+ - (new_offset
+ + (band->orientation == GTK_ORIENTATION_HORIZONTAL
+ ? item_requisition.width
+ : item_requisition.height)));
+ c2->offset = c2->real_offset;
+ }
+ else
+ {
+ guint requisition;
+ GList *lp1;
+
+ requisition = new_offset + (band->orientation == GTK_ORIENTATION_HORIZONTAL
+ ? item_requisition.width
+ : item_requisition.height);
+
+ DEBUG (("Moving forward %d!", requisition));
+
+ for (lp1 = lp; lp1 != NULL && requisition > 0; )
+ {
+ GoDockBandChild *tmp = lp1->data;
+ GList *lp1next;
+
+ if (tmp->drag_offset > requisition)
+ {
+ tmp->real_offset = tmp->drag_offset - requisition;
+ requisition = 0;
+ }
+ else
+ {
+ requisition -= tmp->drag_offset;
+ tmp->real_offset = 0;
+ }
+ tmp->offset = tmp->real_offset;
+
+ DEBUG (("Offset %d (drag %d)", tmp->real_offset, tmp->drag_offset));
+ lp1next = next_not_floating (band, lp1);
+ if (lp1next == NULL)
+ {
+ if (tmp->drag_foll_space > requisition)
+ requisition = 0;
+ else
+ requisition -= tmp->drag_foll_space;
+ }
+
+ lp1 = lp1next;
+ }
+
+ if (requisition > 0)
+ new_offset -= requisition;
+ }
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ reparent_if_needed (band, item, x, GTK_WIDGET (band)->allocation.y);
+ else
+ reparent_if_needed (band, item, GTK_WIDGET (band)->allocation.x, y);
+
+ floating_child = (GoDockBandChild *) band->floating_child->data;
+ floating_child->real_offset = floating_child->offset = new_offset;
+
+ band->children = g_list_remove_link (band->children, band->floating_child);
+
+ if (where == NULL)
+ {
+ band->floating_child->next = band->children;
+ band->children->prev = band->floating_child;
+ band->children = band->floating_child;
+ }
+ else
+ {
+ band->floating_child->next = where->next;
+ band->floating_child->prev = where;
+ if (where->next != NULL)
+ where->next->prev = band->floating_child;
+ where->next = band->floating_child;
+ }
+
+ gtk_widget_queue_resize (((GoDockBandChild *) band->floating_child->data)->widget);
+
+ return TRUE;
+}
+
+static gboolean
+dock_empty_right (GoDockBand *band,
+ GoDockItem *item,
+ GList *where,
+ gint x, gint y)
+{
+ GoDockBandChild *c, *floating_child;
+ GtkOrientation orig_item_orientation;
+ GtkRequisition item_requisition;
+ GtkWidget *item_widget;
+ gint new_offset;
+
+ g_return_val_if_fail (next_not_floating (band, where) == NULL, FALSE);
+ g_return_val_if_fail (band->floating_child != where, FALSE);
+
+ DEBUG (("entering function"));
+
+ if (! docking_allowed (band, item))
+ return FALSE;
+
+ item_widget = GTK_WIDGET (item);
+
+ c = where->data;
+
+ orig_item_orientation = go_dock_item_get_orientation (item);
+ if (orig_item_orientation != band->orientation
+ && ! go_dock_item_set_orientation (item, band->orientation))
+ return FALSE;
+
+ go_dock_item_handle_size_request (item, &item_requisition);
+ if ((c->drag_prev_space + c->drag_foll_space)
+ < (band->orientation == GTK_ORIENTATION_HORIZONTAL
+ ? item_requisition.width
+ : item_requisition.height))
+ {
+ DEBUG (("not enough space %d ", c->drag_prev_space+ c->drag_foll_space));
+
+ /* Restore original orientation. */
+ if (orig_item_orientation != band->orientation)
+ go_dock_item_set_orientation (item, orig_item_orientation);
+
+ return FALSE;
+ }
+
+ gtk_widget_size_request (item_widget, &item_requisition);
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ new_offset = x - (c->widget->allocation.x + c->widget->allocation.width);
+ else
+ new_offset = y - (c->widget->allocation.y + c->widget->allocation.height);
+
+ DEBUG (("x %d y %d new_offset %d width %d foll_space %d",
+ x, y, new_offset, item_widget->allocation.width,
+ c->drag_foll_space));
+
+ if ((guint) (new_offset
+ + (band->orientation == GTK_ORIENTATION_HORIZONTAL
+ ? item_requisition.width
+ : item_requisition.height)) > c->drag_foll_space)
+ {
+ gint excess = (new_offset
+ + (band->orientation == GTK_ORIENTATION_HORIZONTAL
+ ? item_requisition.width
+ : item_requisition.height)
+ - c->drag_foll_space);
+
+ DEBUG (("excess %d new_offset %d", excess, new_offset));
+ if (excess < new_offset)
+ new_offset -= excess;
+ else
+ {
+ attempt_move_backward (band, where, excess - new_offset);
+ new_offset = 0;
+ }
+ }
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ reparent_if_needed (band, item,
+ x, GTK_WIDGET (band)->allocation.y);
+ else
+ reparent_if_needed (band, item,
+ GTK_WIDGET (band)->allocation.x, y);
+
+ floating_child = band->floating_child->data;
+ floating_child->offset = floating_child->real_offset = new_offset;
+
+ band->children = g_list_remove_link (band->children, band->floating_child);
+ where->next = band->floating_child;
+ band->floating_child->prev = where;
+
+ gtk_widget_queue_resize (floating_child->widget);
+
+ return TRUE;
+}
+
+/* Helper function. */
+
+static gboolean
+check_guint_arg (GObject *object,
+ const gchar *name,
+ guint *value_return)
+{
+ GParamSpec *pspec;
+
+ g_return_val_if_fail (object != NULL, FALSE);
+
+ pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), name);
+ if (pspec != NULL) {
+ GValue value = { 0, };
+
+ g_value_init (&value, G_TYPE_UINT);
+ g_object_get_property (G_OBJECT (object), name, &value);
+ *value_return = g_value_get_uint (&value);
+ g_value_unset (&value);
+
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+
+
+/* Exported interface. */
+
+/**
+ * go_dock_band_new:
+ *
+ * Description: Create a new GoDockBand widget.
+ *
+ * Returns: The new GoDockBand widget.
+ **/
+GtkWidget *
+go_dock_band_new (void)
+{
+ GoDockBand *band;
+
+ band = g_object_new (go_dock_band_get_type (), NULL);
+ return GTK_WIDGET (band);
+}
+
+/**
+ * go_dock_band_set_orientation:
+ * @band: A GoDockBand widget
+ * @orientation: New orientation for @band
+ *
+ * Description: Set the orientation for @band.
+ **/
+void
+go_dock_band_set_orientation (GoDockBand *band,
+ GtkOrientation orientation)
+{
+ g_return_if_fail (orientation == GTK_ORIENTATION_HORIZONTAL
+ || orientation == GTK_ORIENTATION_VERTICAL);
+
+ band->orientation = orientation;
+}
+
+/**
+ * go_dock_band_get_orientation:
+ * @band: A GoDockBand widget
+ *
+ * Description: Retrieve the orientation of the specified @band.
+ *
+ * Returns: The orientation of @band.
+ **/
+GtkOrientation
+go_dock_band_get_orientation (GoDockBand *band)
+{
+ return band->orientation;
+}
+
+/**
+ * go_dock_band_insert:
+ * @band: A GoDockBand widget
+ * @child: The widget to be added to @band
+ * @offset: Offset from the previous item
+ * @position: Position within the @band
+ *
+ * Description: Add @child to @band at the specified @position, with
+ * the specified @offset from the previous item (or from the beginning
+ * of the band, if this is the first item).
+ *
+ * Returns: %TRUE if successful, %FALSE if the operation fails.
+ **/
+gboolean
+go_dock_band_insert (GoDockBand *band,
+ GtkWidget *child,
+ guint offset,
+ gint position)
+{
+ GoDockBandChild *band_child;
+
+ DEBUG (("%08x", (unsigned int) band));
+
+ if (GO_IS_DOCK_ITEM (child)
+ && !docking_allowed (band, GO_DOCK_ITEM (child)))
+ return FALSE;
+
+ if (GO_IS_DOCK_ITEM (child) &&
+ !go_dock_item_set_orientation (GO_DOCK_ITEM (child),
+ band->orientation))
+ return FALSE;
+
+ if (position < 0 || position > (gint) band->num_children)
+ position = band->num_children;
+
+ band_child = g_new (GoDockBandChild, 1);
+ band_child->widget = child;
+ band_child->offset = offset;
+ band_child->real_offset = 0;
+
+ if (position == 0)
+ band->children = g_list_prepend (band->children, band_child);
+ else if ((guint) position == band->num_children)
+ band->children = g_list_append (band->children, band_child);
+ else
+ {
+ GList *p;
+
+ p = g_list_nth (band->children, position);
+ g_list_prepend (p, band_child);
+ }
+
+ gtk_widget_set_parent (child, GTK_WIDGET (band));
+
+ if (GTK_WIDGET_REALIZED (child->parent))
+ gtk_widget_realize (child);
+
+ if (GTK_WIDGET_VISIBLE (child->parent) && GTK_WIDGET_VISIBLE (child))
+ {
+ if (GTK_WIDGET_MAPPED (child->parent))
+ gtk_widget_map (child);
+
+ gtk_widget_queue_resize (child);
+ }
+
+ band->num_children++;
+ DEBUG (("now num_children = %d", band->num_children));
+
+ return TRUE;
+}
+
+void
+go_dock_band_move_child (GoDockBand *band,
+ GList *old_child,
+ guint new_num)
+{
+ GList *children;
+ GList *lp;
+
+ children = band->children;
+
+ lp = old_child;
+
+ children = g_list_remove_link (children, lp);
+
+ children = g_list_insert (children, lp->data, new_num);
+
+ g_list_free (lp);
+
+ band->children = children;
+
+ /* FIXME */
+ gtk_widget_queue_resize (GTK_WIDGET (band));
+}
+
+/**
+ * go_dock_band_prepend:
+ * @band: A GoDockBand widget
+ * @child: A widget to be added to @band
+ * @offset: Offset (in pixels) from the beginning of the band
+ *
+ * Description: Add @child to @band with the specified @offset as the
+ * first element.
+ *
+ * Returns: %TRUE if successful, %FALSE if the operation fails.
+ **/
+gboolean
+go_dock_band_prepend (GoDockBand *band,
+ GtkWidget *child,
+ guint offset)
+{
+ return go_dock_band_insert (band, child, offset, 0);
+}
+
+/**
+ * go_dock_band_append:
+ * @band: A GoDockBand widget
+ * @child: A widget to be added to @band
+ * @offset: Offset (in pixels) from the last item of the band
+ *
+ * Description: Add @child to @band with the specified @offset as the
+ * last element.
+ *
+ * Returns: %TRUE if successful, %FALSE if the operation fails.
+ **/
+gboolean
+go_dock_band_append (GoDockBand *band,
+ GtkWidget *child,
+ guint offset)
+{
+ return go_dock_band_insert (band, child, offset, -1);
+}
+
+/**
+ * go_dock_band_set_child_offset:
+ * @band: A GoDockBand widget
+ * @child: Child of @band whose offset must be changed
+ * @offset: New offset value for @child
+ *
+ * Description: Set the offset for the specified @child of @band.
+ **/
+void
+go_dock_band_set_child_offset (GoDockBand *band,
+ GtkWidget *child,
+ guint offset)
+{
+ GList *p;
+
+ p = find_child (band, child);
+ if (p != NULL)
+ {
+ GoDockBandChild *c;
+
+ c = (GoDockBandChild *) p->data;
+ c->offset = offset;
+ gtk_widget_queue_resize (c->widget);
+ }
+}
+
+/**
+ * go_dock_band_get_child_offset:
+ * @band: A GoDockBand widget
+ * @child: Child of @band whose offset must be retrieved
+ *
+ * Description: Retrieve the offset of @child in @band.
+ *
+ * Returns: The offset of @child.
+ **/
+guint
+go_dock_band_get_child_offset (GoDockBand *band,
+ GtkWidget *child)
+{
+ GList *p;
+
+ p = find_child (band, child);
+ if (p != NULL)
+ {
+ GoDockBandChild *c;
+
+ c = (GoDockBandChild *) p->data;
+ return c->offset;
+ }
+
+ return 0;
+}
+
+/**
+ * go_dock_band_get_num_children:
+ * @band: A GoDockBand widget
+ *
+ * Description: Retrieve the number of children in @band.
+ *
+ * Returns: The number of children in @band.
+ **/
+guint
+go_dock_band_get_num_children (GoDockBand *band)
+{
+ return band->num_children;
+}
+
+
+
+/* Private interface. */
+
+void
+go_dock_band_drag_begin (GoDockBand *band, GoDockItem *item)
+{
+ GList *lp;
+ GtkWidget *floating_widget;
+ GtkWidget *item_widget;
+ guint extra_offset = 0;
+
+ DEBUG (("entering function"));
+
+ item_widget = GTK_WIDGET (item);
+ floating_widget = NULL;
+
+ for (lp = band->children; lp != NULL;)
+ {
+ GoDockBandChild *c;
+
+ c = lp->data;
+
+ c->drag_allocation = c->widget->allocation;
+ c->drag_offset = c->real_offset + extra_offset;
+ c->drag_prev_space = c->prev_space;
+ c->drag_foll_space = c->foll_space;
+
+ c->offset = c->real_offset;
+
+ if (c->widget == item_widget)
+ {
+ band->floating_child = lp;
+ floating_widget = item_widget;
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ extra_offset = c->widget->allocation.width + c->real_offset;
+ else
+ extra_offset = c->widget->allocation.height + c->real_offset;
+ }
+ else
+ extra_offset = 0;
+
+ if (lp->next == NULL)
+ break;
+
+ lp = lp->next;
+ }
+
+ if (floating_widget != NULL)
+ {
+ for (lp = band->floating_child->prev; lp != NULL; lp = lp->prev)
+ {
+ GoDockBandChild *c;
+
+ c = lp->data;
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ c->drag_foll_space += item_widget->requisition.width;
+ else
+ c->drag_foll_space += item_widget->requisition.height;
+ }
+ for (lp = band->floating_child->next; lp != NULL; lp = lp->next)
+ {
+ GoDockBandChild *c;
+
+ c = lp->data;
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ c->drag_prev_space += item_widget->requisition.width;
+ else
+ c->drag_prev_space += item_widget->requisition.height;
+ }
+ }
+
+ band->doing_drag = TRUE;
+ band->drag_allocation = GTK_WIDGET (band)->allocation;
+}
+
+gboolean
+go_dock_band_drag_to (GoDockBand *band,
+ GoDockItem *item,
+ gint x, gint y)
+{
+ GtkAllocation *allocation;
+ GList *where;
+ gboolean is_empty;
+
+ g_return_val_if_fail (band->doing_drag, FALSE);
+
+ DEBUG (("%d %d", x, y));
+
+ allocation = & GTK_WIDGET (band)->allocation;
+
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (x < allocation->x)
+ x = allocation->x;
+ if (x >= allocation->x + allocation->width)
+ x = allocation->x + allocation->width - 1;
+ where = find_where (band, x, &is_empty);
+ }
+ else
+ {
+ if (y < allocation->y)
+ y = allocation->y;
+ if (y >= allocation->y + allocation->height)
+ y = allocation->y + allocation->height - 1;
+ where = find_where (band, y, &is_empty);
+ }
+
+ {
+ GList *p;
+
+ for (p = next_if_floating (band, band->children);
+ p != NULL;
+ p = next_not_floating (band, p))
+ {
+ GoDockBandChild *c = p->data;
+
+ c->real_offset = c->offset = c->drag_offset;
+ }
+ }
+
+ if (is_empty)
+ return dock_empty (band, item, where, x, y);
+ else
+ return dock_nonempty (band, item, where, x, y);
+}
+
+void
+go_dock_band_drag_end (GoDockBand *band, GoDockItem *item)
+{
+ g_return_if_fail (band->doing_drag);
+
+ DEBUG (("entering function"));
+
+ if (band->floating_child != NULL)
+ {
+ GoDockBandChild *f;
+
+ /* Minimal sanity check. */
+ f = (GoDockBandChild *) band->floating_child->data;
+ g_return_if_fail (f->widget == GTK_WIDGET (item));
+
+ gtk_widget_queue_resize (f->widget);
+ band->floating_child = NULL;
+ }
+
+ band->doing_drag = FALSE;
+ band->new_for_drag = FALSE;
+}
+
+
+
+/**
+ * go_dock_band_get_item_by_name:
+ * @band: A GoDockBand widget
+ * @name: Name of the child to be retrieved
+ * @position_return: Pointer to a variable holding the position of
+ * the named child
+ * @offset_return: Pointer to a variable holding the offset of the
+ * named child
+ *
+ * Description: Retrieve a named item from @band, and return its
+ * position and offset in *@position_return and @offset_return.
+ *
+ * Return value: The child whose name is @name, or %NULL if no child
+ * of @band has such name.
+ **/
+GoDockItem *
+go_dock_band_get_item_by_name (GoDockBand *band,
+ const char *name,
+ guint *position_return,
+ guint *offset_return)
+{
+ guint pos;
+ GList *lp;
+
+ for (lp = band->children, pos = 0; lp != NULL; lp = lp->next, pos++)
+ {
+ GoDockBandChild *c;
+
+ c = lp->data;
+ if (GO_IS_DOCK_ITEM (c->widget))
+ {
+ GoDockItem *item;
+
+ item = GO_DOCK_ITEM (c->widget);
+ if (strcmp (item->name, name) == 0)
+ {
+ if (position_return != NULL)
+ *position_return = pos;
+ if (offset_return != NULL)
+ *offset_return = c->offset;
+ return item;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+
+void
+go_dock_band_layout_add (GoDockBand *band,
+ GoDockLayout *layout,
+ GoDockPlacement placement,
+ guint band_num)
+{
+ guint child_num;
+ GList *lp;
+
+ for (lp = band->children, child_num = 0;
+ lp != NULL;
+ lp = lp->next, child_num++)
+ {
+ GoDockBandChild *child;
+ GtkWidget *item;
+
+ child = lp->data;
+ item = child->widget;
+
+ if (GO_IS_DOCK_ITEM (item))
+ go_dock_layout_add_item (layout,
+ GO_DOCK_ITEM (item),
+ placement, band_num,
+ child_num, child->offset);
+ }
+}
+
+static GoDock *
+get_dock (GtkWidget *widget)
+{
+ while (widget && !GO_IS_DOCK (widget))
+ widget = widget->parent;
+
+ return (GoDock *) widget;
+}
+
+gint
+_bonobo_dock_band_handle_key_nav (GoDockBand *band,
+ GoDockItem *item,
+ GdkEventKey *event)
+{
+ gboolean handled = FALSE;
+
+ g_return_val_if_fail (GO_IS_DOCK_BAND (band), FALSE);
+ g_return_val_if_fail (GO_IS_DOCK_ITEM (item), FALSE);
+
+ if (event->state & GDK_CONTROL_MASK)
+ {
+ GList *l;
+ int cur_idx = 0;
+ int dest_idx;
+ int num_children = g_list_length (band->children);
+
+ for (l = band->children; l; l = l->next)
+ {
+ GoDockBandChild *child = l->data;
+ if (child->widget == (GtkWidget *)item)
+ break;
+ cur_idx++;
+ }
+
+ g_return_val_if_fail (l != NULL, FALSE);
+
+ dest_idx = cur_idx;
+ if (band->orientation == GTK_ORIENTATION_HORIZONTAL)
+ {
+ if (event->keyval == GDK_Left)
+
+ dest_idx--;
+ if (event->keyval == GDK_Right)
+ dest_idx++;
+ }
+ else
+ {
+ if (event->keyval == GDK_Up)
+ dest_idx--;
+ if (event->keyval == GDK_Down)
+ dest_idx++;
+ }
+
+ if (dest_idx >= num_children)
+ dest_idx = num_children - 1;
+ if (dest_idx < 0)
+ dest_idx = 0;
+ if (dest_idx != cur_idx)
+ {
+ handled = TRUE;
+ go_dock_band_move_child (band, l, dest_idx);
+ }
+ }
+
+ if (!handled)
+ {
+ GoDock *dock = get_dock (GTK_WIDGET (band));
+
+ if (dock)
+ handled = _bonobo_dock_handle_key_nav (dock, band, item, event);
+ }
+
+ return handled;
+}
--- /dev/null
+++ lib/goffice/gui-utils/Makefile.am
@@ -0,0 +1,44 @@
+noinst_LTLIBRARIES = libgoffice-gui-utils.la
+
+AM_CFLAGS = $(GNOME_CFLAGS) $(GSF_CFLAGS) $(GLADE_CFLAGS)
+
+BUILT_SOURCES = \
+ go-marshalers.h \
+ go-marshalers.c
+
+libgoffice_gui_utils_la_SOURCES = \
+ $(BUILT_SOURCES) \
+ go-combo-box.c \
+ go-combo-box.h \
+ go-color-group.c \
+ go-color-group.h \
+ go-color-palette.c \
+ go-color-palette.h \
+ go-combo-color.c \
+ go-combo-color.h \
+ go-combo-pixmaps.h \
+ go-combo-pixmaps.c \
+ go-combo-text.c \
+ go-combo-text.h \
+ \
+ go-action-combo-color.c \
+ go-action-combo-color.h \
+ go-action-combo-pixmaps.c \
+ go-action-combo-pixmaps.h \
+ go-action-combo-stack.c \
+ go-action-combo-stack.h \
+ go-action-combo-text.c \
+ go-action-combo-text.h
+
+go-marshalers.h : go-marshalers.list $(GLIB_GENMARSHAL)
+ $(GLIB_GENMARSHAL) $< --header --prefix=go_ > $@
+go-marshalers.c : go-marshalers.list $(GLIB_GENMARSHAL)
+ $(GLIB_GENMARSHAL) $< --body --prefix=go_ > $@.tmp
+ echo '/* This file has been automatically generated. Do not edit. */' >$@
+ echo '#include "'$*.h'"' >>$@
+ cat $@.tmp >>$@
+ rm -f $@.tmp
+
+EXTRA_DIST = go-marshalers.list
+
+include $(srcdir)/../goffice.mk
--- /dev/null
+++ lib/goffice/gui-utils/go-action-combo-text.c
@@ -0,0 +1,215 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-action-combo-text .c: A custom GtkAction to handle lists in menus/toolbars
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <goffice/goffice-config.h>
+#include "go-action-combo-text.h"
+#include "go-combo-box.h"
+#include "go-combo-text.h"
+//#include <src/gui-util.h>
+#include <gui-util.h>
+
+#include <gtk/gtkaction.h>
+#include <gtk/gtktoolitem.h>
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+
+typedef struct {
+ GtkToolItem base;
+ GoComboText *combo; /* container has a ref, not us */
+} GOToolComboText;
+typedef GtkToolItemClass GOToolComboTextClass;
+
+#define GO_TOOL_COMBO_TEXT_TYPE (go_tool_combo_text_get_type ())
+#define GO_TOOL_COMBO_TEXT(o) (G_TYPE_CHECK_INSTANCE_CAST (o, GO_TOOL_COMBO_TEXT_TYPE, GOToolComboText))
+#define IS_GO_TOOL_COMBO_TEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE (o, GO_TOOL_COMBO_TEXT_TYPE))
+
+static GType go_tool_combo_text_get_type (void);
+#if 0
+static void
+go_tool_combo_text_finalize (GObject *obj)
+{
+ /* Call parent->finalize (obj). */
+}
+static gboolean
+go_tool_combo_text_create_menu_proxy (GtkToolItem *tool_item)
+{
+}
+static gboolean
+go_tool_combo_text_set_tooltip (GtkToolItem *tool_item, GtkTooltips *tooltips,
+ char const *tip_text,
+ char const *tip_private)
+{
+}
+#endif
+static void
+go_tool_combo_text_class_init (GtkToolItemClass *tool_item_klass)
+{
+#if 0
+ gobject_klass->finalize = go_tool_combo_stack_finalize;
+ tool_item_klass->create_menu_proxy = go_tool_combo_stack_create_menu_proxy;
+ tool_item_klass->set_tooltip = go_tool_combo_stack_set_tooltip;
+#endif
+}
+
+static GSF_CLASS (GOToolComboText, go_tool_combo_text,
+ go_tool_combo_text_class_init, NULL,
+ GTK_TYPE_TOOL_ITEM)
+
+/*****************************************************************************/
+
+struct _GOActionComboText {
+ GtkAction base;
+ GSList *elements;
+ char const *largest_elem;
+ char *entry_val;
+};
+typedef struct {
+ GtkActionClass base;
+} GOActionComboTextClass;
+
+static GObjectClass *combo_text_parent;
+
+static void
+set_entry_val (GOActionComboText *taction, char const *text)
+{
+ if (taction->entry_val != text) {
+ g_free (taction->entry_val);
+ taction->entry_val = g_strdup (text);
+ }
+}
+
+#if 0
+static void
+go_action_combo_text_connect_proxy (GtkAction *action, GtkWidget *proxy)
+{
+}
+
+static void
+go_action_combo_disconnect_proxy (GtkAction *action,
+ GtkWidget *proxy)
+{
+}
+#endif
+
+static gboolean
+cb_entry_changed (GoComboText *ct, char const *text, GOActionComboText *taction)
+{
+ set_entry_val (taction, text);
+ gtk_action_activate (GTK_ACTION (taction));
+ return TRUE;
+}
+
+static GtkWidget *
+go_action_combo_create_tool_item (GtkAction *act)
+{
+ GOActionComboText *taction = GO_ACTION_COMBO_TEXT (act);
+ GOToolComboText *tool = g_object_new (GO_TOOL_COMBO_TEXT_TYPE, NULL);
+ GSList *ptr;
+ int tmp, w = -1;
+
+ tool->combo = (GoComboText *)go_combo_text_new (NULL);
+ if (taction->largest_elem != NULL)
+ w = gnm_measure_string (
+ gtk_widget_get_pango_context (GTK_WIDGET (tool->combo)),
+ go_combo_text_get_entry (tool->combo)->style->font_desc,
+ taction->largest_elem);
+ for (ptr = taction->elements; ptr != NULL ; ptr = ptr->next) {
+ go_combo_text_add_item (tool->combo, ptr->data);
+ if (taction->largest_elem == NULL) {
+ tmp = gnm_measure_string (
+ gtk_widget_get_pango_context (GTK_WIDGET (tool->combo)),
+ go_combo_text_get_entry (tool->combo)->style->font_desc,
+ ptr->data);
+ if (w < tmp)
+ w = tmp;
+ }
+ }
+
+ go_combo_box_set_title (GO_COMBO_BOX (tool->combo),
+ _(gtk_action_get_name (act)));
+ gtk_widget_set_size_request (
+ go_combo_text_get_entry (tool->combo), w, -1);
+ g_object_set (G_OBJECT (tool), "visible_vertical", FALSE, NULL);
+
+ go_combo_box_set_relief (GO_COMBO_BOX (tool->combo), GTK_RELIEF_NONE);
+ go_combo_box_set_tearable (GO_COMBO_BOX (tool->combo), TRUE);
+ gtk_container_add (GTK_CONTAINER (tool), GTK_WIDGET (tool->combo));
+ gtk_widget_show (GTK_WIDGET (tool->combo));
+ gtk_widget_show (GTK_WIDGET (tool));
+ g_signal_connect (tool->combo,
+ "entry_changed",
+ G_CALLBACK (cb_entry_changed), taction);
+ return GTK_WIDGET (tool);
+}
+
+static void
+go_action_combo_text_finalize (GObject *obj)
+{
+ combo_text_parent->finalize (obj);
+}
+static void
+go_action_combo_text_class_init (GtkActionClass *gtk_act_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *)gtk_act_klass;
+
+ combo_text_parent = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = go_action_combo_text_finalize;
+
+ gtk_act_klass->create_tool_item = go_action_combo_create_tool_item;
+#if 0
+ gtk_act_klass->create_menu_item = Use the default
+ gtk_act_klass->connect_proxy = go_action_combo_stack_connect_proxy;
+ gtk_act_klass->disconnect_proxy = go_action_combo_stack_disconnect_proxy;
+#endif
+}
+
+GSF_CLASS (GOActionComboText, go_action_combo_text,
+ go_action_combo_text_class_init, NULL,
+ GTK_TYPE_ACTION)
+
+void
+go_action_combo_text_add_item (GOActionComboText *taction, char const *item)
+{
+ taction->elements = g_slist_append (taction->elements, g_strdup (item));
+}
+
+void
+go_action_combo_text_set_width (GOActionComboText *taction, char const *largest_elem)
+{
+ taction->largest_elem = largest_elem;
+}
+
+char const *
+go_action_combo_text_get_entry (GOActionComboText const *a)
+{
+ return a->entry_val;
+}
+
+void
+go_action_combo_text_set_entry (GOActionComboText *taction, char const *text,
+ GOActionComboTextSearchDir dir)
+{
+ GSList *ptr = gtk_action_get_proxies (GTK_ACTION (taction));
+
+ set_entry_val (taction, text);
+ for ( ; ptr != NULL ; ptr = ptr->next)
+ if (IS_GO_TOOL_COMBO_TEXT (ptr->data))
+ go_combo_text_set_text (GO_TOOL_COMBO_TEXT (ptr->data)->combo, text, dir);
+}
--- /dev/null
+++ lib/goffice/split/widgets/widget-format-selector.c
@@ -0,0 +1,1199 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * widget-number-format-selector.c: Implements a widget to select number format.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **/
+
+#include <config.h>
+#include "widget-format-selector.h"
+
+#include <glib/gi18n.h>
+
+#include <format.h>
+#include <mstyle.h>
+#include <style-color.h>
+//#include <sheet.h>
+#include <value.h>
+
+#include <goffice/gui-utils/go-combo-text.h>
+
+#include <gtk/gtksizegroup.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtkspinbutton.h>
+#include <gtk/gtktogglebutton.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkliststore.h>
+#include <gtk/gtkhbox.h>
+#include <gsf/gsf-impl-utils.h>
+
+#include <string.h>
+#include <locale.h>
+
+/* The maximum number of chars in the formatting sample */
+#define FORMAT_PREVIEW_MAX 25
+
+#define SETUP_LOCALE_SWITCH char *oldlocale = NULL
+
+#define START_LOCALE_SWITCH \
+ do { \
+ if (nfs->locale) { \
+ currency_date_format_shutdown (); \
+ oldlocale = g_strdup (setlocale (LC_ALL, NULL)); \
+ gnm_setlocale (LC_ALL, nfs->locale); \
+ currency_date_format_init (); \
+ } \
+ } while (0)
+
+#define END_LOCALE_SWITCH \
+ do { \
+ if (oldlocale) { \
+ currency_date_format_shutdown (); \
+ gnm_setlocale (LC_ALL, oldlocale); \
+ g_free (oldlocale); \
+ currency_date_format_init (); \
+ } \
+ } while (0)
+
+#define FMT_CUSTOM ((FormatFamily)(FMT_SPECIAL + 1))
+
+/*Format Categories*/
+static char const *const format_category_names[] = {
+ N_("General"),
+ N_("Number"),
+ N_("Currency"),
+ N_("Accounting"),
+ N_("Date"),
+ N_("Time"),
+ N_("Percentage"),
+ N_("Fraction"),
+ N_("Scientific"),
+ N_("Text"),
+ N_("Special"),
+ N_("Custom"),
+ NULL
+};
+
+/* The available format widgets */
+typedef enum {
+ F_GENERAL_EXPLANATION,
+ F_NUMBER_EXPLANATION,
+ F_CURRENCY_EXPLANATION,
+ F_ACCOUNTING_EXPLANATION,
+ F_DATE_EXPLANATION,
+ F_TIME_EXPLANATION,
+ F_PERCENTAGE_EXPLANATION,
+ F_FRACTION_EXPLANATION,
+ F_SCIENTIFIC_EXPLANATION,
+ F_TEXT_EXPLANATION,
+ F_SPECIAL_EXPLANATION,
+ F_CUSTOM_EXPLANATION,
+
+ F_SEPARATOR,
+ F_SYMBOL_LABEL, F_SYMBOL,
+ F_ENTRY,
+ F_LIST_LABEL, F_LIST_SCROLL, F_LIST,
+ F_DECIMAL_SPIN,
+ F_NEGATIVE_LABEL, F_NEGATIVE_SCROLL, F_NEGATIVE,
+ F_DECIMAL_LABEL, F_CODE_LABEL, F_SYMBOL_BOX,
+ F_DECIMAL_BOX, F_CODE_BOX, F_MAX_WIDGET
+} FormatWidget;
+
+struct _NumberFormatSelector {
+ GtkHBox box;
+ GladeXML *gui;
+
+ GnmValue *value;
+ char *locale;
+
+ gboolean enable_edit;
+
+ GnmDateConventions const *date_conv;
+
+ struct {
+ GtkTextView *preview;
+ GtkWidget *preview_box;
+ GtkTextBuffer *preview_buffer;
+
+ GtkWidget *widget[F_MAX_WIDGET];
+ GtkWidget *menu;
+ GtkTreeModel *menu_model;
+ GtkSizeGroup *size_group;
+
+ struct {
+ GtkTreeView *view;
+ GtkListStore *model;
+ GtkTreeSelection *selection;
+ } negative_types;
+
+ struct {
+ GtkTreeView *view;
+ GtkListStore *model;
+ GtkTreeSelection *selection;
+ } formats;
+
+ gulong entry_changed_id;
+ GnmFormat *spec;
+ gint current_type;
+ int num_decimals;
+ int negative_format;
+ int currency_index;
+ gboolean use_separator;
+ } format;
+};
+
+typedef struct {
+ GtkHBoxClass parent_class;
+
+ gboolean (*number_format_changed) (NumberFormatSelector *nfs, const char *fmt);
+} NumberFormatSelectorClass;
+
+static GtkHBoxClass *nfs_parent_class;
+
+/* Signals we emit */
+enum {
+ NUMBER_FORMAT_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint nfs_signals[LAST_SIGNAL] = { 0 };
+
+static void format_entry_set_text (NumberFormatSelector *nfs, gchar *text);
+
+static void
+generate_format (NumberFormatSelector *nfs)
+{
+ FormatFamily const page = nfs->format.current_type;
+ GnmFormat *new_format;
+
+ /*
+ * It is a strange idea not to reuse FormatCharacteristics
+ * in this file, so build one.
+ */
+ FormatCharacteristics format = nfs->format.spec->family_info;
+ format.thousands_sep = nfs->format.use_separator;
+ format.num_decimals = nfs->format.num_decimals;
+ format.negative_fmt = nfs->format.negative_format;
+ format.currency_symbol_index = nfs->format.currency_index;
+
+ new_format = style_format_build (page, &format);
+ if (new_format) {
+ char *tmp = style_format_as_XL (new_format, TRUE);
+ format_entry_set_text (nfs, tmp);
+ g_free (tmp);
+ }
+
+ style_format_unref (new_format);
+}
+
+static void
+draw_format_preview (NumberFormatSelector *nfs, gboolean regen_format)
+{
+ gchar *preview;
+ GnmFormat *sf = NULL;
+ GnmColor *c = NULL;
+
+ if (regen_format)
+ generate_format (nfs);
+
+ /* Nothing to sample. */
+ if (nfs->value == NULL)
+ return;
+
+ sf = nfs->format.spec;
+
+ if (sf == NULL || nfs->value == NULL)
+ return;
+
+ if (style_format_is_general (sf) &&
+ VALUE_FMT (nfs->value) != NULL)
+ sf = VALUE_FMT (nfs->value);
+
+ preview = format_value (sf, nfs->value, &c, -1, nfs->date_conv);
+ if (strlen (preview) > FORMAT_PREVIEW_MAX)
+ strcpy (&preview[FORMAT_PREVIEW_MAX - 5], " ...");
+
+ gtk_text_buffer_set_text (nfs->format.preview_buffer, preview, -1);
+ if (c != NULL) {
+ gtk_widget_modify_text (GTK_WIDGET(nfs->format.preview),
+ GTK_STATE_NORMAL, &(c->color));
+ style_color_unref (c);
+ } else {
+ GdkColor color;
+ gdk_color_parse ("black", &color);
+ gtk_widget_modify_text (GTK_WIDGET(nfs->format.preview),
+ GTK_STATE_NORMAL, &color);
+ }
+
+ g_free (preview);
+}
+
+static void
+fillin_negative_samples (NumberFormatSelector *nfs)
+{
+ static char const *const decimals = "098765432109876543210987654321";
+ static char const *const formats[4] = {
+ "-%s%s3%s210%s%s%s%s",
+ "%s%s3%s210%s%s%s%s",
+ "(%s%s3%s210%s%s%s%s)",
+ "(%s%s3%s210%s%s%s%s)"
+ };
+ int const n = 30 - nfs->format.num_decimals;
+
+ FormatFamily const page = nfs->format.current_type;
+ char const *space_b = "", *currency_b;
+ char const *space_a = "", *currency_a;
+ const char *decimal;
+ const char *thousand_sep;
+ int i;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gboolean more;
+ SETUP_LOCALE_SWITCH;
+
+ g_return_if_fail (page == FMT_NUMBER || page == FMT_CURRENCY);
+ g_return_if_fail (nfs->format.num_decimals <= 30);
+
+ START_LOCALE_SWITCH;
+
+ if (nfs->format.use_separator)
+ thousand_sep = format_get_thousand ()->str;
+ else
+ thousand_sep = "";
+ if (nfs->format.num_decimals > 0)
+ decimal = format_get_decimal ()->str;
+ else
+ decimal = "";
+
+ if (page == FMT_CURRENCY) {
+ currency_b = (const gchar *)currency_symbols[nfs->format.currency_index].symbol;
+ /*
+ * FIXME : This should be better hidden.
+ * Ideally the render would do this for us.
+ */
+ if (currency_b[0] == '[' && currency_b[1] == '$') {
+ char const *end = strchr (currency_b+2, '-');
+ if (end == NULL)
+ end = strchr (currency_b+2, ']');
+ currency_b = g_strndup (currency_b+2, end-currency_b-2);
+ } else
+ currency_b = g_strdup (currency_b);
+
+ if (currency_symbols[nfs->format.currency_index].has_space)
+ space_b = " ";
+
+ if (!currency_symbols[nfs->format.currency_index].precedes) {
+ currency_a = currency_b;
+ currency_b = "";
+ space_a = space_b;
+ space_b = "";
+ } else {
+ currency_a = "";
+ }
+ } else
+ currency_a = currency_b = "";
+
+ more = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (nfs->format.negative_types.model), &iter);
+ for (i = 0 ; i < 4; i++) {
+ char *buf = g_strdup_printf (formats[i],
+ currency_b, space_b, thousand_sep, decimal,
+ decimals + n, space_a, currency_a);
+ if (!more)
+ gtk_list_store_append (nfs->format.negative_types.model, &iter);
+ gtk_list_store_set (nfs->format.negative_types.model, &iter,
+ 0, i,
+ 1, buf,
+ 2, (i % 2) ? "red" : NULL,
+ -1);
+ if (more)
+ more = gtk_tree_model_iter_next (GTK_TREE_MODEL (nfs->format.negative_types.model),
+ &iter);
+
+ g_free (buf);
+ }
+
+ /* If non empty then free the string */
+ if (*currency_a)
+ g_free ((char*)currency_a);
+ if (*currency_b)
+ g_free ((char*)currency_b);
+
+ path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, nfs->format.negative_format);
+ gtk_tree_selection_select_path (nfs->format.negative_types.selection, path);
+ gtk_tree_path_free (path);
+
+ END_LOCALE_SWITCH;
+}
+
+static void
+cb_decimals_changed (GtkSpinButton *spin, NumberFormatSelector *nfs)
+{
+ FormatFamily const page = nfs->format.current_type;
+
+ nfs->format.num_decimals = gtk_spin_button_get_value_as_int (spin);
+
+ if (page == FMT_NUMBER || page == FMT_CURRENCY)
+ fillin_negative_samples (nfs);
+
+ draw_format_preview (nfs, TRUE);
+}
+
+static void
+cb_separator_toggle (GtkObject *obj, NumberFormatSelector *nfs)
+{
+ nfs->format.use_separator =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (obj));
+ fillin_negative_samples (nfs);
+
+ draw_format_preview (nfs, TRUE);
+}
+
+static void
+fmt_dialog_init_fmt_list (NumberFormatSelector *nfs, char const *const *formats,
+ GtkTreeIter *select)
+{
+ GtkTreeIter iter;
+ char *fmt;
+ char const *cur_fmt = nfs->format.spec->format;
+
+ for (; *formats; formats++) {
+ gtk_list_store_append (nfs->format.formats.model, &iter);
+ fmt = style_format_str_as_XL (*formats, TRUE);
+ gtk_list_store_set (nfs->format.formats.model, &iter,
+ 0, fmt, -1);
+ g_free (fmt);
+
+ if (!strcmp (*formats, cur_fmt))
+ *select = iter;
+ }
+}
+
+static void
+fmt_dialog_enable_widgets (NumberFormatSelector *nfs, int page)
+{
+ SETUP_LOCALE_SWITCH;
+ static FormatWidget const contents[][12] = {
+ /* General */
+ {
+ F_GENERAL_EXPLANATION,
+ F_MAX_WIDGET
+ },
+ /* Number */
+ {
+ F_NUMBER_EXPLANATION,
+ F_DECIMAL_BOX,
+ F_DECIMAL_LABEL,
+ F_DECIMAL_SPIN,
+ F_SEPARATOR,
+ F_NEGATIVE_LABEL,
+ F_NEGATIVE_SCROLL,
+ F_NEGATIVE,
+ F_MAX_WIDGET
+ },
+ /* Currency */
+ {
+ F_CURRENCY_EXPLANATION,
+ F_DECIMAL_BOX,
+ F_DECIMAL_LABEL,
+ F_DECIMAL_SPIN,
+ F_SEPARATOR,
+ F_SYMBOL_BOX,
+ F_SYMBOL_LABEL,
+ F_SYMBOL,
+ F_NEGATIVE_LABEL,
+ F_NEGATIVE_SCROLL,
+ F_NEGATIVE,
+ F_MAX_WIDGET
+ },
+ /* Accounting */
+ {
+ F_ACCOUNTING_EXPLANATION,
+ F_DECIMAL_BOX,
+ F_DECIMAL_LABEL,
+ F_DECIMAL_SPIN,
+ F_SYMBOL_BOX,
+ F_SYMBOL_LABEL,
+ F_SYMBOL,
+ F_MAX_WIDGET
+ },
+ /* Date */
+ {
+ F_DATE_EXPLANATION,
+ F_LIST_LABEL,
+ F_LIST_SCROLL,
+ F_LIST,
+ F_MAX_WIDGET
+ },
+ /* Time */
+ {
+ F_TIME_EXPLANATION,
+ F_LIST_LABEL,
+ F_LIST_SCROLL,
+ F_LIST,
+ F_MAX_WIDGET
+ },
+ /* Percentage */
+ {
+ F_PERCENTAGE_EXPLANATION,
+ F_DECIMAL_BOX,
+ F_DECIMAL_LABEL,
+ F_DECIMAL_SPIN,
+ F_MAX_WIDGET
+ },
+ /* Fraction */
+ {
+ F_FRACTION_EXPLANATION,
+ F_LIST_LABEL,
+ F_LIST_SCROLL,
+ F_LIST,
+ F_MAX_WIDGET
+ },
+ /* Scientific */
+ {
+ F_SCIENTIFIC_EXPLANATION,
+ F_DECIMAL_BOX,
+ F_DECIMAL_LABEL,
+ F_DECIMAL_SPIN,
+ F_MAX_WIDGET
+ },
+ /* Text */
+ {
+ F_TEXT_EXPLANATION,
+ F_MAX_WIDGET
+ },
+ /* Special */
+ {
+ F_SPECIAL_EXPLANATION,
+ F_MAX_WIDGET
+ },
+ /* Custom */
+ {
+ F_CUSTOM_EXPLANATION,
+ F_CODE_BOX,
+ F_CODE_LABEL,
+ F_ENTRY,
+ F_LIST_LABEL,
+ F_LIST_SCROLL,
+ F_LIST,
+ F_MAX_WIDGET
+ }
+ };
+
+ FormatFamily const old_page = nfs->format.current_type;
+ int i;
+ FormatWidget tmp;
+
+ START_LOCALE_SWITCH;
+
+ /* Hide widgets from old page */
+ if (old_page >= 0) {
+ int i, j;
+ FormatWidget wi, wj;
+ for (i = 0; (wi = contents[old_page][i]) != F_MAX_WIDGET ; ++i) {
+ for (j = 0; (wj = contents[page][j]) != F_MAX_WIDGET ; ++j)
+ if (wi == wj)
+ goto stays;
+ gtk_widget_hide (nfs->format.widget[wi]);
+ stays:
+ ; /* No more */
+ }
+ }
+
+ /* Set the default format if appropriate */
+ if (page == FMT_GENERAL || page == FMT_ACCOUNT || page == FMT_FRACTION || page == FMT_TEXT) {
+ int list_elem = 0;
+ char *tmp;
+ if (page == nfs->format.spec->family)
+ list_elem = nfs->format.spec->family_info.list_element;
+
+ tmp = style_format_str_as_XL (cell_formats[page][list_elem], TRUE);
+ format_entry_set_text (nfs, tmp);
+ g_free (tmp);
+ }
+
+ nfs->format.current_type = page;
+ for (i = 0; (tmp = contents[page][i]) != F_MAX_WIDGET ; ++i) {
+ GtkWidget *w = nfs->format.widget[tmp];
+
+ switch (tmp) {
+ case F_LIST: {
+ int start = 0, end = -1;
+ GtkTreeIter select;
+
+ switch (page) {
+ case FMT_DATE:
+ case FMT_TIME:
+ case FMT_FRACTION:
+ start = end = page;
+ break;
+
+ case FMT_CUSTOM:
+ start = 0; end = 8;
+ break;
+
+ default :
+ g_assert_not_reached ();
+ };
+
+ select.stamp = 0;
+ gtk_list_store_clear (nfs->format.formats.model);
+ for (; start <= end ; ++start)
+ fmt_dialog_init_fmt_list (nfs,
+ cell_formats[start], &select);
+
+ /* If this is the custom page and the format has
+ * not been found append it */
+ /* TODO We should add the list of other custom formats created.
+ * It should be easy. All that is needed is a way to differentiate
+ * the std formats and the custom formats in the GnmFormat hash.
+ */
+ if (page == FMT_CUSTOM && select.stamp == 0) {
+ char *tmp = style_format_as_XL (nfs->format.spec, TRUE);
+ format_entry_set_text (nfs, tmp);
+ g_free (tmp);
+ } else if (select.stamp == 0)
+ gtk_tree_model_get_iter_first (
+ GTK_TREE_MODEL (nfs->format.formats.model),
+ &select);
+
+ if (select.stamp != 0)
+ gtk_tree_selection_select_iter (
+ nfs->format.formats.selection, &select);
+
+ break;
+ }
+
+ case F_NEGATIVE:
+ fillin_negative_samples (nfs);
+ break;
+
+ case F_DECIMAL_SPIN:
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (nfs->format.widget[F_DECIMAL_SPIN]),
+ nfs->format.num_decimals);
+ break;
+
+ case F_SEPARATOR:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (nfs->format.widget[F_SEPARATOR]),
+ nfs->format.use_separator);
+ break;
+
+ default:
+ ; /* Nothing */
+ }
+
+ gtk_widget_show (w);
+ }
+
+#if 0
+ if ((cl = GTK_CLIST (nfs->format.widget[F_LIST])) != NULL)
+ gnumeric_clist_make_selection_visible (cl);
+#endif
+
+ draw_format_preview (nfs, TRUE);
+
+ END_LOCALE_SWITCH;
+}
+
+/*
+ * Callback routine to manage the relationship between the number
+ * formating radio buttons and the widgets required for each mode.
+ */
+
+static void
+cb_format_class_changed (G_GNUC_UNUSED GtkTreeSelection *ignored,
+ NumberFormatSelector *nfs)
+{
+ int selected_item = 0;
+ GList *list;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection
+ (GTK_TREE_VIEW(nfs->format.menu));
+
+ list = gtk_tree_selection_get_selected_rows
+ (selection, &nfs->format.menu_model);
+ if (list) {
+ GtkTreePath *path;
+ path = list->data;
+ selected_item = *(gtk_tree_path_get_indices (path));
+
+ if (selected_item >= 0) {
+ fmt_dialog_enable_widgets (nfs, selected_item);
+ }
+ g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free (list);
+ }
+}
+
+static void
+cb_format_entry_changed (GtkEditable *w, NumberFormatSelector *nfs)
+{
+ char *fmt;
+ if (!nfs->enable_edit)
+ return;
+
+ fmt = style_format_delocalize (gtk_entry_get_text (GTK_ENTRY (w)));
+ if (strcmp (nfs->format.spec->format, fmt)) {
+ style_format_unref (nfs->format.spec);
+ nfs->format.spec = style_format_new_XL (fmt, FALSE);
+ g_signal_emit (GTK_OBJECT (nfs),
+ nfs_signals[NUMBER_FORMAT_CHANGED], 0,
+ fmt);
+ draw_format_preview (nfs, FALSE);
+ }
+ g_free (fmt);
+}
+
+/*
+ * We only want to emit the number format changed signal once for each
+ * format change. When not blocking signals when calling
+ * gtk_entry_set_text, one would be emitted for deleting the old text
+ * and one for inserting the new. That's why we block the signal and
+ * invoke cb_format_entry_changed explicitly.
+ */
+static void
+format_entry_set_text (NumberFormatSelector *nfs, gchar *text)
+{
+ GtkEntry *entry = GTK_ENTRY (nfs->format.widget[F_ENTRY]);
+
+ g_signal_handler_block (entry, nfs->format.entry_changed_id);
+ gtk_entry_set_text (entry, text);
+ g_signal_handler_unblock (entry, nfs->format.entry_changed_id);
+ cb_format_entry_changed (GTK_EDITABLE (entry), nfs);
+}
+
+static void
+cb_format_list_select (GtkTreeSelection *selection, NumberFormatSelector *nfs)
+{
+ GtkTreeIter iter;
+ gchar *text;
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (nfs->format.formats.model),
+ &iter, 0, &text, -1);
+ format_entry_set_text (nfs, text);
+}
+
+static gboolean
+cb_format_currency_select (G_GNUC_UNUSED GtkWidget *ct,
+ char * new_text, NumberFormatSelector *nfs)
+{
+ int i;
+
+ /* ignore the clear while assigning a new value */
+ if (!nfs->enable_edit || new_text == NULL || *new_text == '\0')
+ return FALSE;
+
+ for (i = 0; currency_symbols[i].symbol != NULL ; ++i)
+ if (!strcmp (_(currency_symbols[i].description), new_text)) {
+ nfs->format.currency_index = i;
+ break;
+ }
+
+ if (nfs->format.current_type == 1 || nfs->format.current_type == 2)
+ fillin_negative_samples (nfs);
+ draw_format_preview (nfs, TRUE);
+
+ return TRUE;
+}
+
+static void
+cb_format_negative_form_selected (GtkTreeSelection *selection, NumberFormatSelector *nfs)
+{
+ GtkTreeIter iter;
+ int type;
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (nfs->format.negative_types.model),
+ &iter, 0, &type, -1);
+ nfs->format.negative_format = type;
+ draw_format_preview (nfs, TRUE);
+}
+
+static gint
+funny_currency_order (gconstpointer _a, gconstpointer _b)
+{
+ char const *a = (char const *)_a;
+ char const *b = (char const *)_b;
+
+ /* Keep the special 1 char versions, and both euro forms at the top */
+ gboolean a1 = a[0] && (*(g_utf8_next_char (a)) == '\0' ||
+ 0x20AC == g_utf8_get_char (a)); /* euro */
+ gboolean b1 = b[0] && (*(g_utf8_next_char (b)) == '\0' ||
+ 0x20AC == g_utf8_get_char (b)); /* euro */
+
+ if (a1) {
+ if (b1) {
+ return strcmp (a, b);
+ } else {
+ return -1;
+ }
+ } else {
+ if (b1) {
+ return +1;
+ } else {
+ return strcmp (a, b);
+ }
+ }
+}
+
+static void
+set_format_category (NumberFormatSelector *nfs, int row)
+{
+ GtkTreePath *path;
+ GtkTreeSelection *selection = gtk_tree_view_get_selection
+ ((GTK_TREE_VIEW(nfs->format.menu)));
+
+ path = gtk_tree_path_new_from_indices (row, -1);
+ gtk_tree_selection_select_path (selection, path);
+ gtk_tree_path_free (path);
+}
+
+
+static void
+set_format_category_menu_from_style (NumberFormatSelector *nfs)
+{
+ FormatFamily page;
+
+ g_return_if_fail (IS_NUMBER_FORMAT_SELECTOR (nfs));
+
+ /* Attempt to extract general parameters from the current format */
+ if ((page = nfs->format.spec->family) < 0)
+ page = FMT_CUSTOM; /* Default to custom */
+
+ set_format_category (nfs, page);
+ fmt_dialog_enable_widgets (nfs, page);
+}
+
+static void
+populate_menu (NumberFormatSelector *nfs)
+{
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkCellRenderer *renderer;
+ char const * const *categories = format_category_names;
+
+ nfs->format.menu_model = GTK_TREE_MODEL (gtk_list_store_new
+ (1, G_TYPE_STRING));
+ gtk_tree_view_set_model (GTK_TREE_VIEW (nfs->format.menu),
+ nfs->format.menu_model);
+ selection = gtk_tree_view_get_selection
+ (GTK_TREE_VIEW(nfs->format.menu));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+
+ while (*categories) {
+ gtk_list_store_append
+ (GTK_LIST_STORE (nfs->format.menu_model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (nfs->format.menu_model),
+ &iter, 0, _(*categories), -1);
+ categories++;
+ }
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("", renderer,
+ "text", 0,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW(nfs->format.menu), column);
+
+ g_signal_connect (selection,
+ "changed",
+ G_CALLBACK (cb_format_class_changed), nfs);
+}
+
+
+/*
+ * static void
+ * fmt_dialog_init_format_page (FormatState *state)
+ */
+
+static void
+nfs_init (NumberFormatSelector *nfs)
+{
+ /* The various format widgets */
+ static char const *const widget_names[] = {
+ "format_general_explanation",
+ "format_number_explanation",
+ "format_currency_explanation",
+ "format_accounting_explanation",
+ "format_date_explanation",
+ "format_time_explanation",
+ "format_percentage_explanation",
+ "format_fraction_explanation",
+ "format_scientific_explanation",
+ "format_text_explanation",
+ "format_special_explanation",
+ "format_custom_explanation",
+
+ "format_separator",
+ "format_symbol_label",
+ "format_symbol_select",
+ "format_entry",
+ "format_list_label",
+ "format_list_scroll",
+ "format_list",
+ "format_number_decimals",
+ "format_negatives_label",
+ "format_negatives_scroll",
+ "format_negatives",
+ "format_decimal_label",
+ "format_code_label",
+ "format_symbol_box",
+ "format_decimal_box",
+ "format_code_box",
+ NULL
+ };
+
+ GtkWidget *tmp;
+ GtkTreeViewColumn *column;
+ GoComboText *combo;
+ char const *name;
+ int i;
+ FormatFamily page;
+
+ GtkWidget *toplevel;
+ GtkWidget *old_parent;
+
+ nfs->enable_edit = FALSE;
+ nfs->locale = NULL;
+
+ nfs->gui = gnm_glade_xml_new (NULL, "format-selector.glade", NULL, NULL);
+ if (nfs->gui == NULL)
+ return;
+
+ toplevel = glade_xml_get_widget (nfs->gui, "number_box");
+ old_parent = gtk_widget_get_toplevel (toplevel);
+ gtk_widget_reparent (toplevel, GTK_WIDGET (nfs));
+ gtk_widget_destroy (old_parent);
+ gtk_widget_queue_resize (toplevel);
+
+ nfs->format.spec = style_format_general ();
+ style_format_ref (nfs->format.spec);
+
+ nfs->format.preview = NULL;
+
+ /* The handlers will set the format family later. -1 flags that
+ * all widgets are already hidden. */
+ nfs->format.current_type = -1;
+
+ /* Even if the format was not recognized it has set intelligent defaults */
+ nfs->format.use_separator = nfs->format.spec->family_info.thousands_sep;
+ nfs->format.num_decimals = nfs->format.spec->family_info.num_decimals;
+ nfs->format.negative_format = nfs->format.spec->family_info.negative_fmt;
+ nfs->format.currency_index = nfs->format.spec->family_info.currency_symbol_index;
+
+ nfs->format.preview_box = glade_xml_get_widget (nfs->gui, "preview_box");
+ nfs->format.preview = GTK_TEXT_VIEW (glade_xml_get_widget (nfs->gui, "preview"));
+ {
+ PangoFontMetrics *metrics;
+ PangoContext *context;
+ GtkWidget *w = GTK_WIDGET (nfs->format.preview);
+ gint char_width;
+
+ /* request width in number of chars */
+ context = gtk_widget_get_pango_context (w);
+ metrics = pango_context_get_metrics (context,
+ gtk_widget_get_style(w)->font_desc,
+ pango_context_get_language (context));
+ char_width = pango_font_metrics_get_approximate_char_width (metrics);
+ gtk_widget_set_size_request (w, PANGO_PIXELS (char_width) * FORMAT_PREVIEW_MAX, -1);
+ pango_font_metrics_unref (metrics);
+ }
+ nfs->format.preview_buffer = gtk_text_view_get_buffer (nfs->format.preview);
+
+ nfs->format.menu = glade_xml_get_widget (nfs->gui, "format_menu");
+ populate_menu (nfs);
+
+ /* Collect all the required format widgets and hide them */
+ for (i = 0; (name = widget_names[i]) != NULL; ++i) {
+ tmp = glade_xml_get_widget (nfs->gui, name);
+
+ if (tmp == NULL) {
+ g_warning ("nfs_init : failed to load widget %s", name);
+ }
+
+ g_return_if_fail (tmp != NULL);
+
+ gtk_widget_hide (tmp);
+ nfs->format.widget[i] = tmp;
+ }
+
+ /* set minimum heights */
+ gtk_widget_set_size_request (nfs->format.widget[F_LIST], -1, 100);
+ gtk_widget_set_size_request (nfs->format.widget[F_NEGATIVE], -1, 100);
+
+ /* use size group for better widget alignment */
+ nfs->format.size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ gtk_size_group_add_widget (nfs->format.size_group,
+ nfs->format.widget[F_SYMBOL_LABEL]);
+ gtk_size_group_add_widget (nfs->format.size_group,
+ nfs->format.widget[F_DECIMAL_LABEL]);
+
+ /* hide preview by default until a value is set */
+ gtk_widget_hide (nfs->format.preview_box);
+
+ /* setup the structure of the negative type list */
+ nfs->format.negative_types.model = gtk_list_store_new (3,
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+ nfs->format.negative_types.view = GTK_TREE_VIEW (nfs->format.widget[F_NEGATIVE]);
+ gtk_tree_view_set_model (nfs->format.negative_types.view,
+ GTK_TREE_MODEL (nfs->format.negative_types.model));
+ column = gtk_tree_view_column_new_with_attributes (_("Negative Number Format"),
+ gtk_cell_renderer_text_new (),
+ "text", 1,
+ "foreground", 2,
+ NULL);
+ gtk_tree_view_append_column (nfs->format.negative_types.view, column);
+ nfs->format.negative_types.selection =
+ gtk_tree_view_get_selection (nfs->format.negative_types.view);
+ gtk_tree_selection_set_mode (nfs->format.negative_types.selection,
+ GTK_SELECTION_SINGLE);
+ g_signal_connect (G_OBJECT (nfs->format.negative_types.selection),
+ "changed",
+ G_CALLBACK (cb_format_negative_form_selected), nfs);
+
+ /* Catch changes to the spin box */
+ g_signal_connect (G_OBJECT (nfs->format.widget[F_DECIMAL_SPIN]),
+ "value_changed",
+ G_CALLBACK (cb_decimals_changed), nfs);
+
+ /* Setup special handlers for : Numbers */
+ g_signal_connect (G_OBJECT (nfs->format.widget[F_SEPARATOR]),
+ "toggled",
+ G_CALLBACK (cb_separator_toggle), nfs);
+
+ /* setup custom format list */
+ nfs->format.formats.model =
+ gtk_list_store_new (1, G_TYPE_STRING);
+ nfs->format.formats.view =
+ GTK_TREE_VIEW (nfs->format.widget[F_LIST]);
+ gtk_tree_view_set_model (nfs->format.formats.view,
+ GTK_TREE_MODEL (nfs->format.formats.model));
+ column = gtk_tree_view_column_new_with_attributes (_("Number Formats"),
+ gtk_cell_renderer_text_new (),
+ "text", 0,
+ NULL);
+ gtk_tree_view_append_column (nfs->format.formats.view, column);
+ nfs->format.formats.selection =
+ gtk_tree_view_get_selection (nfs->format.formats.view);
+ gtk_tree_selection_set_mode (nfs->format.formats.selection,
+ GTK_SELECTION_BROWSE);
+ g_signal_connect (G_OBJECT (nfs->format.formats.selection),
+ "changed",
+ G_CALLBACK (cb_format_list_select), nfs);
+
+ /* Setup handler Currency & Accounting currency symbols */
+ combo = GO_COMBO_TEXT (nfs->format.widget[F_SYMBOL]);
+ if (combo != NULL) {
+ GList *ptr, *l = NULL;
+
+ for (i = 0; currency_symbols[i].symbol != NULL ; ++i)
+ l = g_list_append (l, _((gchar *)currency_symbols[i].description));
+ l = g_list_sort (l, funny_currency_order);
+
+ for (ptr = l; ptr != NULL ; ptr = ptr->next)
+ go_combo_text_add_item (combo, ptr->data);
+ g_list_free (l);
+ go_combo_text_set_text (combo,
+ _((const gchar *)currency_symbols[nfs->format.currency_index].description),
+ GO_COMBO_TEXT_FROM_TOP);
+ g_signal_connect (G_OBJECT (combo),
+ "entry_changed",
+ G_CALLBACK (cb_format_currency_select), nfs);
+ }
+
+ /* Setup special handler for Custom */
+ nfs->format.entry_changed_id
+ = g_signal_connect (G_OBJECT (nfs->format.widget[F_ENTRY]),
+ "changed",
+ G_CALLBACK (cb_format_entry_changed), nfs);
+
+ /* Connect signal for format menu */
+ set_format_category_menu_from_style (nfs);
+
+ if ((page = nfs->format.spec->family) < 0)
+ page = FMT_CUSTOM; /* Default to custom */
+ fmt_dialog_enable_widgets (nfs, page);
+
+ nfs->enable_edit = TRUE;
+}
+
+static void
+nfs_destroy (GtkObject *object)
+{
+ NumberFormatSelector *nfs = NUMBER_FORMAT_SELECTOR (object);
+
+ g_free (nfs->locale);
+ nfs->locale = NULL;
+
+ if (nfs->format.spec) {
+ style_format_unref (nfs->format.spec);
+ nfs->format.spec = NULL;
+ }
+
+ if (nfs->format.size_group) {
+ g_object_unref (nfs->format.size_group);
+ nfs->format.size_group = NULL;
+ }
+
+ if (nfs->value) {
+ value_release (nfs->value);
+ nfs->value = NULL;
+ }
+
+ if (nfs->gui) {
+ g_object_unref (G_OBJECT (nfs->gui));
+ nfs->gui = NULL;
+ }
+
+ ((GtkObjectClass *)nfs_parent_class)->destroy (object);
+}
+
+static void
+nfs_class_init (GtkObjectClass *klass)
+{
+ klass->destroy = nfs_destroy;
+
+ nfs_parent_class = g_type_class_peek (gtk_hbox_get_type ());
+
+ nfs_signals[NUMBER_FORMAT_CHANGED] =
+ g_signal_new ("number_format_changed",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NumberFormatSelectorClass, number_format_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
+
+GSF_CLASS (NumberFormatSelector, number_format_selector,
+ nfs_class_init, nfs_init, GTK_TYPE_HBOX)
+
+GtkWidget *
+number_format_selector_new (void)
+{
+ return g_object_new (NUMBER_FORMAT_SELECTOR_TYPE, NULL);
+}
+
+void
+number_format_selector_set_focus (NumberFormatSelector *nfs)
+{
+ g_return_if_fail (IS_NUMBER_FORMAT_SELECTOR (nfs));
+
+ gtk_widget_grab_focus (GTK_WIDGET (nfs->format.menu));
+}
+
+void
+number_format_selector_set_style_format (NumberFormatSelector *nfs,
+ GnmFormat *style_format)
+{
+ GoComboText *combo;
+
+ g_return_if_fail (IS_NUMBER_FORMAT_SELECTOR (nfs));
+ g_return_if_fail (style_format != NULL);
+
+ style_format_ref (style_format);
+
+ style_format_unref (nfs->format.spec);
+
+ nfs->format.spec = style_format;
+
+ nfs->format.use_separator = style_format->family_info.thousands_sep;
+ nfs->format.num_decimals = style_format->family_info.num_decimals;
+ nfs->format.negative_format = style_format->family_info.negative_fmt;
+ nfs->format.currency_index = style_format->family_info.currency_symbol_index;
+
+ combo = GO_COMBO_TEXT (nfs->format.widget[F_SYMBOL]);
+ go_combo_text_set_text
+ (combo,
+ _((const gchar *)currency_symbols[nfs->format.currency_index].description),
+ GO_COMBO_TEXT_FROM_TOP);
+
+ set_format_category_menu_from_style (nfs);
+ draw_format_preview (nfs, TRUE);
+}
+
+void
+number_format_selector_set_value (NumberFormatSelector *nfs,
+ GnmValue const *value)
+{
+ g_return_if_fail (IS_NUMBER_FORMAT_SELECTOR (nfs));
+ g_return_if_fail (value != NULL);
+
+ if (nfs->value) {
+ value_release (nfs->value);
+ }
+ nfs->value = value_dup (value);
+
+ gtk_widget_show (nfs->format.preview_box);
+
+ draw_format_preview (nfs, TRUE);
+}
+
+void
+number_format_selector_set_date_conv (NumberFormatSelector *nfs,
+ GnmDateConventions const *date_conv)
+{
+ g_return_if_fail (IS_NUMBER_FORMAT_SELECTOR (nfs));
+ g_return_if_fail (date_conv != NULL);
+
+ /* FIXME is it safe ? */
+
+ nfs->date_conv = date_conv;
+
+ draw_format_preview (nfs, TRUE);
+}
+
+void
+number_format_selector_editable_enters (NumberFormatSelector *nfs,
+ GtkWindow *window)
+{
+ g_return_if_fail (IS_NUMBER_FORMAT_SELECTOR (nfs));
+
+ gnumeric_editable_enters (window,
+ GTK_WIDGET (nfs->format.widget[F_DECIMAL_SPIN]));
+ gnumeric_editable_enters (window,
+ GTK_WIDGET (nfs->format.widget[F_ENTRY]));
+}
+
+
+void
+number_format_selector_set_locale (NumberFormatSelector *nfs,
+ char const *locale)
+{
+ g_free (nfs->locale);
+ nfs->locale = g_strdup (locale);
+
+ cb_format_class_changed (NULL, nfs);
+}
+
+/* The following utility function should possibly be in format.h but we */
+/* access to the array of category names which are better located here. */
+char const *
+number_format_selector_format_classification (GnmFormat const *style_format)
+{
+ FormatFamily page = style_format->family;
+
+ if (page < 0 || page > FMT_CUSTOM)
+ page = FMT_CUSTOM; /* Default to custom */
+
+ return _(format_category_names[page]);
+}
--- /dev/null
+++ lib/goffice/split/widgets/widget-font-selector.h
@@ -0,0 +1,32 @@
+#ifndef GNUMERIC_WIDGET_FONT_SELECTOR_H
+#define GNUMERIC_WIDGET_FONT_SELECTOR_H
+
+#include <gui-gnumeric.h>
+#include <style.h>
+///#include <libfoocanvas/foo-canvas.h>
+#include <gtk/gtkwindow.h>
+
+#define FONT_SELECTOR_TYPE (font_selector_get_type ())
+#define FONT_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FONT_SELECTOR_TYPE, FontSelector))
+#define IS_FONT_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FONT_SELECTOR_TYPE))
+
+typedef struct _FontSelector FontSelector;
+
+GType font_selector_get_type (void);
+GtkWidget *font_selector_new (void);
+
+void font_selector_set_value (FontSelector *fs, GnmValue const *v);
+void font_selector_set_name (FontSelector *fs, char const *font_name);
+void font_selector_set_style (FontSelector *fs,
+ gboolean is_bold, gboolean is_italic);
+void font_selector_set_underline (FontSelector *fs, StyleUnderlineType sut);
+void font_selector_set_strike (FontSelector *fs, gboolean strikethrough);
+void font_selector_set_color (FontSelector *fs, GnmColor *color);
+void font_selector_set_points (FontSelector *fs, double point_size);
+void font_selector_editable_enters (FontSelector *fs, GtkWindow *dialog);
+
+void font_selector_set_from_pango (FontSelector *fs, PangoFontDescription const *desc);
+void font_selector_get_pango (FontSelector *fs, PangoFontDescription *desc);
+
+#endif /* GNUMERIC_WIDGET_FONT_SELECTOR_H */
+
--- /dev/null
+++ lib/goffice/split/widgets/widget-format-selector.h
@@ -0,0 +1,52 @@
+/**
+ * widget-number-format-selector.h: Implements a widget to select number format.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ **/
+
+#ifndef __WIDGET_FORMAT_SELECTOR_H__
+#define __WIDGET_FORMAT_SELECTOR_H__
+
+//#include <gui-gnumeric.h>
+#include <gui-util.h>
+
+#define NUMBER_FORMAT_SELECTOR_TYPE (number_format_selector_get_type ())
+#define NUMBER_FORMAT_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NUMBER_FORMAT_SELECTOR_TYPE, NumberFormatSelector))
+#define IS_NUMBER_FORMAT_SELECTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NUMBER_FORMAT_SELECTOR_TYPE))
+
+typedef struct _NumberFormatSelector NumberFormatSelector;
+
+
+GType number_format_selector_get_type (void);
+GtkWidget * number_format_selector_new (void);
+
+void number_format_selector_set_focus (NumberFormatSelector *nfs);
+void number_format_selector_set_style_format (NumberFormatSelector *nfs,
+ GnmFormat *style_format);
+void number_format_selector_set_value (NumberFormatSelector *nfs,
+ GnmValue const *value);
+void number_format_selector_set_date_conv (NumberFormatSelector *nfs,
+ GnmDateConventions const *date_conv);
+void number_format_selector_editable_enters (NumberFormatSelector *nfs,
+ GtkWindow *window);
+void number_format_selector_set_locale (NumberFormatSelector *nfs,
+ char const *locale);
+
+/* Number Format Selector Utilities */
+
+char const * number_format_selector_format_classification (GnmFormat const *style_format);
+
+#endif /*__WIDGET_FORMAT_SELECTOR_H__*/
+
--- /dev/null
+++ lib/goffice/split/widgets/Makefile.am
@@ -0,0 +1,10 @@
+noinst_LTLIBRARIES = libgoffice-split-widgets.la
+
+AM_CFLAGS = $(GLIB_CFLAGS) $(GSF_CFLAGS) $(GNOME_CFLAGS) $(ART_CFLAGS) $(GLADE_CFLAGS) $(PRINT_CFLAGS)
+
+libgoffice_split_widgets_la_SOURCES = \
+ widget-format-selector.h \
+ widget-format-selector.c \
+ widget-font-selector.h
+
+include $(srcdir)/../../goffice.mk
Index: gnc-trace.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-trace.c,v
retrieving revision 1.2.20.10
retrieving revision 1.2.20.11
diff -Lsrc/engine/gnc-trace.c -Lsrc/engine/gnc-trace.c -u -r1.2.20.10 -r1.2.20.11
--- src/engine/gnc-trace.c
+++ src/engine/gnc-trace.c
@@ -47,8 +47,8 @@
GNC_LOG_WARNING, /* IO */
GNC_LOG_WARNING, /* REGISTER */
GNC_LOG_WARNING, /* LEDGER */
- GNC_LOG_WARNING, /* HTML */
- GNC_LOG_DEBUG, /* GUI */
+ GNC_LOG_INFO, /* HTML */
+ GNC_LOG_INFO, /* GUI */
GNC_LOG_WARNING, /* SCRUB */
GNC_LOG_WARNING, /* GTK_REG */
GNC_LOG_WARNING, /* GUILE */
Index: utility-reports.scm
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/report/utility-reports/utility-reports.scm,v
retrieving revision 1.5
retrieving revision 1.5.4.1
diff -Lsrc/report/utility-reports/utility-reports.scm -Lsrc/report/utility-reports/utility-reports.scm -u -r1.5 -r1.5.4.1
--- src/report/utility-reports/utility-reports.scm
+++ src/report/utility-reports/utility-reports.scm
@@ -13,4 +13,6 @@
(use-modules (gnucash report view-column))
(use-modules (gnucash report welcome-to-gnucash))
+(use-modules (gnucash report test-graphing))
+
(export gnc:make-welcome-report)
Index: Makefile.am
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/report/utility-reports/Makefile.am,v
retrieving revision 1.6.4.1
retrieving revision 1.6.4.2
diff -Lsrc/report/utility-reports/Makefile.am -Lsrc/report/utility-reports/Makefile.am -u -r1.6.4.1 -r1.6.4.2
--- src/report/utility-reports/Makefile.am
+++ src/report/utility-reports/Makefile.am
@@ -26,7 +26,8 @@
iframe-url.scm \
utility-reports.scm \
view-column.scm \
- welcome-to-gnucash.scm
+ welcome-to-gnucash.scm \
+ test-graphing.scm
EXTRA_DIST = ${gncscmmod_DATA}
--- /dev/null
+++ src/report/utility-reports/test-graphing.scm
@@ -0,0 +1,140 @@
+;; -*-scheme-*-
+
+;; This is a sample guile report generator for GnuCash.
+;; It illustrates the basic techniques used to create
+;; new reports for GnuCash.
+
+(define-module (gnucash report test-graphing))
+(use-modules (gnucash main)) ;; FIXME: delete after we finish modularizing.
+(use-modules (gnucash gnc-module))
+
+(debug-enable 'debug)
+(debug-enable 'backtrace)
+
+(gnc:module-load "gnucash/report/report-system" 0)
+(gnc:module-load "gnucash/gnome-utils" 0) ;for gnc:html-build-url
+
+(define (simple-pie-chart)
+ (let ((chart (gnc:make-html-piechart)))
+ (gnc:html-piechart-set-title! chart "Pie Chart Title")
+ (gnc:html-piechart-set-subtitle! chart "Pie Chart SubTitle")
+ (gnc:html-piechart-set-width! chart 320)
+ (gnc:html-piechart-set-height! chart 240)
+ (gnc:html-piechart-set-data! chart '(25 45 30))
+ (gnc:html-piechart-set-labels! chart '("foo" "bar" "baz"))
+ (gnc:html-piechart-set-colors! chart (gnc:assign-colors 3))
+ chart
+ )
+)
+
+(define (simple-bar-chart)
+ (let ((chart (gnc:make-html-barchart))
+ (text (gnc:make-html-text (gnc:html-markup-p "[bar goes here]"))))
+ (gnc:html-barchart-set-title! chart "Bar Chart Title")
+ (gnc:html-barchart-set-subtitle! chart "Bar Chart SubTitle")
+ (gnc:html-barchart-append-row! chart '(25 45 30))
+ (gnc:html-barchart-append-row! chart '(75 55 70))
+ (gnc:html-barchart-set-width! chart 320)
+ (gnc:html-barchart-set-height! chart 240)
+ (gnc:html-barchart-set-row-labels! chart '("row1" "row2"))
+ (gnc:html-barchart-set-col-labels! chart '("foo" "bar" "baz"))
+ (gnc:html-barchart-set-col-colors! chart (gnc:assign-colors 3))
+ chart))
+
+(define (simple-scatter-chart)
+ (let ((chart (gnc:make-html-scatter))
+ (text (gnc:make-html-text (gnc:html-markup-p "[scatter goes here]"))))
+ (gnc:html-scatter-set-title! chart "Scatter Title")
+ (gnc:html-scatter-set-subtitle! chart "Sctatter SubTitle")
+ (gnc:html-scatter-add-datapoint! chart '(25 75))
+ (gnc:html-scatter-add-datapoint! chart '(45 55))
+ (gnc:html-scatter-add-datapoint! chart '(70 30))
+ (gnc:html-scatter-set-width! chart 320)
+ (gnc:html-scatter-set-height! chart 240)
+ (gnc:html-scatter-set-markercolor! chart (car (gnc:assign-colors 1)))
+ chart))
+
+(define (options-generator)
+ (let* ((options (gnc:new-options)))
+ (gnc:options-set-default-section options "Test Graphing")
+ options)
+ )
+
+;; This is the rendering function. It accepts a database of options
+;; and generates an object of type <html-document>. See the file
+;; report-html.txt for documentation; the file report-html.scm
+;; includes all the relevant Scheme code. The option database passed
+;; to the function is one created by the options-generator function
+;; defined above.
+(define (test-graphing-renderer report-obj)
+ ;; These are some helper functions for looking up option values.
+ (define (get-op section name)
+ (gnc:lookup-option (gnc:report-options report-obj) section name))
+
+ (define (op-value section name)
+ (gnc:option-value (get-op section name)))
+
+ (let ((document (gnc:make-html-document)))
+
+ (gnc:html-document-set-title! document (_ "Graphs"))
+
+ (gnc:html-document-add-object!
+ document
+ (gnc:make-html-text
+ (gnc:html-markup-p
+ (gnc:html-markup/format
+ (_ "Sample graphs:")))))
+
+ (gnc:html-document-add-object!
+ document
+ (gnc:make-html-text (gnc:html-markup-p "Pie:")))
+ (gnc:html-document-add-object! document (simple-pie-chart))
+
+ (gnc:html-document-add-object!
+ document
+ (gnc:make-html-text (gnc:html-markup-p "Bar:")))
+ (gnc:html-document-add-object! document (simple-bar-chart))
+
+ (gnc:html-document-add-object!
+ document
+ (gnc:make-html-text (gnc:html-markup-p "Scatter:")))
+ (gnc:html-document-add-object! document (simple-scatter-chart))
+
+ (gnc:html-document-add-object!
+ document
+ (gnc:make-html-text
+ (gnc:html-markup-p (_ "Done."))))
+
+ document
+ )
+ )
+
+;; Here we define the actual report with gnc:define-report
+(gnc:define-report
+
+ ;; The version of this report.
+ 'version 1
+
+ ;; The name of this report. This will be used, among other things,
+ ;; for making its menu item in the main menu. You need to use the
+ ;; untranslated value here!
+ 'name (N_ "Test Graphing")
+
+ ;; The name in the menu
+ ;; (only necessary if it differs from the name)
+ 'menu-name (N_ "Sample graphs.")
+
+ ;; A tip that is used to provide additional information about the
+ ;; report to the user.
+ 'menu-tip (N_ "Sample graphs.")
+
+ ;; A path describing where to put the report in the menu system.
+ ;; In this case, it's going under the utility menu.
+ 'menu-path (list gnc:menuname-utility)
+
+ ;; The options generator function defined above.
+ 'options-generator options-generator
+ ;; 'options-generator gnc:new-options
+
+ ;; The rendering function defined above.
+ 'renderer test-graphing-renderer)
More information about the gnucash-changes
mailing list