[Gnucash-changes] Initial libgoffice "port".

Joshua Sled jsled at cvs.gnucash.org
Thu Feb 10 20:23:40 EST 2005


Log Message:
-----------
Initial libgoffice "port".

Tags:
----
g2-gog-integ

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

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:
        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/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

Revision Data
-------------
--- /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/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 (&timestamp);
+
+	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 (&current_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/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/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/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/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 */
Index: gnc-main-window.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome/Attic/gnc-main-window.c,v
retrieving revision 1.1.2.33
retrieving revision 1.1.2.33.2.1
diff -Lsrc/gnome/gnc-main-window.c -Lsrc/gnome/gnc-main-window.c -u -r1.1.2.33 -r1.1.2.33.2.1
--- src/gnome/gnc-main-window.c
+++ src/gnome/gnc-main-window.c
@@ -1210,7 +1210,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 ) );
--- /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">&lt;b&gt;Error category&lt;/b&gt;</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">&lt;b&gt;Style&lt;/b&gt;</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">&lt;b&gt;Values&lt;/b&gt;</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,870 @@
+/* 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;
+
+	printf( "drawing a 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,
+											     &copy_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;
+
+	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">&lt;b&gt;Fill&lt;/b&gt;</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">&lt;b&gt;Outline&lt;/b&gt;</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">&lt;b&gt;Line&lt;/b&gt;</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">&lt;b&gt;Marker&lt;/b&gt;</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">&lt;b&gt;_Plot type&lt;/b&gt;</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">&lt;b&gt;_Subtype&lt;/b&gt;</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">&lt;b&gt;Description&lt;/b&gt;</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">&lt;b&gt;Position&lt;/b&gt;</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">&lt;b&gt;Mapping&lt;/b&gt;</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">&lt;b&gt;Major ticks&lt;/b&gt;</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">&lt;b&gt;Minor ticks&lt;/b&gt;</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 = &GTK_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/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/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.36
retrieving revision 1.359.2.36.2.1
diff -Lconfigure.in -Lconfigure.in -u -r1.359.2.36 -r1.359.2.36.2.1
--- 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
@@ -348,6 +355,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)
+gnumeric_icondir='${GNC_SHAREDIR}/pixmaps'
+AC_SUBST(gnumeric_icondir)
+
+
+# /GOG
+
+### --------------------------------------------------------------------------
 ### Variables
 ### Set up all the initial variable values...
 
@@ -872,7 +921,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=
@@ -887,7 +936,6 @@
     AC_SUBST(GLADE_CFLAGS)
     AC_SUBST(GLADE_LIBS)
 
-
     # check for gtkhtml 3.0 and 3.1
     PKG_CHECK_MODULES(GTKHTML, libgtkhtml-3.1 ,,gtkhtml=0)
     if test $gtkhtml
@@ -1025,16 +1073,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
@@ -1100,6 +1151,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.159
retrieving revision 1.1487.2.159.2.1
diff -LChangeLog -LChangeLog -u -r1.1487.2.159 -r1.1487.2.159.2.1
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,7 @@
+2005-02-10  Joshua Sled  <jsled at asynchronous.org>
+
+	* lib/goffice/ : Initial import of libgoffice "port".
+
 2005-01-22  Derek Atkins  <derek at ihtfp.com>
 
 	Stephen Evanchik's patch to convert GncItemEdit to GObject/GLib
Index: HACKING
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/HACKING,v
retrieving revision 1.5.4.2
retrieving revision 1.5.4.2.2.1
diff -LHACKING -LHACKING -u -r1.5.4.2 -r1.5.4.2.2.1
--- HACKING
+++ HACKING
@@ -126,7 +126,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.34
retrieving revision 1.1.2.34.2.1
diff -LGNOME2_STATUS -LGNOME2_STATUS -u -r1.1.2.34 -r1.1.2.34.2.1
--- GNOME2_STATUS
+++ GNOME2_STATUS
@@ -76,6 +76,8 @@
 ------------------------------------------------------------------------------
 | Main Window   | - New registers should open in new top-level  |            |
 |               |   windows.                                    |            |
+|               |   - jsled believes we can do without this for |            |
+|               |     the 2.0 release                           |            |
 |               | - Summary bar isn't shown, even when option in|            |
 |               |   view is checked.                            |            |
 ------------------------------------------------------------------------------
@@ -149,6 +151,7 @@
 | FUNCTIONAL    |     Gnumeric regarding Gnume Office Graphong  |            |
 |               |     [GOG] to be factored out of gnumeric into |            |
 |               |     libgoffice.                               |            |
+|               |   - See <#GOG>, below.                        |            |
 |               | - Save/restore in unknown state; believed to  |            |
 |               |     crash on re-start.                        |            |
 |               | - First report sets reportname text for tab   |            |
@@ -218,3 +221,145 @@
 |               |   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
--- /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 */
Index: Makefile.am
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/lib/Makefile.am,v
retrieving revision 1.6.4.2
retrieving revision 1.6.4.2.2.1
diff -Llib/Makefile.am -Llib/Makefile.am -u -r1.6.4.2 -r1.6.4.2.2.1
--- lib/Makefile.am
+++ lib/Makefile.am
@@ -1,4 +1,4 @@
 
-SUBDIRS = libc guile-www srfi egg
+SUBDIRS = libc guile-www srfi egg goffice
 
 EXTRA_DIST = README guppi-legend.patch
--- /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/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,33 @@
+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
+
+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 = 	\
+	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,292 @@
+#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_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 */
Index: gnc-trace.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-trace.c,v
retrieving revision 1.2.20.9
retrieving revision 1.2.20.9.2.1
diff -Lsrc/engine/gnc-trace.c -Lsrc/engine/gnc-trace.c -u -r1.2.20.9 -r1.2.20.9.2.1
--- 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 */
--- /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
--- /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/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, &paragraph, 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 = &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_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_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/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 */
Index: gnc-gnome-utils.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/gnome-utils/gnc-gnome-utils.c,v
retrieving revision 1.2.4.6
retrieving revision 1.2.4.6.2.1
diff -Lsrc/gnome-utils/gnc-gnome-utils.c -Lsrc/gnome-utils/gnc-gnome-utils.c -u -r1.2.4.6 -r1.2.4.6.2.1
--- 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 "egg-action-group.h"
 #include "gnc-gnome-utils.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,374 @@
+#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);
+}
+
+/*
+ * 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)
+{
+  GtkWidget *widget;
+  GogObject *graph, *chart, *tmp, *legend;
+  GogPlot *plot;
+  GogSeries *series;
+  GOData *labelData, *sliceData;
+  GogRenderer *pixbufRend;
+  GdkPixbuf *buf;
+  gboolean updateStatus;
+  gint w, h;
+  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),
+		//"horizontal",			TRUE,
+		"vary_style_by_element",	TRUE,
+		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);
+    /*
+    gog_style_set_font (GOG_STYLED_OBJECT (tmp)->style,
+                        pango_font_description_from_string ("Sans Bold 10"));
+    */
+  }
+
+  legend = gog_object_add_by_name( chart, "Legend", NULL );
+
+  // Note that this shouldn't be necessary as per discussion with Jody...
+  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, 320., 240., 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), 320, 240, 1. );
+  buf = gog_renderer_pixbuf_get(GOG_RENDERER_PIXBUF(pixbufRend));
+  widget = gtk_image_new_from_pixbuf( buf );
+  gtk_widget_set_size_request( widget, 320, 240 );
+  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);
+
+  PINFO( "piechart rendering." );
+  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)
+{
+  GtkWidget *widget;
+  GogObject *graph, *chart, *tmp, *legend;
+  GogPlot *plot;
+  GogSeries *series;
+  GOData *labelData, *sliceData;
+  GogRenderer *pixbufRend;
+  GdkPixbuf *buf;
+  gboolean updateStatus;
+  gint w, h;
+  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, stacked;
+
+  // First, parse data from the text-ized params.
+  {
+    char *datarowsStr, *datacolsStr, *dataStr, *colLabelsStr, *rowLabelsStr, *colColorsStr;
+
+    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");
+    g_return_val_if_fail( datarowsStr != NULL
+                          && datacolsStr != NULL
+                          && dataStr != NULL
+                          && colLabelsStr != NULL
+                          && rowLabelsStr != NULL
+                          && colColorsStr != NULL, FALSE );
+    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 [cols]
+  // elements => segments [rows]
+  plot = gog_plot_new_by_name( "GogBarColPlot" );
+  if ( !plot )
+  {
+    // FIXME - log betterer
+    printf( "plugin not loaded" );
+    return FALSE;
+  }
+  g_object_set (G_OBJECT (plot),
+		"horizontal",			TRUE,
+		"vary_style_by_element",	TRUE,
+		NULL);
+  gog_object_add_by_name( chart, "Plot", GOG_OBJECT(plot) );
+  series = gog_plot_new_series( plot );
+  labelData = go_data_vector_str_new( row_labels, datarows );
+  gog_series_set_dim( series, 0, labelData, NULL );
+  go_data_emit_changed (GO_DATA (labelData));
+
+  {
+    int i;
+    for ( i = 0; i < datacols; i++ )
+    {
+      sliceData = go_data_vector_val_new( data + (i*datarows), datarows );
+      gog_series_set_dim( series, i+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);
+    /*
+    gog_style_set_font (GOG_STYLED_OBJECT (tmp)->style,
+                        pango_font_description_from_string ("Sans Bold 10"));
+    */
+  }
+
+  legend = gog_object_add_by_name( chart, "Legend", NULL );
+
+  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, 320., 240., 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), 320, 240, 1. );
+  buf = gog_renderer_pixbuf_get(GOG_RENDERER_PIXBUF(pixbufRend));
+  widget = gtk_image_new_from_pixbuf( buf );
+  gtk_widget_set_size_request( widget, 320, 240 );
+  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);
+
+  PINFO( "piechart rendering." );
+  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.9.2.1
diff -Lsrc/gnome-utils/gnc-html.c -Lsrc/gnome-utils/gnc-html.c -u -r1.23.4.9 -r1.23.4.9.2.1
--- 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.24
retrieving revision 1.48.2.24.2.1
diff -Lsrc/gnome-utils/Makefile.am -Lsrc/gnome-utils/Makefile.am -u -r1.48.2.24 -r1.48.2.24.2.1
--- src/gnome-utils/Makefile.am
+++ src/gnome-utils/Makefile.am
@@ -15,6 +15,8 @@
   -I${top_srcdir}/src/network-utils \
   -I${top_srcdir}/src/app-utils \
   -I${top_srcdir}/lib/egg \
+  -I${top_srcdir}/lib/goffice \
+  -I${top_srcdir}/lib/goffice/split \
   -I${top_srcdir}/src \
   ${GUILE_INCS} \
   ${LIBGUPPI_CFLAGS} \
@@ -23,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 \
@@ -58,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 \
@@ -109,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 \
@@ -149,6 +154,7 @@
   ${top_builddir}/src/network-utils/libgncmod-network-utils.la \
   ${top_builddir}/src/app-utils/libgncmod-app-utils.la \
   ${top_builddir}/lib/egg/libegg.la \
+  ${top_builddir}/lib/goffice/libgoffice.la \
   ${GUILE_LIBS} \
   ${LIBGUPPI_LIBS} \
   ${GNOME_LIBS} \
@@ -158,7 +164,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


More information about the gnucash-changes mailing list