[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 (×tamp);
+
+ item->timestamp = timestamp;
+}
+
+time_t
+egg_recent_item_get_timestamp (const EggRecentItem *item)
+{
+ return item->timestamp;
+}
+
+G_CONST_RETURN GList *
+egg_recent_item_get_groups (const EggRecentItem *item)
+{
+ return item->groups;
+}
+
+gboolean
+egg_recent_item_in_group (const EggRecentItem *item, const gchar *group_name)
+{
+ GList *tmp;
+
+ tmp = item->groups;
+ while (tmp != NULL) {
+ gchar *val = (gchar *)tmp->data;
+
+ if (strcmp (group_name, val) == 0)
+ return TRUE;
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
+
+void
+egg_recent_item_add_group (EggRecentItem *item, const gchar *group_name)
+{
+ g_return_if_fail (group_name != NULL);
+
+ if (!egg_recent_item_in_group (item, group_name))
+ item->groups = g_list_append (item->groups, g_strdup (group_name));
+}
+
+void
+egg_recent_item_remove_group (EggRecentItem *item, const gchar *group_name)
+{
+ GList *tmp;
+
+ g_return_if_fail (group_name != NULL);
+
+ tmp = item->groups;
+ while (tmp != NULL) {
+ gchar *val = (gchar *)tmp->data;
+
+ if (strcmp (group_name, val) == 0) {
+ item->groups = g_list_remove (item->groups,
+ val);
+ g_free (val);
+ break;
+ }
+
+ tmp = tmp->next;
+ }
+}
+
+void
+egg_recent_item_set_private (EggRecentItem *item, gboolean priv)
+{
+ item->private_data = priv;
+}
+
+gboolean
+egg_recent_item_get_private (const EggRecentItem *item)
+{
+ return item->private_data;
+}
+
+GType
+egg_recent_item_get_type (void)
+{
+ static GType boxed_type = 0;
+
+ if (!boxed_type) {
+ boxed_type = g_boxed_type_register_static ("EggRecentItem",
+ (GBoxedCopyFunc)egg_recent_item_ref,
+ (GBoxedFreeFunc)egg_recent_item_unref);
+ }
+
+ return boxed_type;
+}
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-model.c
@@ -0,0 +1,1784 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include <goffice/goffice-config.h>
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * James Willcox <jwillcox at cs.indiana.edu>
+ */
+
+#ifdef HAVE_CONFIG_H
+/* #include <config.h> */
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <gtk/gtk.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include <gconf/gconf-client.h>
+#include "egg-recent-model.h"
+#include "egg-recent-item.h"
+
+#define EGG_RECENT_MODEL_FILE_PATH "/.recently-used"
+#define EGG_RECENT_MODEL_BUFFER_SIZE 8192
+
+#define EGG_RECENT_MODEL_MAX_ITEMS 500
+#define EGG_RECENT_MODEL_DEFAULT_LIMIT 10
+#define EGG_RECENT_MODEL_TIMEOUT_LENGTH 200
+
+#define EGG_RECENT_MODEL_KEY_DIR "/desktop/gnome/recent_files"
+#define EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY EGG_RECENT_MODEL_KEY_DIR "/default_limit"
+#define EGG_RECENT_MODEL_EXPIRE_KEY EGG_RECENT_MODEL_KEY_DIR "/expire"
+
+struct _EggRecentModelPrivate {
+ GSList *mime_filter_values; /* list of mime types we allow */
+ GSList *group_filter_values; /* list of groups we allow */
+ GSList *scheme_filter_values; /* list of URI schemes we allow */
+
+ EggRecentModelSort sort_type; /* type of sorting to be done */
+
+ int limit; /* soft limit for length of the list */
+ int expire_days; /* number of days to hold an item */
+
+ char *path; /* path to the file we store stuff in */
+
+ GHashTable *monitors;
+
+ GnomeVFSMonitorHandle *monitor;
+
+ GConfClient *client;
+ gboolean use_default_limit;
+
+ guint limit_change_notify_id;
+ guint expiration_change_notify_id;
+
+ guint changed_timeout;
+};
+
+/* signals */
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static GType model_signals[LAST_SIGNAL] = { 0 };
+
+/* properties */
+enum {
+ PROP_BOGUS,
+ PROP_MIME_FILTERS,
+ PROP_GROUP_FILTERS,
+ PROP_SCHEME_FILTERS,
+ PROP_SORT_TYPE,
+ PROP_LIMIT
+};
+
+typedef struct {
+ GSList *states;
+ GList *items;
+ EggRecentItem *current_item;
+}ParseInfo;
+
+typedef enum {
+ STATE_START,
+ STATE_RECENT_FILES,
+ STATE_RECENT_ITEM,
+ STATE_URI,
+ STATE_MIME_TYPE,
+ STATE_TIMESTAMP,
+ STATE_PRIVATE,
+ STATE_GROUPS,
+ STATE_GROUP
+} ParseState;
+
+typedef struct _ChangedData {
+ EggRecentModel *model;
+ GList *list;
+}ChangedData;
+
+#define TAG_RECENT_FILES "RecentFiles"
+#define TAG_RECENT_ITEM "RecentItem"
+#define TAG_URI "URI"
+#define TAG_MIME_TYPE "Mime-Type"
+#define TAG_TIMESTAMP "Timestamp"
+#define TAG_PRIVATE "Private"
+#define TAG_GROUPS "Groups"
+#define TAG_GROUP "Group"
+
+static void start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error);
+
+static void end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error);
+
+static void text_handler (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error);
+
+static void error_handler (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data);
+
+static GMarkupParser parser = {start_element_handler, end_element_handler,
+ text_handler,
+ NULL,
+ error_handler};
+
+static gboolean
+egg_recent_model_string_match (const GSList *list, const gchar *str)
+{
+ const GSList *tmp;
+
+ if (list == NULL || str == NULL)
+ return TRUE;
+
+ tmp = list;
+
+ while (tmp) {
+ if (g_pattern_match_string (tmp->data, str))
+ return TRUE;
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+egg_recent_model_write_raw (EggRecentModel *model, FILE *file,
+ const gchar *content)
+{
+ int len;
+ int fd;
+ struct stat sbuf;
+
+ rewind (file);
+
+ len = strlen (content);
+ fd = fileno (file);
+
+ if (fstat (fd, &sbuf) < 0)
+ g_warning ("Couldn't stat XML document.");
+
+ if ((off_t)len < sbuf.st_size) {
+ ftruncate (fd, len);
+ }
+
+ if (fputs (content, file) == EOF)
+ return FALSE;
+
+ fsync (fd);
+ rewind (file);
+
+ return TRUE;
+}
+
+static GList *
+egg_recent_model_delete_from_list (GList *list,
+ const gchar *uri)
+{
+ GList *tmp;
+
+ if (!uri)
+ return list;
+
+ tmp = list;
+
+ while (tmp) {
+ EggRecentItem *item = tmp->data;
+ GList *next;
+
+ next = tmp->next;
+
+ if (!strcmp (egg_recent_item_peek_uri (item), uri)) {
+ egg_recent_item_unref (item);
+
+ list = g_list_remove_link (list, tmp);
+ g_list_free_1 (tmp);
+ }
+
+ tmp = next;
+ }
+
+ return list;
+}
+
+static void
+egg_recent_model_add_new_groups (EggRecentItem *item,
+ EggRecentItem *upd_item)
+{
+ const GList *tmp;
+
+ tmp = egg_recent_item_get_groups (upd_item);
+
+ while (tmp) {
+ char *group = tmp->data;
+
+ if (!egg_recent_item_in_group (item, group))
+ egg_recent_item_add_group (item, group);
+
+ tmp = tmp->next;
+ }
+}
+
+static gboolean
+egg_recent_model_update_item (GList *items, EggRecentItem *upd_item)
+{
+ GList *tmp;
+ const char *uri;
+
+ uri = egg_recent_item_peek_uri (upd_item);
+
+ tmp = items;
+
+ while (tmp) {
+ EggRecentItem *item = tmp->data;
+
+ if (gnome_vfs_uris_match (egg_recent_item_peek_uri (item), uri)) {
+ egg_recent_item_set_timestamp (item, (time_t) -1);
+
+ egg_recent_model_add_new_groups (item, upd_item);
+
+ return TRUE;
+ }
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
+
+static gchar *
+egg_recent_model_read_raw (EggRecentModel *model, FILE *file)
+{
+ GString *string;
+ char buf[EGG_RECENT_MODEL_BUFFER_SIZE];
+
+ rewind (file);
+
+ string = g_string_new (NULL);
+ while (fgets (buf, EGG_RECENT_MODEL_BUFFER_SIZE, file)) {
+ string = g_string_append (string, buf);
+ }
+
+ rewind (file);
+
+ return g_string_free (string, FALSE);
+}
+
+
+
+static void
+parse_info_init (ParseInfo *info)
+{
+ info->states = g_slist_prepend (NULL, STATE_START);
+ info->items = NULL;
+}
+
+static void
+parse_info_free (ParseInfo *info)
+{
+ g_slist_free (info->states);
+}
+
+static void
+push_state (ParseInfo *info,
+ ParseState state)
+{
+ info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
+}
+
+static void
+pop_state (ParseInfo *info)
+{
+ g_return_if_fail (info->states != NULL);
+
+ info->states = g_slist_remove (info->states, info->states->data);
+}
+
+static ParseState
+peek_state (ParseInfo *info)
+{
+ g_return_val_if_fail (info->states != NULL, STATE_START);
+
+ return GPOINTER_TO_INT (info->states->data);
+}
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+static void
+start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = (ParseInfo *)user_data;
+
+ if (ELEMENT_IS (TAG_RECENT_FILES))
+ push_state (info, STATE_RECENT_FILES);
+ else if (ELEMENT_IS (TAG_RECENT_ITEM)) {
+ info->current_item = egg_recent_item_new ();
+ push_state (info, STATE_RECENT_ITEM);
+ } else if (ELEMENT_IS (TAG_URI))
+ push_state (info, STATE_URI);
+ else if (ELEMENT_IS (TAG_MIME_TYPE))
+ push_state (info, STATE_MIME_TYPE);
+ else if (ELEMENT_IS (TAG_TIMESTAMP))
+ push_state (info, STATE_TIMESTAMP);
+ else if (ELEMENT_IS (TAG_PRIVATE)) {
+ push_state (info, STATE_PRIVATE);
+ egg_recent_item_set_private (info->current_item, TRUE);
+ } else if (ELEMENT_IS (TAG_GROUPS))
+ push_state (info, STATE_GROUPS);
+ else if (ELEMENT_IS (TAG_GROUP))
+ push_state (info, STATE_GROUP);
+}
+
+static gint
+list_compare_func_mru (gpointer a, gpointer b)
+{
+ EggRecentItem *item_a = (EggRecentItem *)a;
+ EggRecentItem *item_b = (EggRecentItem *)b;
+
+ return item_a->timestamp < item_b->timestamp;
+}
+
+static gint
+list_compare_func_lru (gpointer a, gpointer b)
+{
+ EggRecentItem *item_a = (EggRecentItem *)a;
+ EggRecentItem *item_b = (EggRecentItem *)b;
+
+ return item_a->timestamp > item_b->timestamp;
+}
+
+
+
+static void
+end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = (ParseInfo *)user_data;
+
+ switch (peek_state (info)) {
+ case STATE_RECENT_ITEM:
+ info->items = g_list_append (info->items,
+ info->current_item);
+ if (info->current_item->uri == NULL ||
+ strlen (info->current_item->uri) == 0)
+ g_warning ("URI NOT LOADED");
+ break;
+ default:
+ break;
+ }
+
+ pop_state (info);
+}
+
+static void
+text_handler (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ ParseInfo *info = (ParseInfo *)user_data;
+
+ switch (peek_state (info)) {
+ case STATE_START:
+ case STATE_RECENT_FILES:
+ case STATE_RECENT_ITEM:
+ case STATE_PRIVATE:
+ case STATE_GROUPS:
+ break;
+ case STATE_URI:
+ egg_recent_item_set_uri (info->current_item, text);
+ break;
+ case STATE_MIME_TYPE:
+ egg_recent_item_set_mime_type (info->current_item,
+ text);
+ break;
+ case STATE_TIMESTAMP:
+ egg_recent_item_set_timestamp (info->current_item,
+ (time_t)atoi (text));
+ break;
+ case STATE_GROUP:
+ egg_recent_item_add_group (info->current_item,
+ text);
+ break;
+ }
+
+}
+
+static void
+error_handler (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data)
+{
+ g_warning ("Error in parse: %s", error->message);
+}
+
+static void
+egg_recent_model_enforce_limit (GList *list, int limit)
+{
+ int len;
+ GList *end;
+
+ /* limit < 0 means unlimited */
+ if (limit <= 0)
+ return;
+
+ len = g_list_length (list);
+
+ if (len > limit) {
+ GList *next;
+
+ end = g_list_nth (list, limit-1);
+ next = end->next;
+
+ end->next = NULL;
+
+ EGG_RECENT_ITEM_LIST_UNREF (next);
+ }
+}
+
+static GList *
+egg_recent_model_sort (EggRecentModel *model, GList *list)
+{
+ switch (model->priv->sort_type) {
+ case EGG_RECENT_MODEL_SORT_MRU:
+ list = g_list_sort (list,
+ (GCompareFunc)list_compare_func_mru);
+ break;
+ case EGG_RECENT_MODEL_SORT_LRU:
+ list = g_list_sort (list,
+ (GCompareFunc)list_compare_func_lru);
+ break;
+ case EGG_RECENT_MODEL_SORT_NONE:
+ break;
+ }
+
+ return list;
+}
+
+static gboolean
+egg_recent_model_group_match (EggRecentItem *item, GSList *groups)
+{
+ GSList *tmp;
+
+ tmp = groups;
+
+ while (tmp != NULL) {
+ const gchar * group = (const gchar *)tmp->data;
+
+ if (egg_recent_item_in_group (item, group))
+ return TRUE;
+
+ tmp = tmp->next;
+ }
+
+ return FALSE;
+}
+
+static GList *
+egg_recent_model_filter (EggRecentModel *model,
+ GList *list)
+{
+ EggRecentItem *item;
+ GList *newlist = NULL;
+ gchar *mime_type;
+ gchar *uri;
+
+ g_return_val_if_fail (list != NULL, NULL);
+
+ while (list) {
+ gboolean pass_mime_test = FALSE;
+ gboolean pass_group_test = FALSE;
+ gboolean pass_scheme_test = FALSE;
+ item = (EggRecentItem *)list->data;
+ list = list->next;
+
+ uri = egg_recent_item_get_uri (item);
+
+ /* filter by mime type */
+ if (model->priv->mime_filter_values != NULL) {
+ mime_type = egg_recent_item_get_mime_type (item);
+
+ if (egg_recent_model_string_match
+ (model->priv->mime_filter_values,
+ mime_type))
+ pass_mime_test = TRUE;
+
+ g_free (mime_type);
+ } else
+ pass_mime_test = TRUE;
+
+ /* filter by group */
+ if (pass_mime_test && model->priv->group_filter_values != NULL) {
+ if (egg_recent_model_group_match
+ (item, model->priv->group_filter_values))
+ pass_group_test = TRUE;
+ } else if (egg_recent_item_get_private (item)) {
+ pass_group_test = FALSE;
+ } else
+ pass_group_test = TRUE;
+
+ /* filter by URI scheme */
+ if (pass_mime_test && pass_group_test &&
+ model->priv->scheme_filter_values != NULL) {
+ gchar *scheme;
+
+ scheme = gnome_vfs_get_uri_scheme (uri);
+
+ if (egg_recent_model_string_match
+ (model->priv->scheme_filter_values, scheme))
+ pass_scheme_test = TRUE;
+
+ g_free (scheme);
+ } else
+ pass_scheme_test = TRUE;
+
+ if (pass_mime_test && pass_group_test && pass_scheme_test)
+ newlist = g_list_prepend (newlist, item);
+
+ g_free (uri);
+ }
+
+ if (newlist) {
+ newlist = g_list_reverse (newlist);
+ g_list_free (list);
+ }
+
+
+ return newlist;
+}
+
+
+
+#if 0
+static void
+egg_recent_model_monitor_list_cb (GnomeVFSMonitorHandle *handle,
+ const gchar *monitor_uri,
+ const gchar *info_uri,
+ GnomeVFSMonitorEventType event_type,
+ gpointer user_data)
+{
+ EggRecentModel *model;
+
+ model = EGG_RECENT_MODEL (user_data);
+
+ if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
+ egg_recent_model_delete (model, monitor_uri);
+ g_hash_table_remove (model->priv->monitors, monitor_uri);
+ }
+}
+
+
+
+static void
+egg_recent_model_monitor_list (EggRecentModel *model, GList *list)
+{
+ GList *tmp;
+
+ tmp = list;
+ while (tmp) {
+ EggRecentItem *item = (EggRecentItem *)tmp->data;
+ GnomeVFSMonitorHandle *handle;
+ GnomeVFSResult res;
+ gchar *uri;
+
+ tmp = tmp->next;
+
+ uri = egg_recent_item_get_uri (item);
+ if (g_hash_table_lookup (model->priv->monitors, uri)) {
+ /* already monitoring this one */
+ g_free (uri);
+ continue;
+ }
+
+ res = gnome_vfs_monitor_add (&handle, uri,
+ GNOME_VFS_MONITOR_FILE,
+ egg_recent_model_monitor_list_cb,
+ model);
+
+ if (res == GNOME_VFS_OK)
+ g_hash_table_insert (model->priv->monitors, uri, handle);
+ else
+ g_free (uri);
+ }
+}
+#endif
+
+
+static gboolean
+egg_recent_model_changed_timeout (EggRecentModel *model)
+{
+ egg_recent_model_changed (model);
+
+ return FALSE;
+}
+
+static void
+egg_recent_model_monitor_cb (GnomeVFSMonitorHandle *handle,
+ const gchar *monitor_uri,
+ const gchar *info_uri,
+ GnomeVFSMonitorEventType event_type,
+ gpointer user_data)
+{
+ EggRecentModel *model;
+
+ g_return_if_fail (user_data != NULL);
+ g_return_if_fail (EGG_IS_RECENT_MODEL (user_data));
+ model = EGG_RECENT_MODEL (user_data);
+
+ if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) {
+ if (model->priv->changed_timeout > 0) {
+ g_source_remove (model->priv->changed_timeout);
+ }
+
+ model->priv->changed_timeout = g_timeout_add (
+ EGG_RECENT_MODEL_TIMEOUT_LENGTH,
+ (GSourceFunc)egg_recent_model_changed_timeout,
+ model);
+ }
+}
+
+static void
+egg_recent_model_monitor (EggRecentModel *model, gboolean should_monitor)
+{
+ if (should_monitor && model->priv->monitor == NULL) {
+ char *uri;
+
+ uri = gnome_vfs_get_uri_from_local_path (model->priv->path);
+
+ gnome_vfs_monitor_add (&model->priv->monitor,
+ uri,
+ GNOME_VFS_MONITOR_FILE,
+ egg_recent_model_monitor_cb,
+ model);
+
+ g_free (uri);
+
+ /* if the above fails, don't worry about it.
+ * local notifications will still happen
+ */
+
+ } else if (!should_monitor && model->priv->monitor != NULL) {
+ gnome_vfs_monitor_cancel (model->priv->monitor);
+ model->priv->monitor = NULL;
+ }
+}
+
+static void
+egg_recent_model_set_limit_internal (EggRecentModel *model, int limit)
+{
+ model->priv->limit = limit;
+
+ if (limit <= 0)
+ egg_recent_model_monitor (model, FALSE);
+ else {
+ egg_recent_model_monitor (model, TRUE);
+ egg_recent_model_changed (model);
+ }
+}
+
+static GList *
+egg_recent_model_read (EggRecentModel *model, FILE *file)
+{
+ GList *list=NULL;
+ gchar *content;
+ GMarkupParseContext *ctx;
+ ParseInfo info;
+ GError *error;
+
+ content = egg_recent_model_read_raw (model, file);
+
+ if (strlen (content) <= 0) {
+ g_free (content);
+ return NULL;
+ }
+
+ parse_info_init (&info);
+
+ ctx = g_markup_parse_context_new (&parser, 0, &info, NULL);
+
+ error = NULL;
+ if (!g_markup_parse_context_parse (ctx, content, strlen (content),
+ &error)) {
+ g_warning (error->message);
+ g_error_free (error);
+ error = NULL;
+ goto out;
+ }
+
+ error = NULL;
+ if (!g_markup_parse_context_end_parse (ctx, &error))
+ goto out;
+
+ g_markup_parse_context_free (ctx);
+out:
+ list = info.items;
+
+ parse_info_free (&info);
+
+ g_free (content);
+
+ /*
+ g_print ("Total items: %d\n", g_list_length (list));
+ */
+
+ return list;
+}
+
+
+static gboolean
+egg_recent_model_write (EggRecentModel *model, FILE *file, GList *list)
+{
+ GString *string;
+ gchar *data;
+ EggRecentItem *item;
+ const GList *groups;
+ int i;
+ int ret;
+
+ string = g_string_new ("<?xml version=\"1.0\"?>\n");
+ string = g_string_append (string, "<" TAG_RECENT_FILES ">\n");
+
+ i=0;
+ while (list) {
+ gchar *uri;
+ gchar *mime_type;
+ gchar *escaped_uri;
+ time_t timestamp;
+ item = (EggRecentItem *)list->data;
+
+
+ uri = egg_recent_item_get_uri_utf8 (item);
+ escaped_uri = g_markup_escape_text (uri,
+ strlen (uri));
+ g_free (uri);
+
+ mime_type = egg_recent_item_get_mime_type (item);
+ timestamp = egg_recent_item_get_timestamp (item);
+
+ string = g_string_append (string, " <" TAG_RECENT_ITEM ">\n");
+
+ g_string_append_printf (string,
+ " <" TAG_URI ">%s</" TAG_URI ">\n", escaped_uri);
+
+ if (mime_type)
+ g_string_append_printf (string,
+ " <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n", mime_type);
+ else
+ g_string_append_printf (string,
+ " <" TAG_MIME_TYPE "></" TAG_MIME_TYPE ">\n");
+
+
+ g_string_append_printf (string,
+ " <" TAG_TIMESTAMP ">%d</" TAG_TIMESTAMP ">\n", (int)timestamp);
+
+ if (egg_recent_item_get_private (item))
+ string = g_string_append (string,
+ " <" TAG_PRIVATE "/>\n");
+
+ /* write the groups */
+ string = g_string_append (string,
+ " <" TAG_GROUPS ">\n");
+ groups = egg_recent_item_get_groups (item);
+
+ if (groups == NULL && egg_recent_item_get_private (item))
+ g_warning ("Item with URI \"%s\" marked as private, but"
+ " does not belong to any groups.\n", uri);
+
+ while (groups) {
+ const gchar *group = (const gchar *)groups->data;
+ gchar *escaped_group;
+
+ escaped_group = g_markup_escape_text (group, strlen(group));
+
+ g_string_append_printf (string,
+ " <" TAG_GROUP ">%s</" TAG_GROUP ">\n",
+ escaped_group);
+
+ g_free (escaped_group);
+
+ groups = groups->next;
+ }
+
+ string = g_string_append (string, " </" TAG_GROUPS ">\n");
+
+ string = g_string_append (string,
+ " </" TAG_RECENT_ITEM ">\n");
+
+ g_free (mime_type);
+ g_free (escaped_uri);
+
+ list = list->next;
+ i++;
+ }
+
+ string = g_string_append (string, "</" TAG_RECENT_FILES ">");
+
+ data = g_string_free (string, FALSE);
+
+ ret = egg_recent_model_write_raw (model, file, data);
+
+ g_free (data);
+
+ return ret;
+}
+
+static FILE *
+egg_recent_model_open_file (EggRecentModel *model)
+{
+ FILE *file;
+ mode_t prev_umask;
+
+ file = fopen (model->priv->path, "r+");
+ if (file == NULL) {
+ /* be paranoid */
+ prev_umask = umask (077);
+
+ file = fopen (model->priv->path, "w+");
+
+ umask (prev_umask);
+
+ g_return_val_if_fail (file != NULL, NULL);
+ }
+
+ return file;
+}
+
+static gboolean
+egg_recent_model_lock_file (FILE *file)
+{
+ int fd;
+ gint try = 5;
+
+ rewind (file);
+ fd = fileno (file);
+
+ /* Attempt to lock the file 5 times,
+ * waiting a random interval (< 1 second)
+ * in between attempts.
+ * We should really be doing asynchronous
+ * locking, but requires substantially larger
+ * changes.
+ */
+
+ while (try > 0)
+ {
+ int rand_interval;
+
+ if (lockf (fd, F_TLOCK, 0) == 0)
+ return TRUE;
+
+ rand_interval = 1 + (int) (10.0 * rand()/(RAND_MAX + 1.0));
+
+ g_usleep (100000 * rand_interval);
+
+ --try;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+egg_recent_model_unlock_file (FILE *file)
+{
+ int fd;
+
+ rewind (file);
+ fd = fileno (file);
+
+ return (lockf (fd, F_ULOCK, 0) == 0) ? TRUE : FALSE;
+}
+
+static void
+egg_recent_model_finalize (GObject *object)
+{
+ EggRecentModel *model = EGG_RECENT_MODEL (object);
+
+ egg_recent_model_monitor (model, FALSE);
+
+
+ g_slist_foreach (model->priv->mime_filter_values,
+ (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free (model->priv->mime_filter_values);
+ model->priv->mime_filter_values = NULL;
+
+ g_slist_foreach (model->priv->scheme_filter_values,
+ (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free (model->priv->scheme_filter_values);
+ model->priv->scheme_filter_values = NULL;
+
+ g_slist_foreach (model->priv->group_filter_values,
+ (GFunc) g_free, NULL);
+ g_slist_free (model->priv->group_filter_values);
+ model->priv->group_filter_values = NULL;
+
+
+ if (model->priv->limit_change_notify_id)
+ gconf_client_notify_remove (model->priv->client,
+ model->priv->limit_change_notify_id);
+ model->priv->expiration_change_notify_id = 0;
+
+ if (model->priv->expiration_change_notify_id)
+ gconf_client_notify_remove (model->priv->client,
+ model->priv->expiration_change_notify_id);
+ model->priv->expiration_change_notify_id = 0;
+
+ g_object_unref (model->priv->client);
+ model->priv->client = NULL;
+
+
+ g_free (model->priv->path);
+ model->priv->path = NULL;
+
+ g_hash_table_destroy (model->priv->monitors);
+ model->priv->monitors = NULL;
+
+
+ g_free (model->priv);
+}
+
+static void
+egg_recent_model_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EggRecentModel *model = EGG_RECENT_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_MIME_FILTERS:
+ model->priv->mime_filter_values =
+ (GSList *)g_value_get_pointer (value);
+ break;
+
+ case PROP_GROUP_FILTERS:
+ model->priv->group_filter_values =
+ (GSList *)g_value_get_pointer (value);
+ break;
+
+ case PROP_SCHEME_FILTERS:
+ model->priv->scheme_filter_values =
+ (GSList *)g_value_get_pointer (value);
+ break;
+
+ case PROP_SORT_TYPE:
+ model->priv->sort_type = g_value_get_int (value);
+ break;
+
+ case PROP_LIMIT:
+ egg_recent_model_set_limit (model,
+ g_value_get_int (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_recent_model_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EggRecentModel *model = EGG_RECENT_MODEL (object);
+
+ switch (prop_id)
+ {
+ case PROP_MIME_FILTERS:
+ g_value_set_pointer (value, model->priv->mime_filter_values);
+ break;
+
+ case PROP_GROUP_FILTERS:
+ g_value_set_pointer (value, model->priv->group_filter_values);
+ break;
+
+ case PROP_SCHEME_FILTERS:
+ g_value_set_pointer (value, model->priv->scheme_filter_values);
+ break;
+
+ case PROP_SORT_TYPE:
+ g_value_set_int (value, model->priv->sort_type);
+ break;
+
+ case PROP_LIMIT:
+ g_value_set_int (value, model->priv->limit);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_recent_model_class_init (EggRecentModelClass * klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = egg_recent_model_set_property;
+ object_class->get_property = egg_recent_model_get_property;
+ object_class->finalize = egg_recent_model_finalize;
+
+ model_signals[CHANGED] = g_signal_new ("changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EggRecentModelClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+
+
+ g_object_class_install_property (object_class,
+ PROP_MIME_FILTERS,
+ g_param_spec_pointer ("mime-filters",
+ "Mime Filters",
+ "List of mime types to be allowed.",
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_GROUP_FILTERS,
+ g_param_spec_pointer ("group-filters",
+ "Group Filters",
+ "List of groups to be allowed.",
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_SCHEME_FILTERS,
+ g_param_spec_pointer ("scheme-filters",
+ "Scheme Filters",
+ "List of URI schemes to be allowed.",
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_SORT_TYPE,
+ g_param_spec_int ("sort-type",
+ "Sort Type",
+ "Type of sorting to be done.",
+ 0, EGG_RECENT_MODEL_SORT_NONE,
+ EGG_RECENT_MODEL_SORT_MRU,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_LIMIT,
+ g_param_spec_int ("limit",
+ "Limit",
+ "Max number of items allowed.",
+ -1, EGG_RECENT_MODEL_MAX_ITEMS,
+ EGG_RECENT_MODEL_DEFAULT_LIMIT,
+ G_PARAM_READWRITE));
+
+ klass->changed = NULL;
+}
+
+
+
+static void
+egg_recent_model_limit_changed (GConfClient *client, guint cnxn_id,
+ GConfEntry *entry, gpointer user_data)
+{
+ EggRecentModel *model;
+ GConfValue *value;
+
+ model = EGG_RECENT_MODEL (user_data);
+
+ g_return_if_fail (model != NULL);
+
+ if (model->priv->use_default_limit == FALSE)
+ return; /* ignore this key */
+
+ /* the key was unset, and the schema has apparently failed */
+ if (entry == NULL)
+ return;
+
+ value = gconf_entry_get_value (entry);
+
+ if (value->type != GCONF_VALUE_INT) {
+ g_warning ("Expected GConfValue of type integer, "
+ "got something else");
+ }
+
+
+ egg_recent_model_set_limit_internal (model, gconf_value_get_int (value));
+}
+
+static void
+egg_recent_model_expiration_changed (GConfClient *client, guint cnxn_id,
+ GConfEntry *entry, gpointer user_data)
+{
+
+}
+
+static void
+egg_recent_model_init (EggRecentModel * model)
+{
+ if (!gnome_vfs_init ()) {
+ g_warning ("gnome-vfs initialization failed.");
+ return;
+ }
+
+
+ model->priv = g_new0 (EggRecentModelPrivate, 1);
+
+ model->priv->path = g_strdup_printf ("%s" EGG_RECENT_MODEL_FILE_PATH,
+ g_get_home_dir ());
+
+ model->priv->mime_filter_values = NULL;
+ model->priv->group_filter_values = NULL;
+ model->priv->scheme_filter_values = NULL;
+
+ model->priv->client = gconf_client_get_default ();
+ gconf_client_add_dir (model->priv->client, EGG_RECENT_MODEL_KEY_DIR,
+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+ model->priv->limit_change_notify_id =
+ gconf_client_notify_add (model->priv->client,
+ EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY,
+ egg_recent_model_limit_changed,
+ model, NULL, NULL);
+
+ model->priv->expiration_change_notify_id =
+ gconf_client_notify_add (model->priv->client,
+ EGG_RECENT_MODEL_EXPIRE_KEY,
+ egg_recent_model_expiration_changed,
+ model, NULL, NULL);
+
+ model->priv->expire_days = gconf_client_get_int (
+ model->priv->client,
+ EGG_RECENT_MODEL_EXPIRE_KEY,
+ NULL);
+
+#if 0
+ /* keep this out, for now */
+ model->priv->limit = gconf_client_get_int (
+ model->priv->client,
+ EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY, NULL);
+ model->priv->use_default_limit = TRUE;
+#endif
+ model->priv->limit = EGG_RECENT_MODEL_DEFAULT_LIMIT;
+ model->priv->use_default_limit = FALSE;
+
+ model->priv->monitors = g_hash_table_new_full (
+ g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) gnome_vfs_monitor_cancel);
+
+ model->priv->monitor = NULL;
+ egg_recent_model_monitor (model, TRUE);
+}
+
+
+/**
+ * egg_recent_model_new:
+ * @sort: the type of sorting to use
+ * @limit: maximum number of items in the list
+ *
+ * This creates a new EggRecentModel object.
+ *
+ * Returns: a EggRecentModel object
+ */
+EggRecentModel *
+egg_recent_model_new (EggRecentModelSort sort)
+{
+ EggRecentModel *model;
+
+ model = EGG_RECENT_MODEL (g_object_new (egg_recent_model_get_type (),
+ "sort-type", sort, NULL));
+
+ g_return_val_if_fail (model, NULL);
+
+ return model;
+}
+
+/**
+ * egg_recent_model_add_full:
+ * @model: A EggRecentModel object.
+ * @item: A EggRecentItem
+ *
+ * This function adds an item to the list of recently used URIs.
+ *
+ * Returns: gboolean
+ */
+gboolean
+egg_recent_model_add_full (EggRecentModel * model, EggRecentItem *item)
+{
+ FILE *file;
+ GList *list = NULL;
+ gboolean ret = FALSE;
+ gboolean updated = FALSE;
+ char *uri;
+ time_t t;
+
+ g_return_val_if_fail (model != NULL, FALSE);
+ g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
+
+ uri = egg_recent_item_get_uri (item);
+ if (strncmp (uri, "recent-files://", strlen ("recent-files://")) == 0) {
+ g_free (uri);
+ return FALSE;
+ } else {
+ g_free (uri);
+ }
+
+ file = egg_recent_model_open_file (model);
+ g_return_val_if_fail (file != NULL, FALSE);
+
+ time (&t);
+ egg_recent_item_set_timestamp (item, t);
+
+ if (egg_recent_model_lock_file (file)) {
+
+ /* read existing stuff */
+ list = egg_recent_model_read (model, file);
+
+ /* if it's already there, we just update it */
+ updated = egg_recent_model_update_item (list, item);
+
+ if (!updated) {
+ list = g_list_prepend (list, item);
+
+ egg_recent_model_enforce_limit (list,
+ EGG_RECENT_MODEL_MAX_ITEMS);
+ }
+
+ /* write new stuff */
+ if (!egg_recent_model_write (model, file, list))
+ g_warning ("Write failed: %s", strerror (errno));
+
+ if (!updated)
+ list = g_list_remove (list, item);
+
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+ ret = TRUE;
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ fclose (file);
+ return FALSE;
+ }
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ fclose (file);
+
+ if (model->priv->monitor == NULL) {
+ /* since monitoring isn't working, at least give a
+ * local notification
+ */
+ egg_recent_model_changed (model);
+ }
+
+ return ret;
+}
+
+/**
+ * egg_recent_model_add:
+ * @model: A EggRecentModel object.
+ * @uri: A string URI
+ *
+ * This function adds an item to the list of recently used URIs.
+ *
+ * Returns: gboolean
+ */
+gboolean
+egg_recent_model_add (EggRecentModel *model, const gchar *uri)
+{
+ EggRecentItem *item;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (model != NULL, FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ item = egg_recent_item_new_from_uri (uri);
+
+ g_return_val_if_fail (item != NULL, FALSE);
+
+ ret = egg_recent_model_add_full (model, item);
+
+ egg_recent_item_unref (item);
+
+ return ret;
+}
+
+
+
+/**
+ * egg_recent_model_delete:
+ * @model: A EggRecentModel object.
+ * @uri: The URI you want to delete.
+ *
+ * This function deletes a URI from the file of recently used URIs.
+ *
+ * Returns: gboolean
+ */
+gboolean
+egg_recent_model_delete (EggRecentModel * model, const gchar * uri)
+{
+ FILE *file;
+ GList *list;
+ unsigned int length;
+ gboolean ret = FALSE;
+
+ g_return_val_if_fail (model != NULL, FALSE);
+ g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ file = egg_recent_model_open_file (model);
+ g_return_val_if_fail (file != NULL, FALSE);
+
+ if (egg_recent_model_lock_file (file)) {
+ list = egg_recent_model_read (model, file);
+
+ if (list == NULL)
+ goto out;
+
+ length = g_list_length (list);
+
+ list = egg_recent_model_delete_from_list (list, uri);
+
+ if (length == g_list_length (list)) {
+ /* nothing was deleted */
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+ } else {
+ egg_recent_model_write (model, file, list);
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+ ret = TRUE;
+
+ }
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ return FALSE;
+ }
+
+out:
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ fclose (file);
+
+ g_hash_table_remove (model->priv->monitors, uri);
+
+ if (model->priv->monitor == NULL && ret) {
+ /* since monitoring isn't working, at least give a
+ * local notification
+ */
+ egg_recent_model_changed (model);
+ }
+
+ return ret;
+}
+
+
+/**
+ * egg_recent_model_get_list:
+ * @model: A EggRecentModel object.
+ *
+ * This function gets the current contents of the file
+ *
+ * Returns: a GList
+ */
+GList *
+egg_recent_model_get_list (EggRecentModel *model)
+{
+ FILE *file;
+ GList *list=NULL;
+
+ file = egg_recent_model_open_file (model);
+ g_return_val_if_fail (file != NULL, NULL);
+
+ if (egg_recent_model_lock_file (file)) {
+ list = egg_recent_model_read (model, file);
+
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ fclose (file);
+ return NULL;
+ }
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ if (list != NULL) {
+ list = egg_recent_model_filter (model, list);
+ list = egg_recent_model_sort (model, list);
+
+ egg_recent_model_enforce_limit (list, model->priv->limit);
+ }
+
+ fclose (file);
+
+ return list;
+}
+
+
+
+/**
+ * egg_recent_model_set_limit:
+ * @model: A EggRecentModel object.
+ * @limit: The maximum length of the list
+ *
+ * This function sets the maximum length of the list. Note: This only affects
+ * the length of the list emitted in the "changed" signal, not the list stored
+ * on disk.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_limit (EggRecentModel *model, int limit)
+{
+ model->priv->use_default_limit = FALSE;
+
+ egg_recent_model_set_limit_internal (model, limit);
+}
+
+/**
+ * egg_recent_model_get_limit:
+ * @model: A EggRecentModel object.
+ *
+ * This function gets the maximum length of the list.
+ *
+ * Returns: int
+ */
+int
+egg_recent_model_get_limit (EggRecentModel *model)
+{
+ return model->priv->limit;
+}
+
+
+/**
+ * egg_recent_model_clear:
+ * @model: A EggRecentModel object.
+ *
+ * This function clears the contents of the file
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_clear (EggRecentModel *model)
+{
+ FILE *file;
+ int fd;
+
+ file = egg_recent_model_open_file (model);
+ g_return_if_fail (file != NULL);
+
+ fd = fileno (file);
+
+ if (egg_recent_model_lock_file (file)) {
+ ftruncate (fd, 0);
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ return;
+ }
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ fclose (file);
+}
+
+
+/**
+ * egg_recent_model_set_filter_mime_types:
+ * @model: A EggRecentModel object.
+ *
+ * Sets which mime types are allowed in the list.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_filter_mime_types (EggRecentModel *model,
+ ...)
+{
+ va_list valist;
+ GSList *list = NULL;
+ gchar *str;
+
+ g_return_if_fail (model != NULL);
+
+ if (model->priv->mime_filter_values != NULL) {
+ g_slist_foreach (model->priv->mime_filter_values,
+ (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free (model->priv->mime_filter_values);
+ model->priv->mime_filter_values = NULL;
+ }
+
+ va_start (valist, model);
+
+ str = va_arg (valist, gchar*);
+
+ while (str != NULL) {
+ list = g_slist_prepend (list, g_pattern_spec_new (str));
+
+ str = va_arg (valist, gchar*);
+ }
+
+ va_end (valist);
+
+ model->priv->mime_filter_values = list;
+}
+
+/**
+ * egg_recent_model_set_filter_groups:
+ * @model: A EggRecentModel object.
+ *
+ * Sets which groups are allowed in the list.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_filter_groups (EggRecentModel *model,
+ ...)
+{
+ va_list valist;
+ GSList *list = NULL;
+ gchar *str;
+
+ g_return_if_fail (model != NULL);
+
+ if (model->priv->group_filter_values != NULL) {
+ g_slist_foreach (model->priv->group_filter_values, (GFunc)g_free, NULL);
+ g_slist_free (model->priv->group_filter_values);
+ model->priv->group_filter_values = NULL;
+ }
+
+ va_start (valist, model);
+
+ str = va_arg (valist, gchar*);
+
+ while (str != NULL) {
+ list = g_slist_prepend (list, g_strdup (str));
+
+ str = va_arg (valist, gchar*);
+ }
+
+ va_end (valist);
+
+ model->priv->group_filter_values = list;
+}
+
+/**
+ * egg_recent_model_set_filter_uri_schemes:
+ * @model: A EggRecentModel object.
+ *
+ * Sets which URI schemes (file, http, ftp, etc) are allowed in the list.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_filter_uri_schemes (EggRecentModel *model, ...)
+{
+ va_list valist;
+ GSList *list = NULL;
+ gchar *str;
+
+ g_return_if_fail (model != NULL);
+
+ if (model->priv->scheme_filter_values != NULL) {
+ g_slist_foreach (model->priv->scheme_filter_values,
+ (GFunc) g_pattern_spec_free, NULL);
+ g_slist_free (model->priv->scheme_filter_values);
+ model->priv->scheme_filter_values = NULL;
+ }
+
+ va_start (valist, model);
+
+ str = va_arg (valist, gchar*);
+
+ while (str != NULL) {
+ list = g_slist_prepend (list, g_pattern_spec_new (str));
+
+ str = va_arg (valist, gchar*);
+ }
+
+ va_end (valist);
+
+ model->priv->scheme_filter_values = list;
+}
+
+/**
+ * egg_recent_model_set_sort:
+ * @model: A EggRecentModel object.
+ * @sort: A EggRecentModelSort type
+ *
+ * Sets the type of sorting to be used.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_sort (EggRecentModel *model,
+ EggRecentModelSort sort)
+{
+ g_return_if_fail (model != NULL);
+
+ model->priv->sort_type = sort;
+}
+
+/**
+ * egg_recent_model_changed:
+ * @model: A EggRecentModel object.
+ *
+ * This function causes a "changed" signal to be emitted.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_changed (EggRecentModel *model)
+{
+ GList *list = NULL;
+
+ if (model->priv->limit > 0) {
+ list = egg_recent_model_get_list (model);
+ /* egg_recent_model_monitor_list (model, list); */
+
+ g_signal_emit (G_OBJECT (model), model_signals[CHANGED], 0,
+ list);
+ }
+
+ if (list)
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+}
+
+static void
+egg_recent_model_remove_expired_list (EggRecentModel *model, GList *list)
+{
+ time_t current_time;
+ time_t day_seconds;
+
+ time (¤t_time);
+ day_seconds = model->priv->expire_days*24*60*60;
+
+ while (list != NULL) {
+ EggRecentItem *item = list->data;
+ time_t timestamp;
+
+ timestamp = egg_recent_item_get_timestamp (item);
+
+ if ((timestamp+day_seconds) < current_time) {
+ gchar *uri = egg_recent_item_get_uri (item);
+ egg_recent_model_delete (model, uri);
+
+ g_strdup (uri);
+ }
+
+ list = list->next;
+ }
+}
+
+
+/**
+ * egg_recent_model_remove_expired:
+ * @model: A EggRecentModel object.
+ *
+ * Goes through the entire list, and removes any items that are older than
+ * the user-specified expiration period.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_remove_expired (EggRecentModel *model)
+{
+ FILE *file;
+ GList *list=NULL;
+
+ g_return_if_fail (model != NULL);
+
+ file = egg_recent_model_open_file (model);
+ g_return_if_fail (file != NULL);
+
+ if (egg_recent_model_lock_file (file)) {
+ list = egg_recent_model_read (model, file);
+
+ } else {
+ g_warning ("Failed to lock: %s", strerror (errno));
+ return;
+ }
+
+ if (!egg_recent_model_unlock_file (file))
+ g_warning ("Failed to unlock: %s", strerror (errno));
+
+ if (list != NULL) {
+ egg_recent_model_remove_expired_list (model, list);
+ EGG_RECENT_ITEM_LIST_UNREF (list);
+ }
+
+ fclose (file);
+}
+
+/**
+ * egg_recent_model_get_type:
+ *
+ * This returns a GType representing a EggRecentModel object.
+ *
+ * Returns: a GType
+ */
+GType
+egg_recent_model_get_type (void)
+{
+ static GType egg_recent_model_type = 0;
+
+ if(!egg_recent_model_type) {
+ static const GTypeInfo egg_recent_model_info = {
+ sizeof (EggRecentModelClass),
+ NULL, /* base init */
+ NULL, /* base finalize */
+ (GClassInitFunc)egg_recent_model_class_init, /* class init */
+ NULL, /* class finalize */
+ NULL, /* class data */
+ sizeof (EggRecentModel),
+ 0,
+ (GInstanceInitFunc) egg_recent_model_init
+ };
+
+ egg_recent_model_type = g_type_register_static (G_TYPE_OBJECT,
+ "EggRecentModel",
+ &egg_recent_model_info, 0);
+ }
+
+ return egg_recent_model_type;
+}
+
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-item.h
@@ -0,0 +1,80 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+
+#ifndef __EGG_RECENT_ITEM_H__
+#define __EGG_RECENT_ITEM_H__
+
+#include <time.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_RECENT_ITEM (egg_recent_item_get_type ())
+
+#define EGG_RECENT_ITEM_LIST_UNREF(list) \
+ g_list_foreach (list, (GFunc)egg_recent_item_unref, NULL); \
+ g_list_free (list);
+
+typedef struct _EggRecentItem EggRecentItem;
+
+struct _EggRecentItem {
+ /* do not access any of these directly */
+ gchar *uri;
+ gchar *mime_type;
+ time_t timestamp;
+
+ gboolean private_data;
+
+ GList *groups;
+
+ int refcount;
+};
+
+GType egg_recent_item_get_type (void) G_GNUC_CONST;
+
+/* constructors */
+EggRecentItem * egg_recent_item_new (void);
+
+EggRecentItem * egg_recent_item_ref (EggRecentItem *item);
+EggRecentItem * egg_recent_item_unref (EggRecentItem *item);
+
+/* automatically fetches the mime type, etc */
+EggRecentItem * egg_recent_item_new_from_uri (const gchar *uri);
+
+gboolean egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri);
+gchar * egg_recent_item_get_uri (const EggRecentItem *item);
+gchar * egg_recent_item_get_uri_utf8 (const EggRecentItem *item);
+gchar * egg_recent_item_get_uri_for_display (const EggRecentItem *item);
+gchar * egg_recent_item_get_short_name (const EggRecentItem *item);
+
+void egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime);
+gchar * egg_recent_item_get_mime_type (const EggRecentItem *item);
+
+void egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp);
+time_t egg_recent_item_get_timestamp (const EggRecentItem *item);
+
+G_CONST_RETURN gchar *egg_recent_item_peek_uri (const EggRecentItem *item);
+
+
+/* groups */
+G_CONST_RETURN GList * egg_recent_item_get_groups (const EggRecentItem *item);
+
+gboolean egg_recent_item_in_group (const EggRecentItem *item,
+ const gchar *group_name);
+
+void egg_recent_item_add_group (EggRecentItem *item,
+ const gchar *group_name);
+
+void egg_recent_item_remove_group (EggRecentItem *item,
+ const gchar *group_name);
+
+void egg_recent_item_set_private (EggRecentItem *item,
+ gboolean priv);
+
+gboolean egg_recent_item_get_private (const EggRecentItem *item);
+
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_ITEM_H__ */
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent.h
@@ -0,0 +1,7 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include "egg-recent-item.h"
+#include "egg-recent-model.h"
+#include "egg-recent-view.h"
+#include "egg-recent-view-bonobo.h"
+#include "egg-recent-view-gtk.h"
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-util.c
@@ -0,0 +1,141 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include <goffice/goffice-config.h>
+/* #include <config.h> */
+#include <stdio.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#include <libgnomeui/gnome-icon-lookup.h>
+#endif
+#include <math.h>
+#include "egg-recent-util.h"
+
+#define EGG_RECENT_UTIL_HOSTNAME_SIZE 512
+
+/* ripped out of gedit2 */
+gchar*
+egg_recent_util_escape_underlines (const gchar* text)
+{
+ GString *str;
+ gint length;
+ const gchar *p;
+ const gchar *end;
+
+ g_return_val_if_fail (text != NULL, NULL);
+
+ length = strlen (text);
+
+ str = g_string_new ("");
+
+ p = text;
+ end = text + length;
+
+ while (p != end)
+ {
+ const gchar *next;
+ next = g_utf8_next_char (p);
+
+ switch (*p)
+ {
+ case '_':
+ g_string_append (str, "__");
+ break;
+ default:
+ g_string_append_len (str, p, next - p);
+ break;
+ }
+
+ p = next;
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+#ifndef USE_STABLE_LIBGNOMEUI
+static GdkPixbuf *
+load_icon_file (char *filename,
+ guint nominal_size)
+{
+ GdkPixbuf *pixbuf, *scaled_pixbuf;
+ guint width, height;
+
+ pixbuf = gdk_pixbuf_new_from_file_at_size (filename, nominal_size, nominal_size, NULL);
+
+ if (pixbuf == NULL) {
+ return NULL;
+ }
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ /* if the icon is larger than the nominal size, scale down */
+ if (MAX (width, height) > nominal_size) {
+ if (width > height) {
+ height = height * nominal_size / width;
+ width = nominal_size;
+ } else {
+ width = width * nominal_size / height;
+ height = nominal_size;
+ }
+ scaled_pixbuf = gdk_pixbuf_scale_simple
+ (pixbuf, width, height, GDK_INTERP_BILINEAR);
+ g_object_unref (pixbuf);
+ pixbuf = scaled_pixbuf;
+ }
+
+ return pixbuf;
+}
+
+GdkPixbuf *
+egg_recent_util_get_icon (GnomeIconTheme *theme, const gchar *uri,
+ const gchar *mime_type, int size)
+{
+ gchar *icon;
+ gchar *filename;
+ const GnomeIconData *icon_data;
+ GdkPixbuf *pixbuf;
+
+ icon = gnome_icon_lookup (theme, NULL, uri, NULL, NULL,
+ mime_type, 0, NULL);
+
+
+ g_return_val_if_fail (icon != NULL, NULL);
+
+ filename = gnome_icon_theme_lookup_icon (theme, icon,
+ size,
+ &icon_data,
+ NULL);
+ g_free (icon);
+
+ if (filename == NULL) {
+ return NULL;
+ }
+
+ pixbuf = load_icon_file (filename, size);
+ g_free (filename);
+
+
+ return pixbuf;
+}
+#endif /* !USE_STABLE_LIBGNOMEUI */
+
+gchar *
+egg_recent_util_get_unique_id (void)
+{
+ char hostname[EGG_RECENT_UTIL_HOSTNAME_SIZE];
+ time_t the_time;
+ guint32 rand;
+ int pid;
+
+ gethostname (hostname, EGG_RECENT_UTIL_HOSTNAME_SIZE);
+
+ time (&the_time);
+ rand = g_random_int ();
+ pid = getpid ();
+
+ return g_strdup_printf ("%s-%d-%d-%d", hostname, (int)time, (int)rand, (int)pid);
+}
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-util.h
@@ -0,0 +1,25 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+
+#ifndef __EGG_RECENT_UTIL__
+#define __EGG_RECENT_UTIL__
+
+#include <gtk/gtk.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#endif
+
+G_BEGIN_DECLS
+
+gchar * egg_recent_util_escape_underlines (const gchar *uri);
+gchar * egg_recent_util_get_unique_id (void);
+#ifndef USE_STABLE_LIBGNOMEUI
+GdkPixbuf * egg_recent_util_get_icon (GnomeIconTheme *theme,
+ const gchar *uri,
+ const gchar *mime_type,
+ int size);
+#endif
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_UTIL__ */
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-view-gtk.c
@@ -0,0 +1,820 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+#include <goffice/goffice-config.h>
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ * James Willcox <jwillcox at cs.indiana.edu>
+ */
+
+#ifdef HAVE_CONFIG_H
+/* #include <config.h> */
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <libgnomevfs/gnome-vfs.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#endif
+#include <gconf/gconf-client.h>
+#include "egg-recent-model.h"
+#include "egg-recent-view.h"
+#include "egg-recent-view-gtk.h"
+#include "egg-recent-util.h"
+#include "egg-recent-item.h"
+
+struct _EggRecentViewGtk {
+ GObject parent_instance; /* We emit signals */
+
+ GtkWidget *menu;
+ GtkWidget *start_menu_item;
+
+ gboolean leading_sep;
+ gboolean trailing_sep;
+
+ gulong changed_cb_id;
+
+ gchar *uid;
+
+ gboolean show_icons;
+ gboolean show_numbers;
+#ifndef USE_STABLE_LIBGNOMEUI
+ GnomeIconTheme *theme;
+#endif
+
+ GtkTooltips *tooltips;
+ EggRecentViewGtkTooltipFunc tooltip_func;
+ gpointer tooltip_func_data;
+
+ EggRecentModel *model;
+ GConfClient *client;
+ GtkIconSize icon_size;
+};
+
+
+
+struct _EggRecentViewGtkMenuData {
+ EggRecentViewGtk *view;
+ EggRecentItem *item;
+};
+
+typedef struct _EggRecentViewGtkMenuData EggRecentViewGtkMenuData;
+
+enum {
+ ACTIVATE,
+ LAST_SIGNAL
+};
+
+/* GObject properties */
+enum {
+ PROP_BOGUS,
+ PROP_MENU,
+ PROP_START_MENU_ITEM,
+ PROP_SHOW_ICONS,
+ PROP_SHOW_NUMBERS
+};
+
+static guint view_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+egg_recent_view_gtk_clear (EggRecentViewGtk *view)
+{
+ GList *menu_children;
+ GList *p;
+ GObject *menu_item;
+ gint *menu_data=NULL;
+
+ g_return_if_fail (view->menu != NULL);
+
+ menu_children = gtk_container_get_children (GTK_CONTAINER (view->menu));
+
+ p = menu_children;
+ while (p != NULL) {
+ menu_item = (GObject *)p->data;
+
+ menu_data = (gint *)g_object_get_data (menu_item,
+ view->uid);
+
+ if (menu_data) {
+ gtk_container_remove (GTK_CONTAINER (view->menu),
+ GTK_WIDGET (menu_item));
+
+ }
+
+ p = p->next;
+ }
+}
+
+
+static gint
+egg_recent_view_gtk_find_menu_offset (EggRecentViewGtk *view)
+{
+ gint i;
+ GList *menu_children;
+ GList *p;
+ GtkWidget *menu_item;
+ gint menu_loc=-1;
+
+ g_return_val_if_fail (view, 0);
+
+ menu_children = GTK_MENU_SHELL (view->menu)->children;
+
+ i = 0;
+ p = menu_children;
+ while (p != NULL) {
+ menu_item = (GtkWidget *)p->data;
+
+ if (menu_item == view->start_menu_item) {
+ menu_loc = i;
+ break;
+ }
+
+ p = p->next;
+ i++;
+ }
+
+ return menu_loc;
+}
+
+static void
+egg_recent_view_gtk_menu_cb (GtkWidget *menu, gpointer data)
+{
+ EggRecentViewGtkMenuData *md = (EggRecentViewGtkMenuData *) data;
+ EggRecentItem *item;
+
+ g_return_if_fail (md);
+ g_return_if_fail (md->item);
+ g_return_if_fail (md->view);
+ g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (md->view));
+
+ item = md->item;
+
+ egg_recent_item_ref (item);
+
+ g_signal_emit (G_OBJECT(md->view), view_signals[ACTIVATE], 0,
+ item);
+
+ egg_recent_item_unref (item);
+}
+
+static void
+egg_recent_view_gtk_destroy_cb (gpointer data, GClosure *closure)
+{
+ EggRecentViewGtkMenuData *md = data;
+
+ egg_recent_item_unref (md->item);
+ g_free (md);
+}
+
+static GtkWidget *
+egg_recent_view_gtk_new_separator (EggRecentViewGtk *view)
+{
+ GtkWidget *retval;
+
+ g_return_val_if_fail (view, NULL);
+
+ retval = gtk_separator_menu_item_new ();
+
+ /**
+ * this is a tag so we can distinguish our menu items
+ * from others that may be in the menu.
+ */
+ g_object_set_data (G_OBJECT (retval),
+ view->uid,
+ GINT_TO_POINTER (1));
+
+
+ gtk_widget_show (retval);
+
+ return retval;
+}
+
+static GtkWidget *
+egg_recent_view_gtk_new_menu_item (EggRecentViewGtk *view,
+ EggRecentItem *item,
+ gint index)
+{
+ GtkWidget *menu_item;
+ EggRecentViewGtkMenuData *md;
+ gchar *mime_type;
+ GtkWidget *image;
+ GdkPixbuf *pixbuf;
+ gchar *text;
+ gchar *short_name;
+ gchar *escaped;
+
+ g_return_val_if_fail (view, NULL);
+ g_return_val_if_fail (item, NULL);
+
+ short_name = egg_recent_item_get_short_name (item);
+ if (!short_name)
+ return NULL;
+
+ escaped = egg_recent_util_escape_underlines (short_name);
+ g_free (short_name);
+
+ if (view->show_numbers) {
+ /* avoid having conflicting mnemonics */
+ if (index >= 10)
+ text = g_strdup_printf ("%d. %s", index,
+ escaped);
+ else
+ text = g_strdup_printf ("_%d. %s", index,
+ escaped);
+ g_free (escaped);
+ } else {
+ text = escaped;
+ }
+
+ mime_type = egg_recent_item_get_mime_type (item);
+#ifndef USE_STABLE_LIBGNOMEUI
+ {
+ int width, height;
+ gchar *uri;
+
+ gtk_icon_size_lookup_for_settings
+ (gtk_widget_get_settings (view->menu),
+ view->icon_size,
+ &width, &height);
+
+ uri = egg_recent_item_get_uri (item);
+ pixbuf = egg_recent_util_get_icon (view->theme, uri,
+ mime_type,
+ height);
+ g_free (uri);
+ }
+#else
+ pixbuf = NULL;
+#endif
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ if (pixbuf)
+ g_object_unref (pixbuf);
+
+ if (view->show_icons)
+ gtk_widget_show (image);
+
+ menu_item = gtk_image_menu_item_new_with_mnemonic (text);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
+ image);
+
+ md = g_new0 (EggRecentViewGtkMenuData, 1);
+ md->view = view;
+ md->item = egg_recent_item_ref (item);
+
+ g_signal_connect_data (G_OBJECT (menu_item), "activate",
+ G_CALLBACK (egg_recent_view_gtk_menu_cb),
+ md,
+ (GClosureNotify)egg_recent_view_gtk_destroy_cb,
+ 0);
+
+ g_free (mime_type);
+ g_free (text);
+
+ /**
+ * this is a tag so we can distinguish our menu items
+ * from others that may be in the menu.
+ */
+ g_object_set_data (G_OBJECT (menu_item),
+ view->uid,
+ GINT_TO_POINTER (1));
+
+
+ gtk_widget_show (menu_item);
+
+ return menu_item;
+}
+
+static void
+egg_recent_view_gtk_add_to_menu (EggRecentViewGtk *view,
+ EggRecentItem *item,
+ gint display,
+ gint index)
+{
+ GtkWidget *menu_item;
+ gint menu_offset;
+
+ g_return_if_fail (view);
+ g_return_if_fail (view->menu);
+
+ menu_offset = egg_recent_view_gtk_find_menu_offset (view);
+
+ if (item != NULL)
+ menu_item = egg_recent_view_gtk_new_menu_item (view, item, display);
+ else
+ menu_item = egg_recent_view_gtk_new_separator (view);
+
+ if (view->tooltip_func != NULL && menu_item != NULL) {
+ view->tooltip_func (view->tooltips, menu_item,
+ item, view->tooltip_func_data);
+ }
+
+ if (menu_item)
+ gtk_menu_shell_insert (GTK_MENU_SHELL (view->menu), menu_item,
+ menu_offset+index);
+}
+
+static void
+egg_recent_view_gtk_set_list (EggRecentViewGtk *view, GList *list)
+{
+ EggRecentItem *item;
+ GList *p;
+ gint display=1;
+ gint index=1;
+
+ g_return_if_fail (view);
+
+ egg_recent_view_gtk_clear (view);
+
+ if (view->leading_sep) {
+ egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
+ index++;
+ }
+
+ p = list;
+ while (p != NULL) {
+ item = (EggRecentItem *)p->data;
+
+ egg_recent_view_gtk_add_to_menu (view, item, display, index);
+
+ p = p->next;
+ display++;
+ index++;
+ }
+
+ if (view->trailing_sep)
+ egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
+}
+
+static void
+model_changed_cb (EggRecentModel *model, GList *list, EggRecentViewGtk *view)
+{
+ if (list != NULL)
+ egg_recent_view_gtk_set_list (view, list);
+ else
+ egg_recent_view_gtk_clear (view);
+}
+
+static EggRecentModel *
+egg_recent_view_gtk_get_model (EggRecentView *view_parent)
+{
+ EggRecentViewGtk *view;
+
+ g_return_val_if_fail (view_parent != NULL, NULL);
+ view = EGG_RECENT_VIEW_GTK (view_parent);
+ return view->model;
+}
+
+static void
+egg_recent_view_gtk_set_model (EggRecentView *view_parent,
+ EggRecentModel *model)
+{
+ EggRecentViewGtk *view;
+
+ g_return_if_fail (view_parent != NULL);
+ view = EGG_RECENT_VIEW_GTK (view_parent);
+
+ if (view->model != NULL) {
+ g_object_unref (view->model);
+ g_signal_handler_disconnect (G_OBJECT (model),
+ view->changed_cb_id);
+ }
+
+ view->model = model;
+ g_object_ref (view->model);
+
+ view->changed_cb_id = g_signal_connect_object (G_OBJECT (model),
+ "changed",
+ G_CALLBACK (model_changed_cb),
+ view, 0);
+
+ egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_set_leading_sep (EggRecentViewGtk *view, gboolean val)
+{
+ view->leading_sep = val;
+
+ egg_recent_view_gtk_clear (view);
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_set_trailing_sep (EggRecentViewGtk *view, gboolean val)
+{
+ view->trailing_sep = val;
+
+ egg_recent_view_gtk_clear (view);
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+static void
+egg_recent_view_gtk_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
+
+ switch (prop_id)
+ {
+ case PROP_MENU:
+ egg_recent_view_gtk_set_menu (view,
+ GTK_WIDGET (g_value_get_object (value)));
+ break;
+ case PROP_START_MENU_ITEM:
+ egg_recent_view_gtk_set_start_menu_item (view,
+ g_value_get_object (value));
+ break;
+ case PROP_SHOW_ICONS:
+ egg_recent_view_gtk_show_icons (view,
+ g_value_get_boolean (value));
+ break;
+ case PROP_SHOW_NUMBERS:
+ egg_recent_view_gtk_show_numbers (view,
+ g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_recent_view_gtk_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
+
+ switch (prop_id)
+ {
+ case PROP_MENU:
+ g_value_set_object (value, view->menu);
+ break;
+ case PROP_START_MENU_ITEM:
+ g_value_set_object (value, view->start_menu_item);
+ break;
+ case PROP_SHOW_ICONS:
+ g_value_set_boolean (value, view->show_icons);
+ break;
+ case PROP_SHOW_NUMBERS:
+ g_value_set_boolean (value, view->show_numbers);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_recent_view_gtk_finalize (GObject *object)
+{
+ EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
+
+ g_signal_handler_disconnect (G_OBJECT (view->model),
+ view->changed_cb_id);
+
+ g_free (view->uid);
+
+ g_object_unref (view->menu);
+ g_object_unref (view->model);
+#ifndef USE_STABLE_LIBGNOMEUI
+ g_object_unref (view->theme);
+#endif
+ g_object_unref (view->client);
+
+ g_object_unref (view->tooltips);
+}
+
+static void
+egg_recent_view_gtk_class_init (EggRecentViewGtkClass * klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = egg_recent_view_gtk_set_property;
+ object_class->get_property = egg_recent_view_gtk_get_property;
+ object_class->finalize = egg_recent_view_gtk_finalize;
+
+ view_signals[ACTIVATE] = g_signal_new ("activate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EggRecentViewGtkClass, activate),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ EGG_TYPE_RECENT_ITEM);
+
+ g_object_class_install_property (object_class,
+ PROP_MENU,
+ g_param_spec_object ("menu",
+ "Menu",
+ "The GtkMenuShell this object will update.",
+ gtk_menu_get_type(),
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_START_MENU_ITEM,
+ g_param_spec_object ("start-menu-item",
+ "Start Menu Item",
+ "The menu item that precedes where are menu items will go",
+ gtk_menu_item_get_type (),
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_SHOW_ICONS,
+ g_param_spec_boolean ("show-icons",
+ "Show Icons",
+ "Whether or not to show icons",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_SHOW_NUMBERS,
+ g_param_spec_boolean ("show-numbers",
+ "Show Numbers",
+ "Whether or not to show numbers",
+ TRUE,
+ G_PARAM_READWRITE));
+
+ klass->activate = NULL;
+}
+
+static void
+egg_recent_view_init (EggRecentViewClass *iface)
+{
+ iface->do_get_model = egg_recent_view_gtk_get_model;
+ iface->do_set_model = egg_recent_view_gtk_set_model;
+}
+
+static void
+show_menus_changed_cb (GConfClient *client,
+ guint cnxn_id,
+ GConfEntry *entry,
+ EggRecentViewGtk *view)
+{
+ GConfValue *value;
+
+ value = gconf_entry_get_value (entry);
+
+ g_return_if_fail (value->type == GCONF_VALUE_BOOL);
+
+ egg_recent_view_gtk_show_icons (view,
+ gconf_value_get_bool (value));
+
+}
+
+#ifndef USE_STABLE_LIBGNOMEUI
+static void
+theme_changed_cb (GnomeIconTheme *theme, EggRecentViewGtk *view)
+{
+ if (view->model != NULL)
+ egg_recent_model_changed (view->model);
+}
+#endif
+
+static void
+egg_recent_view_gtk_init (EggRecentViewGtk * view)
+{
+ view->client = gconf_client_get_default ();
+
+ view->show_icons =
+ gconf_client_get_bool (view->client,
+ "/desktop/gnome/interface/menus_have_icons",
+ NULL);
+
+ gconf_client_add_dir (view->client, "/desktop/gnome/interface",
+ GCONF_CLIENT_PRELOAD_NONE,
+ NULL);
+ gconf_client_notify_add (view->client,
+ "/desktop/gnome/interface/menus_have_icons",
+ (GConfClientNotifyFunc)show_menus_changed_cb,
+ view, NULL, NULL);
+
+
+ view->leading_sep = FALSE;
+ view->trailing_sep = FALSE;
+
+ view->uid = egg_recent_util_get_unique_id ();
+#ifndef USE_STABLE_LIBGNOMEUI
+ view->theme = gnome_icon_theme_new ();
+ gnome_icon_theme_set_allow_svg (view->theme, TRUE);
+ g_signal_connect_object (view->theme, "changed",
+ G_CALLBACK (theme_changed_cb), view, 0);
+#endif
+ view->tooltips = gtk_tooltips_new ();
+ g_object_ref (view->tooltips);
+ gtk_object_sink (GTK_OBJECT (view->tooltips));
+ view->tooltip_func = NULL;
+ view->tooltip_func_data = NULL;
+
+ view->icon_size = GTK_ICON_SIZE_MENU;
+}
+
+void
+egg_recent_view_gtk_set_icon_size (EggRecentViewGtk *view,
+ GtkIconSize icon_size)
+{
+ if (view->icon_size != icon_size) {
+ view->icon_size = icon_size;
+ egg_recent_model_changed (view->model);
+ } else {
+ view->icon_size = icon_size;
+ }
+}
+
+GtkIconSize
+egg_recent_view_gtk_get_icon_size (EggRecentViewGtk *view)
+{
+ return view->icon_size;
+}
+
+void
+egg_recent_view_gtk_show_icons (EggRecentViewGtk *view, gboolean show)
+{
+ view->show_icons = show;
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_show_numbers (EggRecentViewGtk *view, gboolean show)
+{
+ view->show_numbers = show;
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_set_tooltip_func (EggRecentViewGtk *view,
+ EggRecentViewGtkTooltipFunc func,
+ gpointer user_data)
+{
+ view->tooltip_func = func;
+ view->tooltip_func_data = user_data;
+
+ if (view->model)
+ egg_recent_model_changed (view->model);
+}
+
+/**
+ * egg_recent_view_gtk_set_menu:
+ * @view: A EggRecentViewGtk object.
+ * @menu: The GtkMenuShell to put the menu items in.
+ *
+ * Use this function to change the GtkMenuShell that the recent
+ * documents appear in.
+ *
+ */
+void
+egg_recent_view_gtk_set_menu (EggRecentViewGtk *view,
+ GtkWidget *menu)
+{
+ g_return_if_fail (view);
+ g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
+ g_return_if_fail (menu);
+
+ if (view->menu != NULL)
+ g_object_unref (view->menu);
+
+ view->menu = menu;
+ g_object_ref (view->menu);
+}
+
+/**
+ * egg_recent_view_gtk_set_start_menu_item:
+ * @view: A EggRecentViewGtk object.
+ * @start_menu_item: The menu item that appears just before where our menu
+ * items should appear
+ *
+ */
+void
+egg_recent_view_gtk_set_start_menu_item (EggRecentViewGtk *view,
+ GtkWidget *menu_item)
+{
+ g_return_if_fail (view);
+ g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
+
+ view->start_menu_item = menu_item;
+}
+
+/**
+ * egg_recent_view_gtk_get_menu:
+ * @view: A EggRecentViewGtk object.
+ *
+ */
+GtkWidget *
+egg_recent_view_gtk_get_menu (EggRecentViewGtk *view)
+{
+ return view->menu;
+}
+
+/**
+ * egg_recent_view_gtk_get_start_menu_item
+ * @view: A EggRecentViewGtk object.
+ *
+ */
+GtkWidget *
+egg_recent_view_gtk_get_start_menu_item (EggRecentViewGtk *view)
+{
+ return view->start_menu_item;
+}
+
+
+/**
+ * egg_recent_view_gtk_new:
+ * @appname: The name of your application.
+ * @limit: The maximum number of items allowed.
+ *
+ * This creates a new EggRecentViewGtk object.
+ *
+ * Returns: a EggRecentViewGtk object
+ */
+EggRecentViewGtk *
+egg_recent_view_gtk_new (GtkWidget *menu, GtkWidget *start_menu_item)
+{
+ EggRecentViewGtk *view;
+
+ g_return_val_if_fail (menu, NULL);
+
+ view = EGG_RECENT_VIEW_GTK (g_object_new (egg_recent_view_gtk_get_type (),
+ "start-menu-item",
+ start_menu_item,
+ "menu", menu,
+ "show-numbers", TRUE, NULL));
+
+ g_return_val_if_fail (view, NULL);
+
+ return view;
+}
+
+/**
+ * egg_recent_view_gtk_get_type:
+ * @:
+ *
+ * This returns a GType representing a EggRecentViewGtk object.
+ *
+ * Returns: a GType
+ */
+GType
+egg_recent_view_gtk_get_type (void)
+{
+ static GType egg_recent_view_gtk_type = 0;
+
+ if(!egg_recent_view_gtk_type) {
+ static const GTypeInfo egg_recent_view_gtk_info = {
+ sizeof (EggRecentViewGtkClass),
+ NULL, /* base init */
+ NULL, /* base finalize */
+ (GClassInitFunc)egg_recent_view_gtk_class_init, /* class init */
+ NULL, /* class finalize */
+ NULL, /* class data */
+ sizeof (EggRecentViewGtk),
+ 0,
+ (GInstanceInitFunc) egg_recent_view_gtk_init
+ };
+
+ static const GInterfaceInfo view_info =
+ {
+ (GInterfaceInitFunc) egg_recent_view_init,
+ NULL,
+ NULL
+ };
+
+ egg_recent_view_gtk_type = g_type_register_static (G_TYPE_OBJECT,
+ "EggRecentViewGtk",
+ &egg_recent_view_gtk_info, 0);
+ g_type_add_interface_static (egg_recent_view_gtk_type,
+ EGG_TYPE_RECENT_VIEW,
+ &view_info);
+ }
+
+ return egg_recent_view_gtk_type;
+}
+
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/Makefile.am
@@ -0,0 +1,33 @@
+INCLUDES = \
+ $(GNUMERIC_CFLAGS) -I$(top_srcdir)/src -DUSE_STABLE_LIBGNOMEUI \
+ \
+ \
+ -DEGG_COMPILATION
+
+noinst_LIBRARIES = libeggrecent.a
+
+vfsdir = $(libdir)/gnome-vfs-2.0/modules
+
+vfsconfdir = $(sysconfdir)/gnome-vfs-2.0/modules
+
+libeggrecent_a_LIBADD = $(EGG_RECENT_LIBS)
+
+libeggrecent_a_SOURCES = \
+ egg-recent.h \
+ egg-recent-item.h \
+ egg-recent-item.c \
+ egg-recent-model.c \
+ egg-recent-model.h \
+ egg-recent-view.c \
+ egg-recent-view.h \
+ egg-recent-view-gtk.c \
+ egg-recent-view-gtk.h \
+ \
+ \
+ egg-recent-util.c \
+ egg-recent-util.h
+
+
+
+
+
--- /dev/null
+++ lib/goffice/cut-n-paste/egg-recent-files/egg-recent-view-gtk.h
@@ -0,0 +1,66 @@
+/* File import from libegg to gnumeric by import-egg. Do not edit. */
+
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef __EGG_RECENT_VIEW_GTK_H__
+#define __EGG_RECENT_VIEW_GTK_H__
+
+G_BEGIN_DECLS
+
+#include <gtk/gtk.h>
+#include "egg-recent-item.h"
+
+#define EGG_RECENT_VIEW_GTK(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, egg_recent_view_gtk_get_type (), EggRecentViewGtk)
+#define EGG_RECENT_VIEW_GTK_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, egg_recent_view_gtk_get_type (), EggRecentViewGtkClass)
+#define EGG_IS_RECENT_VIEW_GTK(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_view_gtk_get_type ())
+
+typedef void (*EggRecentViewGtkTooltipFunc) (GtkTooltips *tooltips,
+ GtkWidget *menu,
+ EggRecentItem *item,
+ gpointer user_data);
+
+typedef struct _EggRecentViewGtk EggRecentViewGtk;
+
+typedef struct _EggRecentViewGtkClass EggRecentViewGtkClass;
+
+struct _EggRecentViewGtkClass {
+ GObjectClass parent_class;
+
+ void (*activate) (EggRecentViewGtk *view, EggRecentItem *item);
+};
+
+GType egg_recent_view_gtk_get_type (void);
+
+EggRecentViewGtk * egg_recent_view_gtk_new (GtkWidget *menu,
+ GtkWidget *start_menu_item);
+
+void egg_recent_view_gtk_set_menu (EggRecentViewGtk *view,
+ GtkWidget *menu);
+GtkWidget * egg_recent_view_gtk_get_menu (EggRecentViewGtk *view);
+
+
+void egg_recent_view_gtk_set_start_menu_item (EggRecentViewGtk *view,
+ GtkWidget *menu_item);
+GtkWidget *egg_recent_view_gtk_get_start_menu_item (EggRecentViewGtk *view);
+
+void egg_recent_view_gtk_set_leading_sep (EggRecentViewGtk *view,
+ gboolean val);
+
+void egg_recent_view_gtk_set_trailing_sep (EggRecentViewGtk *view,
+ gboolean val);
+
+void egg_recent_view_gtk_show_icons (EggRecentViewGtk *view,
+ gboolean show);
+void egg_recent_view_gtk_show_numbers (EggRecentViewGtk *view,
+ gboolean show);
+
+void egg_recent_view_gtk_set_tooltip_func (EggRecentViewGtk *view,
+ EggRecentViewGtkTooltipFunc func,
+ gpointer user_data);
+
+void egg_recent_view_gtk_set_icon_size (EggRecentViewGtk *view,
+ GtkIconSize icon_size);
+GtkIconSize egg_recent_view_gtk_get_icon_size (EggRecentViewGtk *view);
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_VIEW_GTK_H__ */
--- /dev/null
+++ lib/goffice/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"><b>Error category</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="category_alignment">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkComboBox" id="category_combo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">None
+Absolute
+Relative
+Percent</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="style_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="style_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Style</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="style_table">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="display_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Dis_play:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">5</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="width_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Width:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_RIGHT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">5</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">width</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="line_width_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Line width:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_RIGHT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">5</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">line_width</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="color_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Colo_r:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_RIGHT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">5</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="width">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">4 0 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="line_width">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">False</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">1 0 100 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="values_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="values_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Values</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="values_table">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="plus_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">(+)</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="minus_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">(-)</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-chart.c
@@ -0,0 +1,759 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-chart.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-chart-impl.h>
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-graph-impl.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-grid.h>
+#include <goffice/graph/gog-grid-line.h>
+#include <goffice/graph/gog-renderer.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <math.h>
+
+enum {
+ CHART_PROP_0,
+ CHART_PROP_CARDINALITY_VALID
+};
+
+static GType gog_chart_view_get_type (void);
+static GObjectClass *chart_parent_klass;
+
+static void
+gog_chart_update (GogObject *obj)
+{
+ GogChart *chart = GOG_CHART (obj);
+ unsigned full = chart->full_cardinality;
+ unsigned visible = chart->visible_cardinality;
+
+ gog_chart_get_cardinality (chart, NULL, NULL);
+
+ if (full != chart->full_cardinality ||
+ visible != chart->visible_cardinality)
+ g_object_notify (G_OBJECT (chart), "cardinality-valid");
+}
+
+static void
+gog_chart_finalize (GObject *obj)
+{
+ GogChart *chart = GOG_CHART (obj);
+
+ /* on exit the role remove routines are not called */
+ g_slist_free (chart->plots);
+ g_slist_free (chart->axes);
+
+ (chart_parent_klass->finalize) (obj);
+}
+
+static void
+gog_chart_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogChart *chart = GOG_CHART (obj);
+ switch (param_id) {
+ case CHART_PROP_CARDINALITY_VALID:
+ g_value_set_boolean (value, chart->cardinality_valid);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_chart_children_reordered (GogObject *obj)
+{
+ GSList *ptr, *accum = NULL;
+ GogChart *chart = GOG_CHART (obj);
+
+ for (ptr = obj->children; ptr != NULL ; ptr = ptr->next)
+ if (IS_GOG_PLOT (ptr->data))
+ accum = g_slist_prepend (accum, ptr->data);
+ g_slist_free (chart->plots);
+ chart->plots = g_slist_reverse (accum);
+
+ gog_chart_request_cardinality_update (chart);
+}
+
+static void
+role_plot_post_add (GogObject *parent, GogObject *plot)
+{
+ GogChart *chart = GOG_CHART (parent);
+ gboolean ok = TRUE;
+
+ /* APPEND to keep order, there won't be that many */
+ chart->plots = g_slist_append (chart->plots, plot);
+ gog_chart_request_cardinality_update (chart);
+
+ if (chart->plots->next == NULL)
+ ok = gog_chart_axis_set_assign (chart,
+ gog_plot_axis_set_pref (GOG_PLOT (plot)));
+ ok |= gog_plot_axis_set_assign (GOG_PLOT (plot),
+ chart->axis_set);
+
+ /* a quick post condition to keep us on our toes */
+ g_return_if_fail (ok);
+}
+
+static void
+role_plot_pre_remove (GogObject *parent, GogObject *plot)
+{
+ GogChart *chart = GOG_CHART (parent);
+ gog_plot_axis_clear (GOG_PLOT (plot), GOG_AXIS_SET_ALL);
+ chart->plots = g_slist_remove (chart->plots, plot);
+ gog_chart_request_cardinality_update (chart);
+}
+
+static gboolean
+role_grid_can_add (GogObject const *parent)
+{
+ GogChart const *chart = GOG_CHART (parent);
+ return chart->grid == NULL && chart->axis_set == GOG_AXIS_SET_XY;
+}
+static void
+role_grid_post_add (GogObject *parent, GogObject *child)
+{
+ GogChart *chart = GOG_CHART (parent);
+ g_return_if_fail (chart->grid == NULL);
+ chart->grid = child;
+}
+
+static void
+role_grid_pre_remove (GogObject *parent, GogObject *grid)
+{
+ GogChart *chart = GOG_CHART (parent);
+ g_return_if_fail (chart->grid == grid);
+ chart->grid = NULL;
+}
+
+static gboolean
+axis_can_add (GogObject const *parent, GogAxisType t)
+{
+ GogChart *chart = GOG_CHART (parent);
+ if (chart->axis_set == GOG_AXIS_SET_UNKNOWN)
+ return FALSE;
+ return (chart->axis_set & (1 << t)) != 0;
+}
+static gboolean
+axis_can_remove (GogObject const *child)
+{
+ return NULL == gog_axis_contributors (GOG_AXIS (child));
+}
+
+static void
+axis_post_add (GogObject *axis, GogAxisType t)
+{
+ GogChart *chart = GOG_CHART (axis->parent);
+ g_object_set (G_OBJECT (axis), "type", (int)t, NULL);
+ chart->axes = g_slist_prepend (chart->axes, axis);
+}
+
+static void
+axis_pre_remove (GogObject *parent, GogObject *axis)
+{
+ GogChart *chart = GOG_CHART (parent);
+ gog_axis_clear_contributors (GOG_AXIS (axis));
+ chart->axes = g_slist_remove (chart->axes, axis);
+}
+
+static gboolean x_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_X); }
+static void x_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_X); }
+static gboolean y_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_Y); }
+static void y_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_Y); }
+static gboolean z_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_Z); }
+static void z_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_Z); }
+static gboolean circular_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_CIRCULAR); }
+static void circular_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_CIRCULAR); }
+static gboolean radial_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_RADIAL); }
+static void radial_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_RADIAL); }
+
+static GogObjectRole const roles[] = {
+ { N_("Legend"), "GogLegend", 0,
+ GOG_POSITION_COMPASS, GOG_POSITION_E|GOG_POSITION_ALIGN_CENTER, GOG_OBJECT_NAME_BY_ROLE,
+ NULL, NULL, NULL, NULL, NULL, NULL, { -1 } },
+ { N_("Title"), "GogLabel", 1,
+ GOG_POSITION_COMPASS, GOG_POSITION_N|GOG_POSITION_ALIGN_CENTER, GOG_OBJECT_NAME_BY_ROLE,
+ NULL, NULL, NULL, NULL, NULL, NULL, { -1 } },
+ { N_("Grid"), "GogGrid", 0,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_grid_can_add, NULL, NULL, role_grid_post_add, role_grid_pre_remove, NULL, { -1 } },
+ { N_("X-Axis"), "GogAxis", 1,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ x_axis_can_add, axis_can_remove, NULL, x_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_X } },
+ { N_("Y-Axis"), "GogAxis", 2,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ y_axis_can_add, axis_can_remove, NULL, y_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_Y } },
+ { N_("Z-Axis"), "GogAxis", 3,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ z_axis_can_add, axis_can_remove, NULL, z_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_Z } },
+ { N_("Circular-Axis"), "GogAxis", 1,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ circular_axis_can_add, axis_can_remove, NULL, circular_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_CIRCULAR } },
+ { N_("Radial-Axis"), "GogAxis", 2,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ radial_axis_can_add, axis_can_remove, NULL, radial_axis_post_add, axis_pre_remove, NULL,
+ { GOG_AXIS_RADIAL } },
+ { N_("Plot"), "GogPlot", 4, /* keep the axis before the plots */
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_TYPE,
+ NULL, NULL, NULL, role_plot_post_add, role_plot_pre_remove, NULL, { -1 } }
+};
+
+static void
+gog_chart_class_init (GogObjectClass *gog_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *)gog_klass;
+
+ chart_parent_klass = g_type_class_peek_parent (gog_klass);
+ gobject_klass->finalize = gog_chart_finalize;
+ gobject_klass->get_property = gog_chart_get_property;
+
+ g_object_class_install_property (gobject_klass, CHART_PROP_CARDINALITY_VALID,
+ g_param_spec_boolean ("cardinality-valid", "cardinality-valid",
+ "Is the charts cardinality currently vaid",
+ FALSE, G_PARAM_READABLE));
+
+ gog_klass->view_type = gog_chart_view_get_type ();
+ gog_klass->update = gog_chart_update;
+ gog_klass->children_reordered = gog_chart_children_reordered;
+ gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+}
+
+static void
+gog_chart_init (GogChart *chart)
+{
+ chart->x = 0;
+ chart->y = 0;
+ chart->cols = 0;
+ chart->rows = 0;
+
+ /* start as true so that we can queue an update when it changes */
+ chart->cardinality_valid = TRUE;
+ chart->axis_set = GOG_AXIS_SET_UNKNOWN;
+}
+
+GSF_CLASS (GogChart, gog_chart,
+ gog_chart_class_init, gog_chart_init,
+ GOG_OUTLINED_OBJECT_TYPE)
+
+/**
+ * gog_chart_get_position :
+ * @chart : const #GogChart
+ * @x :
+ * @y :
+ * @cols :
+ * @rows :
+ *
+ * Returns TRUE if the chart has been positioned.
+ **/
+gboolean
+gog_chart_get_position (GogChart const *chart,
+ unsigned *x, unsigned *y, unsigned *cols, unsigned *rows)
+{
+ g_return_val_if_fail (GOG_CHART (chart), FALSE);
+
+ if (chart->cols <= 0 || chart->rows <= 0)
+ return FALSE;
+
+ if (x != NULL) *x = chart->x;
+ if (y != NULL) *y = chart->y;
+ if (cols != NULL) *cols = chart->cols;
+ if (rows != NULL) *rows = chart->rows;
+
+ return TRUE;
+}
+
+/**
+ * gog_chart_set_position :
+ * @chart : #GogChart
+ * @x :
+ * @y :
+ * @cols :
+ * @rows :
+ *
+ **/
+void
+gog_chart_set_position (GogChart *chart,
+ unsigned x, unsigned y, unsigned cols, unsigned rows)
+{
+ g_return_if_fail (GOG_CHART (chart) != NULL);
+
+ if (chart->x == x && chart->y == y &&
+ chart->cols == cols && chart->rows == rows)
+ return;
+
+ chart->x = x;
+ chart->y = y;
+ chart->cols = cols;
+ chart->rows = rows;
+
+ gog_graph_validate_chart_layout (GOG_GRAPH (GOG_OBJECT (chart)->parent));
+ gog_object_emit_changed (GOG_OBJECT (chart), TRUE);
+}
+
+void
+gog_chart_get_cardinality (GogChart *chart, unsigned *full, unsigned *visible)
+{
+ GSList *ptr;
+ unsigned tmp_full, tmp_visible;
+
+ g_return_if_fail (GOG_CHART (chart) != NULL);
+
+ if (!chart->cardinality_valid) {
+ chart->cardinality_valid = TRUE;
+ chart->full_cardinality = chart->visible_cardinality = 0;
+ for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next) {
+ gog_plot_get_cardinality (ptr->data, &tmp_full, &tmp_visible);
+ chart->full_cardinality += tmp_full;
+ chart->visible_cardinality += tmp_visible;
+ }
+ }
+
+ if (full != NULL)
+ *full = chart->full_cardinality;
+ if (visible != NULL)
+ *visible = chart->visible_cardinality;
+}
+
+void
+gog_chart_request_cardinality_update (GogChart *chart)
+{
+ g_return_if_fail (GOG_CHART (chart) != NULL);
+
+ if (chart->cardinality_valid) {
+ chart->cardinality_valid = FALSE;
+ gog_object_request_update (GOG_OBJECT (chart));
+ }
+}
+
+void
+gog_chart_foreach_elem (GogChart *chart, gboolean only_visible,
+ GogEnumFunc handler, gpointer data)
+{
+ GSList *ptr;
+
+ g_return_if_fail (GOG_CHART (chart) != NULL);
+ g_return_if_fail (chart->cardinality_valid);
+
+ for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next)
+ gog_plot_foreach_elem (ptr->data, only_visible, handler, data);
+}
+
+GSList *
+gog_chart_get_plots (GogChart const *chart)
+{
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, NULL);
+ return chart->plots;
+}
+
+GogAxisSet
+gog_chart_axis_set (GogChart const *chart)
+{
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, GOG_AXIS_SET_UNKNOWN);
+ return chart->axis_set;
+}
+
+gboolean
+gog_chart_axis_set_is_valid (GogChart const *chart, GogAxisSet type)
+{
+ GSList *ptr;
+
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, FALSE);
+
+ for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next)
+ if (!gog_plot_axis_set_is_valid (ptr->data, type))
+ return FALSE;
+ return TRUE;
+}
+
+static void
+gog_chart_add_axis (GogChart *chart, GogAxisType type)
+{
+ unsigned i = G_N_ELEMENTS (roles);
+ while (i-- > 0)
+ if (roles[i].user.i == (int)type) {
+ gog_object_add_by_role (GOG_OBJECT (chart), roles + i, NULL);
+ return;
+ }
+ g_warning ("unknown axis type %d", type);
+}
+
+gboolean
+gog_chart_axis_set_assign (GogChart *chart, GogAxisSet axis_set)
+{
+ GogAxis *axis;
+ GSList *ptr;
+ GogAxisType type;
+
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, FALSE);
+
+ if (chart->axis_set == axis_set)
+ return TRUE;
+ chart->axis_set = axis_set;
+
+ if (chart->grid != NULL && axis_set != GOG_AXIS_SET_XY) {
+ GogObject *grid = chart->grid; /* clear_parent clears ::grid */
+ gog_object_clear_parent (GOG_OBJECT (grid));
+ g_object_unref (grid);
+ } else if (chart->grid == NULL && axis_set == GOG_AXIS_SET_XY)
+ gog_object_add_by_name (GOG_OBJECT (chart), "Grid", NULL);
+
+ /* Add at least 1 instance of any required axis */
+ for (type = 0 ; type < GOG_AXIS_TYPES ; type++)
+ if ((axis_set & (1 << type))) {
+ GSList *tmp = gog_chart_get_axis (chart, type);
+ if (tmp == NULL)
+ gog_chart_add_axis (chart, type);
+ else
+ g_slist_free (tmp);
+ }
+
+ /* link the plots */
+ for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next)
+ if (!gog_plot_axis_set_assign (ptr->data, axis_set))
+ return FALSE;
+
+ /* remove any existing axis that do not fit this scheme */
+ for (ptr = GOG_OBJECT (chart)->children ; ptr != NULL ; ) {
+ axis = ptr->data;
+ ptr = ptr->next; /* list may change under us */
+ if (IS_GOG_AXIS (axis)) {
+ type = -1;
+ g_object_get (G_OBJECT (axis), "type", &type, NULL);
+ if (type < 0 || type >= GOG_AXIS_TYPES) {
+ g_warning ("Invalid axis");
+ continue;
+ }
+
+ if (0 == (axis_set & (1 << type))) {
+ gog_object_clear_parent (GOG_OBJECT (axis));
+ g_object_unref (axis);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * gog_chart_get_axis :
+ * @chart : #GogChart
+ * @target : #GogAxisType
+ *
+ * Return a list which the caller must free of all axis of type @target
+ * associated with @chart.
+ **/
+GSList *
+gog_chart_get_axis (GogChart const *chart, GogAxisType target)
+{
+ GSList *ptr, *res = NULL;
+ GogAxis *axis;
+ int type;
+
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, NULL);
+
+ for (ptr = GOG_OBJECT (chart)->children ; ptr != NULL ; ptr = ptr->next) {
+ axis = ptr->data;
+ if (IS_GOG_AXIS (axis)) {
+ type = -1;
+ g_object_get (G_OBJECT (axis), "type", &type, NULL);
+ if (type < 0 || type >= GOG_AXIS_TYPES) {
+ g_warning ("Invalid axis");
+ continue;
+ }
+ if (type == target)
+ res = g_slist_prepend (res, axis);
+ }
+ }
+
+ return res;
+}
+
+/**
+ * gog_chart_get_grid :
+ * @chart : #GogChart
+ *
+ * Returns the grid associated with @chart if one exists
+ * otherwise NULL.
+ **/
+GogGrid *
+gog_chart_get_grid (GogChart const *chart)
+{
+ g_return_val_if_fail (GOG_CHART (chart) != NULL, NULL);
+ return GOG_GRID (chart->grid);
+}
+
+/*********************************************************************/
+
+typedef struct {
+ GogOutlinedView base;
+
+ GogViewAllocation plot_area;
+} GogChartView;
+typedef GogOutlinedViewClass GogChartViewClass;
+
+#define GOG_CHART_VIEW_TYPE (gog_chart_view_get_type ())
+#define GOG_CHART_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_CHART_VIEW_TYPE, GogChartView))
+#define IS_GOG_CHART_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_CHART_VIEW_TYPE))
+
+static GogViewClass *cview_parent_klass;
+
+GogViewAllocation const *
+gog_chart_view_get_plot_area (GogView const *view)
+{
+ g_return_val_if_fail ((GOG_CHART_VIEW (view) != NULL), NULL);
+
+ return & (GOG_CHART_VIEW(view)->plot_area);
+}
+
+static void
+child_request (GogView *view, GogViewAllocation *res,
+ GogViewAllocation const *plot_area,
+ gboolean allocate)
+{
+ GSList *ptr;
+ GogView *child;
+ GogAxis const *axis;
+ GogViewRequisition req;
+ GogViewAllocation allocation;
+
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (child->model->position != GOG_POSITION_SPECIAL ||
+ !IS_GOG_AXIS (child->model))
+ continue;
+
+ axis = GOG_AXIS (child->model);
+ req.w = req.h = 0.;
+ gog_view_size_request (child, &req);
+ allocation = *plot_area;
+ switch (gog_axis_get_atype (axis)) {
+ case GOG_AXIS_X:
+ if (req.h > 0) {
+ res->h -= req.h;
+ allocation.h = req.h;
+ if (gog_axis_get_pos (axis) == GOG_AXIS_AT_HIGH) {
+ allocation.y = res->y;
+ res->y += req.h;
+ } else
+ allocation.y = res->y + res->h;
+ }
+
+
+ break;
+ case GOG_AXIS_Y:
+ if (req.w > 0) {
+ res->w -= req.w;
+ allocation.w = req.w;
+ if (gog_axis_get_pos (axis) == GOG_AXIS_AT_LOW) {
+ allocation.x = res->x;
+ res->x += req.w;
+ } else
+ allocation.x = res->x + res->w;
+ }
+ break;
+ default:
+ break;
+ }
+ if (allocate)
+ gog_view_size_allocate (child, &allocation);
+ }
+}
+
+static void
+gog_chart_view_size_allocate (GogView *view, GogViewAllocation const *allocation)
+{
+ GSList *ptr;
+ GogView *child;
+ GogChart *chart = GOG_CHART (view->model);
+ GogViewAllocation res = *allocation;
+ GogViewAllocation tmp, axis_alloc;
+
+ (cview_parent_klass->size_allocate) (view, &res);
+
+ res = view->residual;
+ switch (chart->axis_set) {
+
+ case GOG_AXIS_SET_XY:
+ {
+ GogViewPadding axis_padding, padding = {0., 0., 0., 0.};
+
+ tmp = res;
+ child_request (view, &res, &res, FALSE);
+
+ /* FIXME: we need to iterate until convergence */
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (child->model->position != GOG_POSITION_SPECIAL ||
+ !IS_GOG_AXIS (child->model))
+ continue;
+
+ gog_axis_view_padding_request (child, &axis_padding, &res);
+ padding.wr = MAX (padding.wr, axis_padding.wr);
+ padding.wl = MAX (padding.wl, axis_padding.wl);
+ padding.hb = MAX (padding.hb, axis_padding.hb);
+ padding.ht = MAX (padding.ht, axis_padding.ht);
+ }
+ res.x += padding.wl;
+ res.w -= padding.wl + padding.wr;
+ res.y += padding.ht;
+ res.h -= padding.ht + padding.hb;
+
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (child->model->position != GOG_POSITION_SPECIAL ||
+ !IS_GOG_AXIS (child->model))
+ continue;
+
+ switch (gog_axis_get_atype (GOG_AXIS (child->model)))
+ {
+ case GOG_AXIS_X:
+ axis_alloc = tmp;
+ axis_alloc.x = res.x;
+ axis_alloc.w = res.w;
+ gog_view_size_allocate (child, &axis_alloc);
+ break;
+
+ case GOG_AXIS_Y:
+ axis_alloc = tmp;
+ axis_alloc.y = res.y;
+ axis_alloc.h = res.h;
+ gog_view_size_allocate (child, &axis_alloc);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ break;
+
+ case GOG_AXIS_SET_RADAR:
+ /* Give the axes the whole residual area. */
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (IS_GOG_AXIS (child->model))
+ gog_view_size_allocate (child, &res);
+ }
+ break;
+ case GOG_AXIS_SET_NONE:
+ break;
+
+ case GOG_AXIS_SET_UNKNOWN:
+ return;
+ default:
+ g_warning ("only have layout engine for xy, radar, and none currently");
+ return;
+ }
+
+ /* overlay all the plots in the residual */
+ for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
+ child = ptr->data;
+ if (child->model->position == GOG_POSITION_SPECIAL &&
+ (IS_GOG_PLOT (child->model) || child->model == chart->grid))
+ gog_view_size_allocate (child, &res);
+ }
+
+ GOG_CHART_VIEW(view)->plot_area = res;
+}
+
+static void
+gog_chart_view_init (GogChartView *cview)
+{
+}
+
+static void
+grid_line_render (GSList *start_ptr, GogViewAllocation const *bbox)
+{
+ GSList *ptr, *child_ptr;
+ GogView *child_view, *axis_child_view;
+
+ /* Render minor lines first */
+ for (ptr = start_ptr; ptr != NULL; ptr = ptr->next) {
+ child_view = ptr->data;
+ if (IS_GOG_AXIS (child_view->model)) {
+ for (child_ptr = child_view->children; child_ptr != NULL; child_ptr = child_ptr->next) {
+ axis_child_view = child_ptr->data;
+ if (IS_GOG_GRID_LINE (axis_child_view->model) &&
+ gog_grid_line_is_minor (GOG_GRID_LINE (axis_child_view->model)))
+ gog_view_render (axis_child_view, bbox);
+ }
+ }
+ }
+ /* then render major lines */
+ for (ptr = start_ptr; ptr != NULL; ptr = ptr->next) {
+ child_view = ptr->data;
+ if (IS_GOG_AXIS (child_view->model)) {
+ for (child_ptr = child_view->children; child_ptr != NULL; child_ptr = child_ptr->next) {
+ axis_child_view = child_ptr->data;
+ if (IS_GOG_GRID_LINE (axis_child_view->model) &&
+ !gog_grid_line_is_minor (GOG_GRID_LINE (axis_child_view->model)))
+ gog_view_render (axis_child_view, bbox);
+ }
+ }
+ }
+}
+
+static void
+gog_chart_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GSList *ptr;
+ GogView *child_view;
+ gboolean grid_line_rendered = FALSE;
+
+ cview_parent_klass->render (view, bbox);
+
+ /* KLUDGE: render grid lines before axis */
+ for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
+ child_view = ptr->data;
+ if (!grid_line_rendered && IS_GOG_AXIS (child_view->model)) {
+ grid_line_render (ptr, bbox);
+ grid_line_rendered = TRUE;
+ }
+ gog_view_render (ptr->data, bbox);
+ }
+}
+
+static void
+gog_chart_view_class_init (GogChartViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+ GogOutlinedViewClass *oview_klass = (GogOutlinedViewClass *) gview_klass;
+
+ cview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->size_allocate = gog_chart_view_size_allocate;
+ view_klass->clip = TRUE;
+ view_klass->render = gog_chart_view_render;
+ oview_klass->call_parent_render = FALSE;
+}
+
+static GSF_CLASS (GogChartView, gog_chart_view,
+ gog_chart_view_class_init, gog_chart_view_init,
+ GOG_OUTLINED_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/go-data.h
@@ -0,0 +1,84 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-data.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GO_DATA_H
+#define GO_DATA_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/utils/goffice-utils.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GO_DATA_TYPE (go_data_get_type ())
+#define GO_DATA(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_TYPE, GOData))
+#define IS_GO_DATA(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_TYPE))
+
+GType go_data_get_type (void);
+gboolean go_data_needs_recalc (GOData const *dat);
+GOData *go_data_dup (GOData const *src);
+gboolean go_data_eq (GOData const *a, GOData const *b);
+GOFormat *go_data_preferred_fmt (GOData const *dat);
+char *go_data_as_str (GOData const *dat);
+gboolean go_data_from_str (GOData *dat, char const *str);
+void go_data_emit_changed (GOData *dat);
+
+/*************************************************************************/
+
+#define GO_DATA_SCALAR_TYPE (go_data_scalar_get_type ())
+#define GO_DATA_SCALAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_SCALAR_TYPE, GODataScalar))
+#define IS_GO_DATA_SCALAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_SCALAR_TYPE))
+
+GType go_data_scalar_get_type (void);
+
+double go_data_scalar_get_value (GODataScalar *val);
+char const *go_data_scalar_get_str (GODataScalar *val);
+
+/*************************************************************************/
+
+#define GO_DATA_VECTOR_TYPE (go_data_vector_get_type ())
+#define GO_DATA_VECTOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_VECTOR_TYPE, GODataVector))
+#define IS_GO_DATA_VECTOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_VECTOR_TYPE))
+
+GType go_data_vector_get_type (void);
+
+int go_data_vector_get_len (GODataVector *vec);
+double *go_data_vector_get_values (GODataVector *vec);
+double go_data_vector_get_value (GODataVector *vec, unsigned i);
+char *go_data_vector_get_str (GODataVector *vec, unsigned i);
+void go_data_vector_get_minmax (GODataVector *vec, double *min, double *max);
+
+/*************************************************************************/
+
+#define GO_DATA_MATRIX_TYPE (go_data_matrix_get_type ())
+#define GO_DATA_MATRIX(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GO_DATA_MATRIX_TYPE, GODataMatrix))
+#define IS_GO_DATA_MATRIX(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_DATA_MATRIX_TYPE))
+
+GType go_data_matrix_get_type (void);
+
+GOMatrixSize go_data_matrix_get_size (GODataMatrix *mat);
+double *go_data_matrix_get_values (GODataMatrix *mat);
+double go_data_matrix_get_value (GODataMatrix *mat, unsigned i, unsigned j);
+char *go_data_matrix_get_str (GODataMatrix *mat, unsigned i, unsigned j);
+void go_data_matrix_get_minmax (GODataMatrix *mat, double *min, double *max);
+
+G_END_DECLS
+
+#endif /* GO_DATA_H */
--- /dev/null
+++ lib/goffice/graph/gog-object.h
@@ -0,0 +1,156 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-object.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_OBJECT_H
+#define GOG_OBJECT_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+#include <command-context.h> /* for GnmCmdContext */
+#include <libart_lgpl/art_rect.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GOG_OBJECT_NAME_BY_ROLE = 1,
+ GOG_OBJECT_NAME_BY_TYPE = 2,
+ GOG_OBJECT_NAME_MANUALLY = 3
+} GogObjectNamingConv;
+
+struct _GogObjectRole {
+ char const *id; /* for persistence */
+ char const *is_a_typename;
+ unsigned priority;
+
+ guint32 allowable_positions;
+ GogObjectPosition default_position;
+ GogObjectNamingConv naming_conv;
+
+ gboolean (*can_add) (GogObject const *parent);
+ gboolean (*can_remove) (GogObject const *child);
+ GogObject *(*allocate) (GogObject *parent);
+ void (*post_add) (GogObject *parent, GogObject *child);
+ void (*pre_remove) (GogObject *parent, GogObject *child);
+ void (*post_remove) (GogObject *parent, GogObject *child);
+
+ union { /* allow people to tack some useful tidbits on the end */
+ int i;
+ float f;
+ gpointer p;
+ } user;
+};
+
+struct _GogObject {
+ GObject base;
+
+ char *user_name; /* user assigned, NULL will fall back to id */
+ char *id; /* system generated */
+ GogObjectRole const *role;
+
+ GogObject *parent;
+ GSList *children;
+
+ GogObjectPosition position;
+ GogViewAllocation *manual_position;
+
+ unsigned needs_update : 1;
+ unsigned being_updated : 1;
+ unsigned explicitly_typed_role : 1; /* did we create it automaticly */
+};
+
+typedef struct {
+ GObjectClass base;
+
+ GHashTable *roles;
+ GType view_type;
+
+ unsigned use_parent_as_proxy : 1; /* when we change, pretend it was our parent */
+
+ /* Virtuals */
+ void (*update) (GogObject *obj);
+ void (*parent_changed) (GogObject *obj, gboolean was_set);
+ char const *(*type_name) (GogObject const *obj);
+ gpointer (*editor) (GogObject *obj,
+ GogDataAllocator *dalloc, GnmCmdContext *cc);
+
+ /* signals */
+ void (*changed) (GogObject *obj, gboolean size);
+ void (*name_changed) (GogObject *obj);
+ void (*possible_additions_changed) (GogObject const *obj);
+ void (*child_added) (GogObject *parent, GogObject *child);
+ void (*child_removed) (GogObject *parent, GogObject *child);
+ void (*child_name_changed) (GogObject const *obj, GogObject const *child);
+ void (*children_reordered) (GogObject *obj);
+} GogObjectClass;
+
+#define GOG_OBJECT_TYPE (gog_object_get_type ())
+#define GOG_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_OBJECT_TYPE, GogObject))
+#define IS_GOG_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_OBJECT_TYPE))
+#define GOG_OBJECT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_OBJECT_TYPE, GogObjectClass))
+#define IS_GOG_OBJECT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_OBJECT_TYPE))
+#define GOG_OBJECT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_OBJECT_TYPE, GogObjectClass))
+
+#define GOG_PARAM_PERSISTENT (1 << (G_PARAM_USER_SHIFT+0))
+#define GOG_PARAM_FORCE_SAVE (1 << (G_PARAM_USER_SHIFT+1)) /* even if the value == default */
+
+GType gog_object_get_type (void);
+
+GogObject *gog_object_dup (GogObject const *obj, GogObject *new_parent);
+GogObject *gog_object_get_parent (GogObject const *obj);
+GogObject *gog_object_get_parent_typed (GogObject const *obj, GType t);
+GogGraph *gog_object_get_graph (GogObject const *obj);
+GogTheme *gog_object_get_theme (GogObject const *obj);
+char const *gog_object_get_id (GogObject const *obj);
+char const *gog_object_get_name (GogObject const *obj);
+void gog_object_set_name (GogObject *obj, char *name, GError **err);
+GSList *gog_object_get_children (GogObject const *obj, GogObjectRole const *filter);
+GogObject *gog_object_get_child_by_role(GogObject const *obj, GogObjectRole const *role);
+gpointer gog_object_get_editor (GogObject *obj,
+ GogDataAllocator *dalloc, GnmCmdContext *cc);
+GogView *gog_object_new_view (GogObject const *obj, GogView *view);
+gboolean gog_object_is_deletable (GogObject const *obj);
+GSList *gog_object_possible_additions (GogObject const *obj);
+GogObject *gog_object_add_by_role (GogObject *parent,
+ GogObjectRole const *role, GogObject *child);
+GogObject *gog_object_add_by_name (GogObject *parent,
+ char const *role, GogObject *child);
+void gog_object_can_reorder (GogObject const *obj,
+ gboolean *inc_ok, gboolean *dec_ok);
+GogObject *gog_object_reorder (GogObject const *obj,
+ gboolean inc, gboolean goto_max);
+GogObjectPosition gog_object_get_pos (GogObject const *obj);
+gboolean gog_object_set_pos (GogObject *obj, GogObjectPosition p);
+
+GogObjectRole const *gog_object_find_role_by_name (GogObject const *obj,
+ char const *role);
+
+/* protected */
+void gog_object_update (GogObject *obj);
+gboolean gog_object_request_update (GogObject *obj);
+void gog_object_emit_changed (GogObject *obj, gboolean size);
+gboolean gog_object_clear_parent (GogObject *obj);
+gboolean gog_object_set_parent (GogObject *child, GogObject *parent,
+ GogObjectRole const *role, char *name);
+void gog_object_register_roles (GogObjectClass *klass,
+ GogObjectRole const *roles, unsigned n_roles);
+
+G_END_DECLS
+
+#endif /* GOG_OBJECT_H */
--- /dev/null
+++ lib/goffice/graph/gog-object.c
@@ -0,0 +1,873 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-object.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-graph-impl.h> /* for gog_graph_request_update */
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/go-data.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <stdlib.h>
+
+enum {
+ CHILD_ADDED,
+ CHILD_REMOVED,
+ CHILD_NAME_CHANGED,
+ CHILDREN_REORDERED,
+ NAME_CHANGED,
+ CHANGED,
+ LAST_SIGNAL
+};
+static gulong gog_object_signals [LAST_SIGNAL] = { 0, };
+
+static GObjectClass *parent_klass;
+static void
+gog_object_finalize (GObject *gobj)
+{
+ GogObject *obj = GOG_OBJECT (gobj);
+
+ g_free (obj->user_name); obj->user_name = NULL;
+ g_free (obj->id); obj->id = NULL;
+
+ g_slist_foreach (obj->children, (GFunc) g_object_unref, NULL);
+ g_slist_free (obj->children);
+ obj->children = NULL;
+
+ (parent_klass->finalize) (gobj);
+}
+
+static void
+gog_object_parent_changed (GogObject *child, gboolean was_set)
+{
+ GSList *ptr = child->children;
+ for (; ptr != NULL ; ptr = ptr->next) {
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (ptr->data);
+ (*klass->parent_changed) (ptr->data, was_set);
+ }
+
+ if (IS_GOG_DATASET (child))
+ gog_dataset_parent_changed (GOG_DATASET (child), was_set);
+}
+
+static void
+gog_object_class_init (GObjectClass *klass)
+{
+ GogObjectClass *gog_klass = (GogObjectClass *)klass;
+ parent_klass = g_type_class_peek_parent (klass);
+ klass->finalize = gog_object_finalize;
+ gog_klass->parent_changed = gog_object_parent_changed;
+
+ gog_object_signals [CHILD_ADDED] = g_signal_new ("child-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, child_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ gog_object_signals [CHILD_REMOVED] = g_signal_new ("child-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, child_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ gog_object_signals [CHILD_NAME_CHANGED] = g_signal_new ("child-name-changed",
+ G_TYPE_FROM_CLASS (gog_klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, child_name_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ gog_object_signals [CHILDREN_REORDERED] = g_signal_new ("children-reordered",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, children_reordered),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ gog_object_signals [NAME_CHANGED] = g_signal_new ("name-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, name_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ gog_object_signals [CHANGED] = g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogObjectClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+static void
+gog_object_init (GogObject *obj)
+{
+ obj->children = NULL;
+ obj->user_name = NULL;
+ obj->id = NULL;
+ obj->needs_update = FALSE;
+ obj->being_updated = FALSE;
+}
+
+GSF_CLASS (GogObject, gog_object,
+ gog_object_class_init, gog_object_init,
+ G_TYPE_OBJECT)
+
+static char *
+gog_object_generate_name (GogObject *obj)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+ GogObject *tmp;
+ char const *type_name;
+ unsigned name_len, i, max_index = 0;
+ GSList *ptr;
+
+ g_return_val_if_fail (klass != NULL, NULL);
+ g_return_val_if_fail (obj->role != NULL, NULL);
+
+ switch (obj->role->naming_conv) {
+ default :
+ case GOG_OBJECT_NAME_MANUALLY :
+ g_warning ("Role %s should not be autogenerating names",
+ obj->role->id);
+
+ case GOG_OBJECT_NAME_BY_ROLE :
+ g_return_val_if_fail (obj->role != NULL, NULL);
+ type_name = _(obj->role->id);
+ break;
+
+ case GOG_OBJECT_NAME_BY_TYPE :
+ g_return_val_if_fail (klass->type_name != NULL, NULL);
+ type_name = _((*klass->type_name) (obj));
+ break;
+ }
+
+ g_return_val_if_fail (type_name != NULL, NULL);
+ name_len = strlen (type_name);
+
+ for (ptr = obj->parent->children; ptr != NULL ; ptr = ptr->next) {
+ tmp = GOG_OBJECT (ptr->data);
+ if (tmp->id != NULL &&
+ 0 == strncmp (type_name, tmp->id, name_len)) {
+ i = strtol (tmp->id + name_len, NULL, 10);
+ if (max_index < i)
+ max_index = i;
+ }
+ }
+ return g_strdup_printf ("%s%d", type_name, max_index + 1);
+}
+
+/**
+ * gog_object_dup :
+ * @src : #GogObject
+ * @new_parent : #GogObject the parent tree for the object (can be NULL)
+ *
+ * Create a deep copy of @obj using @new_parent as its parent.
+ **/
+GogObject *
+gog_object_dup (GogObject const *src, GogObject *new_parent)
+{
+ gint n, last;
+ GParamSpec **props;
+ GogObject *dst = NULL;
+ GSList *ptr;
+ GValue val = { 0 };
+
+ if (src == NULL)
+ return NULL;
+
+ g_return_val_if_fail (GOG_OBJECT (src) != NULL, NULL);
+
+ if (src->role == NULL || src->explicitly_typed_role)
+ dst = g_object_new (G_OBJECT_TYPE (src), NULL);
+ if (new_parent)
+ dst = gog_object_add_by_role (new_parent, src->role, dst);
+
+ dst->position = src->position;
+ /* properties */
+ props = g_object_class_list_properties (G_OBJECT_GET_CLASS (src), &n);
+ while (n-- > 0)
+ if (props[n]->flags & GOG_PARAM_PERSISTENT) {
+ g_value_init (&val, props[n]->value_type);
+ g_object_get_property (G_OBJECT (src), props[n]->name, &val);
+ g_object_set_property (G_OBJECT (dst), props[n]->name, &val);
+ g_value_unset (&val);
+ }
+ g_free (props);
+
+ if (IS_GOG_DATASET (src)) { /* convenience to save data */
+ GogDataset const *src_set = GOG_DATASET (src);
+ GogDataset *dst_set = GOG_DATASET (dst);
+
+ gog_dataset_dims (src_set, &n, &last);
+ for ( ; n <= last ; n++)
+ gog_dataset_set_dim (dst_set, n,
+ go_data_dup (gog_dataset_get_dim (src_set, n)),
+ NULL);
+ }
+
+ for (ptr = src->children; ptr != NULL ; ptr = ptr->next)
+ /* children added directly to new parent, no need to use the
+ * function result */
+ gog_object_dup (ptr->data, dst);
+
+ return dst;
+}
+
+/**
+ * gog_object_get_parent :
+ * @obj : a #GogObject
+ *
+ * Returns @obj's parent, potentially NULL if it has not been added to a
+ * heirarchy yet. does not change ref-count in any way.
+ **/
+GogObject *
+gog_object_get_parent (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+ return obj->parent;
+}
+
+/**
+ * gog_object_get_parent_typed :
+ * @obj : a #GogObject
+ * @type : a #GType
+ *
+ * Returns @obj's parent of type @type, potentially NULL if it has not been
+ * added to a heirarchy yet or none of the parents are of type @type.
+ **/
+GogObject *
+gog_object_get_parent_typed (GogObject const *obj, GType t)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+
+ for (; obj != NULL ; obj = obj->parent)
+ if (G_TYPE_CHECK_INSTANCE_TYPE (obj, t))
+ return GOG_OBJECT (obj); /* const cast */
+ return NULL;
+}
+
+/**
+ * gog_object_get_graph :
+ * @obj : const * #GogObject
+ *
+ * Returns the parent graph.
+ **/
+GogGraph *
+gog_object_get_graph (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+
+ for (; obj != NULL ; obj = obj->parent)
+ if (IS_GOG_GRAPH (obj))
+ return GOG_GRAPH (obj);
+ return NULL;
+}
+
+GogTheme *
+gog_object_get_theme (GogObject const *obj)
+{
+ GogGraph *graph = gog_object_get_graph (obj);
+
+ return (graph != NULL) ? gog_graph_get_theme (graph) : NULL;
+}
+
+/**
+ * gog_object_get_name :
+ * @obj : a #GogObject
+ *
+ * No need to free the result
+ **/
+char const *
+gog_object_get_name (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+ return (obj->user_name != NULL && *obj->user_name != '\0') ? obj->user_name : obj->id;
+}
+
+/**
+ * gog_object_set_name :
+ * @obj : #GogObject
+ * @name :
+ * @err : #GError
+ *
+ * Assign the new name and signals that it has changed.
+ * NOTE : it _absorbs_ @name rather than copying it, and generates a new name
+ * if @name == NULL
+ **/
+void
+gog_object_set_name (GogObject *obj, char *name, GError **err)
+{
+ GogObject *tmp;
+
+ g_return_if_fail (GOG_OBJECT (obj) != NULL);
+
+ if (obj->user_name == name)
+ return;
+ g_free (obj->user_name);
+ obj->user_name = name;
+
+ g_signal_emit (G_OBJECT (obj),
+ gog_object_signals [NAME_CHANGED], 0);
+
+ for (tmp = obj; tmp != NULL ; tmp = tmp->parent)
+ g_signal_emit (G_OBJECT (tmp),
+ gog_object_signals [CHILD_NAME_CHANGED], 0, obj);
+}
+
+/**
+ * gog_object_get_children :
+ * @obj : a #GogObject
+ * @filter : an optional #GogObjectRole to use as a filter
+ *
+ * The list needs to be Freed
+ **/
+GSList *
+gog_object_get_children (GogObject const *obj, GogObjectRole const *filter)
+{
+ GSList *ptr, *res = NULL;
+
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+
+ if (filter == NULL)
+ return g_slist_copy (obj->children);
+
+ for (ptr = obj->children ; ptr != NULL ; ptr = ptr->next)
+ if (GOG_OBJECT (ptr->data)->role == filter)
+ res = g_slist_prepend (res, ptr->data);
+ return g_slist_reverse (res);
+}
+
+/**
+ * gog_object_get_child_by_role :
+ * @obj : a #GogObject
+ * @role : a #GogObjectRole to use as a filter
+ *
+ * A convenience routine to handle a unique child
+ * Returns NULL and spews an error if there is more than one.
+ **/
+GogObject *
+gog_object_get_child_by_role (GogObject const *obj, GogObjectRole const *role)
+{
+ GogObject *res = NULL;
+ GSList *children = gog_object_get_children (obj, role);
+
+ if (children != NULL && children->next == NULL)
+ res = children->data;
+ g_slist_free (children);
+ return res;
+}
+
+/**
+ * gog_object_is_deletable :
+ * @obj : a #GogObject
+ *
+ * Can the specified @obj be deleted ?
+ **/
+gboolean
+gog_object_is_deletable (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, FALSE);
+
+ if (IS_GOG_GRAPH (obj))
+ return FALSE;
+
+ return obj->role == NULL || obj->role->can_remove == NULL ||
+ (obj->role->can_remove) (obj);
+}
+
+struct possible_add_closure {
+ GSList *res;
+ GogObject const *parent;
+};
+
+static void
+cb_collect_possible_additions (char const *name, GogObjectRole const *role,
+ struct possible_add_closure *data)
+{
+ if (role->can_add == NULL || (role->can_add) (data->parent))
+ data->res = g_slist_prepend (data->res, (gpointer)role);
+}
+
+static int
+gog_object_position_cmp (GogObjectPosition pos)
+{
+ if (pos & GOG_POSITION_COMPASS)
+ return 0;
+ if (pos == GOG_POSITION_SPECIAL)
+ return 2;
+ return 1; /* GOG_POSITION_MANUAL */
+}
+
+static int
+gog_role_cmp (GogObjectRole const *a, GogObjectRole const *b)
+{
+ int index_a = gog_object_position_cmp (a->allowable_positions);
+ int index_b = gog_object_position_cmp (b->allowable_positions);
+
+ /* intentionally reverse to put SPECIAL at the top */
+ if (index_a < index_b)
+ return 1;
+ else if (index_a > index_b)
+ return -1;
+ return b->priority - a->priority;
+}
+
+static int
+gog_role_cmp_full (GogObjectRole const *a, GogObjectRole const *b)
+{
+ int res = gog_role_cmp (a, b);
+ if (res != 0)
+ return res;
+ return g_utf8_collate (a->id, b->id);
+}
+
+/**
+ * gog_object_possible_additions :
+ * @parent : a #GogObject
+ *
+ * returns a list of GogObjectRoles that could be added
+ *
+ * The resulting list needs to be freed
+ **/
+GSList *
+gog_object_possible_additions (GogObject const *parent)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (parent);
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ if (klass->roles != NULL) {
+ struct possible_add_closure data;
+ data.res = NULL;
+ data.parent = parent;
+
+ g_hash_table_foreach (klass->roles,
+ (GHFunc) cb_collect_possible_additions, &data);
+
+ return g_slist_sort (data.res, (GCompareFunc) gog_role_cmp_full);
+ }
+
+ return NULL;
+}
+
+/**
+ * gog_object_can_reorder :
+ * @obj : #GogObject
+ * @inc_ok : possibly NULL pointer.
+ * @dec_ok : possibly NULL pointer.
+ *
+ * If @obj can move forward or backward in its parents child list
+ **/
+void
+gog_object_can_reorder (GogObject const *obj, gboolean *inc_ok, gboolean *dec_ok)
+{
+ GogObject const *parent;
+ GSList *ptr;
+
+ g_return_if_fail (GOG_OBJECT (obj) != NULL);
+
+ if (inc_ok != NULL)
+ *inc_ok = FALSE;
+ if (dec_ok != NULL)
+ *dec_ok = FALSE;
+
+ if (obj->parent == NULL || gog_object_get_graph (obj) == NULL)
+ return;
+ parent = obj->parent;
+ ptr = parent->children;
+
+ g_return_if_fail (ptr != NULL);
+
+ /* find a pointer to the previous sibling */
+ if (ptr->data != obj) {
+ while (ptr->next != NULL && ptr->next->data != obj)
+ ptr = ptr->next;
+
+ g_return_if_fail (ptr->next != NULL);
+
+ if (inc_ok != NULL &&
+ !gog_role_cmp (((GogObject *)ptr->data)->role, obj->role))
+ *inc_ok = TRUE;
+
+ ptr = ptr->next;
+ }
+
+ /* ptr now points at @obj */
+ if (dec_ok != NULL && ptr->next != NULL &&
+ !gog_role_cmp (obj->role, ((GogObject *)ptr->next->data)->role))
+ *dec_ok = TRUE;
+}
+
+/**
+ * gog_object_reorder :
+ * @obj : #GogObject
+ * @inc :
+ * @goto_max :
+ *
+ * Returns the object just before @obj in the new ordering.
+ **/
+GogObject *
+gog_object_reorder (GogObject const *obj, gboolean inc, gboolean goto_max)
+{
+ GogObject *parent, *obj_follows;
+ GSList **ptr, *tmp;
+
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
+
+ if (obj->parent == NULL || gog_object_get_graph (obj) == NULL)
+ return NULL;
+ parent = obj->parent;
+
+ if (inc)
+ parent->children = g_slist_reverse (parent->children);
+
+ for (ptr = &parent->children; *ptr != NULL && (*ptr)->data != obj ;)
+ ptr = &(*ptr)->next;
+
+ g_return_val_if_fail (*ptr != NULL, NULL);
+ g_return_val_if_fail ((*ptr)->next != NULL, NULL);
+
+ tmp = *ptr;
+ *ptr = tmp->next;
+ ptr = &(*ptr)->next;
+
+ while (goto_max && *ptr != NULL &&
+ !gog_role_cmp (obj->role, ((GogObject *)((*ptr)->data))->role))
+ ptr = &(*ptr)->next;
+
+ tmp->next = *ptr;
+ *ptr = tmp;
+
+ if (inc)
+ parent->children = g_slist_reverse (parent->children);
+
+ if (parent->children->data != obj) {
+ for (tmp = parent->children ; tmp->next->data != obj ; )
+ tmp = tmp->next;
+ obj_follows = tmp->data;
+ } else
+ obj_follows = NULL;
+
+ /* Pass the sibling that precedes obj, or NULL if is the head */
+ g_signal_emit (G_OBJECT (parent),
+ gog_object_signals [CHILDREN_REORDERED], 0);
+ gog_object_emit_changed (parent, TRUE);
+
+ return obj_follows;
+}
+
+/**
+ * gog_object_get_editor :
+ * @obj : #GogObject
+ * @dalloc : #GogDataAllocator
+ * @cc : #GnmCmdContext
+ *
+ **/
+gpointer
+gog_object_get_editor (GogObject *obj, GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+ g_return_val_if_fail (klass != NULL, NULL);
+ if (klass->editor) {
+ /* If there are pending updates do them before creating the editor
+ * to avoid expensive widget changes later */
+ gog_graph_force_update (gog_object_get_graph (obj));
+ return (*klass->editor) (obj, dalloc, cc);
+ }
+ return NULL;
+}
+
+/**
+ * gog_object_new_view :
+ * @obj : a #GogObject
+ * @data :
+ **/
+GogView *
+gog_object_new_view (GogObject const *obj, GogView *parent)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ if (klass->view_type != 0)
+ /* set model before parent */
+ return g_object_new (klass->view_type,
+ "model", obj,
+ "parent", parent,
+ NULL);
+
+ return NULL;
+}
+
+void
+gog_object_update (GogObject *obj)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+ GSList *ptr;
+
+ g_return_if_fail (klass != NULL);
+
+ ptr = obj->children; /* depth first */
+ for (; ptr != NULL ; ptr = ptr->next)
+ gog_object_update (ptr->data);
+
+ if (obj->needs_update) {
+ obj->needs_update = FALSE;
+ obj->being_updated = TRUE;
+ gog_debug (0, g_warning ("updating %s (%p)", G_OBJECT_TYPE_NAME (obj), obj););
+ if (klass->update != NULL)
+ (*klass->update) (obj);
+ obj->being_updated = FALSE;
+ }
+}
+
+gboolean
+gog_object_request_update (GogObject *obj)
+{
+ GogGraph *graph;
+ g_return_val_if_fail (GOG_OBJECT (obj), FALSE);
+ g_return_val_if_fail (!obj->being_updated, FALSE);
+
+ if (obj->needs_update)
+ return FALSE;
+
+ graph = gog_object_get_graph (obj);
+ if (graph == NULL) /* we are not linked into a graph yet */
+ return FALSE;
+
+ gog_graph_request_update (graph);
+ obj->needs_update = TRUE;
+
+ return TRUE;
+}
+
+void
+gog_object_emit_changed (GogObject *obj, gboolean resize)
+{
+ GogObjectClass *gog_klass;
+
+ g_return_if_fail (GOG_OBJECT (obj));
+
+ gog_klass = GOG_OBJECT_GET_CLASS (obj);
+
+ if (gog_klass->use_parent_as_proxy) {
+ obj = obj->parent;
+ if (obj != NULL) {
+ g_return_if_fail (IS_GOG_OBJECT (obj));
+ gog_object_emit_changed (obj, resize);
+ }
+ return;
+ }
+ g_signal_emit (G_OBJECT (obj),
+ gog_object_signals [CHANGED], 0, resize);
+}
+
+/******************************************************************************/
+
+/**
+ * gog_object_clear_parent :
+ * @obj : #GogObject
+ *
+ * Does _not_ unref the child, which in effect adds a ref by freeing up the ref
+ * previously associated with the parent.
+ **/
+gboolean
+gog_object_clear_parent (GogObject *obj)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+ GogObject *parent;
+
+ g_return_val_if_fail (GOG_OBJECT (obj), FALSE);
+ g_return_val_if_fail (obj->parent != NULL, FALSE);
+ g_return_val_if_fail (gog_object_is_deletable (obj), FALSE);
+
+ parent = obj->parent;
+ g_signal_emit (G_OBJECT (parent),
+ gog_object_signals [CHILD_REMOVED], 0, obj);
+ (*klass->parent_changed) (obj, FALSE);
+
+ if (obj->role != NULL && obj->role->pre_remove != NULL)
+ (obj->role->pre_remove) (parent, obj);
+
+ parent->children = g_slist_remove (parent->children, obj);
+ obj->parent = NULL;
+
+ if (obj->role != NULL && obj->role->post_remove != NULL)
+ (obj->role->post_remove) (parent, obj);
+
+ obj->role = NULL;
+
+ return TRUE;
+}
+
+/**
+ * gog_object_set_parent :
+ * @child : #GogObject.
+ * @parent : #GogObject.
+ * @id : optionally %NULL.
+ * @role : a static string that can be sent to @parent::add
+ *
+ * Absorbs a ref to @child
+ **/
+gboolean
+gog_object_set_parent (GogObject *child, GogObject *parent,
+ GogObjectRole const *role, char *id)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (child);
+ GSList **step;
+
+ g_return_val_if_fail (GOG_OBJECT (child), FALSE);
+ g_return_val_if_fail (child->parent == NULL, FALSE);
+ g_return_val_if_fail (role != NULL, FALSE);
+
+ child->parent = parent;
+ child->role = role;
+ child->position = role->default_position;
+
+ /* Insert sorted based on hokey little ordering */
+ step = &parent->children;
+ while (*step != NULL &&
+ gog_role_cmp_full (GOG_OBJECT ((*step)->data)->role, role) >= 0)
+ step = &((*step)->next);
+ *step = g_slist_prepend (*step, child);
+
+ g_free (child->id);
+ g_free (child->user_name);
+ child->id = (id != NULL) ? id : gog_object_generate_name (child);
+ if (child->id == NULL) child->id = g_strdup ("BROKEN");
+
+ if (role->post_add != NULL)
+ (role->post_add) (parent, child);
+ (*klass->parent_changed) (child, TRUE);
+
+ g_signal_emit (G_OBJECT (parent),
+ gog_object_signals [CHILD_ADDED], 0, child);
+
+ return TRUE;
+}
+
+GogObject *
+gog_object_add_by_role (GogObject *parent, GogObjectRole const *role, GogObject *child)
+{
+ GType is_a;
+ gboolean const explicitly_typed_role = (child != NULL);
+
+ g_return_val_if_fail (role != NULL, NULL);
+ g_return_val_if_fail (GOG_OBJECT (parent) != NULL, NULL);
+
+ is_a = g_type_from_name (role->is_a_typename);
+
+ g_return_val_if_fail (is_a != 0, NULL);
+
+ if (child == NULL)
+ child = (role->allocate)
+ ? (role->allocate) (parent)
+ : g_object_new (is_a, NULL);
+
+ g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (child, is_a), NULL);
+ child->explicitly_typed_role = explicitly_typed_role;
+ if (gog_object_set_parent (child, parent, role, NULL))
+ return child;
+ g_object_unref (child);
+ return NULL;
+}
+
+/**
+ * gog_object_add_by_name :
+ * @parent : #GogObject
+ * @role :
+ * @child : optionally null #GogObject
+ *
+ * Returns a newly created child of @parent in @role. If @child is provided,
+ * it is assumed to be an unaffiliated object that will be assigned in @role.
+ * On failure return NULL.
+ **/
+GogObject *
+gog_object_add_by_name (GogObject *parent,
+ char const *role, GogObject *child)
+{
+ return gog_object_add_by_role (parent,
+ gog_object_find_role_by_name (parent, role), child);
+}
+
+GogObjectPosition
+gog_object_get_pos (GogObject const *obj)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, GOG_POSITION_SPECIAL);
+ return obj->position;
+}
+
+/**
+ * gog_object_set_pos :
+ * @obj : #GogObject
+ * @pos : #GogObjectPosition
+ *
+ * Attempts to set the position of @obj to @pos.
+ * Returns TRUE the new position is permitted.
+ **/
+gboolean
+gog_object_set_pos (GogObject *obj, GogObjectPosition pos)
+{
+ g_return_val_if_fail (GOG_OBJECT (obj) != NULL, FALSE);
+ g_return_val_if_fail (obj->role != NULL, FALSE);
+
+ if (obj->position == pos)
+ return TRUE;
+
+ if ((obj->role->allowable_positions & pos) !=
+ (pos & (GOG_POSITION_COMPASS|GOG_POSITION_ANY_MANUAL)))
+ return FALSE;
+ obj->position = pos;
+ gog_object_emit_changed (obj, TRUE);
+ return TRUE;
+}
+
+GogObjectRole const *
+gog_object_find_role_by_name (GogObject const *obj, char const *role)
+{
+ GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ return g_hash_table_lookup (klass->roles, role);
+}
+
+void
+gog_object_register_roles (GogObjectClass *klass,
+ GogObjectRole const *roles, unsigned n_roles)
+{
+ unsigned i;
+ if (klass->roles == NULL)
+ klass->roles = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (i = 0 ; i < n_roles ; i++) {
+ g_return_if_fail (g_hash_table_lookup (klass->roles,
+ (gpointer )roles[i].id) == NULL);
+ g_hash_table_replace (klass->roles,
+ (gpointer )roles[i].id, (gpointer) (roles + i));
+ }
+}
--- /dev/null
+++ lib/goffice/graph/gog-outlined-object.c
@@ -0,0 +1,171 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-outlined-object.c : some utility classes for objects with outlines.
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-outlined-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/utils/go-units.h>
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+enum {
+ OUTLINED_OBJECT_PROP_0,
+ OUTLINED_OBJECT_PROP_PADDING_PTS
+};
+
+static void
+gog_outlined_object_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (obj);
+
+ switch (param_id) {
+ case OUTLINED_OBJECT_PROP_PADDING_PTS :
+ goo->padding_pts = g_value_get_double (value);
+ gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+}
+
+static void
+gog_outlined_object_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (obj);
+
+ switch (param_id) {
+ case OUTLINED_OBJECT_PROP_PADDING_PTS:
+ g_value_set_double (value, goo->padding_pts);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_outlined_object_class_init (GObjectClass *gobject_klass)
+{
+ gobject_klass->set_property = gog_outlined_object_set_property;
+ gobject_klass->get_property = gog_outlined_object_get_property;
+
+ g_object_class_install_property (gobject_klass, OUTLINED_OBJECT_PROP_PADDING_PTS,
+ g_param_spec_double ("padding_pts", "Padding Pts",
+ "# of pts separating charts in the grid.",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+}
+
+static void
+gog_outlined_object_init (GogOutlinedObject *goo)
+{
+ goo->padding_pts = GO_CM_TO_PT ((double).25);
+}
+
+GSF_CLASS (GogOutlinedObject, gog_outlined_object,
+ gog_outlined_object_class_init, gog_outlined_object_init,
+ GOG_STYLED_OBJECT_TYPE)
+
+double
+gog_outlined_object_get_pad (GogOutlinedObject const *goo)
+{
+ g_return_val_if_fail (GOG_OUTLINED_OBJECT (goo) != NULL, 0.);
+ return goo->padding_pts;
+}
+
+/*****************************************************************************/
+
+static GogViewClass *oview_parent_klass;
+
+static void
+gog_outlined_view_size_request (GogView *v, GogViewRequisition *req)
+{
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (v->model);
+ double outline = gog_renderer_line_size (v->renderer,
+ goo->base.style->outline.width);
+ double is_outline_visible = gog_style_is_outline_visible (goo->base.style);
+
+ if (goo->base.style->fill.type != GOG_FILL_STYLE_NONE || is_outline_visible) {
+ req->w += outline * 2 +
+ gog_renderer_pt2r_y (v->renderer, goo->padding_pts);
+ req->h += outline * 2 +
+ gog_renderer_pt2r_y (v->renderer, goo->padding_pts);
+ }
+}
+
+static void
+gog_outlined_view_size_allocate (GogView *v, GogViewAllocation const *a)
+{
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (v->model);
+ GogViewAllocation res = *a;
+ double outline = gog_renderer_line_size (v->renderer,
+ goo->base.style->outline.width);
+ double is_outline_visible = gog_style_is_outline_visible (goo->base.style);
+
+ /* We only need internal padding if there is an outline or a pattern */
+ if (goo->base.style->fill.type != GOG_FILL_STYLE_NONE || is_outline_visible) {
+ double pad_x = gog_renderer_pt2r_x (v->renderer, goo->padding_pts);
+ double pad_y = gog_renderer_pt2r_y (v->renderer, goo->padding_pts);
+ res.x += outline + pad_x/2;
+ res.y += outline + pad_y/2;
+ res.w -= outline * 2. + pad_x;
+ res.h -= outline * 2. + pad_y;
+ }
+ (oview_parent_klass->size_allocate) (v, &res);
+}
+
+static void
+gog_outlined_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogOutlinedViewClass *klass = GOG_OUTLINED_VIEW_GET_CLASS (view);
+
+ GogStyledObject *sobj = GOG_STYLED_OBJECT (view->model);
+ gog_renderer_push_style (view->renderer, sobj->style);
+ gog_renderer_draw_sharp_rectangle (view->renderer, &view->allocation, NULL);
+ gog_renderer_pop_style (view->renderer);
+
+ if (klass->call_parent_render)
+ (oview_parent_klass->render) (view, bbox);
+}
+
+static void
+gog_outlined_view_class_init (GogOutlinedViewClass *oview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) oview_klass;
+
+ oview_parent_klass = g_type_class_peek_parent (view_klass);
+ view_klass->size_request = gog_outlined_view_size_request;
+ view_klass->size_allocate = gog_outlined_view_size_allocate;
+ view_klass->render = gog_outlined_view_render;
+
+ oview_klass->call_parent_render = TRUE;
+}
+
+GSF_CLASS (GogOutlinedView, gog_outlined_view,
+ gog_outlined_view_class_init, NULL,
+ GOG_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-styled-object.h
@@ -0,0 +1,59 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-styled-object.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_STYLED_OBJECT_H
+#define GOG_STYLED_OBJECT_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogStyledObject {
+ GogObject base;
+
+ GogStyle *style;
+};
+
+typedef struct {
+ GogObjectClass base;
+
+ /* virtual */
+ void (*init_style) (GogStyledObject *obj, GogStyle *style);
+
+ /* signal */
+ void (*style_changed) (GogStyledObject *obj, GogStyle const *new_style);
+} GogStyledObjectClass;
+
+#define GOG_STYLED_OBJECT_TYPE (gog_styled_object_get_type ())
+#define GOG_STYLED_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_STYLED_OBJECT_TYPE, GogStyledObject))
+#define IS_GOG_STYLED_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_STYLED_OBJECT_TYPE))
+#define GOG_STYLED_OBJECT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_STYLED_OBJECT_TYPE, GogStyledObjectClass))
+
+GType gog_styled_object_get_type (void);
+gboolean gog_styled_object_set_style (GogStyledObject *gso, GogStyle *style);
+GogStyle *gog_styled_object_get_style (GogStyledObject *gso);
+GogStyle *gog_styled_object_get_auto_style (GogStyledObject *gso);
+void gog_styled_object_style_changed (GogStyledObject *gso);
+void gog_styled_object_apply_theme (GogStyledObject *gso, GogStyle *style);
+
+G_END_DECLS
+
+#endif /* GOG_STYLED_OBJECT_H */
--- /dev/null
+++ lib/goffice/graph/gog-renderer-gnome-print.c
@@ -0,0 +1,646 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-gnome-print.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+/* <style.h> is only needed for gnm_font_find_closest_from_weight_slant */
+#include <style.h>
+#include <goffice/graph/gog-renderer-gnome-print.h>
+#include <goffice/graph/gog-renderer-impl.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/utils/go-color.h>
+#include <goffice/utils/go-units.h>
+#include <goffice/utils/go-font.h>
+#include <goffice/utils/go-marker.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+#include <libart_lgpl/art_render_gradient.h>
+#include <libart_lgpl/art_render_svp.h>
+
+#include <math.h>
+#include <string.h>
+
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+#include <libgnomeprint/gnome-print-pango.h>
+#endif
+
+#define GOG_RENDERER_GNOME_PRINT_TYPE (gog_renderer_gnome_print_get_type ())
+#define GOG_RENDERER_GNOME_PRINT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RENDERER_GNOME_PRINT_TYPE, GogRendererGnomePrint))
+#define IS_GOG_RENDERER_GNOME_PRINT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RENDERER_GNOME_PRINT_TYPE))
+
+typedef struct _GogRendererGnomePrint GogRendererGnomePrint;
+
+struct _GogRendererGnomePrint {
+ GogRenderer base;
+
+ GPtrArray *fonts;
+ GnomePrintContext *gp_context;
+ PangoLayout *layout;
+};
+
+typedef GogRendererClass GogRendererGnomePrintClass;
+
+static GObjectClass *parent_klass;
+
+static GType gog_renderer_gnome_print_get_type (void);
+
+static void
+gog_renderer_gnome_print_finalize (GObject *obj)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (obj);
+
+ if (prend->gp_context != NULL) {
+ g_object_unref (prend->gp_context);
+ prend->gp_context = NULL;
+ }
+
+ if (prend->layout) {
+ g_object_unref (prend->layout);
+ prend->layout = NULL;
+ }
+
+ if (prend->fonts != NULL) {
+ int i;
+ GnomeFont *font;
+ for (i = prend->fonts->len; i-- > 0 ; ) {
+ font = g_ptr_array_index (prend->fonts, i);
+ if (font != NULL)
+ gnome_font_unref (font);
+ }
+
+ g_ptr_array_free (prend->fonts, TRUE);
+ prend->fonts = NULL;
+ }
+
+ (*parent_klass->finalize) (obj);
+}
+
+/*
+ * print_make_rectangle_path
+ * @pc print context
+ * @left left side x coordinate
+ * @bottom bottom side y coordinate
+ * @right right side x coordinate
+ * @top top side y coordinate
+ *
+ * Make a rectangular path.
+ * */
+
+static void
+print_make_rectangle_path (GnomePrintContext *pc,
+ double left, double bottom,
+ double right, double top)
+{
+ g_return_if_fail (pc != NULL);
+
+ gnome_print_newpath (pc);
+ gnome_print_moveto (pc, left, bottom);
+ gnome_print_lineto (pc, left, top);
+ gnome_print_lineto (pc, right, top);
+ gnome_print_lineto (pc, right, bottom);
+ gnome_print_closepath (pc);
+}
+
+static GnomeFont *
+get_font (GogRendererGnomePrint *prend, GOFont const *gf)
+{
+ GnomeFont *res = NULL;
+
+ if (gf->font_index < (int)prend->fonts->len)
+ res = g_ptr_array_index (prend->fonts, gf->font_index);
+ else
+ g_ptr_array_set_size (prend->fonts, gf->font_index+1);
+
+ if (res == NULL) {
+ PangoFontDescription *desc = gf->desc;
+ res = gnm_font_find_closest_from_weight_slant (
+ pango_font_description_get_family (desc),
+ pango_font_description_get_weight (desc) >= PANGO_WEIGHT_BOLD ? GNOME_FONT_BOLD : GNOME_FONT_REGULAR,
+ pango_font_description_get_style (desc) != PANGO_STYLE_NORMAL,
+ prend->base.zoom * pango_font_description_get_size (desc) / PANGO_SCALE);
+ g_ptr_array_index (prend->fonts, gf->font_index) = res;
+ }
+
+ return res;
+}
+
+static void
+set_color (GogRendererGnomePrint *prend, GOColor color)
+{
+ double r = ((double) UINT_RGBA_R (color)) / 255.;
+ double g = ((double) UINT_RGBA_G (color)) / 255.;
+ double b = ((double) UINT_RGBA_B (color)) / 255.;
+ double a = ((double) UINT_RGBA_A (color)) / 255.;
+ gnome_print_setrgbcolor (prend->gp_context, r, g, b);
+ gnome_print_setopacity (prend->gp_context, a);
+}
+
+static void
+gog_renderer_gnome_print_clip_push (GogRenderer *rend, GogRendererClip *clip)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (rend);
+
+ gnome_print_gsave (prend->gp_context);
+ print_make_rectangle_path (prend->gp_context,
+ clip->area.x, -clip->area.y,
+ clip->area.w + clip->area.x,
+ -clip->area.h - clip->area.y);
+ gnome_print_clip (prend->gp_context);
+}
+
+static void
+gog_renderer_gnome_print_clip_pop (GogRenderer *rend, GogRendererClip *clip)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (rend);
+
+ gnome_print_grestore (prend->gp_context);
+}
+
+static void
+draw_path (GogRendererGnomePrint *prend, ArtVpath const *path)
+{
+ gnome_print_newpath (prend->gp_context);
+ for ( ; path->code != ART_END ; path++)
+ switch (path->code) {
+ case ART_MOVETO_OPEN :
+ case ART_MOVETO :
+ gnome_print_moveto (prend->gp_context,
+ path->x, -path->y);
+ break;
+ case ART_LINETO :
+ gnome_print_lineto (prend->gp_context,
+ path->x, -path->y);
+ break;
+ default :
+ break;
+ }
+}
+
+static void
+setup_clip (GogRendererGnomePrint *prend, GogViewAllocation const *bound)
+{
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_moveto (prend->gp_context, bound->x, -bound->y);
+ gnome_print_lineto (prend->gp_context, bound->x + bound->w, -bound->y);
+ gnome_print_lineto (prend->gp_context, bound->x + bound->w, -bound->y - bound->h);
+ gnome_print_lineto (prend->gp_context, bound->x, -bound->y - bound->h);
+ gnome_print_clip (prend->gp_context);
+}
+
+static void
+set_dash (GogRendererGnomePrint *prend, ArtVpathDash *dash)
+{
+ if (dash == NULL ||
+ dash->n_dash == 0)
+ gnome_print_setdash (prend->gp_context, 0, NULL, 0.);
+ else
+ gnome_print_setdash (prend->gp_context, dash->n_dash, dash->dash, dash->offset);
+}
+
+static void
+gog_renderer_gnome_print_draw_path (GogRenderer *renderer, ArtVpath const *path,
+ GogViewAllocation const *bound)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (renderer);
+ GogStyle const *style = renderer->cur_style;
+
+ if (style->line.dash_type == GO_LINE_NONE)
+ return;
+
+ set_color (prend, style->line.color);
+ set_dash (prend, renderer->line_dash);
+ gnome_print_setlinewidth (prend->gp_context,
+ gog_renderer_line_size (renderer, style->line.width));
+
+ if (bound != NULL)
+ setup_clip (prend, bound);
+
+ if (style->line.dash_type != GO_LINE_SOLID && renderer->cur_clip != NULL) {
+ ArtVpath *clipped = go_line_clip_vpath (path, &renderer->cur_clip->area);
+ draw_path (prend, clipped);
+ g_free (clipped);
+ } else
+ draw_path (prend, path);
+
+ gnome_print_stroke (prend->gp_context);
+
+ if (bound != NULL)
+ gnome_print_grestore (prend->gp_context);
+}
+
+static void
+print_image (GogRendererGnomePrint *prend, GdkPixbuf *image, int w, int h)
+{
+ if (gdk_pixbuf_get_has_alpha (image))
+ gnome_print_rgbaimage (prend->gp_context,
+ gdk_pixbuf_get_pixels (image), w, h,
+ gdk_pixbuf_get_rowstride (image));
+ else
+ gnome_print_rgbimage (prend->gp_context,
+ gdk_pixbuf_get_pixels (image), w, h,
+ gdk_pixbuf_get_rowstride (image));
+}
+
+#define PIXBUF_SIZE 1024
+static void
+gog_renderer_gnome_print_draw_polygon (GogRenderer *renderer, ArtVpath const *path,
+ gboolean narrow, GogViewAllocation const *bound)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (renderer);
+ GogStyle const *style = renderer->cur_style;
+ gboolean with_outline = (!narrow && style->outline.dash_type != GO_LINE_NONE);
+ GdkPixbuf *image;
+ ArtDRect bbox;
+ ArtRender *render;
+ gint i, j, imax, jmax, w, h, x, y;
+ GOColor color;
+ ArtGradientLinear gradient;
+ ArtGradientStop stops[2];
+
+ if (bound != NULL)
+ setup_clip (prend, bound);
+
+ if (style->fill.type != GOG_FILL_STYLE_NONE || with_outline) {
+ if (style->outline.dash_type != GO_LINE_SOLID && renderer->cur_clip != NULL) {
+ ArtVpath *clipped = go_line_clip_vpath (path, &renderer->cur_clip->area);
+ draw_path (prend, clipped);
+ g_free (clipped);
+ } else
+ draw_path (prend, path);
+ gnome_print_closepath (prend->gp_context);
+ }
+
+ if (style->fill.type != GOG_FILL_STYLE_NONE) {
+
+ art_vpath_bbox_drect (path, &bbox);
+
+ switch (style->fill.type) {
+ case GOG_FILL_STYLE_PATTERN:
+ gnome_print_gsave (prend->gp_context);
+ if (go_pattern_is_solid (&style->fill.pattern, &color)) {
+ set_color (prend, color);
+ gnome_print_fill (prend->gp_context);
+ } else {
+ ArtSVP *fill = art_svp_from_vpath ((ArtVpath *)path);
+ image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, bbox.x1, bbox.y1);
+ gdk_pixbuf_fill (image, 0);
+ go_pattern_render_svp (&style->fill.pattern,
+ fill, 0, 0, bbox.x1, bbox.y1,
+ gdk_pixbuf_get_pixels (image),
+ gdk_pixbuf_get_rowstride (image));
+
+ gnome_print_translate (prend->gp_context, 0, - bbox.y1);
+ gnome_print_scale (prend->gp_context, bbox.x1, bbox.y1);
+ gnome_print_rgbaimage (prend->gp_context,
+ gdk_pixbuf_get_pixels (image),
+ gdk_pixbuf_get_width (image),
+ gdk_pixbuf_get_height (image),
+ gdk_pixbuf_get_rowstride (image));
+
+ art_free (fill);
+ g_object_unref (image);
+ }
+ gnome_print_grestore (prend->gp_context);
+ break;
+
+ case GOG_FILL_STYLE_GRADIENT:
+ image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, PIXBUF_SIZE, PIXBUF_SIZE);
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_clip (prend->gp_context);
+ render = art_render_new (0, 0, PIXBUF_SIZE, PIXBUF_SIZE,
+ gdk_pixbuf_get_pixels (image),
+ gdk_pixbuf_get_rowstride (image),
+ gdk_pixbuf_get_n_channels (image) - 1,
+ 8, ART_ALPHA_SEPARATE, NULL);
+
+ go_gradient_setup (&gradient,
+ style->fill.gradient.dir,
+ style->fill.pattern.back, style->fill.pattern.fore,
+ 0, 0, PIXBUF_SIZE, PIXBUF_SIZE,
+ stops);
+ art_render_gradient_linear (render,
+ &gradient, ART_FILTER_NEAREST);
+ art_render_invoke (render);
+ gnome_print_translate (prend->gp_context, bbox.x0, - bbox.y1);
+ gnome_print_scale (prend->gp_context, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0);
+ gnome_print_rgbaimage (prend->gp_context,
+ gdk_pixbuf_get_pixels (image),
+ gdk_pixbuf_get_width (image),
+ gdk_pixbuf_get_height (image),
+ gdk_pixbuf_get_rowstride (image));
+ gnome_print_grestore (prend->gp_context);
+ g_object_unref (image);
+ break;
+
+ case GOG_FILL_STYLE_IMAGE:
+ image = style->fill.image.image;
+ if (image == NULL)
+ break;
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_clip (prend->gp_context);
+ switch (style->fill.image.type) {
+ case GOG_IMAGE_CENTERED:
+ w = (bbox.x1 - bbox.x0) - gdk_pixbuf_get_width (image);
+ if (w > 0) w /= 2.; else w = 0.;
+ h = (bbox.y1 - bbox.y0) - gdk_pixbuf_get_height (image);
+ if (h > 0) h /= 2.; else h = 0.;
+
+ gnome_print_translate (prend->gp_context,
+ bbox.x0 + w, - bbox.y1 - h);
+ print_image (prend, image,
+ gdk_pixbuf_get_width (image),
+ gdk_pixbuf_get_height (image));
+ break;
+ case GOG_IMAGE_STRETCHED:
+ gnome_print_translate (prend->gp_context, bbox.x0, - bbox.y1);
+ gnome_print_scale (prend->gp_context, bbox.x1 - bbox.x0, bbox.y1 - bbox.y0);
+ print_image (prend, image,
+ gdk_pixbuf_get_width (image),
+ gdk_pixbuf_get_height (image));
+ break;
+
+ case GOG_IMAGE_WALLPAPER:
+ imax = (bbox.x1 - bbox.x0) / (w = gdk_pixbuf_get_width (image));
+ jmax = (bbox.y1 - bbox.y0) / (h = gdk_pixbuf_get_height (image));
+ x = 0;
+ for (i = 0; i < imax; i++) {
+ y = 0;
+ for (j = 0; j < jmax; j++) {
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_translate (prend->gp_context,
+ bbox.x0 + x,
+ - y - h - bbox.y0);
+ gnome_print_scale (prend->gp_context, w, h);
+ print_image (prend, image, w, h);
+ gnome_print_grestore (prend->gp_context);
+ y += h;
+ }
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_translate (prend->gp_context,
+ bbox.x0 + x,
+ - y - (int)(bbox.y1 - bbox.y0) % h - bbox.y0);
+ gnome_print_scale (prend->gp_context, w, (int)(bbox.y1 - bbox.y0) % h);
+ print_image (prend, image, w, (int)(bbox.y1 - bbox.y0) % h);
+ gnome_print_grestore (prend->gp_context);
+ x += w;
+ }
+ y = 0;
+ for (j = 0; j < jmax; j++) {
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_translate (prend->gp_context, bbox.x0 + x, - y - h - bbox.y0);
+ gnome_print_scale (prend->gp_context, (int)(bbox.x1 - bbox.x0) % w, h);
+ print_image (prend, image, (int)(bbox.x1 - bbox.x0) % w, h);
+ gnome_print_grestore (prend->gp_context);
+ y += h;
+ }
+ gnome_print_gsave (prend->gp_context);
+ gnome_print_translate (prend->gp_context, bbox.x0 + x, - y - (int)(bbox.y1 - bbox.y0) % h - bbox.y0);
+ gnome_print_scale (prend->gp_context, (int)(bbox.x1 - bbox.x0) % w, (int)(bbox.y1 - bbox.y0) % h);
+ print_image (prend, image, (int)(bbox.x1 - bbox.x0) % w, (int)(bbox.y1 - bbox.y0) % h);
+ gnome_print_grestore (prend->gp_context);
+ break;
+ }
+ gnome_print_grestore (prend->gp_context);
+ break;
+
+ case GOG_FILL_STYLE_NONE:
+ break; /* impossible */
+ }
+ }
+
+ if (with_outline) {
+ set_color (prend, style->outline.color);
+ set_dash (prend, renderer->outline_dash);
+ gnome_print_setlinewidth (prend->gp_context,
+ gog_renderer_line_size (renderer, style->outline.width));
+ gnome_print_stroke (prend->gp_context);
+ }
+ if (bound != NULL)
+ gnome_print_grestore (prend->gp_context);
+}
+
+static void
+gog_renderer_gnome_print_draw_text (GogRenderer *rend, char const *text,
+ GogViewAllocation const *pos, GtkAnchorType anchor,
+ GogViewAllocation *result)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (rend);
+ GnomeFont *gfont = get_font (prend, rend->cur_style->font.font);
+
+ if (text[0]) {
+ double x, y, w, h;
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+ int iw, ih;
+ const double dummy_dpi = 300; /* FIXME: What exactly is this? */
+ PangoFontDescription *pango_font = /* FIXME: can i get the pango font directly ? */
+ gnome_font_get_pango_description (gfont, dummy_dpi);
+
+ pango_layout_set_font_description (prend->layout, pango_font);
+ pango_layout_set_text (prend->layout, text, -1);
+ pango_layout_get_size (prend->layout, &iw, &ih);
+ w = iw / (double)PANGO_SCALE;
+ h = ih / (double)PANGO_SCALE;
+#else
+ /* This code will die when we require libgnomeprint 2.8 */
+ double font_ascent = gnome_font_get_ascender (gfont);
+ w = gnome_font_get_width_utf8 (gfont, text);
+ h = font_ascent + gnome_font_get_descender (gfont);
+#endif
+ x = pos->x;
+ switch (anchor) {
+ case GTK_ANCHOR_CENTER : case GTK_ANCHOR_N : case GTK_ANCHOR_S :
+ x -= w / 2.0;
+ break;
+ case GTK_ANCHOR_NE : case GTK_ANCHOR_SE : case GTK_ANCHOR_E :
+ x -= w;
+ break;
+ default : break;
+ }
+ if (x <= 0)
+ x = 0;
+
+ y = pos->y;
+ switch (anchor) {
+ case GTK_ANCHOR_CENTER : case GTK_ANCHOR_E : case GTK_ANCHOR_W :
+ y -= h / 2.0;
+ break;
+ case GTK_ANCHOR_SE : case GTK_ANCHOR_S : case GTK_ANCHOR_SW :
+ y -= h;
+ break;
+ default : break;
+ }
+ if (y <= 0)
+ y = 0;
+
+#warning "add clipping"
+
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+ gnome_print_moveto (prend->gp_context,x, -y);
+ gnome_print_pango_layout (prend->gp_context, prend->layout);
+ pango_font_description_free (pango_font);
+#else
+ /* This code will die when we require libgnomeprint 2.8 */
+ gnome_print_setfont (prend->gp_context, gfont);
+ gnome_print_moveto (prend->gp_context, x, -y - font_ascent);
+ gnome_print_show (prend->gp_context, text);
+#endif
+ if (result != NULL) {
+ result->x = x;
+ result->y = y;
+ result->w = w;
+ result->h = h;
+ }
+ }
+}
+
+static void
+gog_renderer_gnome_print_measure_text (GogRenderer *rend,
+ char const *text, GogViewRequisition *size)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (rend);
+ GnomeFont *gfont = get_font (prend, rend->cur_style->font.font);
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+ int iw, ih;
+ const double dummy_dpi = 300; /* FIXME: What exactly is this? */
+ PangoFontDescription *pango_font = /* FIXME: can i get the pango font directly ? */
+ gnome_font_get_pango_description (gfont, dummy_dpi);
+
+ pango_layout_set_font_description (prend->layout, pango_font);
+ pango_layout_set_text (prend->layout, text, -1);
+ pango_layout_get_size (prend->layout, &iw, &ih);
+ size->w = iw / (double)PANGO_SCALE;
+ size->h = ih / (double)PANGO_SCALE;
+#else
+ size->w = gnome_font_get_width_utf8 (gfont, text);
+ size->h = gnome_font_get_ascender (gfont) - gnome_font_get_descender (gfont);
+#endif
+}
+
+static void
+gog_renderer_gnome_print_draw_marker (GogRenderer *renderer, double x, double y)
+{
+ GogRendererGnomePrint *prend = GOG_RENDERER_GNOME_PRINT (renderer);
+ GOMarker *marker = renderer->cur_style->marker.mark;
+ ArtVpath const *outline_path_raw, *fill_path_raw;
+ ArtVpath *outline_path, *fill_path;
+ double scaling[6], translation[6], affine[6];
+ double half_size;
+
+ g_return_if_fail (marker != NULL);
+
+ go_marker_get_paths (marker, &outline_path_raw, &fill_path_raw);
+
+ if ((outline_path_raw == NULL) ||
+ (fill_path_raw == NULL))
+ return;
+
+ gnome_print_gsave (prend->gp_context);
+
+ half_size = gog_renderer_line_size (renderer, marker->size) / 2.0;
+ art_affine_scale (scaling, half_size, half_size);
+ art_affine_translate (translation, x, y);
+ art_affine_multiply (affine, scaling, translation);
+
+ outline_path = art_vpath_affine_transform (outline_path_raw, affine);
+ fill_path = art_vpath_affine_transform (fill_path_raw, affine);
+
+ gnome_print_setlinecap (prend->gp_context, ART_PATH_STROKE_CAP_ROUND);
+ set_color (prend, marker->fill_color);
+ draw_path (prend, fill_path);
+ gnome_print_closepath (prend->gp_context);
+ gnome_print_fill (prend->gp_context);
+
+ set_color (prend, marker->outline_color);
+ gnome_print_setlinewidth (prend->gp_context,
+ gog_renderer_line_size (renderer,
+ go_marker_get_outline_width (marker)));
+ draw_path (prend, outline_path);
+ gnome_print_stroke (prend->gp_context);
+ gnome_print_newpath (prend->gp_context);
+
+ gnome_print_grestore (prend->gp_context);
+
+ g_free (outline_path);
+ g_free (fill_path);
+}
+
+static void
+gog_renderer_gnome_print_class_init (GogRendererClass *rend_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) rend_klass;
+
+ parent_klass = g_type_class_peek_parent (rend_klass);
+ gobject_klass->finalize = gog_renderer_gnome_print_finalize;
+ rend_klass->clip_push = gog_renderer_gnome_print_clip_push;
+ rend_klass->clip_pop = gog_renderer_gnome_print_clip_pop;
+ rend_klass->draw_path = gog_renderer_gnome_print_draw_path;
+ rend_klass->draw_polygon = gog_renderer_gnome_print_draw_polygon;
+ rend_klass->draw_text = gog_renderer_gnome_print_draw_text;
+ rend_klass->draw_marker = gog_renderer_gnome_print_draw_marker;
+ rend_klass->measure_text = gog_renderer_gnome_print_measure_text;
+}
+
+static void
+gog_renderer_gnome_print_init (GogRendererGnomePrint *prend)
+{
+ prend->gp_context = NULL;
+ prend->fonts = g_ptr_array_new ();
+}
+
+static GSF_CLASS (GogRendererGnomePrint, gog_renderer_gnome_print,
+ gog_renderer_gnome_print_class_init, gog_renderer_gnome_print_init,
+ GOG_RENDERER_TYPE)
+
+void
+gog_graph_print_to_gnome_print (GogGraph *graph,
+ GnomePrintContext *gp_context,
+ double width, double height)
+{
+ GogViewAllocation allocation;
+ GogRendererGnomePrint *prend =
+ g_object_new (GOG_RENDERER_GNOME_PRINT_TYPE,
+ "model", graph,
+ "zoom", 1.,
+ NULL);
+ prend->gp_context = g_object_ref (gp_context);
+#ifdef HAVE_GNOME_PRINT_PANGO_CREATE_LAYOUT
+ prend->layout = gnome_print_pango_create_layout (prend->gp_context);
+#else
+ prend->layout = 0;
+#endif
+ allocation.x = 0.;
+ allocation.y = 0.;
+ allocation.w = width;
+ allocation.h = height;
+ gog_view_size_allocate (prend->base.view, &allocation);
+
+ /* FIXME FIXME FIXME this is a workaround for a bug in libgnomeprint
+ * where line with width == 1.0 don't scale properly before an other
+ * line width is set.
+ *
+ * http://bugzilla.gnome.org/show_bug.cgi?id=149452
+ */
+ gnome_print_setlinewidth (prend->gp_context, 0.1);
+
+ gog_view_render (prend->base.view, NULL);
+ g_object_unref (prend);
+}
--- /dev/null
+++ lib/goffice/graph/gog-plot-engine.h
@@ -0,0 +1,59 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-plot-engine.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_PLOT_ENGINE_H
+#define GOG_PLOT_ENGINE_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogPlotType {
+ GogPlotFamily *family;
+ char *engine;
+
+ char *name, *sample_image_file;
+ char *description; /* untranslated */
+ int col, row;
+
+ GHashTable *properties;
+};
+
+struct _GogPlotFamily {
+ char *name, *sample_image_file;
+
+ GHashTable *types;
+};
+
+/* GogPlotFamily hashed by name */
+GHashTable const *gog_plot_families (void);
+GogPlotFamily *gog_plot_family_by_name (char const *name);
+GogPlotFamily *gog_plot_family_register (char const *name, char const *sample_image_file);
+GogPlotType *gog_plot_type_register (GogPlotFamily *famlily, int col, int row,
+ char const *name, char const *sample_image_file,
+ char const *description, char const *engine);
+
+void gog_plugin_services_init (void);
+void gog_plugin_services_shutdown (void);
+
+G_END_DECLS
+
+#endif /* GOG_PLOT_ENGINE_H */
--- /dev/null
+++ lib/goffice/graph/gog-grid.h
@@ -0,0 +1,37 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-grid.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_GRID_H
+#define GOG_GRID_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_GRID_TYPE (gog_grid_get_type ())
+#define GOG_GRID(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRID_TYPE, GogGrid))
+#define IS_GOG_GRID(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRID_TYPE))
+
+GType gog_grid_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_GRID_H */
--- /dev/null
+++ lib/goffice/graph/gog-renderer-pixbuf.c
@@ -0,0 +1,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,
+ ©_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"><b>Fill</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment4">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="fill_type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Type:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_type_menu</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="fill_type_menu">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">None
+Pattern
+Gradient
+Image</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHSeparator" id="hseparator1">
+ <property name="visible">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="fill_notebook">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkLabel" id="label31">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="fill_pattern_table">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">2</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="fill_pattern_type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Pattern:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_pattern_foreground_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Foreground:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_pattern_background_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Background:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label17">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="fill_gradient_table">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">4</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="fill_gradient_direction_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Direction:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_gradient_start_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Start:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_gradient_type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">T_ype:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_gradient_type</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="fill_gradient_end_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_End:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkComboBox" id="fill_gradient_type">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">2 Colors
+Brightness</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="fill_gradient_brightness_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="label29">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_brighter</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_gradient_brightness</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHScale" id="fill_gradient_brightness">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="draw_value">False</property>
+ <property name="value_pos">GTK_POS_TOP</property>
+ <property name="digits">0</property>
+ <property name="update_policy">GTK_UPDATE_CONTINUOUS</property>
+ <property name="inverted">False</property>
+ <property name="adjustment">0 0 100 10 10 10</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label30">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_darker</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_gradient_brightness</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">4</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label18">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkTable" id="fill_image_table">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="fill_image_fit_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Fit:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">fill_image_fit</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="fill_image_fit">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes">stretched
+wallpaper</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="fill_image_select_picture">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="stock">gtk-open</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label32">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Select...</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">shrink|fill</property>
+ <property name="y_options">shrink</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label33">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkImage" id="fill_image_sample">
+ <property name="width_request">100</property>
+ <property name="height_request">60</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="image-size-label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options">fill</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label19">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="outline_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="outline_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Outline</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment5">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="outline_table">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="outline_size_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Size:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">outline_size_spin</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="outline_size_spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 30 0.1 1 1</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label35">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">pts</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="outline_color_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Co_lor:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label37">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">St_yle:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="line_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="line_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Line</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment6">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="line_table">
+ <property name="visible">True</property>
+ <property name="n_rows">3</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="line_color_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Co_lor:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="line_size_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Size:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">line_size_spin</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="line_size_spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">1</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">0 0 30 0.1 1 1</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label34">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">pts</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label36">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">St_yle:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="marker_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="marker_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Marker</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment7">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">18</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkTable" id="marker_table">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">3</property>
+ <property name="homogeneous">False</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="marker_shape_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Sha_pe:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="bottom_attach">1</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="marker_size_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Si_ze:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">marker_size_spin</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkSpinButton" id="marker_size_spin">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="climb_rate">1</property>
+ <property name="digits">0</property>
+ <property name="numeric">True</property>
+ <property name="update_policy">GTK_UPDATE_ALWAYS</property>
+ <property name="snap_to_ticks">False</property>
+ <property name="wrap">False</property>
+ <property name="adjustment">5 1 200 1 10 10</property>
+ </widget>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">pts</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="right_attach">3</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="marker_outline_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">O_utline color:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="marker_fill_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Fill color:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="right_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">fill</property>
+ <property name="y_options"></property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-renderer-pixbuf.h
@@ -0,0 +1,42 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-pixbuf.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_RENDERER_PIXBUF_H
+#define GOG_RENDERER_PIXBUF_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GOG_RENDERER_PIXBUF_TYPE (gog_renderer_pixbuf_get_type ())
+#define GOG_RENDERER_PIXBUF(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_RENDERER_PIXBUF_TYPE, GogRendererPixbuf))
+#define IS_GOG_RENDERER_PIXBUF(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_RENDERER_PIXBUF_TYPE))
+
+typedef struct _GogRendererPixbuf GogRendererPixbuf;
+
+GType gog_renderer_pixbuf_get_type (void);
+GdkPixbuf *gog_renderer_pixbuf_get (GogRendererPixbuf *prend);
+gboolean gog_renderer_pixbuf_update (GogRendererPixbuf *prend, int w, int h, double zoom);
+
+G_END_DECLS
+
+#endif /* GOG_RENDERER_PIXBUF_H */
--- /dev/null
+++ lib/goffice/graph/go-data.c
@@ -0,0 +1,432 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-data.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/graph/go-data-impl.h>
+#include <goffice/utils/go-math.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#define GO_DATA_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_TYPE, GODataClass))
+#define IS_GO_DATA_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_TYPE))
+#define GO_DATA_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_TYPE, GODataClass))
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static gulong go_data_signals [LAST_SIGNAL] = { 0, };
+
+/* trivial fall back */
+static GOData *
+go_data_dup_real (GOData const *src)
+{
+ char *str = go_data_as_str (src);
+ GOData *dst = g_object_new (G_OBJECT_TYPE (src), NULL);
+ if (dst != NULL)
+ go_data_from_str (dst, str);
+ g_free (str);
+
+ return dst;
+}
+
+static void
+go_data_init (GOData *data)
+{
+ data->flags = 0;
+}
+
+#if 0
+static GObjectClass *parent_klass;
+static void
+go_data_finalize (GOData *obj)
+{
+ g_warning ("finalize");
+ (parent_klass->finalize) (obj);
+}
+#endif
+
+static void
+go_data_class_init (GODataClass *klass)
+{
+ go_data_signals [CHANGED] = g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GODataClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ klass->dup = go_data_dup_real;
+#if 0
+ {
+ GObjectClass *gobj_klass = (GObjectClass *)klass;
+ gobj_klass->finalize = go_data_finalize;
+ parent_klass = g_type_class_peek_parent (klass);
+ }
+#endif
+}
+
+GSF_CLASS_ABSTRACT (GOData, go_data,
+ go_data_class_init, go_data_init,
+ G_TYPE_OBJECT)
+
+/**
+ * go_data_dup :
+ * @src : #GOData
+ *
+ * A deep copy of @src.
+ **/
+GOData *
+go_data_dup (GOData const *src)
+{
+ if (src != NULL) {
+ GODataClass const *klass = GO_DATA_GET_CLASS (src);
+ g_return_val_if_fail (klass != NULL, NULL);
+ return (*klass->dup) (src);
+ }
+ return NULL;
+}
+
+/**
+ * go_data_eq :
+ * @a : #GOData
+ * @b : #GOData
+ *
+ * Returns TRUE if @a and @b are the same
+ **/
+gboolean
+go_data_eq (GOData const *a, GOData const *b)
+{
+ if (a == b)
+ return TRUE;
+ else {
+ GODataClass *a_klass = GO_DATA_GET_CLASS (a);
+ GODataClass *b_klass = GO_DATA_GET_CLASS (b);
+
+ g_return_val_if_fail (a_klass != NULL, FALSE);
+ g_return_val_if_fail (a_klass->eq != NULL, FALSE);
+
+ if (a_klass != b_klass)
+ return FALSE;
+
+ return (*a_klass->eq) (a, b);
+ }
+}
+
+/**
+ * go_data_prefered_fmt :
+ * @dat : #GOData
+ *
+ * Caller is responsible for unrefing the result.
+ * Returns the fmt preferred by the the data
+ **/
+GOFormat *
+go_data_preferred_fmt (GOData const *dat)
+{
+ GODataClass const *klass = GO_DATA_GET_CLASS (dat);
+ g_return_val_if_fail (klass != NULL, NULL);
+ if (klass->preferred_fmt)
+ return (*klass->preferred_fmt) (dat);
+ return NULL;
+}
+
+/**
+ * go_data_as_str :
+ * @dat : #GOData
+ *
+ * Return a string representation of the data source that the caller is
+ * responsible for freeing
+ *
+ * NOTE : This is the _source_ not the content.
+ **/
+char *
+go_data_as_str (GOData const *dat)
+{
+ GODataClass const *klass = GO_DATA_GET_CLASS (dat);
+ g_return_val_if_fail (klass != NULL, NULL);
+ return (*klass->as_str) (dat);
+}
+
+/**
+ * go_data_from_str :
+ * @dat : #GOData
+ * @str :
+ *
+ * De-serializes the source information returned from go_data_as_str.
+ * Returns FALSE on error.
+ **/
+gboolean
+go_data_from_str (GOData *dat, char const *str)
+{
+ GODataClass const *klass = GO_DATA_GET_CLASS (dat);
+ g_return_val_if_fail (klass != NULL, FALSE);
+ return (*klass->from_str) (dat, str);
+}
+
+/**
+ * go_data_emit_changed :
+ * @dat : #GOData
+ *
+ * protected utility to emit a 'changed' signal
+ **/
+void
+go_data_emit_changed (GOData *dat)
+{
+ GODataClass const *klass = GO_DATA_GET_CLASS (dat);
+
+ g_return_if_fail (klass != NULL);
+
+ if (klass->emit_changed)
+ (*klass->emit_changed) (dat);
+
+ g_signal_emit (G_OBJECT (dat), go_data_signals [CHANGED], 0);
+}
+
+/*************************************************************************/
+
+#define GO_DATA_SCALAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_SCALAR_TYPE, GODataScalarClass))
+#define IS_GO_DATA_SCALAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_SCALAR_TYPE))
+#define GO_DATA_SCALAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_SCALAR_TYPE, GODataScalarClass))
+
+GSF_CLASS_ABSTRACT (GODataScalar, go_data_scalar,
+ NULL, NULL,
+ GO_DATA_TYPE)
+
+double
+go_data_scalar_get_value (GODataScalar *scalar)
+{
+ GODataScalarClass const *klass = GO_DATA_SCALAR_GET_CLASS (scalar);
+ g_return_val_if_fail (klass != NULL, 0.); /* TODO : make this a nan */
+ return (*klass->get_value) (scalar);
+}
+
+char const *
+go_data_scalar_get_str (GODataScalar *scalar)
+{
+ GODataScalarClass const *klass = GO_DATA_SCALAR_GET_CLASS (scalar);
+ g_return_val_if_fail (klass != NULL, NULL);
+ return (*klass->get_str) (scalar);
+}
+
+/*************************************************************************/
+
+#define GO_DATA_VECTOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_VECTOR_TYPE, GODataVectorClass))
+#define IS_GO_DATA_VECTOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_VECTOR_TYPE))
+#define GO_DATA_VECTOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_VECTOR_TYPE, GODataVectorClass))
+
+static void
+go_data_vector_emit_changed (GOData *data)
+{
+ data->flags &= ~(GO_DATA_CACHE_IS_VALID | GO_DATA_VECTOR_LEN_CACHED);
+}
+static void
+go_data_vector_class_init (GODataClass *klass)
+{
+ klass->emit_changed = go_data_vector_emit_changed;
+}
+
+GSF_CLASS_ABSTRACT (GODataVector, go_data_vector,
+ go_data_vector_class_init, NULL,
+ GO_DATA_TYPE)
+
+int
+go_data_vector_get_len (GODataVector *vec)
+{
+ if (! (vec->base.flags & GO_DATA_VECTOR_LEN_CACHED)) {
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+
+ g_return_val_if_fail (klass != NULL, 0);
+
+ (*klass->load_len) (vec);
+
+ g_return_val_if_fail (vec->base.flags & GO_DATA_VECTOR_LEN_CACHED, 0);
+ }
+
+ return vec->len;
+}
+
+double *
+go_data_vector_get_values (GODataVector *vec)
+{
+ if (! (vec->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ (*klass->load_values) (vec);
+
+ g_return_val_if_fail (vec->base.flags & GO_DATA_CACHE_IS_VALID, NULL);
+ }
+
+ return vec->values;
+}
+
+double
+go_data_vector_get_value (GODataVector *vec, unsigned i)
+{
+ if (! (vec->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+ g_return_val_if_fail (klass != NULL, go_nan);
+ return (*klass->get_value) (vec, i);
+ }
+
+ g_return_val_if_fail ((int)i < vec->len, go_nan);
+ return vec->values [i];
+}
+
+char *
+go_data_vector_get_str (GODataVector *vec, unsigned i)
+{
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+ char *res;
+
+ g_return_val_if_fail (klass != NULL, g_strdup (""));
+ g_return_val_if_fail ((int)i < vec->len, g_strdup (""));
+
+ res = (*klass->get_str) (vec, i);
+ if (res == NULL)
+ return g_strdup ("");
+ return res;
+}
+
+void
+go_data_vector_get_minmax (GODataVector *vec, double *min, double *max)
+{
+ if (! (vec->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataVectorClass const *klass = GO_DATA_VECTOR_GET_CLASS (vec);
+
+ g_return_if_fail (klass != NULL);
+
+ (*klass->load_values) (vec);
+
+ g_return_if_fail (vec->base.flags & GO_DATA_CACHE_IS_VALID);
+ }
+
+ if (min != NULL)
+ *min = vec->minimum;
+ if (max != NULL)
+ *max = vec->maximum;
+}
+
+/*************************************************************************/
+
+#define GO_DATA_MATRIX_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GO_DATA_MATRIX_TYPE, GODataMatrixClass))
+#define IS_GO_DATA_MATRIX_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GO_DATA_MATRIX_TYPE))
+#define GO_DATA_MATRIX_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GO_DATA_MATRIX_TYPE, GODataMatrixClass))
+
+static void
+go_data_matrix_emit_changed (GOData *data)
+{
+ data->flags &= ~(GO_DATA_CACHE_IS_VALID | GO_DATA_MATRIX_SIZE_CACHED);
+}
+
+static void
+go_data_matrix_class_init (GODataClass *klass)
+{
+ klass->emit_changed = go_data_matrix_emit_changed;
+}
+
+GSF_CLASS_ABSTRACT (GODataMatrix, go_data_matrix,
+ go_data_matrix_class_init, NULL,
+ GO_DATA_TYPE)
+
+GOMatrixSize
+go_data_matrix_get_size (GODataMatrix *mat)
+{
+ if (! (mat->base.flags & GO_DATA_MATRIX_SIZE_CACHED)) {
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+ static GOMatrixSize null_size = {0, 0};
+
+ g_return_val_if_fail (klass != NULL, null_size);
+
+ (*klass->load_size) (mat);
+
+ g_return_val_if_fail (mat->base.flags & GO_DATA_MATRIX_SIZE_CACHED, null_size);
+ }
+
+ return mat->size;
+}
+
+double *
+go_data_matrix_get_values (GODataMatrix *mat)
+{
+ if (! (mat->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ (*klass->load_values) (mat);
+
+ g_return_val_if_fail (mat->base.flags & GO_DATA_CACHE_IS_VALID, NULL);
+ }
+
+ return mat->values;
+}
+
+double
+go_data_matrix_get_value (GODataMatrix *mat, unsigned i, unsigned j)
+{
+ if (! (mat->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+ g_return_val_if_fail (klass != NULL, go_nan);
+ return (*klass->get_value) (mat, i, j);
+ }
+
+ g_return_val_if_fail (((int)i < mat->size.rows) && ((int)j < mat->size.columns), go_nan);
+ return mat->values[i * mat->size.columns + j];
+}
+
+char *
+go_data_matrix_get_str (GODataMatrix *mat, unsigned i, unsigned j)
+{
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+ char *res;
+
+ g_return_val_if_fail (klass != NULL, NULL);
+
+ res = (*klass->get_str) (mat, i, j);
+ if (res == NULL)
+ return g_strdup ("");
+ return res;
+}
+
+void
+go_data_matrix_get_minmax (GODataMatrix *mat, double *min, double *max)
+{
+ if (! (mat->base.flags & GO_DATA_CACHE_IS_VALID)) {
+ GODataMatrixClass const *klass = GO_DATA_MATRIX_GET_CLASS (mat);
+
+ g_return_if_fail (klass != NULL);
+
+ (*klass->load_values) (mat);
+
+ g_return_if_fail (mat->base.flags & GO_DATA_CACHE_IS_VALID);
+ }
+
+ if (min != NULL)
+ *min = mat->minimum;
+ if (max != NULL)
+ *max = mat->maximum;
+}
--- /dev/null
+++ lib/goffice/graph/README
@@ -0,0 +1,30 @@
+GOffice Graph -- A graphing library based on gtk technologies
+Jody Goldberg <jody at gnome.org>
+
+ licensed under the terms of the GNU GPL included in the file COPYING.
+
+Terminology
+-----------
+
+graph == the parent which contains a set of charts layed out in tabular form
+chart == a rectangular region with axis, titles, and legends, and multiple
+ plots
+plot == a set of drawings, overlapping with a chart with axis assigned
+series == 1 displayable set of data of dimensionality related to its
+ associated plot type
+element == 1 'point' in a series
+
+Mailing lists
+-------------
+
+ There is a no mailing list to discuss only the graphing. For now discussion
+ will be on the Gnumeric list, to subscribe send a mail to:
+
+ gnumeric-list-request at gnome.org
+
+ And in the body of the message write "subscribe"
+
+ An archive of the mailing lists is available in:
+
+ http://www.gnome.org/mailing-lists/archives/gnumeric-list/
+
--- /dev/null
+++ lib/goffice/graph/gog-axis.c
@@ -0,0 +1,2453 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-axis.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-grid-line.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-label.h>
+#include <goffice/graph/gog-plot.h>
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-format.h>
+#include <goffice/utils/go-math.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+//#include <src/gui-util.h>
+//#include <src/format.h>
+//#include <src/widgets/widget-format-selector.h>
+#include <gui-util.h>
+#include <format.h>
+#include <widgets/widget-format-selector.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtkcheckbutton.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkcombobox.h>
+#include <gtk/gtkmisc.h>
+#include <glade/glade-xml.h>
+
+#include <string.h>
+
+struct _GogAxis {
+ GogStyledObject base;
+
+ GogAxisType type;
+ GogAxisPosition pos;
+ GSList *contributors;
+
+ GogDatasetElement source [AXIS_ELEM_MAX_ENTRY];
+ double auto_bound [AXIS_ELEM_MAX_ENTRY];
+ struct {
+ gboolean tick_in, tick_out;
+ int size_pts;
+ } major, minor;
+ gboolean major_tick_labeled;
+ gboolean inverted; /* apply to all map type */
+
+ double min_val, max_val;
+ double logical_min_val, logical_max_val;
+ gpointer min_contrib, max_contrib; /* NULL means use the manual sources */
+ gboolean is_discrete;
+ gboolean center_on_ticks;
+ GODataVector *labels;
+ GogPlot *plot_that_supplied_labels;
+ GOFormat *format, *assigned_format;
+
+ GogAxisMapDesc const *map_desc;
+
+ GogAxisTick *ticks;
+ unsigned tick_nbr;
+};
+
+typedef GogStyledObjectClass GogAxisClass;
+
+static GType gog_axis_view_get_type (void);
+
+static GObjectClass *parent_klass;
+
+enum {
+ AXIS_PROP_0,
+ AXIS_PROP_TYPE,
+ AXIS_PROP_POS,
+ AXIS_PROP_POS_STR,
+ AXIS_PROP_INVERT,
+ AXIS_PROP_MAP,
+ AXIS_PROP_MAJOR_TICK_LABELED,
+ AXIS_PROP_MAJOR_TICK_IN,
+ AXIS_PROP_MAJOR_TICK_OUT,
+ AXIS_PROP_MAJOR_TICK_SIZE_PTS,
+ AXIS_PROP_MINOR_TICK_IN,
+ AXIS_PROP_MINOR_TICK_OUT,
+ AXIS_PROP_MINOR_TICK_SIZE_PTS,
+ AXIS_PROP_ASSIGNED_FORMAT_STR_XL
+};
+
+#define TICK_LABEL_PAD_VERT 0
+#define TICK_LABEL_PAD_HORIZ 1
+
+#define GOG_AXIS_MAX_TICK_NBR 1000
+#define GOG_AXIS_LOG_AUTO_MAX_MAJOR_TICK_NBR 8
+#define GOG_AXIS_DISCRETE_AUTO_MAX_MAJOR_TICK_NBR 20
+
+static void gog_axis_set_ticks (GogAxis *axis,int tick_nbr, GogAxisTick *ticks);
+
+static GogAxisTick *
+get_adjusted_tick_array (GogAxisTick *ticks, int allocated_size, int exact_size)
+{
+ GogAxisTick *tmp_ticks;
+
+ if (exact_size > 0) {
+ if (exact_size != allocated_size) {
+ tmp_ticks = g_new (GogAxisTick, exact_size);
+ memcpy (tmp_ticks, ticks, sizeof (GogAxisTick) * exact_size);
+ g_free (ticks);
+ } else
+ tmp_ticks = ticks;
+ } else {
+ g_free (ticks);
+ tmp_ticks = NULL;
+ }
+
+ return tmp_ticks;
+}
+
+static GogAxisTick *
+create_invalid_axis_ticks (double min, double max, gboolean draw_labels) {
+ GogAxisTick *ticks;
+
+ ticks = g_new (GogAxisTick, 2);
+ ticks[0].position = min;
+ ticks[1].position = max;
+ ticks[0].type = ticks[1].type = GOG_AXIS_TICK_MAJOR;
+ ticks[0].label = draw_labels ? g_strdup ("##") : NULL;
+ ticks[1].label = draw_labels ? g_strdup ("##") : NULL;
+
+ return ticks;
+}
+
+/*
+ * Discrete mapping
+ */
+
+typedef struct
+{
+ double min;
+ double max;
+ double scale;
+ double a;
+ double b;
+} MapData;
+
+static gboolean
+map_discrete_init (GogAxisMap *map, double offset, double length)
+{
+ MapData *data;
+
+ map->data = g_new (MapData, 1);
+ data = map->data;
+
+ if (gog_axis_get_bounds (map->axis, &data->min, &data->max)) {
+ data->scale = (map->axis->center_on_ticks)?
+ 1.0 / (data->max - data->min - 1):
+ 1.0 / (data->max - data->min);
+ data->a = data->scale * length;
+ data->b = offset - data->a * data->min;
+ return TRUE;
+ }
+ data->min = 0.0;
+ data->max = 1.0;
+ data->scale = 1.0;
+ data->a = length;
+ data->b = offset;
+ return FALSE;
+}
+
+static double
+map_discrete (GogAxisMap *map, double value)
+{
+ MapData *data = map->data;
+
+ return (value - data->min) * data->scale;
+}
+
+static double
+map_discrete_to_canvas (GogAxisMap *map, double value, gboolean inverted)
+{
+ MapData *data = map->data;
+
+ return inverted ?
+ ((map->axis->center_on_ticks)?
+ (data->min + data->max - 1 - value) * data->a + data->b :
+ (data->min + data->max - value) * data->a + data->b) :
+ value * data->a + data->b;
+}
+
+static void
+map_discrete_auto_bound (GogAxis *axis,
+ double minimum,
+ double maximum,
+ double *bound)
+{
+ if ((maximum - minimum) > GOG_AXIS_DISCRETE_AUTO_MAX_MAJOR_TICK_NBR)
+ bound [AXIS_ELEM_MAJOR_TICK] =
+ bound [AXIS_ELEM_MINOR_TICK] =
+ ceil ((maximum - minimum + 1.0) /
+ (double) GOG_AXIS_DISCRETE_AUTO_MAX_MAJOR_TICK_NBR);
+ else
+ bound [AXIS_ELEM_MAJOR_TICK] =
+ bound [AXIS_ELEM_MINOR_TICK] = 1.;
+
+ bound [AXIS_ELEM_CROSS_POINT] = 1.;
+ bound [AXIS_ELEM_MIN] = minimum;
+ bound [AXIS_ELEM_MAX] = maximum;
+}
+
+static void
+map_discrete_calc_ticks (GogAxis *axis,
+ gboolean draw_labels)
+{
+ GogAxisTick *ticks;
+ gboolean valid;
+ double maximum, minimum;
+ int tick_nbr;
+ int i, count;
+ int major_tick, major_label;
+
+ major_tick = rint (gog_axis_get_entry (axis, AXIS_ELEM_MAJOR_TICK, NULL));
+ major_label = rint (gog_axis_get_entry (axis, AXIS_ELEM_MINOR_TICK, NULL));
+ if (major_tick < 1)
+ major_tick = 1;
+ if (major_label < 1)
+ major_label = 1;
+
+ valid = gog_axis_get_bounds (axis, &minimum, &maximum);
+ if (!valid) {
+ gog_axis_set_ticks (axis, 2, create_invalid_axis_ticks (0.0, 1.0, draw_labels));
+ return;
+ }
+
+ tick_nbr = rint (maximum -minimum) + 1;
+ if (axis->center_on_ticks)
+ tick_nbr--;
+ if (tick_nbr < 1) {
+ gog_axis_set_ticks (axis, 0, NULL);
+ return;
+ }
+ ticks = g_new (GogAxisTick, tick_nbr);
+
+ count = 0;
+ for (i = 0; i < tick_nbr; i++) {
+ ticks[count].position = (double) (i);
+ ticks[count].type = GOG_AXIS_TICK_MINOR;
+ ticks[count].label = NULL;
+
+ if (i % major_tick == 0)
+ ticks[count].type = GOG_AXIS_TICK_MAJOR;
+
+ /* Minimum >= .0 test is a trick to know if it's a barcol or an area/line plot */
+ if (i == 0 && minimum >=.0 && !axis->center_on_ticks)
+ ticks[count].type = GOG_AXIS_TICK_NONE;
+ if ((i % major_label == 0) && draw_labels &&
+ (i < tick_nbr - 1 || minimum >= .0)) {
+ if (axis->labels != NULL) {
+ if (i < go_data_vector_get_len (axis->labels))
+ ticks[count].label = go_data_vector_get_str (axis->labels, i);
+ else
+ ticks[count].label = NULL;
+ }
+ else
+ ticks[count].label = g_strdup_printf ("%d", i + 1);
+ }
+
+ count++;
+ }
+
+ ticks = get_adjusted_tick_array (ticks, tick_nbr, count);
+ gog_axis_set_ticks (axis, count, ticks);
+}
+
+/*
+ * Linear mapping
+ */
+
+static gboolean
+map_linear_init (GogAxisMap *map, double offset, double length)
+{
+ MapData *data;
+
+ map->data = g_new (MapData, 1);
+ data = (MapData *) map->data;
+
+ if (gog_axis_get_bounds (map->axis, &data->min, &data->max)) {
+ data->scale = 1 / (data->max - data->min);
+ data->a = data->scale * length;
+ data->b = offset - data->a * data->min;
+ return TRUE;
+ }
+ data->min = 0.0;
+ data->max = 1.0;
+ data->scale = 1.0;
+ data->a = length;
+ data->b = offset;
+ return FALSE;
+}
+
+static double
+map_linear (GogAxisMap *map, double value)
+{
+ MapData *data = map->data;
+
+ return (value - data->min) * data->scale;
+}
+
+static double
+map_linear_to_canvas (GogAxisMap *map, double value, gboolean inverted)
+{
+ MapData *data = map->data;
+
+ return inverted ?
+ (data->min + data->max - value) * data->a + data->b :
+ value * data->a + data->b;
+}
+
+static void
+map_linear_auto_bound (GogAxis *axis, double minimum, double maximum, double *bound)
+{
+ double step, range, mant;
+ int expon;
+
+ range = fabs (maximum - minimum);
+
+ /* handle singletons */
+ if (go_sub_epsilon (range) <= 0.) {
+ if (maximum > 0)
+ minimum = 0.;
+ else if (minimum < 0.)
+ maximum = 0.;
+ else {
+ maximum = 1;
+ minimum = 0;
+ }
+
+ range = fabs (maximum - minimum);
+ }
+
+ step = pow (10, go_fake_floor (log10 (range)));
+ if (range/step < 1.6)
+ step /= 5.; /* .2 .4 .6 */
+ else if (range/step < 3)
+ step /= 2.; /* 0 5 10 */
+ else if (range/step > 8)
+ step *= 2.; /* 2 4 6 */
+
+ /* we want the bounds to be loose so jump up a step if we get too close */
+ mant = frexp (minimum / step, &expon);
+ bound [AXIS_ELEM_MIN] = step * floor (ldexp (mant - DBL_EPSILON, expon));
+ mant = frexp (maximum / step, &expon);
+ bound [AXIS_ELEM_MAX] = step * ceil (ldexp (mant + DBL_EPSILON, expon));
+ bound [AXIS_ELEM_MAJOR_TICK] = step;
+ bound [AXIS_ELEM_MINOR_TICK] = step / 5.;
+
+ /* pull to zero if its nearby (do not pull both directions to 0) */
+ if (bound [AXIS_ELEM_MIN] > 0 &&
+ (bound [AXIS_ELEM_MIN] - 10. * step) < 0)
+ bound [AXIS_ELEM_MIN] = 0;
+ else if (bound [AXIS_ELEM_MAX] < 0 &&
+ (bound [AXIS_ELEM_MAX] + 10. * step) < 0)
+ bound [AXIS_ELEM_MAX] = 0;
+
+ /* The epsilon shift can pull us away from a zero we want to
+ * keep (eg percentage bars withno negative elements) */
+ if (bound [AXIS_ELEM_MIN] < 0 && minimum >= 0.)
+ bound [AXIS_ELEM_MIN] = 0;
+ else if (bound [AXIS_ELEM_MAX] > 0 && maximum <= 0.)
+ bound [AXIS_ELEM_MAX] = 0;
+}
+
+static void
+map_linear_calc_ticks (GogAxis *axis,
+ gboolean draw_labels)
+{
+ GogAxisTick *ticks;
+ double maximum, minimum;
+ double tick_step;
+ double major_tick, minor_tick;
+ int tick_nbr, ratio, i;
+
+ major_tick = gog_axis_get_entry (axis, AXIS_ELEM_MAJOR_TICK, NULL);
+ minor_tick = gog_axis_get_entry (axis, AXIS_ELEM_MINOR_TICK, NULL);
+ if (minor_tick < major_tick)
+ tick_step = minor_tick;
+ else
+ tick_step = major_tick;
+ ratio = rint (major_tick / tick_step);
+
+ if (!gog_axis_get_bounds (axis, &minimum, &maximum)) {
+ gog_axis_set_ticks (axis, 2, create_invalid_axis_ticks (0.0, 1.0, draw_labels));
+ return;
+ }
+
+ tick_nbr = floor (go_add_epsilon ((maximum - minimum) / tick_step + 1.0));
+ if (tick_nbr < 1 || tick_nbr > GOG_AXIS_MAX_TICK_NBR) {
+ gog_axis_set_ticks (axis, 0, NULL);
+ return;
+ }
+ ticks = g_new0 (GogAxisTick, tick_nbr);
+
+ for (i = 0; i < tick_nbr; i++) {
+ ticks[i].position = minimum + (double) i * tick_step;
+ if (fabs (ticks[i].position) < tick_step / 1E10)
+ ticks[i].position = 0.0;
+ if (rint (fmod (i, ratio)) == 0) {
+ ticks[i].type = GOG_AXIS_TICK_MAJOR;
+ if (draw_labels) {
+ if (axis->assigned_format == NULL ||
+ style_format_is_general (axis->assigned_format))
+ ticks[i].label = go_format_value (axis->format, ticks[i].position);
+ else
+ ticks[i].label = go_format_value (axis->assigned_format, ticks[i].position);
+ }
+ else
+ ticks[i].label = NULL;
+ }
+ else {
+ ticks[i].type = GOG_AXIS_TICK_MINOR;
+ ticks[i].label = NULL;
+ }
+ }
+ gog_axis_set_ticks (axis, tick_nbr, ticks);
+}
+
+/*
+ * Logarithmic mapping
+ */
+
+typedef struct
+{
+ double min;
+ double max;
+ double scale;
+ double a;
+ double b;
+ double a_inv;
+ double b_inv;
+} MapLogData;
+
+static gboolean
+map_log_init (GogAxisMap *map, double offset, double length)
+{
+ MapLogData *data;
+
+ map->data = g_new (MapLogData, 1);
+ data = map->data;
+
+ if (gog_axis_get_bounds (map->axis, &data->min, &data->max))
+ if (data->min > 0.0) {
+ data->min = log (data->min);
+ data->max = log (data->max);
+ data->scale = 1/ (data->max - data->min);
+ data->a = data->scale * length;
+ data->b = offset - data->a * data->min;
+ data->a_inv = -data->scale * length;
+ data->b_inv = offset + length - data->a_inv * data->min;
+ return TRUE;
+ }
+
+ data->min = 0.0;
+ data->max = log (10.0);
+ data->scale = 1 / log(10.0);
+ data->a = data->scale * length;
+ data->b = offset;
+ data->a_inv = -data->scale * length;
+ data->b_inv = offset + length;
+
+ return FALSE;
+}
+
+static double
+map_log (GogAxisMap *map, double value)
+{
+ MapLogData *data = map->data;
+
+ return (log (value) - data->min) * data->scale;
+}
+
+static double
+map_log_to_canvas (GogAxisMap *map, double value, gboolean inverted)
+{
+ MapLogData *data = map->data;
+ double result;
+
+ if (value <= 0.)
+ /* Make libart happy */
+ result = inverted ? -DBL_MAX : DBL_MAX;
+ else
+ result = inverted ?
+ log (value) * data->a_inv + data->b_inv :
+ log (value) * data->a + data->b;
+
+ return result;
+}
+
+static void
+map_log_auto_bound (GogAxis *axis, double minimum, double maximum, double *bound)
+{
+ double step;
+
+ if (maximum <= 0.0)
+ maximum = 1.0;
+ if (minimum <= 0.0)
+ minimum = maximum / 100.0;
+ if (maximum < minimum)
+ maximum = minimum * 100.0;
+
+ maximum = ceil (log10 (maximum));
+ minimum = floor (log10 (minimum));
+
+ step = ceil ((maximum - minimum + 1.0) /
+ (double) GOG_AXIS_LOG_AUTO_MAX_MAJOR_TICK_NBR);
+
+ bound [AXIS_ELEM_MIN] = pow ( 10.0, minimum);
+ bound [AXIS_ELEM_MAX] = pow ( 10.0, maximum);
+ bound [AXIS_ELEM_MAJOR_TICK] = step;
+ bound [AXIS_ELEM_MINOR_TICK] = 8;
+}
+
+static void
+map_log_calc_ticks (GogAxis *axis,
+ gboolean draw_labels)
+{
+ GogAxisTick *ticks;
+ double maximum, minimum;
+ double position;
+ int major_tick, minor_tick, major_label, start_tick;
+ int tick_nbr, i, j;
+ int count;
+
+ major_label = rint (gog_axis_get_entry (axis, AXIS_ELEM_MAJOR_TICK, NULL));
+ minor_tick = rint (gog_axis_get_entry (axis, AXIS_ELEM_MINOR_TICK, NULL) + 1.0);
+
+ if (!gog_axis_get_bounds (axis, &minimum, &maximum) || major_label < 1) {
+ gog_axis_set_ticks (axis, 2, create_invalid_axis_ticks (1.0, 10.0, draw_labels));
+ return;
+ }
+ if (minimum <= 0.0) {
+ gog_axis_set_ticks (axis, 2, create_invalid_axis_ticks (1.0, 10.0, draw_labels));
+ return;
+ }
+
+ start_tick = ceil (log10 (minimum));
+ tick_nbr = major_tick = ceil (ceil (log10 (maximum)) - floor (log10 (minimum)) + 1.0);
+ tick_nbr *= minor_tick;
+ if (tick_nbr < 1 || tick_nbr > GOG_AXIS_MAX_TICK_NBR) {
+ gog_axis_set_ticks (axis, 0, NULL);
+ return;
+ }
+ ticks = g_new0 (GogAxisTick, tick_nbr);
+
+ count = 0;
+ for (i = 0; i < major_tick; i++) {
+ position = pow (10.0, i + start_tick);
+ if (position >= go_sub_epsilon (minimum) && go_sub_epsilon (position) <= maximum) {
+ ticks[count].position = position;
+ if ((i) % major_label == 0 && draw_labels) {
+ ticks[count].type = GOG_AXIS_TICK_MAJOR;
+ if (axis->assigned_format == NULL ||
+ style_format_is_general (axis->assigned_format))
+ ticks[count].label = go_format_value (axis->format, ticks[count].position);
+ else
+ ticks[count].label = go_format_value (axis->assigned_format, ticks[count].position);
+ count++;
+ }
+ else {
+ ticks[count].type = GOG_AXIS_TICK_MINOR;
+ ticks[count].label = NULL;
+ count++;
+ }
+ }
+ for (j = 1; j < minor_tick; j++) {
+ position = pow (10.0, i + start_tick) * (9.0 / (double)minor_tick * (double) j + 1.0);
+ if (position >= go_sub_epsilon (minimum) && go_sub_epsilon (position) <= maximum) {
+ ticks[count].position = position;
+ ticks[count].type = GOG_AXIS_TICK_MINOR;
+ ticks[count].label = NULL;
+ count++;
+ }
+ }
+ }
+
+ ticks = get_adjusted_tick_array (ticks, tick_nbr, count);
+ gog_axis_set_ticks (axis, count, ticks);
+}
+
+static const GogAxisMapDesc map_desc_discrete =
+{
+ map_discrete, map_discrete_to_canvas,
+ map_discrete_init, NULL,
+ map_discrete_auto_bound, map_discrete_calc_ticks,
+ N_("Discrete"), N_("Discrete mapping")
+};
+
+static const GogAxisMapDesc map_descs[] =
+{
+ {
+ map_linear, map_linear_to_canvas,
+ map_linear_init, NULL,
+ map_linear_auto_bound, map_linear_calc_ticks,
+ N_("Linear"), N_("Linear mapping")
+ },
+ {
+ map_log, map_log_to_canvas,
+ map_log_init, NULL,
+ map_log_auto_bound, map_log_calc_ticks,
+ N_("Log"), N_("Logarithm mapping")
+ }
+};
+
+static void
+gog_axis_map_set_by_num (GogAxis *axis, unsigned num)
+{
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ if (num >= 0 && num < G_N_ELEMENTS (map_descs))
+ g_object_set (G_OBJECT (axis), "map-name", map_descs[num].name, NULL);
+ else
+ g_object_set (G_OBJECT (axis), "map-name", "", NULL);
+}
+
+static void
+gog_axis_map_populate_combo (GogAxis *axis, GtkComboBox *combo)
+{
+ unsigned i;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (map_descs); i++) {
+ gtk_combo_box_append_text (combo, _(map_descs[i].name));
+ if (!g_ascii_strcasecmp (map_descs[i].name,
+ axis->map_desc->name))
+ gtk_combo_box_set_active (combo, i);
+ }
+}
+
+static void
+gog_axis_map_set (GogAxis *axis, char const *name)
+{
+ unsigned i, map = 0;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ if (name != NULL)
+ for (i = 0; i < G_N_ELEMENTS(map_descs); i++)
+ if (!g_ascii_strcasecmp (name, map_descs[i].name)) {
+ map = i;
+ break;
+ }
+
+ axis->map_desc = &map_descs[map];
+}
+
+/**
+ * gog_axis_map_is_valid
+ * @axis : #GogAxis
+ *
+ * Return TRUE if map is valid, ie bounds are valid.
+ **/
+
+gboolean
+gog_axis_map_is_valid (GogAxisMap *map)
+{
+ g_return_val_if_fail (map != NULL, FALSE);
+
+ return map->is_valid;
+}
+
+/**
+ * gog_axis_map_new :
+ * @axis : #GogAxis
+ * @offset : start of plot area.
+ * @length : length of plot area.
+ *
+ * Return a new GogAxisMap for data mapping to plot window.
+ * offset and length are optional parameters to be used with
+ * gog_axis_map_to_canvas in order to translates data coordinates
+ * into canvas space.
+ **/
+
+GogAxisMap *
+gog_axis_map_new (GogAxis *axis, double offset, double length)
+{
+ GogAxisMap *map;
+
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, NULL);
+
+ map = g_new0 (GogAxisMap, 1);
+
+ g_object_ref (axis);
+ map->desc = axis->is_discrete ? &map_desc_discrete : axis->map_desc;
+ map->axis = axis;
+ map->data = NULL;
+ map->is_valid = FALSE;
+
+ if (map->desc->init != NULL)
+ map->is_valid = map->desc->init (map, offset, length);
+
+ return map;
+}
+
+/**
+ * gog_axis_map :
+ * @map : #GogAxisMap
+ * value : value to map to plot space.
+ *
+ * Return a value where [0,1.0] means a data within plot
+ * bounds.
+ * */
+
+double
+gog_axis_map (GogAxisMap *map,
+ double value)
+{
+ return (map->axis->inverted ?
+ 1.0 - map->desc->map (map, value) :
+ map->desc->map (map, value));
+}
+
+/**
+ * gog_axis_map_to_canvas :
+ * @map : #GogAxisMap
+ * @value : value to map to canvas space.
+ *
+ * Return a value in canvas coordinates, where
+ * [offset,offset+length] means a data within plot bounds.
+ **/
+
+double
+gog_axis_map_to_canvas (GogAxisMap *map,
+ double value)
+{
+ return map->desc->map_to_canvas (map, value, map->axis->inverted);
+}
+
+/**
+ * gog_axis_map_free :
+ * @map : #GogAxisMap
+ *
+ * Free GogAxisMap object.
+ **/
+
+void
+gog_axis_map_free (GogAxisMap *map)
+{
+ g_return_if_fail (map != NULL);
+
+ if (map->desc->destroy != NULL)
+ map->desc->destroy (map);
+
+ g_object_unref (map->axis);
+ g_free (map->data);
+ g_free (map);
+}
+
+static void
+gog_axis_auto_bound (GogAxis *axis)
+{
+ double maximum, minimum;
+ double tmp;
+ gboolean user_defined;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ minimum = axis->min_val;
+ maximum = axis->max_val;
+
+ tmp = gog_axis_get_entry (axis, AXIS_ELEM_MIN, &user_defined);
+ if (user_defined) minimum = tmp;
+
+ tmp = gog_axis_get_entry (axis, AXIS_ELEM_MAX, &user_defined);
+ if (user_defined) maximum = tmp;
+
+ if (axis->is_discrete)
+ map_desc_discrete.auto_bound (axis, minimum, maximum, axis->auto_bound);
+ else
+ if (axis->map_desc->auto_bound)
+ axis->map_desc->auto_bound (axis, minimum, maximum, axis->auto_bound);
+}
+
+static void
+gog_axis_set_ticks (GogAxis *axis, int tick_nbr, GogAxisTick *ticks)
+{
+ unsigned i;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ if (axis->ticks != NULL) {
+ for (i = 0; i < axis->tick_nbr; i++)
+ g_free (axis->ticks[i].label);
+
+ g_free (axis->ticks);
+ }
+
+ axis->tick_nbr = tick_nbr;
+ axis->ticks = ticks;
+}
+
+static void
+gog_axis_calc_ticks (GogAxis *axis)
+{
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ if (axis->is_discrete)
+ map_desc_discrete.calc_ticks (axis,
+ axis->major_tick_labeled);
+ else
+ if (axis->map_desc->calc_ticks)
+ axis->map_desc->calc_ticks (axis,
+ axis->major_tick_labeled);
+}
+
+/*****************************************************************************/
+
+static void
+role_label_post_add (GogObject *parent, GogObject *label)
+{
+ GogAxis const *axis = GOG_AXIS (parent);
+ if (axis->pos == GOG_AXIS_AT_LOW)
+ label->position = (axis->type == GOG_AXIS_X)
+ ? (GOG_POSITION_S|GOG_POSITION_ALIGN_CENTER)
+ : (GOG_POSITION_W|GOG_POSITION_ALIGN_CENTER);
+ else
+ label->position = (axis->type == GOG_AXIS_X)
+ ? (GOG_POSITION_N|GOG_POSITION_ALIGN_CENTER)
+ : (GOG_POSITION_E|GOG_POSITION_ALIGN_CENTER);
+}
+
+static gboolean
+role_grid_line_can_add (GogObject const *parent, char const *type)
+{
+ GSList *children;
+
+ children = gog_object_get_children (parent,
+ gog_object_find_role_by_name (GOG_OBJECT (parent), type));
+ if (children != NULL) {
+ g_slist_free (children);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+role_grid_line_major_can_add (GogObject const *parent)
+{
+ GogAxis *axis = GOG_AXIS (parent);
+ GogAxisType type = gog_axis_get_atype (axis);
+
+ return ((type == GOG_AXIS_X || type == GOG_AXIS_Y || type == GOG_AXIS_RADIAL) &&
+ role_grid_line_can_add (parent, "MajorGrid"));
+}
+
+static gboolean
+role_grid_line_minor_can_add (GogObject const *parent)
+{
+ GogAxis *axis = GOG_AXIS (parent);
+ GogAxisType type = gog_axis_get_atype (axis);
+
+ return (!gog_axis_is_discrete (GOG_AXIS (parent)) &&
+ (type == GOG_AXIS_X || type == GOG_AXIS_Y || type == GOG_AXIS_RADIAL) &&
+ role_grid_line_can_add (parent, "MinorGrid"));
+}
+
+static void
+role_grid_line_major_post_add (GogObject *parent, GogObject *child)
+{
+ g_object_set (G_OBJECT (child), "is_minor", (gboolean)FALSE, NULL);
+}
+
+static void
+role_grid_line_minor_post_add (GogObject *parent, GogObject *child)
+{
+ g_object_set (G_OBJECT (child), "is_minor", (gboolean)TRUE, NULL);
+}
+
+static gboolean
+gog_axis_set_pos (GogAxis *axis, GogAxisPosition pos)
+{
+ GSList *ptr;
+ if (axis->pos == pos)
+ return FALSE;
+ axis->pos = pos;
+
+ for (ptr = GOG_OBJECT (axis)->children ; ptr != NULL ; ptr = ptr->next)
+ if (IS_GOG_LABEL (ptr->data))
+ role_label_post_add (GOG_OBJECT (axis), ptr->data);
+ return TRUE;
+}
+
+/**
+ * gog_axis_set_format :
+ * @axis : #GogAxis
+ * @fmt : #GOFormat
+ *
+ * Absorbs a reference to @fmt, and accepts NULL.
+ * returns TRUE if things changed
+ **/
+static gboolean
+gog_axis_set_format (GogAxis *axis, GOFormat *fmt)
+{
+ if (go_format_eq (fmt, axis->assigned_format)) {
+ go_format_unref (fmt);
+ return FALSE;
+ }
+ if (axis->assigned_format != NULL)
+ go_format_unref (axis->assigned_format);
+ axis->assigned_format = fmt;
+ return TRUE;
+}
+
+static void
+gog_axis_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogAxis *axis = GOG_AXIS (obj);
+ gboolean resized = FALSE;
+ gboolean calc_ticks = FALSE;
+ gboolean request_update = FALSE;
+ int itmp;
+
+ switch (param_id) {
+ case AXIS_PROP_TYPE:
+ itmp = g_value_get_int (value);
+ if (axis->type != itmp) {
+ axis->type = itmp;
+ resized = TRUE;
+ }
+ break;
+ case AXIS_PROP_POS:
+ resized = gog_axis_set_pos (axis, g_value_get_int (value));
+ break;
+ case AXIS_PROP_POS_STR: {
+ char const *str = g_value_get_string (value);
+ if (str == NULL)
+ return;
+ else if (!g_ascii_strcasecmp (str, "low"))
+ itmp = GOG_AXIS_AT_LOW;
+ else if (!g_ascii_strcasecmp (str, "middle"))
+ itmp = GOG_AXIS_IN_MIDDLE;
+ else if (!g_ascii_strcasecmp (str, "high"))
+ itmp = GOG_AXIS_AT_HIGH;
+ else
+ return;
+ resized = gog_axis_set_pos (axis, itmp);
+ break;
+ }
+ case AXIS_PROP_INVERT:
+ axis->inverted = g_value_get_boolean (value);
+ calc_ticks = TRUE;
+ break;
+ case AXIS_PROP_MAP :
+ gog_axis_map_set (axis, g_value_get_string (value));
+ request_update = TRUE;
+ break;
+
+ case AXIS_PROP_MAJOR_TICK_LABELED:
+ itmp = g_value_get_boolean (value);
+ if (axis->major_tick_labeled != itmp) {
+ axis->major_tick_labeled = itmp;
+ resized = TRUE;
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MAJOR_TICK_IN :
+ itmp = g_value_get_boolean (value);
+ if (axis->major.tick_in != itmp) {
+ axis->major.tick_in = itmp;
+ if (itmp != axis->major.tick_out)
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MAJOR_TICK_OUT :
+ itmp = g_value_get_boolean (value);
+ if (axis->major.tick_out != itmp) {
+ axis->major.tick_out = itmp;
+ resized = axis->major.size_pts > 0;
+ if (itmp != axis->major.tick_in)
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MAJOR_TICK_SIZE_PTS:
+ itmp = g_value_get_int (value);
+ if (axis->major.size_pts != itmp) {
+ axis->major.size_pts = itmp;
+ resized = axis->major.tick_out;
+ }
+ break;
+
+ case AXIS_PROP_MINOR_TICK_IN :
+ itmp = g_value_get_boolean (value);
+ if (axis->minor.tick_in != itmp) {
+ axis->minor.tick_in = itmp;
+ if (itmp != axis->minor.tick_out)
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MINOR_TICK_OUT :
+ itmp = g_value_get_boolean (value);
+ if (axis->minor.tick_out != itmp) {
+ axis->minor.tick_out = itmp;
+ resized = axis->minor.size_pts > 0;
+ if (itmp != axis->minor.tick_in)
+ calc_ticks = TRUE;
+ }
+ break;
+ case AXIS_PROP_MINOR_TICK_SIZE_PTS:
+ itmp = g_value_get_int (value);
+ if (axis->minor.size_pts != itmp) {
+ axis->minor.size_pts = itmp;
+ resized = axis->minor.tick_out;
+ }
+ break;
+ case AXIS_PROP_ASSIGNED_FORMAT_STR_XL : {
+ char const *str = g_value_get_string (value);
+ resized = gog_axis_set_format (axis, (str != NULL)
+ ? go_format_new_from_XL (str, FALSE)
+ : NULL);
+ calc_ticks = resized;
+ break;
+ }
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ if (request_update)
+ gog_object_request_update (GOG_OBJECT (axis));
+ else {
+ if (calc_ticks)
+ gog_axis_calc_ticks (axis);
+ gog_object_emit_changed (GOG_OBJECT (obj), resized);
+ }
+}
+
+static void
+gog_axis_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogAxis const *axis = GOG_AXIS (obj);
+
+ switch (param_id) {
+ case AXIS_PROP_TYPE:
+ g_value_set_int (value, axis->type);
+ break;
+ case AXIS_PROP_POS:
+ g_value_set_int (value, axis->pos);
+ break;
+ case AXIS_PROP_POS_STR:
+ switch (axis->pos) {
+ case GOG_AXIS_AT_LOW:
+ g_value_set_static_string (value, "low");
+ break;
+ case GOG_AXIS_IN_MIDDLE:
+ g_value_set_static_string (value, "middle");
+ break;
+ case GOG_AXIS_AT_HIGH:
+ g_value_set_static_string (value, "high");
+ break;
+ }
+ break;
+ case AXIS_PROP_INVERT:
+ g_value_set_boolean (value, axis->inverted);
+ break;
+ case AXIS_PROP_MAP:
+ g_value_set_string (value, axis->map_desc->name);
+ break;
+
+ case AXIS_PROP_MAJOR_TICK_LABELED:
+ g_value_set_boolean (value, axis->major_tick_labeled);
+ break;
+ case AXIS_PROP_MAJOR_TICK_IN:
+ g_value_set_boolean (value, axis->major.tick_in);
+ break;
+ case AXIS_PROP_MAJOR_TICK_OUT:
+ g_value_set_boolean (value, axis->major.tick_out);
+ break;
+ case AXIS_PROP_MAJOR_TICK_SIZE_PTS:
+ g_value_set_int (value, axis->major.size_pts);
+ break;
+
+ case AXIS_PROP_MINOR_TICK_IN:
+ g_value_set_boolean (value, axis->minor.tick_in);
+ break;
+ case AXIS_PROP_MINOR_TICK_OUT:
+ g_value_set_boolean (value, axis->minor.tick_out);
+ break;
+ case AXIS_PROP_MINOR_TICK_SIZE_PTS:
+ g_value_set_int (value, axis->minor.size_pts);
+ break;
+ case AXIS_PROP_ASSIGNED_FORMAT_STR_XL :
+ if (axis->assigned_format != NULL)
+ g_value_take_string (value,
+ go_format_as_XL (axis->assigned_format, FALSE));
+ else
+ g_value_set_static_string (value, NULL);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_axis_finalize (GObject *obj)
+{
+ GogAxis *axis = GOG_AXIS (obj);
+
+ gog_axis_clear_contributors (axis);
+
+ g_slist_free (axis->contributors); axis->contributors = NULL;
+ if (axis->labels != NULL) {
+ g_object_unref (axis->labels);
+ axis->labels = NULL;
+ /* this is for information only, no ref */
+ axis->plot_that_supplied_labels = NULL;
+ }
+ if (axis->assigned_format != NULL) {
+ go_format_unref (axis->assigned_format);
+ axis->assigned_format = NULL;
+ }
+ if (axis->format != NULL) {
+ go_format_unref (axis->format);
+ axis->format = NULL;
+ }
+
+ gog_axis_set_ticks (axis, 0, NULL);
+
+ gog_dataset_finalize (GOG_DATASET (axis));
+ (parent_klass->finalize) (obj);
+}
+
+/**
+ * gog_axis_get_entry :
+ * @axis : #GogAxis
+ * @i :
+ * @user_defined : an optionally NULL pointr to gboolean
+ *
+ * Returns the value of axis element @i and sets @user_defined or
+ * NaN on error
+ **/
+double
+gog_axis_get_entry (GogAxis const *axis, GogAxisElemType i, gboolean *user_defined)
+{
+ GOData *dat;
+
+ if (user_defined)
+ *user_defined = FALSE;
+
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, go_nan);
+ g_return_val_if_fail (i >= AXIS_ELEM_MIN && i < AXIS_ELEM_MAX_ENTRY, go_nan);
+
+ dat = axis->source [i].data;
+ if (dat != NULL && IS_GO_DATA_SCALAR (dat)) {
+ double tmp = go_data_scalar_get_value (GO_DATA_SCALAR (dat));
+ if (go_finite (tmp)) {
+ if (user_defined)
+ *user_defined = TRUE;
+ return tmp;
+ }
+ }
+
+ return axis->auto_bound [i];
+}
+
+static void
+gog_axis_update (GogObject *obj)
+{
+ GSList *ptr;
+ GogAxis *axis = GOG_AXIS (obj);
+ double old_min = axis->auto_bound [AXIS_ELEM_MIN];
+ double old_max = axis->auto_bound [AXIS_ELEM_MAX];
+ GOData *labels;
+ GogPlotBoundInfo bounds;
+
+ gog_debug (0, g_warning ("axis::update"););
+
+ if (axis->labels != NULL) {
+ g_object_unref (axis->labels);
+ axis->labels = NULL;
+ axis->plot_that_supplied_labels = NULL;
+ }
+ axis->is_discrete = TRUE;
+ axis->min_val = DBL_MAX;
+ axis->max_val = -DBL_MAX;
+ axis->min_contrib = axis->max_contrib = NULL;
+ if (axis->format != NULL) {
+ go_format_unref (axis->format);
+ axis->format = NULL;
+ }
+
+ /* everything else is initialized in gog_plot_get_axis_bounds */
+ bounds.fmt = NULL;
+ for (ptr = axis->contributors ; ptr != NULL ; ptr = ptr->next) {
+ labels = gog_plot_get_axis_bounds (GOG_PLOT (ptr->data),
+ axis->type, &bounds);
+
+ /* value dimensions have more information than index dimensions.
+ * At least thats what I am guessing today*/
+ if (!bounds.is_discrete)
+ axis->is_discrete = FALSE;
+ else if (axis->labels == NULL && labels != NULL) {
+ g_object_ref (labels);
+ axis->labels = GO_DATA_VECTOR (labels);
+ axis->plot_that_supplied_labels = GOG_PLOT (ptr->data);
+ axis->center_on_ticks = bounds.center_on_ticks;
+ }
+
+ if (axis->min_val > bounds.val.minima) {
+ axis->min_val = bounds.val.minima;
+ axis->logical_min_val = bounds.logical.minima;
+ axis->min_contrib = ptr->data;
+ } else if (axis->min_contrib == ptr->data) {
+ axis->min_contrib = NULL;
+ axis->min_val = bounds.val.minima;
+ }
+
+ if (axis->max_val < bounds.val.maxima) {
+ axis->max_val = bounds.val.maxima;
+ axis->logical_max_val = bounds.logical.maxima;
+ axis->max_contrib = ptr->data;
+ } else if (axis->max_contrib == ptr->data) {
+ axis->max_contrib = NULL;
+ axis->max_val = bounds.val.maxima;
+ }
+ }
+ axis->format = bounds.fmt; /* just absorb the ref if it exists */
+
+ gog_axis_auto_bound (axis);
+
+ if (go_finite (axis->logical_min_val) &&
+ axis->auto_bound [AXIS_ELEM_MIN] < axis->logical_min_val)
+ axis->auto_bound [AXIS_ELEM_MIN] = axis->logical_min_val;
+ if (go_finite (axis->logical_max_val) &&
+ axis->auto_bound [AXIS_ELEM_MAX] > axis->logical_max_val)
+ axis->auto_bound [AXIS_ELEM_MAX] = axis->logical_max_val;
+
+ gog_axis_calc_ticks (axis);
+
+ /* FIXME: there isn't an easy way to know if a discrete axis
+ * needs to emit a changed signal. So allways emit changed signal if
+ * axis is discrete */
+ if (old_min != axis->auto_bound [AXIS_ELEM_MIN] ||
+ old_max != axis->auto_bound [AXIS_ELEM_MAX] ||
+ axis->is_discrete)
+ gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
+}
+
+static void
+cb_pos_changed (GtkToggleButton *toggle_button, GObject *axis)
+{
+ g_object_set (axis,
+ "pos_str", gtk_toggle_button_get_active (toggle_button) ? "low" : "high",
+ NULL);
+}
+
+static void
+cb_axis_toggle_changed (GtkToggleButton *toggle_button, GObject *axis)
+{
+ g_object_set (axis,
+ gtk_widget_get_name (GTK_WIDGET (toggle_button)),
+ gtk_toggle_button_get_active (toggle_button),
+ NULL);
+}
+
+typedef struct {
+ GtkWidget *editor;
+ GtkToggleButton *toggle;
+ GogDataset *set;
+ unsigned dim;
+} ElemToggleData;
+
+static void
+cb_enable_dim (GtkToggleButton *toggle_button, ElemToggleData *closure)
+{
+ gboolean is_auto = gtk_toggle_button_get_active (toggle_button);
+ double bound = GOG_AXIS (closure->set)->auto_bound [closure->dim];
+
+ gtk_widget_set_sensitive (closure->editor, !is_auto);
+
+ if (is_auto) /* clear the data */
+ gog_dataset_set_dim (closure->set, closure->dim, NULL, NULL);
+
+ if (go_finite (bound) && DBL_MAX > bound && bound > -DBL_MAX) {
+ char *str = g_strdup_printf ("%g", bound);
+ g_object_set (closure->editor, "text", str, NULL);
+ g_free (str);
+ } else
+ g_object_set (closure->editor, "text", "", NULL);
+}
+
+static void
+cb_axis_bound_changed (GogObject *axis, gboolean resize, ElemToggleData *closure)
+{
+ if (gtk_toggle_button_get_active (closure->toggle)) {
+ double bound = GOG_AXIS (closure->set)->auto_bound [closure->dim];
+ if (go_finite (bound) && DBL_MAX > bound && bound > -DBL_MAX) {
+ char *str = g_strdup_printf ("%g", bound);
+ g_object_set (closure->editor, "text", str, NULL);
+ g_free (str);
+ } else
+ g_object_set (closure->editor, "text", "", NULL);
+ }
+}
+
+static void
+make_dim_editor (GogDataset *set, GtkTable *table, unsigned dim,
+ GogDataAllocator *dalloc, char const * const *dim_name)
+{
+ ElemToggleData *info;
+ GClosure *closure;
+ GtkWidget *editor = gog_data_allocator_editor (dalloc, set, dim, GOG_DATA_SCALAR);
+ char *txt = g_strconcat (_(dim_name [dim]), ":", NULL);
+ GtkWidget *toggle = gtk_check_button_new_with_mnemonic (txt);
+ g_free (txt);
+
+ info = g_new0 (ElemToggleData, 1);
+ info->editor = editor;
+ info->set = set;
+ info->dim = dim;
+ info->toggle = GTK_TOGGLE_BUTTON (toggle);
+ g_signal_connect (G_OBJECT (toggle),
+ "toggled",
+ G_CALLBACK (cb_enable_dim), info);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ gog_dataset_get_dim (set, dim) == NULL);
+
+ closure = g_cclosure_new (G_CALLBACK (cb_axis_bound_changed),
+ info, (GClosureNotify)g_free);
+ g_object_watch_closure (G_OBJECT (toggle), closure);
+ g_signal_connect_closure (G_OBJECT (set),
+ "changed",
+ closure, FALSE);
+
+ gtk_table_attach (table, toggle,
+ 0, 1, dim + 1, dim + 2, GTK_FILL, 0, 0, 0);
+ gtk_table_attach (table, editor,
+ 1, 2, dim + 1, dim + 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+}
+
+static void
+cb_axis_fmt_changed (G_GNUC_UNUSED GtkWidget *widget,
+ char *fmt,
+ GObject *axis)
+{
+ g_object_set (axis, "assigned-format-string-XL", fmt, NULL);
+}
+
+static void
+cb_map_combo_changed (GtkComboBox *combo,
+ GogAxis *axis)
+{
+ gog_axis_map_set_by_num (axis, gtk_combo_box_get_active (combo));
+}
+
+#if 0
+static void
+cb_axis_fmt_assignment_toggled (GtkToggleButton *toggle_button, GtkNotebook *notebook)
+{
+ /* any time the toggle changes assume the user wanted to select the page too */
+ gtk_notebook_set_current_page (notebook, 0); /* assume it is the first page */
+}
+#endif
+
+static gpointer
+gog_axis_editor (GogObject *gobj, GogDataAllocator *dalloc, GnmCmdContext *cc)
+{
+ static guint axis_pref_page = 0;
+ static char const *toggle_props[] = {
+ "invert-axis",
+ "major-tick-labeled",
+ "major-tick-out",
+ "major-tick-in",
+ "minor-tick-out",
+ "minor-tick-in"
+ };
+ GtkWidget *w, *notebook; /* , *cbox; */
+ GtkTable *table;
+ unsigned i = 0;
+ GogAxis *axis = GOG_AXIS (gobj);
+ GogDataset *set = GOG_DATASET (gobj);
+ GladeXML *gui;
+
+ /* No preferences for circular axis */
+ if (axis->type == GOG_AXIS_CIRCULAR)
+ return NULL;
+
+ gui = gnm_glade_xml_new (cc, "gog-axis-prefs.glade", "axis_pref_box", NULL);
+ if (gui == NULL)
+ return NULL;
+ notebook = gtk_notebook_new ();
+
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook),
+ glade_xml_get_widget (gui, "axis_pref_box"),
+ gtk_label_new (_("Details")));
+ gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, notebook);
+
+ if (!axis->is_discrete) {
+ GtkWidget *w = glade_xml_get_widget (gui, "map_type_combo");
+ gog_axis_map_populate_combo (axis, GTK_COMBO_BOX (w));
+ g_signal_connect_object (G_OBJECT (w),
+ "changed",
+ G_CALLBACK (cb_map_combo_changed),
+ axis, 0);
+ } else {
+ GtkWidget *w = glade_xml_get_widget (gui, "map_type_box");
+ gtk_widget_hide (w);
+ }
+
+ w = glade_xml_get_widget (gui, "axis_low");
+ if (axis->pos == GOG_AXIS_AT_LOW)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), TRUE);
+ else
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (
+ glade_xml_get_widget (gui, "axis_high")), TRUE);
+ g_signal_connect_object (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_pos_changed), axis, 0);
+
+ for (i = 0; i < G_N_ELEMENTS (toggle_props) ; i++) {
+ gboolean cur_val;
+ GtkWidget *w = glade_xml_get_widget (gui, toggle_props[i]);
+
+ g_object_get (G_OBJECT (gobj), toggle_props[i], &cur_val, NULL);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), cur_val);
+ g_signal_connect_object (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_axis_toggle_changed), axis, 0);
+ }
+ if (axis->is_discrete) {
+ /* Hide minor tick properties */
+ GtkWidget *w = glade_xml_get_widget (gui, "minor_tick_box");
+ gtk_widget_hide (w);
+ }
+
+ /* Bounds Page */
+ w = gtk_table_new (1, 2, FALSE);
+ table = GTK_TABLE (w);
+ w = gtk_label_new (_("Automatic"));
+ gtk_misc_set_alignment (GTK_MISC (w), 0., .5);
+ gtk_table_set_row_spacings (table, 6);
+ gtk_table_set_col_spacings (table, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_table_attach (table, w, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+ if (axis->is_discrete) {
+ static char const * const dim_names[] = {
+ NULL,
+ NULL,
+ N_("Categories between _ticks"),
+ N_("Categories between _labels"),
+ N_("_Cross at category #")
+ };
+ for (i = AXIS_ELEM_MAJOR_TICK; i < AXIS_ELEM_MAX_ENTRY ; i++)
+ make_dim_editor (set, table, i, dalloc, dim_names);
+ gtk_widget_show_all (GTK_WIDGET (table));
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (table),
+ gtk_label_new (_("Bounds")));
+ } else {
+ static char const * const dim_names[] = {
+ N_("M_in"),
+ N_("M_ax"),
+ N_("Ma_jor Ticks"),
+ N_("Mi_nor Ticks"),
+ N_("_Cross")
+ };
+
+ for (i = AXIS_ELEM_MIN; i < AXIS_ELEM_MAX_ENTRY ; i++)
+ make_dim_editor (set, table, i, dalloc, dim_names);
+ gtk_widget_show_all (GTK_WIDGET (table));
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), GTK_WIDGET (table),
+ gtk_label_new (_("Bounds")));
+
+ w = number_format_selector_new ();
+ if (axis->assigned_format != NULL && !style_format_is_general (axis->assigned_format))
+ number_format_selector_set_style_format (NUMBER_FORMAT_SELECTOR (w),
+ axis->assigned_format);
+ else if (axis->format != NULL)
+ number_format_selector_set_style_format (NUMBER_FORMAT_SELECTOR (w),
+ axis->format);
+
+#if 0
+ /* TOO CHEESY to go into production
+ * We need a way to toggle auto vs user formats
+ * but the selector is too tall already
+ * disable for now */
+ cbox = gtk_check_button_new_with_label (_("Format"));
+ g_signal_connect (G_OBJECT (cbox),
+ "toggled",
+ G_CALLBACK (cb_axis_fmt_assignment_toggled), notebook);
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), w, cbox);
+#else
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), w,
+ gtk_label_new (_("Format")));
+#endif
+
+ gtk_widget_show (w);
+ g_signal_connect (G_OBJECT (w),
+ "number_format_changed",
+ G_CALLBACK (cb_axis_fmt_changed), axis);
+ }
+
+ g_object_set_data_full (G_OBJECT (notebook), "gui", gui,
+ (GDestroyNotify)g_object_unref);
+
+ gog_style_handle_notebook (notebook, &axis_pref_page);
+ gtk_widget_show (GTK_WIDGET (notebook));
+ return notebook;
+}
+
+static void
+gog_axis_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_LINE | GOG_STYLE_FONT;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_axis_class_init (GObjectClass *gobject_klass)
+{
+ static GogObjectRole const roles[] = {
+ { N_("Label"), "GogLabel", 0,
+ GOG_POSITION_COMPASS, GOG_POSITION_S|GOG_POSITION_ALIGN_CENTER, GOG_OBJECT_NAME_BY_ROLE,
+ NULL, NULL, NULL, role_label_post_add, NULL, NULL, { -1 } },
+ { N_("MinorGrid"), "GogGridLine", 0,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_grid_line_minor_can_add, NULL, NULL, role_grid_line_minor_post_add, NULL, NULL, { -1 } },
+ { N_("MajorGrid"), "GogGridLine", 1,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_grid_line_major_can_add, NULL, NULL, role_grid_line_major_post_add, NULL, NULL, { -1 } }
+ };
+
+ GogObjectClass *gog_klass = (GogObjectClass *) gobject_klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) gog_klass;
+
+ parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->set_property = gog_axis_set_property;
+ gobject_klass->get_property = gog_axis_get_property;
+ gobject_klass->finalize = gog_axis_finalize;
+
+ /* no need to persist, the role handles that */
+ g_object_class_install_property (gobject_klass, AXIS_PROP_TYPE,
+ g_param_spec_int ("type", "Type",
+ "GogAxisType",
+ GOG_AXIS_UNKNOWN, GOG_AXIS_TYPES, GOG_AXIS_UNKNOWN, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_POS,
+ g_param_spec_int ("pos", "pos",
+ "GogAxisPosition",
+ GOG_AXIS_AT_LOW, GOG_AXIS_AT_HIGH, GOG_AXIS_AT_LOW, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_POS_STR,
+ g_param_spec_string ("pos_str", "pos_str",
+ "Where to position an axis low, high, or crossing",
+ "low", G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ g_object_class_install_property (gobject_klass, AXIS_PROP_INVERT,
+ g_param_spec_boolean ("invert-axis", NULL,
+ "Scale from high to low rather than low to high",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAP,
+ g_param_spec_string ("map-name", "MapName",
+ "The name of the map for scaling",
+ "linear", G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAJOR_TICK_LABELED,
+ g_param_spec_boolean ("major-tick-labeled", NULL,
+ "Show labels for major ticks",
+ TRUE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAJOR_TICK_IN,
+ g_param_spec_boolean ("major-tick-in", NULL,
+ "Major tick marks inside the axis",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAJOR_TICK_OUT,
+ g_param_spec_boolean ("major-tick-out", NULL,
+ "Major tick marks outside the axis",
+ TRUE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MAJOR_TICK_SIZE_PTS,
+ g_param_spec_int ("major-tick-size-pts", "major-tick-size-pts",
+ "Size of the major tick marks in pts",
+ 0, 20, 4, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MINOR_TICK_IN,
+ g_param_spec_boolean ("minor-tick-in", NULL,
+ "Minor tick marks inside the axis",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MINOR_TICK_OUT,
+ g_param_spec_boolean ("minor-tick-out", NULL,
+ "Minor tick marks outside the axis",
+ FALSE, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_MINOR_TICK_SIZE_PTS,
+ g_param_spec_int ("minor-tick-size-pts", NULL,
+ "Size of the minor tick marks in pts",
+ 0, 15, 2, G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+ g_object_class_install_property (gobject_klass, AXIS_PROP_ASSIGNED_FORMAT_STR_XL,
+ g_param_spec_string ("assigned-format-string-XL", NULL,
+ "The user assigned format to use for non-discrete axis labels (XL format)",
+ "General", G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+
+ gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+ gog_klass->update = gog_axis_update;
+ gog_klass->editor = gog_axis_editor;
+ gog_klass->view_type = gog_axis_view_get_type ();
+ style_klass->init_style = gog_axis_init_style;
+}
+
+static void
+gog_axis_init (GogAxis *axis)
+{
+ axis->type = GOG_AXIS_UNKNOWN;
+ axis->pos = GOG_AXIS_AT_LOW;
+ axis->contributors = NULL;
+ axis->minor.tick_in = axis->minor.tick_out = axis->major.tick_in = FALSE;
+ axis->major.tick_out = TRUE;
+ axis->major_tick_labeled = TRUE;
+ axis->inverted = FALSE;
+ axis->major.size_pts = 4;
+ axis->minor.size_pts = 2;
+
+ /* yes we want min = MAX */
+ axis->min_val = DBL_MAX;
+ axis->max_val = -DBL_MAX;
+ axis->min_contrib = axis->max_contrib = NULL;
+ axis->is_discrete = FALSE;
+ axis->center_on_ticks = FALSE;
+ axis->labels = NULL;
+ axis->plot_that_supplied_labels = NULL;
+ axis->format = axis->assigned_format = NULL;
+
+ gog_axis_map_set (axis, NULL);
+
+ axis->ticks = NULL;
+ axis->tick_nbr = 0;
+}
+
+static void
+gog_axis_dataset_dims (GogDataset const *set, int *first, int *last)
+{
+ *first = AXIS_ELEM_MIN;
+ *last = AXIS_ELEM_CROSS_POINT;
+}
+
+static GogDatasetElement *
+gog_axis_dataset_get_elem (GogDataset const *set, int dim_i)
+{
+ GogAxis *axis = GOG_AXIS (set);
+ if (AXIS_ELEM_MIN <= dim_i && dim_i <= AXIS_ELEM_CROSS_POINT)
+ return &axis->source[dim_i];
+ return NULL;
+}
+
+static void
+gog_axis_dim_changed (GogDataset *set, int dim_i)
+{
+ gog_axis_update (GOG_OBJECT (set));
+ gog_object_emit_changed (GOG_OBJECT (set), TRUE);
+}
+
+static void
+gog_axis_dataset_init (GogDatasetClass *iface)
+{
+ iface->dims = gog_axis_dataset_dims;
+ iface->get_elem = gog_axis_dataset_get_elem;
+ iface->dim_changed = gog_axis_dim_changed;
+}
+
+GSF_CLASS_FULL (GogAxis, gog_axis,
+ gog_axis_class_init, gog_axis_init,
+ GOG_STYLED_OBJECT_TYPE, 0,
+ GSF_INTERFACE (gog_axis_dataset_init, GOG_DATASET_TYPE))
+
+
+GogAxisType
+gog_axis_get_atype (GogAxis const *axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, GOG_AXIS_UNKNOWN);
+ return axis->type;
+}
+
+GogAxisPosition
+gog_axis_get_pos (GogAxis const *axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, GOG_AXIS_IN_MIDDLE);
+ return axis->pos;
+}
+
+/**
+ * gog_axis_is_discrete :
+ * @axis : #GogAxis
+ *
+ * Returns TRUE if @axis enumerates a set of discrete items, rather than a
+ * continuous value
+ **/
+gboolean
+gog_axis_is_discrete (GogAxis const *axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, FALSE);
+ return axis->is_discrete;
+}
+
+/**
+ * gog_axis_get_bounds :
+ * @axis : #GogAxis
+ * @minima : result
+ * @maxima : result
+ *
+ * return TRUE if the bounds stored in @minima and @maxima are sane
+ **/
+gboolean
+gog_axis_get_bounds (GogAxis const *axis, double *minima, double *maxima)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, FALSE);
+ g_return_val_if_fail (minima != NULL, FALSE);
+ g_return_val_if_fail (maxima != NULL, FALSE);
+
+ *minima = gog_axis_get_entry (axis, AXIS_ELEM_MIN, NULL);
+ *maxima = gog_axis_get_entry (axis, AXIS_ELEM_MAX, NULL);
+
+ return go_finite (*minima) && go_finite (*maxima) && *minima < *maxima;
+}
+
+/**
+ * gog_axis_get_ticks :
+ * @axis : #GogAxis
+ * @ticks : result
+ *
+ * Retrieve an array of tick descriptions, and return the number of ticks.
+ **/
+unsigned
+gog_axis_get_ticks (GogAxis *axis, GogAxisTick **ticks)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, 0);
+ g_return_val_if_fail (ticks != NULL, 0);
+
+ *ticks = axis->ticks;
+ return axis->tick_nbr;
+}
+
+/**
+ * gog_axis_get_labels :
+ * @axi : #GogAxis
+ * @plot_that_labeled_axis : #GogPlot
+ *
+ * Return the possibly NULL #GOData used as a label for this axis
+ * along with the plot that it was associated with
+ **/
+GOData *
+gog_axis_get_labels (GogAxis const *axis, GogPlot **plot_that_labeled_axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, NULL);
+
+ if (axis->is_discrete) {
+ if (plot_that_labeled_axis != NULL)
+ *plot_that_labeled_axis = axis->plot_that_supplied_labels;
+ return GO_DATA (axis->labels);
+ }
+ if (plot_that_labeled_axis != NULL)
+ *plot_that_labeled_axis = NULL;
+ return NULL;
+}
+
+/**
+ * gog_axis_add_contributor :
+ * @axis : #GogAxis
+ * @contrib : #GogObject (can we relax this to use an interface ?)
+ *
+ * Register @contrib as taking part in the negotiation of @axis's bounds.
+ **/
+void
+gog_axis_add_contributor (GogAxis *axis, GogObject *contrib)
+{
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+ g_return_if_fail (g_slist_find (axis->contributors, contrib) == NULL);
+
+ axis->contributors = g_slist_prepend (axis->contributors, contrib);
+
+ gog_object_request_update (GOG_OBJECT (axis));
+}
+
+/**
+ * gog_axis_del_contributor :
+ * @axis : #GogAxis
+ * @contrib : #GogObject (can we relax this to use an interface ?)
+ *
+ * @contrib no longer takes part in the negotiation of @axis's bounds.
+ **/
+void
+gog_axis_del_contributor (GogAxis *axis, GogObject *contrib)
+{
+ gboolean update = FALSE;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+ g_return_if_fail (g_slist_find (axis->contributors, contrib) != NULL);
+
+ if (axis->min_contrib == contrib) {
+ axis->min_contrib = NULL;
+ update = TRUE;
+ }
+ if (axis->max_contrib == contrib) {
+ axis->max_contrib = NULL;
+ update = TRUE;
+ }
+ axis->contributors = g_slist_remove (axis->contributors, contrib);
+
+ if (update)
+ gog_object_request_update (GOG_OBJECT (axis));
+}
+
+void
+gog_axis_clear_contributors (GogAxis *axis)
+{
+ GSList *ptr, *list;
+ GogAxisSet filter;
+
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ filter = 1 << axis->type;
+ list = g_slist_copy (axis->contributors);
+ for (ptr = list; ptr != NULL ; ptr = ptr->next)
+ gog_plot_axis_clear (GOG_PLOT (ptr->data), filter);
+ g_slist_free (list);
+}
+
+GSList const *
+gog_axis_contributors (GogAxis *axis)
+{
+ g_return_val_if_fail (GOG_AXIS (axis) != NULL, NULL);
+
+ return axis->contributors;
+}
+
+/**
+ * gog_axis_bound_changed :
+ * @axis : #GogAxis
+ * @contrib : #GogObject
+**/
+void
+gog_axis_bound_changed (GogAxis *axis, GogObject *contrib)
+{
+ g_return_if_fail (GOG_AXIS (axis) != NULL);
+
+ gog_object_request_update (GOG_OBJECT (axis));
+}
+
+/****************************************************************************/
+
+typedef GogView GogAxisView;
+typedef GogViewClass GogAxisViewClass;
+
+#define GOG_AXIS_VIEW_TYPE (gog_axis_view_get_type ())
+#define GOG_AXIS_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_AXIS_VIEW_TYPE, GogAxisView))
+#define IS_GOG_AXIS_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_AXIS_VIEW_TYPE))
+
+static GogViewClass *aview_parent_klass;
+
+void
+gog_axis_view_padding_request (GogView *v, GogViewPadding *padding,
+ GogViewAllocation *bbox)
+{
+ GogAxis *axis = GOG_AXIS (v->model);
+ GogViewRequisition txt_size;
+ gboolean const is_horiz = axis->type == GOG_AXIS_X;
+ unsigned i;
+ double tick_major = 0., tick_minor = 0.;
+ double txt_max_h, txt_max_w;
+ double line_width = gog_renderer_line_size (
+ v->renderer, axis->base.style->line.width);
+
+ double Xl, Xr, wl, wr, xm;
+ gboolean label_found = FALSE;
+ double position;
+ char *label = NULL;
+ double padding_l, padding_r, padding_label;
+ gboolean is_l_valid, is_r_valid;
+
+ GogAxisMap *map;
+
+ padding->wr = padding->wl = padding->ht = padding->hb = 0.;
+
+ if ((axis->type != GOG_AXIS_X && axis->type != GOG_AXIS_Y))
+ return;
+
+ map = gog_axis_map_new (axis, 0., 1.);
+
+ if (is_horiz) {
+ xm = bbox->w - bbox->x;
+ } else {
+ xm = bbox->h - bbox->y;
+ }
+
+
+ padding_l = padding_r = padding_label = 0.;
+ Xl = DBL_MAX;
+ Xr = -DBL_MAX;
+ wl = wr = 0.;
+ txt_max_w = txt_max_h = 0.;
+ gog_renderer_push_style (v->renderer, axis->base.style);
+ for (i = 0; i < axis->tick_nbr; i++) {
+ label = axis->ticks[i].label;
+ if (label != NULL) {
+ label_found = TRUE;
+ position = gog_axis_map (map, axis->ticks[i].position);
+ gog_renderer_measure_text (v->renderer, label,
+ &txt_size);
+
+ if (txt_max_h < txt_size.h)
+ txt_max_h = txt_size.h;
+ if (txt_max_w < txt_size.w)
+ txt_max_w = txt_size.w;
+
+ if (position < Xl) {
+ Xl = position;
+ wl = (is_horiz ? txt_size.w : txt_size.h) / 2.;
+ }
+ if (position > Xr) {
+ Xr = position;
+ wr = (is_horiz ? txt_size.w : txt_size.h) / 2.;
+ }
+ }
+ }
+ gog_renderer_pop_style (v->renderer);
+
+ if (label_found) {
+ if (Xl != Xr) {
+ padding_l = (Xr * wl + Xl * (wr - xm)) / (Xr - Xl);
+ padding_r = xm - (((Xr - 1.0) * wl + (Xl - 1.0) * (wr - xm)) / (Xr - Xl));
+ is_l_valid = padding_l > 0.;
+ is_r_valid = padding_r > 0.;
+ } else {
+ is_l_valid = Xl > .5;
+ is_r_valid = ! is_l_valid;
+ }
+
+ if (!is_l_valid) {
+ padding_l = 0.;
+ if (Xr > 0.)
+ padding_r = MAX (xm - ((xm - wr) / Xr), 0.);
+ else
+ padding_r = 0.;
+ } else if (!is_r_valid) {
+ padding_r = 0.;
+ if (Xl < 1.0)
+ padding_l = MAX ((wl -Xl * xm) / (1 - Xl), 0.);
+ else
+ padding_l = 0.;
+ }
+ }
+
+ if (is_horiz) {
+ if (line_width > 0) {
+ if (axis->major.tick_out)
+ tick_major = gog_renderer_pt2r_y (v->renderer,
+ axis->major.size_pts);
+ if (axis->minor.tick_out)
+ tick_minor = gog_renderer_pt2r_y (v->renderer,
+ axis->minor.size_pts);
+ }
+ padding_label = (axis->is_discrete ?
+ MAX (txt_max_h, MAX (tick_minor, tick_major)):
+ MAX (tick_major + txt_max_h, tick_minor)) +
+ line_width;
+ } else {
+ if (line_width > 0) {
+ if (axis->major.tick_out)
+ tick_major = gog_renderer_pt2r_x (v->renderer,
+ axis->major.size_pts);
+ if (axis->minor.tick_out)
+ tick_minor = gog_renderer_pt2r_x (v->renderer,
+ axis->minor.size_pts);
+ }
+ padding_label = (axis->is_discrete ?
+ MAX (txt_max_w, MAX (tick_major, tick_minor)):
+ MAX (tick_major + txt_max_w, tick_minor)) +
+ line_width;
+ }
+
+ if (is_horiz) {
+ padding->wl = padding_l;
+ padding->wr = padding_r;
+ if (axis->pos == GOG_AXIS_AT_LOW) {
+ padding->hb = padding_label;
+ padding->ht = line_width;
+ } else {
+ padding->hb = line_width;
+ padding->ht = padding_label;
+ }
+ } else {
+ padding->ht = padding_r;
+ padding->hb = padding_l;
+ if (axis->pos == GOG_AXIS_AT_LOW) {
+ padding->wl = padding_label;
+ padding->wr = line_width;
+ } else {
+ padding->wl = line_width;
+ padding->wr = padding_label;
+ }
+ }
+
+ gog_axis_map_free (map);
+}
+
+static void
+gog_axis_view_size_request (GogView *v, GogViewRequisition *req)
+{
+ gog_view_size_child_request (v, req, req);
+}
+
+static gboolean
+overlap (GogViewAllocation const *bbox1, GogViewAllocation const *bbox2) {
+ return (!((MAX (bbox2->x, bbox2->x + bbox2->w) < MIN (bbox1->x, bbox1->x + bbox1->w)) ||
+ (MAX (bbox2->y, bbox2->y + bbox2->h) < MIN (bbox1->y, bbox1->y + bbox1->h)) ||
+ (MIN (bbox2->x, bbox2->x + bbox2->w) > MAX (bbox1->x, bbox1->x + bbox1->w)) ||
+ (MIN (bbox2->y, bbox2->y + bbox2->h) > MAX (bbox1->y, bbox1->y + bbox1->h))));
+}
+
+static void
+draw_axis_from_a_to_b (GogView *v, GogAxis *axis, double ax, double ay, double bx, double by,
+ gboolean draw_label)
+{
+ GogAxisMap *map = NULL;
+ ArtVpath path[3];
+ double line_width;
+ double axis_length, axis_angle, label_angle;
+ double tick_len;
+ double major_out_x = 0., major_out_y= 0., major_in_x = 0., major_in_y = 0.;
+ double minor_out_x = 0., minor_out_y= 0., minor_in_x = 0., minor_in_y = 0.;
+ double cos_alpha, sin_alpha;
+ double pos, pos_x, pos_y, offset, label_offset;
+ unsigned i;
+ GogViewRequisition txt_size;
+ GogViewAllocation label_pos, label_result, label_old = {0., 0., 0., 0.};
+ gboolean draw_major, draw_minor;
+ gboolean is_line_visible;
+
+ axis_length = sqrt ((ax-bx)*(ax-bx)+(ay-by)*(ay-by));
+ if (bx - ax != 0) {
+ axis_angle = atan ((double)(by-ay)/(double)(bx-ax));
+ if (bx < ax) {
+ axis_angle += M_PI;
+ }
+ } else {
+ if (ay > by) {
+ axis_angle = - M_PI/2.0;
+ } else {
+ axis_angle = M_PI/2.0;
+ }
+ }
+ label_angle = fmod (axis_angle + 2.0 * M_PI, M_PI);
+ if (label_angle > M_PI / 2.0)
+ label_angle = M_PI - label_angle;
+ cos_alpha = cos (axis_angle + M_PI / 2.0);
+ sin_alpha = sin (axis_angle + M_PI / 2.0);
+
+ is_line_visible = gog_style_is_line_visible (axis->base.style);
+ line_width = gog_renderer_line_size (v->renderer, axis->base.style->line.width) / 2;
+ if (is_line_visible)
+ {
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_END;
+
+ path[0].x = ax;
+ path[0].y = ay;
+ path[1].x = bx;
+ path[1].y = by;
+ gog_renderer_draw_path (v->renderer, path, NULL);
+
+ map = gog_axis_map_new (axis, 0., axis_length);
+ }
+
+ draw_major = axis->major.tick_in || axis->major.tick_out;
+ draw_minor = axis->minor.tick_in || axis->minor.tick_out;
+
+ tick_len = gog_renderer_pt2r_x (v->renderer, axis->minor.size_pts) + line_width;
+ minor_out_x = axis->minor.tick_out ? - tick_len * cos_alpha : 0.;
+ minor_out_y = axis->minor.tick_out ? - tick_len * sin_alpha : 0.;
+ minor_in_x = axis->minor.tick_in ? tick_len * cos_alpha : 0.;
+ minor_in_y = axis->minor.tick_in ? tick_len * sin_alpha : 0.;
+ tick_len = gog_renderer_pt2r_x (v->renderer, axis->major.size_pts) + line_width;
+ major_out_x = axis->major.tick_out ? - tick_len * cos_alpha : 0.;
+ major_out_y = axis->major.tick_out ? - tick_len * sin_alpha : 0.;
+ major_in_x = axis->major.tick_in ? tick_len * cos_alpha : 0.;
+ major_in_y = axis->major.tick_in ? tick_len * sin_alpha : 0.;
+ label_offset = gog_renderer_pt2r_x (v->renderer, TICK_LABEL_PAD_HORIZ) +
+ axis->major.tick_in ? tick_len : 0.;
+
+ for (i = 0; i < axis->tick_nbr; i++) {
+ pos = gog_axis_map_to_canvas (map, axis->ticks[i].position);
+ pos_x = ax + pos * cos (axis_angle);
+ pos_y = ay + pos * sin (axis_angle);
+
+ if (is_line_visible) {
+ switch (axis->ticks[i].type) {
+ case GOG_AXIS_TICK_MAJOR:
+ if (draw_major) {
+ path[0].x = major_out_x + pos_x;
+ path[1].x = major_in_x + pos_x;
+ path[0].y = major_out_y + pos_y;
+ path[1].y = major_in_y + pos_y;
+ gog_renderer_draw_path (v->renderer, path, NULL);
+ }
+ break;
+
+ case GOG_AXIS_TICK_MINOR:
+ if (draw_minor) {
+ path[0].x = minor_out_x + pos_x;
+ path[1].x = minor_in_x + pos_x;
+ path[0].y = minor_out_y + pos_y;
+ path[1].y = minor_in_y + pos_y;
+ gog_renderer_draw_path (v->renderer, path, NULL);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (axis->ticks[i].label != NULL &&
+ gog_axis_map (map, axis->ticks[i].position) >= 0.1 &&
+ draw_label) {
+ gog_renderer_measure_text (v->renderer, axis->ticks[i].label, &txt_size);
+ offset = (txt_size.h * cos (label_angle) + txt_size.w * sin (label_angle)) / 2.0 + label_offset;
+ label_pos.x = pos_x + offset * cos_alpha;
+ label_pos.y = pos_y + offset * sin_alpha;
+ label_pos.w = txt_size.w;
+ label_pos.h = txt_size.h;
+ if (!overlap (&label_pos, &label_old)) {
+ gog_renderer_draw_text (v->renderer, axis->ticks[i].label,
+ &label_pos, GTK_ANCHOR_CENTER, &label_result);
+ label_old = label_pos;
+ }
+ }
+ }
+
+ if (is_line_visible)
+ gog_axis_map_free (map);
+}
+
+static void
+gog_axis_view_render_children (GogView *view, GogViewAllocation const *bbox)
+{
+ GSList *ptr;
+
+ /* Render every child except grid lines. Those are rendered
+ * before in gog_chart_view since we don't want to render them
+ * over axis. */
+ for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
+ if (!IS_GOG_GRID_LINE (GOG_VIEW (ptr->data)->model))
+ gog_view_render (ptr->data, bbox);
+ }
+}
+
+static void
+gog_axis_view_render (GogView *v, GogViewAllocation const *bbox)
+{
+ GtkAnchorType anchor;
+ GogViewAllocation const *area;
+ GogViewAllocation label_pos, label_result;
+ GogViewRequisition txt_size;
+ double last_label_pos = .0, last_label_size = -DBL_MAX;
+ ArtVpath path[3];
+ GogAxis *axis = GOG_AXIS (v->model);
+ GogStyle *style = axis->base.style;
+ unsigned i;
+ double tick_len, label_pad, dir, center, label_spacing = 0.;
+ double line_width = gog_renderer_line_size (v->renderer,
+ style->line.width) / 2.;
+ double pos, offset;
+ double major_in = 0., major_out = 0.;
+ double minor_in = 0., minor_out = 0.;
+ gboolean draw_major, draw_minor;
+ gboolean is_line_visible = gog_style_is_line_visible (style);
+
+ gog_axis_view_render_children (v, bbox);
+
+ g_return_if_fail (axis->pos != GOG_AXIS_IN_MIDDLE);
+ g_return_if_fail (v->parent != NULL);
+ area = gog_chart_view_get_plot_area (v->parent);
+ g_return_if_fail (area != NULL);
+
+ gog_renderer_push_style (v->renderer, style);
+
+ draw_major = (axis->major.tick_in || axis->major.tick_out) && is_line_visible;
+ draw_minor = (axis->minor.tick_in || axis->minor.tick_out) && is_line_visible;
+
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_END;
+
+ switch (axis->type) {
+ case GOG_AXIS_X:
+ switch (axis->pos) {
+ default :
+ case GOG_AXIS_AT_LOW:
+ anchor = GTK_ANCHOR_N;
+ dir = 1.;
+ center = area->y + area->h;
+ break;
+
+ case GOG_AXIS_AT_HIGH:
+ anchor = GTK_ANCHOR_S;
+ dir = -1.;
+ center = area->y;
+ break;
+ }
+
+ tick_len = gog_renderer_pt2r_y (v->renderer, axis->major.size_pts);
+ major_out = axis->major.tick_out ? center + dir * (line_width + tick_len) : center;
+ major_in = axis->major.tick_in ? center - dir * (line_width + tick_len) : center;
+ tick_len = gog_renderer_pt2r_y (v->renderer, axis->minor.size_pts);
+ minor_out = axis->minor.tick_out ? center + dir * (line_width + tick_len) : center;
+ minor_in = axis->minor.tick_in ? center - dir * (line_width + tick_len) : center;
+
+ if (is_line_visible) {
+ path[0].y = path[1].y = center;
+ path[0].x = area->x - line_width;
+ path[1].x = area->x + area->w + line_width;
+ gog_renderer_draw_sharp_path (v->renderer, path, NULL);
+ }
+
+ if (axis->major_tick_labeled) {
+ label_pad = gog_renderer_pt2r_y (v->renderer, TICK_LABEL_PAD_VERT);
+ label_pos.y = (axis->major.tick_out && (!axis->is_discrete || axis->center_on_ticks))
+ ? major_out + dir * label_pad
+ : center + dir * (line_width + label_pad);
+ label_pos.h = area->h - line_width;
+ label_pos.w = -1;
+ gog_renderer_measure_text (v->renderer, "0", &txt_size);
+ label_spacing = txt_size.w;
+ }
+
+ if (axis->tick_nbr > 0) {
+ GogAxisMap *map = gog_axis_map_new (axis, area->x, area->w);
+ offset = (axis->is_discrete && !axis->center_on_ticks)? -0.5 : 0.0;
+ for (i = 0; i < axis->tick_nbr; i++) {
+
+ if (is_line_visible) {
+ pos = gog_axis_map_to_canvas (map, axis->ticks[i].position + offset);
+ switch (axis->ticks[i].type) {
+ case GOG_AXIS_TICK_MAJOR:
+ if (draw_major) {
+ path[0].x = path[1].x = pos;
+ path[0].y = major_out;
+ path[1].y = major_in;
+ gog_renderer_draw_sharp_path
+ (v->renderer, path, NULL);
+ }
+ break;
+
+ case GOG_AXIS_TICK_MINOR:
+ if (draw_minor) {
+ path[0].x = path[1].x = pos;
+ path[0].y = minor_out;
+ path[1].y = minor_in;
+ gog_renderer_draw_sharp_path
+ (v->renderer, path, NULL);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ if (axis->ticks[i].label != NULL) {
+ label_pos.x = gog_axis_map_to_canvas (map, axis->ticks[i].position);
+ if (fabs (last_label_pos - label_pos.x) > last_label_size + label_spacing) {
+ gog_renderer_measure_text (v->renderer, axis->ticks[i].label, &txt_size);
+ txt_size.w /= 2.0;
+ if (fabs (last_label_pos - label_pos.x) > last_label_size + txt_size.w + label_spacing) {
+ gog_renderer_draw_text (v->renderer, axis->ticks[i].label,
+ &label_pos, anchor, &label_result);
+ last_label_pos = label_pos.x;
+ last_label_size = txt_size.w;
+ }
+ }
+ }
+ }
+ gog_axis_map_free (map);
+ }
+
+ break;
+
+ case GOG_AXIS_Y:
+ switch (axis->pos) {
+ default :
+ case GOG_AXIS_AT_LOW:
+ anchor = GTK_ANCHOR_E;
+ dir = -1.;
+ center = area->x;
+ break;
+ case GOG_AXIS_AT_HIGH:
+ anchor = GTK_ANCHOR_W;
+ dir = 1.;
+ center = area->x + area->w;
+ break;
+ }
+
+ tick_len = gog_renderer_pt2r_x (v->renderer, axis->major.size_pts);
+ major_out = axis->major.tick_out ? center + dir * (line_width + tick_len) : center;
+ major_in = axis->major.tick_in ? center - dir * (line_width + tick_len) : center;
+ tick_len = gog_renderer_pt2r_x (v->renderer, axis->minor.size_pts);
+ minor_out = axis->minor.tick_out ? center + dir * (line_width + tick_len) : center;
+ minor_in = axis->minor.tick_in ? center - dir * (line_width + tick_len) : center;
+
+ if (is_line_visible) {
+ path[0].x = path[1].x = center;
+ path[0].y = area->y + area->h + line_width;
+ path[1].y = area->y - line_width;
+ gog_renderer_draw_sharp_path (v->renderer, path, NULL);
+ }
+
+ if (axis->major_tick_labeled) {
+ label_pad = gog_renderer_pt2r_x (v->renderer, TICK_LABEL_PAD_HORIZ);
+ label_pos.x = (axis->major.tick_out && (!axis->is_discrete || axis->center_on_ticks))
+ ? major_out + dir * label_pad
+ : center + dir * (line_width + label_pad);
+ label_pos.w = area->w - line_width;
+ label_pos.h = -1;
+ }
+
+ if (axis->tick_nbr > 0) {
+ GogAxisMap *map = gog_axis_map_new (axis, area->h + area->y, -area->h);
+ offset = (axis->is_discrete && !axis->center_on_ticks)? -0.5 : 0.0;
+ for (i = 0; i < axis->tick_nbr; i++) {
+
+ if (is_line_visible) {
+ pos = gog_axis_map_to_canvas (map, axis->ticks[i].position + offset);
+ switch (axis->ticks[i].type) {
+ case GOG_AXIS_TICK_MAJOR:
+ if (draw_major) {
+ path[0].y = path[1].y = pos;
+ path[0].x = major_out;
+ path[1].x = major_in;
+ gog_renderer_draw_sharp_path
+ (v->renderer, path, NULL);
+ }
+ break;
+
+ case GOG_AXIS_TICK_MINOR:
+ if (draw_minor) {
+ path[0].y = path[1].y = pos;
+ path[0].x = minor_out;
+ path[1].x = minor_in;
+ gog_renderer_draw_sharp_path
+ (v->renderer, path, NULL);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (axis->ticks[i].label != NULL) {
+ label_pos.y = gog_axis_map_to_canvas (map, axis->ticks[i].position);
+ if (fabs (last_label_pos - label_pos.y) > last_label_size) {
+ gog_renderer_measure_text (v->renderer, axis->ticks[i].label, &txt_size);
+ txt_size.h /= 2.0;
+ if (fabs (last_label_pos - label_pos.y) > last_label_size + txt_size.h) {
+ gog_renderer_draw_text (v->renderer, axis->ticks[i].label,
+ &label_pos, anchor, &label_result);
+ last_label_pos = label_pos.y;
+ last_label_size = txt_size.h;
+ }
+ }
+ }
+ }
+ gog_axis_map_free (map);
+ }
+
+ break;
+
+ case GOG_AXIS_CIRCULAR:
+ break;
+
+ case GOG_AXIS_RADIAL: {
+ double center_x, center_y, radius;
+ unsigned i, num_radii;
+ double circular_min, circular_max;
+ GogAxis *circular_axis;
+ GogChart *chart;
+ GSList *axis_list;
+
+ center_x = area->x + (area->w/2);
+ center_y = area->y + (area->h/2);
+ radius = v->allocation.h > v->allocation.w
+ ? v->allocation.w / 2.0
+ : v->allocation.h / 2.0;
+
+ g_return_if_fail (v->parent != NULL);
+ g_return_if_fail (v->parent->model != NULL);
+ g_return_if_fail (IS_GOG_CHART(v->parent->model));
+ chart = GOG_CHART(v->parent->model);
+ axis_list = gog_chart_get_axis (chart, GOG_AXIS_CIRCULAR);
+ g_return_if_fail (axis_list != NULL);
+ g_return_if_fail (axis_list->data != NULL);
+ g_return_if_fail (IS_GOG_AXIS(axis_list->data));
+ circular_axis = GOG_AXIS(axis_list->data);
+ g_slist_free (axis_list);
+ gog_axis_get_bounds (circular_axis, &circular_min, &circular_max);
+ num_radii = rint (circular_max);
+
+ for (i = 0; i < num_radii; i++) {
+ double angle_rad = i * (2.0 * M_PI / num_radii);
+ draw_axis_from_a_to_b (v, axis,
+ center_x, center_y,
+ center_x + radius * sin (angle_rad),
+ center_y - radius * cos (angle_rad),
+ i == 0);
+ }
+ break;
+ }
+ default :
+ break;
+ }
+
+ gog_renderer_pop_style (v->renderer);
+}
+
+static void
+gog_axis_view_class_init (GogAxisViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+
+ aview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->size_request = gog_axis_view_size_request;
+ view_klass->render = gog_axis_view_render;
+}
+
+static GSF_CLASS (GogAxisView, gog_axis_view,
+ gog_axis_view_class_init, NULL,
+ GOG_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-guru-type-selector.glade
@@ -0,0 +1,350 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window1">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">window1</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkAlignment" id="type_selector">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">6</property>
+ <property name="right_padding">6</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="type_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>_Plot type</b></property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">type_treeview</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="vscrollbar_policy">GTK_POLICY_NEVER</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+ <child>
+ <widget class="GtkTreeView" id="type_treeview">
+ <property name="width_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="rules_hint">False</property>
+ <property name="reorderable">False</property>
+ <property name="enable_search">True</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="subtype_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>_Subtype</b></property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ <property name="mnemonic_widget">type_treeview</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="canvas_container">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment10">
+ <property name="visible">True</property>
+ <property name="xalign">1</property>
+ <property name="yalign">0</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkButton" id="sample_button">
+ <property name="visible">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">False</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">0</property>
+ <property name="yscale">0</property>
+ <property name="top_padding">0</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">0</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">2</property>
+
+ <child>
+ <widget class="GtkImage" id="image27">
+ <property name="visible">True</property>
+ <property name="stock">gtk-dialog-info</property>
+ <property name="icon_size">4</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="sample_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Show sample</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkLabel" id="description_title_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Description</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkFrame" id="frame2">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="label_yalign">0.5</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+
+ <child>
+ <widget class="GtkLabel" id="description_label">
+ <property name="height_request">60</property>
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Test de description</property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">True</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="xpad">6</property>
+ <property name="ypad">6</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-axis.h
@@ -0,0 +1,113 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-axis.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_AXIS_H
+#define GOG_AXIS_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GOG_AXIS_AT_LOW = -1,
+ GOG_AXIS_IN_MIDDLE = 0,
+ GOG_AXIS_AT_HIGH = 1
+} GogAxisPosition;
+
+typedef enum {
+ AXIS_ELEM_MIN = 0,
+ AXIS_ELEM_MAX,
+ AXIS_ELEM_MAJOR_TICK,
+ AXIS_ELEM_MINOR_TICK,
+ AXIS_ELEM_CROSS_POINT,
+ AXIS_ELEM_MAX_ENTRY
+} GogAxisElemType;
+
+typedef enum {
+ GOG_AXIS_TICK_NONE,
+ GOG_AXIS_TICK_MAJOR,
+ GOG_AXIS_TICK_MINOR
+} GogAxisTickTypes;
+
+typedef struct {
+ double position;
+ GogAxisTickTypes type;
+ char *label;
+} GogAxisTick;
+
+#define GOG_AXIS_TYPE (gog_axis_get_type ())
+#define GOG_AXIS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_AXIS_TYPE, GogAxis))
+#define IS_GOG_AXIS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_AXIS_TYPE))
+
+GType gog_axis_get_type (void);
+
+GogAxisType gog_axis_get_atype (GogAxis const *axis);
+GogAxisPosition gog_axis_get_pos (GogAxis const *axis);
+gboolean gog_axis_is_discrete (GogAxis const *axis);
+gboolean gog_axis_get_bounds (GogAxis const *axis,
+ double *minima, double *maxima);
+unsigned gog_axis_get_ticks (GogAxis *axis, GogAxisTick **ticks);
+GOData *gog_axis_get_labels (GogAxis const *axis,
+ GogPlot **plot_that_labeled_axis);
+double gog_axis_get_entry (GogAxis const *axis, GogAxisElemType i,
+ gboolean *user_defined);
+
+void gog_axis_add_contributor (GogAxis *axis, GogObject *contrib);
+void gog_axis_del_contributor (GogAxis *axis, GogObject *contrib);
+GSList const *gog_axis_contributors (GogAxis *axis);
+void gog_axis_clear_contributors (GogAxis *axis);
+void gog_axis_bound_changed (GogAxis *axis, GogObject *contrib);
+
+void gog_axis_view_padding_request (GogView *v, GogViewPadding *padding,
+ GogViewAllocation *bbox);
+
+typedef struct _GogAxisMapDesc GogAxisMapDesc;
+
+typedef struct {
+ GogAxis *axis;
+ GogAxisMapDesc const *desc;
+ gpointer data;
+ gboolean is_valid; /* Default to FALSE if desc::init == NULL */
+} GogAxisMap;
+
+struct _GogAxisMapDesc {
+ double (*map) (GogAxisMap *map, double value);
+ double (*map_to_canvas) (GogAxisMap *map, double value, gboolean inverted);
+ gboolean (*init) (GogAxisMap *map, double offset, double length);
+ void (*destroy) (GogAxisMap *map);
+ void (*auto_bound) (GogAxis *axis,
+ double minimum, double maximum,
+ double *bound);
+ void (*calc_ticks) (GogAxis *axis,
+ gboolean draw_labels);
+ char const *name;
+ char const *description;
+};
+
+GogAxisMap* gog_axis_map_new (GogAxis *axis, double offset, double length);
+double gog_axis_map (GogAxisMap *map, double x);
+double gog_axis_map_to_canvas (GogAxisMap *map, double x);
+void gog_axis_map_free (GogAxisMap *map);
+gboolean gog_axis_map_is_valid (GogAxisMap *map);
+
+G_END_DECLS
+
+#endif /* GOG_AXIS_H */
--- /dev/null
+++ lib/goffice/graph/gog-graph.h
@@ -0,0 +1,54 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-graph.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_GRAPH_H
+#define GOG_GRAPH_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_GRAPH_TYPE (gog_graph_get_type ())
+#define GOG_GRAPH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRAPH_TYPE, GogGraph))
+#define IS_GOG_GRAPH(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRAPH_TYPE))
+
+GType gog_graph_get_type (void);
+GType gog_graph_view_get_type (void);
+
+gboolean gog_graph_validate_chart_layout (GogGraph *graph);
+unsigned gog_graph_num_cols (GogGraph const *graph);
+unsigned gog_graph_num_rows (GogGraph const *graph);
+
+/* convenience wrappers */
+GogGraph *gog_graph_dup (GogGraph const *graph);
+GogTheme *gog_graph_get_theme (GogGraph const *graph);
+void gog_graph_set_theme (GogGraph *graph, GogTheme *theme);
+
+/* data management */
+GSList *gog_graph_get_data (GogGraph const *graph);
+
+/* internal routines for use by series */
+GOData *gog_graph_ref_data (GogGraph *graph, GOData *dat);
+void gog_graph_unref_data (GogGraph *graph, GOData *dat);
+
+G_END_DECLS
+
+#endif /* GOG_GRAPH_H */
--- /dev/null
+++ lib/goffice/graph/gog-view.h
@@ -0,0 +1,89 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-view.h : A sized render engine for an item.
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_VIEW_H
+#define GOG_VIEW_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogView {
+ GObject base;
+
+ GogObject *model;
+
+ GogRenderer *renderer; /* not NULL */
+ GogView *parent; /* potentially NULL */
+ GSList *children;
+
+ GogViewAllocation allocation; /* in renderer units */
+ GogViewAllocation residual; /* left over after compass children are placed */
+ unsigned allocation_valid : 1; /* adjust our layout when child changes size */
+ unsigned child_allocations_valid : 1; /* some children need to adjust their layout */
+ unsigned being_updated : 1;
+};
+
+typedef struct {
+ GObjectClass base;
+
+ gboolean clip;
+
+ /* Virtuals */
+ void (*state_init) (GogView *);
+ void (*size_request) (GogView *, GogViewRequisition *r);
+ void (*size_allocate) (GogView *, GogViewAllocation const *a);
+ void (*render) (GogView *, GogViewAllocation const *bbox);
+ gboolean (*info_at_point) (GogView *, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name);
+} GogViewClass;
+
+#define GOG_VIEW_TYPE (gog_view_get_type ())
+#define GOG_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_VIEW_TYPE, GogView))
+#define IS_GOG_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_VIEW_TYPE))
+#define GOG_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_VIEW_TYPE, GogViewClass))
+#define IS_GOG_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_VIEW_TYPE))
+#define GOG_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_VIEW_TYPE, GogViewClass))
+
+GType gog_view_get_type (void);
+
+GogObject *gog_view_get_model (GogView const *view);
+void gog_view_render (GogView *v, GogViewAllocation const *bbox);
+void gog_view_queue_redraw (GogView *v);
+void gog_view_queue_resize (GogView *v);
+void gog_view_size_request (GogView *v, GogViewRequisition *req);
+void gog_view_size_allocate (GogView *v, GogViewAllocation const *a);
+gboolean gog_view_update_sizes (GogView *v);
+gboolean gog_view_info_at_point (GogView *container, double x, double y,
+ GogObject const *cur_selection,
+ GogObject **obj, char **name);
+GogView *gog_view_find_child_view (GogView const *container,
+ GogObject const *target_model);
+
+/* protected */
+void gog_view_size_child_request (GogView *v,
+ GogViewRequisition const *avail,
+ GogViewRequisition *req);
+
+G_END_DECLS
+
+#endif /* GOG_VIEW_H */
--- /dev/null
+++ lib/goffice/graph/gog-chart.h
@@ -0,0 +1,60 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-chart.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_CHART_H
+#define GOG_CHART_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_CHART_TYPE (gog_chart_get_type ())
+#define GOG_CHART(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_CHART_TYPE, GogChart))
+#define IS_GOG_CHART(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_CHART_TYPE))
+
+GType gog_chart_get_type (void);
+
+gboolean gog_chart_get_position (GogChart const *chart, unsigned *x, unsigned *y,
+ unsigned *cols, unsigned *rows);
+void gog_chart_set_position (GogChart *chart, unsigned x, unsigned y,
+ unsigned cols, unsigned rows);
+
+void gog_chart_request_cardinality_update (GogChart *chart);
+void gog_chart_get_cardinality (GogChart *chart,
+ unsigned *full, unsigned *visible);
+void gog_chart_foreach_elem (GogChart *chart, gboolean only_visible,
+ GogEnumFunc handler, gpointer data);
+GSList *gog_chart_get_plots (GogChart const *chart);
+
+GogAxisSet gog_chart_axis_set (GogChart const *chart);
+gboolean gog_chart_axis_set_is_valid (GogChart const *chart, GogAxisSet type);
+gboolean gog_chart_axis_set_assign (GogChart *chart, GogAxisSet type);
+GSList *gog_chart_get_axis (GogChart const *chart, GogAxisType type);
+
+GogGrid *gog_chart_get_grid (GogChart const *chart);
+
+/* View utils */
+GogViewAllocation const *gog_chart_view_get_plot_area (GogView const *view);
+
+G_END_DECLS
+
+#endif /* GOG_CHART_H */
--- /dev/null
+++ lib/goffice/graph/gog-data-set.h
@@ -0,0 +1,69 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-data-set.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_DATA_SET_H
+#define GOG_DATA_SET_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GOData *data;
+ GogDataset *set;
+ int dim_i;
+ gulong handler;
+} GogDatasetElement;
+typedef struct {
+ GTypeInterface base;
+
+ GogDatasetElement *(*get_elem) (GogDataset const *set, int dim_i);
+ void (*set_dim) (GogDataset *set, int dim_i,
+ GOData *val, GError **err);
+ void (*dims) (GogDataset const *set, int *first, int *last);
+ void (*dim_changed) (GogDataset *set, int dim_i);
+} GogDatasetClass;
+
+#define GOG_DATASET_TYPE (gog_dataset_get_type ())
+#define GOG_DATASET(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_DATASET_TYPE, GogDataset))
+#define IS_GOG_DATASET(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_DATASET_TYPE))
+#define GOG_DATASET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_DATASET_TYPE, GogDatasetClass))
+#define IS_GOG_DATASET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_DATASET_TYPE))
+#define GOG_DATASET_GET_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GOG_DATASET_TYPE, GogDatasetClass))
+
+GType gog_dataset_get_type (void);
+
+void gog_dataset_dims (GogDataset const *set, int *first, int *last);
+GOData *gog_dataset_get_dim (GogDataset const *set, int dim_i);
+void gog_dataset_set_dim (GogDataset *set, int dim_i,
+ GOData *val, GError **err);
+
+/* protected */
+void gog_dataset_finalize (GogDataset *set);
+void gog_dataset_parent_changed (GogDataset *set, gboolean was_set);
+GogDatasetElement *gog_dataset_get_elem (GogDataset const *set, int dim_i);
+void gog_dataset_set_dim_internal (GogDataset *set, int dim_i,
+ GOData *val, GogGraph *graph);
+
+G_END_DECLS
+
+#endif /* GOG_DATA_SET_H */
--- /dev/null
+++ lib/goffice/graph/gog-grid.c
@@ -0,0 +1,110 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-grid.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-grid.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-renderer.h>
+
+// #include <src/gui-util.h>
+#include <gui-util.h>
+#include <glib/gi18n.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+struct _GogGrid {
+ GogStyledObject base;
+};
+typedef GogStyledObjectClass GogGridClass;
+
+
+static GType gog_grid_view_get_type (void);
+static GogViewClass *gview_parent_klass;
+
+static void
+gog_grid_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_FILL | GOG_STYLE_OUTLINE;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_grid_class_init (GogGridClass *klass)
+{
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+
+ gog_klass->view_type = gog_grid_view_get_type ();
+ style_klass->init_style = gog_grid_init_style;
+}
+
+GSF_CLASS (GogGrid, gog_grid,
+ gog_grid_class_init, NULL,
+ GOG_STYLED_OBJECT_TYPE)
+
+/************************************************************************/
+
+typedef GogView GogGridView;
+typedef GogViewClass GogGridViewClass;
+
+#define GOG_GRID_VIEW_TYPE (gog_grid_view_get_type ())
+#define GOG_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRID_VIEW_TYPE, GogGridView))
+#define IS_GOG_GRID_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRID_VIEW_TYPE))
+
+static void
+gog_grid_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogGrid *grid = GOG_GRID (view->model);
+ ArtVpath path[6];
+
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_LINETO;
+ path[3].code = ART_LINETO;
+ path[4].code = ART_LINETO;
+ path[5].code = ART_END;
+ path[0].x = path[1].x = path[4].x = view->allocation.x;
+ path[2].x = path[3].x = path[0].x + view->allocation.w;
+ path[0].y = path[3].y = path[4].y = view->allocation.y;
+ path[1].y = path[2].y = path[0].y + view->allocation.h;
+
+ gog_renderer_push_style (view->renderer, grid->base.style);
+ gog_renderer_draw_sharp_polygon (view->renderer, path, FALSE, NULL);
+ gog_renderer_pop_style (view->renderer);
+ (gview_parent_klass->render) (view, bbox);
+}
+
+static void
+gog_grid_view_class_init (GogGridViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+
+ gview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->render = gog_grid_view_render;
+}
+
+static GSF_CLASS (GogGridView, gog_grid_view,
+ gog_grid_view_class_init, NULL,
+ GOG_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-label.c
@@ -0,0 +1,254 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-label.c
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-label.h>
+#include <goffice/graph/gog-outlined-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-renderer.h>
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/go-data.h>
+
+// #include <src/gui-util.h>
+#include <gui-util.h>
+#include <glib/gi18n.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkalignment.h>
+
+struct _GogLabel {
+ GogOutlinedObject base;
+
+ GogDatasetElement text;
+ gboolean allow_markup;
+};
+typedef GogStyledObjectClass GogLabelClass;
+
+enum {
+ LABEL_PROP_0,
+ LABEL_PROP_ALLOW_MARKUP,
+};
+
+static GType gog_label_view_get_type (void);
+static GObjectClass *label_parent_klass;
+static GogViewClass *lview_parent_klass;
+
+static void
+gog_label_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogLabel *label = GOG_LABEL (obj);
+
+ switch (param_id) {
+ case LABEL_PROP_ALLOW_MARKUP :
+ label->allow_markup = g_value_get_boolean (value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_label_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogLabel *label = GOG_LABEL (obj);
+
+ switch (param_id) {
+ case LABEL_PROP_ALLOW_MARKUP :
+ g_value_set_boolean (value, label->allow_markup);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_label_finalize (GObject *obj)
+{
+ gog_dataset_finalize (GOG_DATASET (obj));
+ (*label_parent_klass->finalize) (obj);
+}
+
+static gpointer
+gog_label_editor (GogObject *gobj, GogDataAllocator *dalloc, GnmCmdContext *cc)
+{
+ static guint label_pref_page = 0;
+ GtkWidget *notebook = gtk_notebook_new ();
+ GtkWidget *hbox = gtk_hbox_new (FALSE, 12);
+ GtkWidget *alignment = gtk_alignment_new (0, 0, 1, 0);
+
+ gtk_container_set_border_width (GTK_CONTAINER (alignment), 12);
+ gtk_box_pack_start (GTK_BOX (hbox),
+ gtk_label_new_with_mnemonic (_("_Text:")), FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox),
+ gog_data_allocator_editor (dalloc, GOG_DATASET (gobj), 0, GOG_DATA_SCALAR),
+ TRUE, TRUE, 0);
+ gtk_container_add (GTK_CONTAINER (alignment), hbox);
+ gtk_widget_show_all (alignment);
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), alignment,
+ gtk_label_new (_("Data")));
+ gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, notebook);
+ gog_style_handle_notebook (notebook, &label_pref_page);
+ return notebook;
+}
+
+static void
+gog_label_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL | GOG_STYLE_FONT;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_label_class_init (GogLabelClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+
+ label_parent_klass = g_type_class_peek_parent (klass);
+ gobject_klass->set_property = gog_label_set_property;
+ gobject_klass->get_property = gog_label_get_property;
+ gobject_klass->finalize = gog_label_finalize;
+ g_object_class_install_property (gobject_klass, LABEL_PROP_ALLOW_MARKUP,
+ g_param_spec_boolean ("allow-markup", "allow-markup",
+ "Support basic html-ish markup",
+ TRUE, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+
+ gog_klass->editor = gog_label_editor;
+ gog_klass->view_type = gog_label_view_get_type ();
+ style_klass->init_style = gog_label_init_style;
+}
+
+static void
+gog_label_dims (GogDataset const *set, int *first, int *last)
+{
+ *first = *last = 0;
+}
+
+static GogDatasetElement *
+gog_label_get_elem (GogDataset const *set, int dim_i)
+{
+ GogLabel *label = GOG_LABEL (set);
+ return &label->text;
+}
+
+static void
+gog_label_dim_changed (GogDataset *set, int dim_i)
+{
+ gog_object_emit_changed (GOG_OBJECT (set), TRUE);
+}
+
+static void
+gog_label_dataset_init (GogDatasetClass *iface)
+{
+ iface->dims = gog_label_dims;
+ iface->get_elem = gog_label_get_elem;
+ iface->dim_changed = gog_label_dim_changed;
+}
+
+GSF_CLASS_FULL (GogLabel, gog_label,
+ gog_label_class_init, NULL,
+ GOG_OUTLINED_OBJECT_TYPE, 0,
+ GSF_INTERFACE (gog_label_dataset_init, GOG_DATASET_TYPE))
+
+/************************************************************************/
+
+typedef GogOutlinedView GogLabelView;
+typedef GogOutlinedViewClass GogLabelViewClass;
+
+#define GOG_LABEL_VIEW_TYPE (gog_label_view_get_type ())
+#define GOG_LABEL_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_LABEL_VIEW_TYPE, GogLabelView))
+#define IS_GOG_LABEL_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_LABEL_VIEW_TYPE))
+
+static void
+gog_label_view_size_request (GogView *v, GogViewRequisition *req)
+{
+ GogLabel *l = GOG_LABEL (v->model);
+
+ req->w = req->h = 0.;
+ if (l->text.data != NULL) {
+ char const *text = go_data_scalar_get_str (GO_DATA_SCALAR (l->text.data));
+ if (text != NULL) {
+ gog_renderer_push_style (v->renderer, l->base.base.style);
+ gog_renderer_measure_text (v->renderer, text, req);
+ gog_renderer_pop_style (v->renderer);
+ }
+ }
+ lview_parent_klass->size_request (v, req);
+}
+
+static void
+gog_label_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogLabel *l = GOG_LABEL (view->model);
+ GogOutlinedObject *goo = GOG_OUTLINED_OBJECT (view->model);
+ GogStyle *style = l->base.base.style;
+
+ gog_renderer_push_style (view->renderer, style);
+ if (l->text.data != NULL) {
+ char const *text = go_data_scalar_get_str (GO_DATA_SCALAR (l->text.data));
+ if (text != NULL) {
+ double outline = gog_renderer_line_size (
+ view->renderer, goo->base.style->outline.width);
+ if (style->fill.type != GOG_FILL_STYLE_NONE || outline > 0.) {
+ GogViewRequisition req;
+ GogViewAllocation rect;
+ double pad_x = gog_renderer_pt2r_x (view->renderer, goo->padding_pts);
+ double pad_y = gog_renderer_pt2r_y (view->renderer, goo->padding_pts);
+
+ gog_renderer_measure_text (view->renderer, text, &req);
+ rect = view->allocation;
+ rect.w = req.w + 2. * outline + pad_x;
+ rect.h = req.h + 2. * outline + pad_y;
+ gog_renderer_draw_sharp_rectangle (view->renderer, &rect, NULL);
+ }
+ gog_renderer_draw_text (view->renderer, text,
+ &view->residual, GTK_ANCHOR_NW, NULL);
+ }
+ }
+ gog_renderer_pop_style (view->renderer);
+}
+
+static void
+gog_label_view_class_init (GogLabelViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+
+ lview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->size_request = gog_label_view_size_request;
+ view_klass->render = gog_label_view_render;
+}
+
+static GSF_CLASS (GogLabelView, gog_label_view,
+ gog_label_view_class_init, NULL,
+ GOG_OUTLINED_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-data-allocator.c
@@ -0,0 +1,61 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-data-allocator.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/go-data.h>
+
+GType
+gog_data_allocator_get_type (void)
+{
+ static GType gog_data_allocator_type = 0;
+
+ if (!gog_data_allocator_type) {
+ static GTypeInfo const gog_data_allocator_info = {
+ sizeof (GogDataAllocatorClass), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ gog_data_allocator_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GogDataAllocator", &gog_data_allocator_info, 0);
+ }
+
+ return gog_data_allocator_type;
+}
+
+void
+gog_data_allocator_allocate (GogDataAllocator *dalloc, GogPlot *plot)
+{
+ g_return_if_fail (IS_GOG_DATA_ALLOCATOR (dalloc));
+ GOG_DATA_ALLOCATOR_GET_CLASS (dalloc)->allocate (dalloc, plot);
+}
+
+gpointer
+gog_data_allocator_editor (GogDataAllocator *dalloc, GogDataset *set,
+ int dim_i, GogDataType data_type)
+{
+ g_return_val_if_fail (IS_GOG_DATA_ALLOCATOR (dalloc), NULL);
+ return GOG_DATA_ALLOCATOR_GET_CLASS (dalloc)->editor (dalloc, set,
+ dim_i, data_type);
+}
--- /dev/null
+++ lib/goffice/graph/gog-grid-line.h
@@ -0,0 +1,46 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-grid-line.h
+ *
+ * Copyright (C) 2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_GRID_LINE_H
+#define GOG_GRID_LINE_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+//typedef enum {
+// GOG_GRID_LINE_MAJOR,
+// GOG_GRID_LINE_MINOR,
+// GOG_GRID_LINE_TYPES
+//} GogGridLineType;
+
+#define GOG_GRID_LINE_TYPE (gog_grid_line_get_type ())
+#define GOG_GRID_LINE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRID_LINE_TYPE, GogGridLine))
+#define IS_GOG_GRID_LINE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRID_LINE_TYPE))
+
+GType gog_grid_line_get_type (void);
+
+gboolean gog_grid_line_is_minor (GogGridLine *ggl);
+
+G_END_DECLS
+
+#endif /* GOG_GRID_LINE_H */
--- /dev/null
+++ lib/goffice/graph/gog-axis-prefs.glade
@@ -0,0 +1,545 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window2">
+ <property name="visible">True</property>
+ <property name="title" translatable="yes">window2</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_NONE</property>
+ <property name="modal">False</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+
+ <child>
+ <widget class="GtkHBox" id="axis_pref_box">
+ <property name="border_width">12</property>
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">24</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox7">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkVBox" id="position_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Position</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment8">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="axis_low">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Low</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkRadioButton" id="axis_high">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_High</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">axis_low</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkRadioButton" id="axis_cross">
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Cross</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">axis_low</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkOptionMenu" id="axis_cross_menu">
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="history">-1</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="mapping_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Mapping</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment9">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="invert-axis">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Invert axis</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="map_type_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkLabel" id="map_label">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Type:</property>
+ <property name="use_underline">True</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkComboBox" id="map_type_combo">
+ <property name="visible">True</property>
+ <property name="items" translatable="yes"></property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="vbox10">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">12</property>
+
+ <child>
+ <widget class="GtkVBox" id="major_tick_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="major_tick_header">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Major ticks</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment10">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox5">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="major-tick-labeled">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Show Labels</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="major-tick-out">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Outside</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="major-tick-in">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">_Inside</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkVBox" id="minor_tick_box">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkLabel" id="minor_tick_header">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Minor ticks</b></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">True</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment11">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">12</property>
+ <property name="right_padding">0</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox6">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkCheckButton" id="minor-tick-out">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">O_utside</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkCheckButton" id="minor-tick-in">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label" translatable="yes">I_nside</property>
+ <property name="use_underline">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="active">False</property>
+ <property name="inconsistent">False</property>
+ <property name="draw_indicator">True</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-grid-line.c
@@ -0,0 +1,293 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-grid-line.c
+ *
+ * Copyright (C) 2004 Emmanuel Pacaud (emmanuel.pacaud at univ-poitiers.fr)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <math.h>
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-axis.h>
+#include <goffice/graph/gog-grid-line.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-renderer.h>
+
+//#include <src/gui-util.h>
+#include <gui-util.h>
+#include <glib/gi18n.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+struct _GogGridLine {
+ GogStyledObject base;
+
+ gboolean is_minor;
+};
+
+typedef GogStyledObjectClass GogGridLineClass;
+
+
+static GType gog_grid_line_view_get_type (void);
+static GogViewClass *gview_parent_klass;
+
+enum {
+ GRID_LINE_PROP_0,
+ GRID_LINE_PROP_IS_MINOR,
+};
+
+gboolean
+gog_grid_line_is_minor (GogGridLine *ggl)
+{
+ g_return_val_if_fail (GOG_GRID_LINE (ggl) != NULL, FALSE);
+
+ return ggl->is_minor;
+}
+
+static void
+gog_grid_line_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_LINE;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_grid_line_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogGridLine *grid_line = GOG_GRID_LINE (obj);
+
+ switch (param_id) {
+ case GRID_LINE_PROP_IS_MINOR:
+ grid_line->is_minor = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_grid_line_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogGridLine *grid_line = GOG_GRID_LINE (obj);
+
+ switch (param_id) {
+ case GRID_LINE_PROP_IS_MINOR:
+ g_value_set_boolean (value, grid_line->is_minor);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_grid_line_class_init (GogGridLineClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+
+ gobject_klass->set_property = gog_grid_line_set_property;
+ gobject_klass->get_property = gog_grid_line_get_property;
+ gog_klass->view_type = gog_grid_line_view_get_type ();
+ style_klass->init_style = gog_grid_line_init_style;
+
+ g_object_class_install_property (gobject_klass, GRID_LINE_PROP_IS_MINOR,
+ g_param_spec_boolean ("is_minor", "is_minor",
+ "Are these minor grid lines", FALSE, G_PARAM_READWRITE));
+}
+
+GSF_CLASS (GogGridLine, gog_grid_line,
+ gog_grid_line_class_init, NULL,
+ GOG_STYLED_OBJECT_TYPE)
+
+/************************************************************************/
+
+typedef GogView GogGridLineView;
+typedef GogViewClass GogGridLineViewClass;
+
+#define GOG_GRID_LINE_VIEW_TYPE (gog_grid_line_view_get_type ())
+#define GOG_GRID_LINE8VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_GRID_LINE_VIEW_TYPE, GogGridLineView))
+#define IS_GOG_GRID_LINE_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_GRID_LINE_VIEW_TYPE))
+
+static void
+gog_grid_line_view_render (GogView *view, GogViewAllocation const *bbox)
+{
+ GogGridLine *grid_line = GOG_GRID_LINE (view->model);
+ GSList *axis_list;
+ GogAxis *axis, *circular_axis;
+ GogChart *chart;
+ GogView *chart_view;
+ GogStyle *style;
+ GogAxisType axis_type;
+ GogAxisMap *map = NULL;
+ GogAxisTick *ticks;
+ unsigned tick_nbr, i, j;
+ double center_x, center_y, radius;
+ double circular_min, circular_max;
+ double angle, position;
+ unsigned num_radii;
+ ArtVpath path[3];
+ ArtVpath *c_path;
+ GogViewAllocation const *plot_area;
+ double line_width;
+
+ axis = GOG_AXIS (view->model->parent);
+ g_return_if_fail (axis != NULL);
+ chart = GOG_CHART (view->model->parent->parent);
+ g_return_if_fail (chart != NULL);
+ g_return_if_fail (view->parent != NULL);
+ chart_view = GOG_VIEW (view->parent->parent);
+ g_return_if_fail (chart_view != NULL);
+
+ axis_type = gog_axis_get_atype (axis);
+ tick_nbr = gog_axis_get_ticks (axis, &ticks);
+ if (tick_nbr < 1)
+ return;
+
+ plot_area = gog_chart_view_get_plot_area (chart_view);
+ style = gog_styled_object_get_style (GOG_STYLED_OBJECT (grid_line));
+ gog_renderer_push_style (view->renderer, style);
+ line_width = gog_renderer_line_size (view->renderer, style->line.width);
+
+ path[0].code = ART_MOVETO;
+ path[1].code = ART_LINETO;
+ path[2].code = ART_END;
+
+ if (line_width > 0) {
+ switch (axis_type) {
+ case GOG_AXIS_X:
+ case GOG_AXIS_Y:
+
+ switch (axis_type) {
+ case GOG_AXIS_X:
+ map = gog_axis_map_new (axis, plot_area->x, plot_area->w);
+ break;
+ case GOG_AXIS_Y:
+ map = gog_axis_map_new (axis, plot_area->y +plot_area->h, -plot_area->h);
+ break;
+ default:
+ return;
+ }
+
+ switch (axis_type) {
+ case GOG_AXIS_X:
+ for (i = 0; i < tick_nbr; i++) {
+ if ((ticks[i].type == GOG_AXIS_TICK_MAJOR && !grid_line->is_minor) ||
+ (ticks[i].type == GOG_AXIS_TICK_MINOR && grid_line->is_minor)) {
+ path[0].y = plot_area->y;
+ path[1].y = plot_area->y + plot_area->h;
+ path[0].x =
+ path[1].x = gog_axis_map_to_canvas (map, ticks[i].position);
+ gog_renderer_draw_sharp_path (view->renderer, path, NULL);
+ }
+ }
+ break;
+ case GOG_AXIS_Y:
+ for (i = 0; i < tick_nbr; i++) {
+ if ((ticks[i].type == GOG_AXIS_TICK_MAJOR && !grid_line->is_minor) ||
+ (ticks[i].type == GOG_AXIS_TICK_MINOR && grid_line->is_minor)) {
+ path[0].x = plot_area->x;
+ path[1].x = plot_area->x + plot_area->w;
+ path[0].y =
+ path[1].y = gog_axis_map_to_canvas (map, ticks[i].position);
+ gog_renderer_draw_sharp_path (view->renderer, path, NULL);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ gog_axis_map_free (map);
+ break;
+
+ case GOG_AXIS_RADIAL:
+ center_x = plot_area->x + (plot_area->w/2);
+ center_y = plot_area->y + (plot_area->h/2);
+ radius = plot_area->h > plot_area->w
+ ? plot_area->w / 2.0
+ : plot_area->h / 2.0;
+ map = gog_axis_map_new (axis, 0., radius);
+
+ axis_list = gog_chart_get_axis (chart, GOG_AXIS_CIRCULAR);
+ if (axis_list == NULL)
+ break;
+ circular_axis = GOG_AXIS (axis_list->data);
+ g_slist_free (axis_list);
+ gog_axis_get_bounds (circular_axis, &circular_min, &circular_max);
+ num_radii = rint (circular_max);
+ if (num_radii < 3) {
+ gog_axis_map_free (map);
+ break;
+ }
+ c_path = g_new (ArtVpath, num_radii + 2);
+ c_path[num_radii + 1].code = ART_END;
+
+ for (i = 0; i < tick_nbr; i++) {
+ if ((ticks[i].type == GOG_AXIS_TICK_MAJOR && !grid_line->is_minor) ||
+ (ticks[i].type == GOG_AXIS_TICK_MINOR && grid_line->is_minor)) {
+ position = gog_axis_map_to_canvas (map, ticks[i].position);
+ c_path[0].x = center_x;
+ c_path[0].y = center_y - position;
+ c_path[0].code = ART_MOVETO;
+ for (j = 1; j < num_radii + 1; j++) {
+ angle = j * 2.0 * M_PI/(num_radii) - M_PI / 2.;
+ c_path[j].x = center_x + cos (angle) * position;
+ c_path[j].y = center_y + sin (angle) * position;
+ c_path[j].code = ART_LINETO;
+ }
+ gog_renderer_draw_path (view->renderer, c_path, NULL);
+ }
+ }
+
+ g_free (c_path);
+ gog_axis_map_free (map);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ gog_renderer_pop_style (view->renderer);
+}
+
+static void
+gog_grid_line_view_class_init (GogGridLineViewClass *gview_klass)
+{
+ GogViewClass *view_klass = (GogViewClass *) gview_klass;
+
+ gview_parent_klass = g_type_class_peek_parent (gview_klass);
+ view_klass->render = gog_grid_line_view_render;
+}
+
+static GSF_CLASS (GogGridLineView, gog_grid_line_view,
+ gog_grid_line_view_class_init, NULL,
+ GOG_VIEW_TYPE)
--- /dev/null
+++ lib/goffice/graph/gog-styled-object.c
@@ -0,0 +1,226 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-styled-object.c : A base class for objects that have associated styles
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-graph.h>
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+enum {
+ STYLED_OBJECT_PROP_0,
+ STYLED_OBJECT_PROP_STYLE
+};
+
+enum {
+ STYLE_CHANGED,
+ LAST_SIGNAL
+};
+static gulong gog_styled_object_signals [LAST_SIGNAL] = { 0, };
+static GObjectClass *parent_klass;
+
+void
+gog_styled_object_style_changed (GogStyledObject *obj)
+{
+ g_signal_emit (G_OBJECT (obj),
+ gog_styled_object_signals [STYLE_CHANGED], 0, obj->style);
+}
+
+static void
+gog_styled_object_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogStyledObject *gso = GOG_STYLED_OBJECT (obj);
+ gboolean resize = FALSE;
+
+ switch (param_id) {
+
+ case STYLED_OBJECT_PROP_STYLE :
+ resize = gog_styled_object_set_style (gso,
+ g_value_get_object (value));
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+ gog_object_emit_changed (GOG_OBJECT (obj), resize);
+}
+
+static void
+gog_styled_object_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogStyledObject *gso = GOG_STYLED_OBJECT (obj);
+
+ switch (param_id) {
+ case STYLED_OBJECT_PROP_STYLE :
+ g_value_set_object (value, gso->style);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_styled_object_finalize (GObject *obj)
+{
+ GogStyledObject *gso = GOG_STYLED_OBJECT (obj);
+
+ if (gso->style != NULL) {
+ g_object_unref (gso->style);
+ gso->style = NULL;
+ }
+
+ (*parent_klass->finalize) (obj);
+}
+
+static gpointer
+styled_object_editor (GogObject *gobj, GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ return gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, NULL);
+}
+
+static void
+gog_styled_object_parent_changed (GogObject *obj, gboolean was_set)
+{
+ GogObjectClass *gog_object_klass = GOG_OBJECT_CLASS (parent_klass);
+ if (was_set) {
+ GogStyledObject *gso = GOG_STYLED_OBJECT (obj);
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ gso->style, GOG_OBJECT (gso), 0, TRUE);
+ gog_styled_object_apply_theme (gso, gso->style);
+ }
+ gog_object_klass->parent_changed (obj, was_set);
+}
+
+static void
+gog_styled_object_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ style->interesting_fields = GOG_STYLE_OUTLINE | GOG_STYLE_FILL; /* default */
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), 0, FALSE);
+}
+
+static void
+gog_styled_object_class_init (GogObjectClass *gog_klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) gog_klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) gog_klass;
+
+ gog_styled_object_signals [STYLE_CHANGED] = g_signal_new ("style-changed",
+ G_TYPE_FROM_CLASS (gog_klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GogStyledObjectClass, style_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE,
+ 1, G_TYPE_OBJECT);
+
+ parent_klass = g_type_class_peek_parent (gog_klass);
+ gobject_klass->set_property = gog_styled_object_set_property;
+ gobject_klass->get_property = gog_styled_object_get_property;
+ gobject_klass->finalize = gog_styled_object_finalize;
+ gog_klass->editor = styled_object_editor;
+ gog_klass->parent_changed = gog_styled_object_parent_changed;
+ style_klass->init_style = gog_styled_object_init_style;
+
+ g_object_class_install_property (gobject_klass, STYLED_OBJECT_PROP_STYLE,
+ g_param_spec_object ("style", "style",
+ "GogStyle *",
+ GOG_STYLE_TYPE, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
+}
+
+static void
+gog_styled_object_init (GogStyledObject *gso)
+{
+ gso->style = gog_style_new (); /* use the defaults */
+}
+
+GSF_CLASS (GogStyledObject, gog_styled_object,
+ gog_styled_object_class_init, gog_styled_object_init,
+ GOG_OBJECT_TYPE)
+
+gboolean
+gog_styled_object_set_style (GogStyledObject *gso,
+ GogStyle *style)
+{
+ gboolean resize;
+
+ g_return_val_if_fail (GOG_STYLED_OBJECT (gso) != NULL, FALSE);
+
+ if (gso->style == style)
+ return FALSE;
+ style = gog_style_dup (style);
+
+ /* which fields are we interested in for this object */
+ gog_styled_object_apply_theme (gso, style);
+ gog_styled_object_style_changed (gso);
+ resize = gog_style_is_different_size (gso->style, style);
+ if (gso->style != NULL)
+ g_object_unref (gso->style);
+ gso->style = style;
+
+ return resize;
+}
+
+/**
+ * gog_styled_object_get_style :
+ * @gso : #GogStyledObject
+ *
+ * Returns a pointer to @gso's style but does not reference it.
+ **/
+GogStyle *
+gog_styled_object_get_style (GogStyledObject *gso)
+{
+ g_return_val_if_fail (GOG_STYLED_OBJECT (gso) != NULL, NULL);
+ return gso->style;
+}
+
+/**
+ * gog_styled_object_get_auto_style :
+ * @gso : #GogStyledObject
+ *
+ * Returns a new style that is initialized with the auto values for @gso.
+ * Caller is responsible for the result.
+ **/
+GogStyle *
+gog_styled_object_get_auto_style (GogStyledObject *gso)
+{
+ GogStyle *res = gog_style_dup (gso->style);
+ gog_style_force_auto (res);
+ gog_styled_object_apply_theme (gso, res);
+ return res;
+}
+
+void
+gog_styled_object_apply_theme (GogStyledObject *gso, GogStyle *style)
+{
+ GogGraph const *graph = gog_object_get_graph (GOG_OBJECT (gso));
+ if (graph != NULL) {
+ GogStyledObjectClass *klass = GOG_STYLED_OBJECT_GET_CLASS (gso);
+ (klass->init_style) (gso, style);
+ }
+}
--- /dev/null
+++ lib/goffice/graph/gog-plot-impl.h
@@ -0,0 +1,95 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-plot-impl.h : implementation details for the abstract 'plot' interface
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_PLOT_IMPL_H
+#define GOG_PLOT_IMPL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-plot.h>
+#include <goffice/graph/gog-series-impl.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-view.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+struct _GogPlotDesc {
+ unsigned num_series_min, num_series_max;
+ unsigned num_axis;
+ GogSeriesDesc series;
+};
+
+struct _GogPlot {
+ GogObject base;
+ GogChart *chart; /* potentially NULL */
+
+ GSList *series;
+ unsigned full_cardinality, visible_cardinality;
+ gboolean cardinality_valid;
+ unsigned index_num;
+ gboolean vary_style_by_element;
+
+ GogAxis *axis[GOG_AXIS_TYPES];
+
+ /* Usually a copy from the class but it's here to allow a GogPlotType to
+ * override things without requiring a completely new class */
+ GogPlotDesc desc;
+};
+
+typedef struct {
+ GogObjectClass base;
+
+ GogPlotDesc desc;
+ GType series_type;
+
+ /* Virtuals */
+
+ GogAxisSet (*axis_set_pref) (GogPlot const *plot);
+ gboolean (*axis_set_is_valid) (GogPlot const *plot, GogAxisSet type);
+ gboolean (*axis_set_assign) (GogPlot *plot, GogAxisSet type);
+ GOData *(*axis_get_bounds) (GogPlot *plot, GogAxisType axis,
+ GogPlotBoundInfo *bounds);
+
+ gboolean (*supports_vary_style_by_element) (GogPlot const *plot);
+
+ void (*foreach_elem) (GogPlot *plot, gboolean only_visible,
+ GogEnumFunc handler, gpointer data);
+} GogPlotClass;
+
+#define GOG_PLOT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_PLOT_TYPE, GogPlotClass))
+#define IS_GOG_PLOT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_PLOT_TYPE))
+#define GOG_PLOT_ITEM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_PLOT_TYPE, GogPlotClass))
+
+/* protected */
+
+/*****************************************************************************/
+
+#define GOG_PLOT_VIEW_TYPE (gog_plot_view_get_type ())
+#define GOG_PLOT_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PLOT_VIEW_TYPE, GogPlotView))
+#define IS_GOG_PLOT_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PLOT_VIEW_TYPE))
+
+typedef GogView GogPlotView;
+typedef GogViewClass GogPlotViewClass;
+GType gog_plot_view_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_PLOT_GROUP_IMPL_H */
--- /dev/null
+++ lib/goffice/graph/gog-control-foocanvas.c
@@ -0,0 +1,265 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-control-foocanvas.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-control-foocanvas.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-object.h>
+#include <libfoocanvas/foo-canvas-util.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+#include <math.h>
+
+enum {
+ CTRL_FOO_PROP_0,
+ CTRL_FOO_PROP_H,
+ CTRL_FOO_PROP_W,
+ CTRL_FOO_PROP_MODEL,
+ CTRL_FOO_PROP_RENDERER,
+ CTRL_FOO_PROP_LOGICAL_WIDTH_PTS,
+ CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS
+};
+
+static GObjectClass *parent_klass;
+
+#define GOG_CONTROL_FOOCANVAS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_CONTROL_FOOCANVAS_TYPE, GogControlFooCanvasClass))
+#define IS_GOG_CONTROL_FOOCANVAS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_CONTROL_FOOCANVAS_TYPE))
+#define GOG_CONTROL_FOOCANVAS_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((k), GOG_CONTROL_FOOCANVAS_TYPE, GogControlFooCanvasClass))
+
+static void
+gog_control_foocanvas_set_property (GObject *gobject, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (gobject);
+ gboolean setup_renderer = (ctrl->renderer == NULL);
+
+ switch (param_id) {
+ case CTRL_FOO_PROP_H: ctrl->new_h = g_value_get_double (value); break;
+ case CTRL_FOO_PROP_W: ctrl->new_w = g_value_get_double (value); break;
+
+ case CTRL_FOO_PROP_MODEL:
+ if (ctrl->renderer != NULL)
+ g_object_unref (ctrl->renderer);
+ ctrl->renderer = g_object_new (GOG_RENDERER_PIXBUF_TYPE,
+ "model", g_value_get_object (value),
+ NULL);
+ break;
+
+ case CTRL_FOO_PROP_RENDERER:
+ if (ctrl->renderer != NULL)
+ g_object_unref (ctrl->renderer);
+ ctrl->renderer = GOG_RENDERER_PIXBUF (g_value_get_object (value));
+ if (ctrl->renderer != NULL)
+ g_object_ref (ctrl->renderer);
+ break;
+ case CTRL_FOO_PROP_LOGICAL_WIDTH_PTS :
+ g_object_set_property (G_OBJECT (ctrl->renderer),
+ "logical_width_pts", value);
+ break;
+ case CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS :
+ g_object_set_property (G_OBJECT (ctrl->renderer),
+ "logical_height_pts", value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ if (setup_renderer && ctrl->renderer != NULL)
+ g_signal_connect_object (G_OBJECT (ctrl->renderer),
+ "request_update",
+ G_CALLBACK (foo_canvas_item_request_update),
+ ctrl, G_CONNECT_SWAPPED);
+ foo_canvas_item_request_update (FOO_CANVAS_ITEM (ctrl));
+}
+
+static void
+gog_control_foocanvas_get_property (GObject *gobject, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (gobject);
+
+ switch (param_id) {
+ case CTRL_FOO_PROP_H: g_value_set_double (value, ctrl->new_h); break;
+ case CTRL_FOO_PROP_W: g_value_set_double (value, ctrl->new_w); break;
+ case CTRL_FOO_PROP_RENDERER : g_value_set_object (value, ctrl->renderer); break;
+ case CTRL_FOO_PROP_LOGICAL_WIDTH_PTS :
+ g_object_get_property (G_OBJECT (ctrl->renderer),
+ "logical_width_pts", value);
+ break;
+ case CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS :
+ g_object_get_property (G_OBJECT (ctrl->renderer),
+ "logical_height_pts", value);
+ break;
+
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gog_control_foocanvas_finalize (GObject *obj)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (obj);
+
+ if (ctrl->model != NULL) {
+ g_object_unref (ctrl->model);
+ ctrl->model = NULL;
+ }
+ if (ctrl->renderer != NULL) {
+ g_object_unref (ctrl->renderer);
+ ctrl->renderer = NULL;
+ }
+ (*parent_klass->finalize) (obj);
+}
+
+static void
+gog_control_foocanvas_draw (FooCanvasItem *item, GdkDrawable *drawable,
+ GdkEventExpose *ev)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (item);
+ GdkPixbuf *buffer = gog_renderer_pixbuf_get (ctrl->renderer);
+ GdkRectangle display_rect, draw_rect;
+ GdkRegion *draw_region;
+
+ if (buffer) {
+ display_rect.x = item->x1;
+ display_rect.y = item->y1;
+ display_rect.width = item->x2 - item->x1;
+ display_rect.height = item->y2 - item->y1;
+
+ draw_region = gdk_region_rectangle (&display_rect);
+ gdk_region_intersect (draw_region, ev->region);
+ if (!gdk_region_empty (draw_region)) {
+ gdk_region_get_clipbox (draw_region, &draw_rect);
+ gdk_draw_pixbuf (drawable, NULL, buffer,
+ /* pixbuf 0, 0 is at pix_rect.x, pix_rect.y */
+ draw_rect.x - display_rect.x,
+ draw_rect.y - display_rect.y,
+ draw_rect.x,
+ draw_rect.y,
+ draw_rect.width,
+ draw_rect.height,
+ GDK_RGB_DITHER_NORMAL, 0, 0);
+ }
+ gdk_region_destroy (draw_region);
+ }
+
+ /* we are a canvas group, there could be some children */
+ if (FOO_CANVAS_ITEM_CLASS (parent_klass)->draw)
+ (FOO_CANVAS_ITEM_CLASS (parent_klass)->draw) (item, drawable, ev);
+}
+
+static void
+gog_control_foocanvas_update (FooCanvasItem *item,
+ double i2w_dx, double i2w_dy, gint flags)
+{
+ GogControlFooCanvas *ctrl = GOG_CONTROL_FOOCANVAS (item);
+ gboolean redraw;
+ int x1, x2, y1, y2;
+ int orig_x1 = item->x1, orig_x2 = item->x2, orig_y1 = item->y1, orig_y2 = item->y2;
+
+ if (FOO_CANVAS_ITEM_CLASS (parent_klass)->update)
+ (FOO_CANVAS_ITEM_CLASS (parent_klass)->update) (item, i2w_dx, i2w_dy, flags);
+ /* foo_canvas_group_update wipes the bbox */
+ item->x1 = orig_x1; item->x2 = orig_x2;
+ item->y1 = orig_y1; item->y2 = orig_y2;
+
+ foo_canvas_w2c (item->canvas, ctrl->base.xpos, ctrl->base.ypos, &x1, &y1);
+ foo_canvas_w2c (item->canvas, ctrl->base.xpos + ctrl->new_w, ctrl->base.ypos + ctrl->new_h, &x2, &y2);
+
+ redraw = gog_renderer_pixbuf_update (ctrl->renderer, x2-x1, y2-y1,
+ item->canvas->pixels_per_unit);
+ if (item->x1 != x1 || item->y1 != y1 || item->x2 != x2 || item->y2 != y2)
+ foo_canvas_update_bbox (FOO_CANVAS_ITEM (ctrl), x1, y1, x2, y2);
+ else if (redraw)
+ foo_canvas_item_request_redraw (FOO_CANVAS_ITEM (ctrl));
+}
+
+static void
+gog_control_foocanvas_bounds (FooCanvasItem *item,
+ double *x1, double *y1, double *x2, double *y2)
+{
+ *x1 = item->x1;
+ *x2 = item->x2;
+ *y1 = item->y1;
+ *y2 = item->y2;
+}
+
+static double
+gog_control_foocanvas_point (FooCanvasItem *item, double x, double y, int cx, int cy,
+ FooCanvasItem **actual_item)
+{
+ *actual_item = item;
+ return 0.;
+}
+
+static void
+gog_control_foocanvas_class_init (GogControlFooCanvasClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ FooCanvasItemClass *item_klass = (FooCanvasItemClass *) klass;
+
+ parent_klass = g_type_class_peek_parent (klass);
+
+ gobject_klass->set_property = gog_control_foocanvas_set_property;
+ gobject_klass->get_property = gog_control_foocanvas_get_property;
+ gobject_klass->finalize = gog_control_foocanvas_finalize;
+ item_klass->draw = gog_control_foocanvas_draw;
+ item_klass->update = gog_control_foocanvas_update;
+ item_klass->bounds = gog_control_foocanvas_bounds;
+ item_klass->point = gog_control_foocanvas_point;
+
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_H,
+ g_param_spec_double ("h", _("H"), _("Height"),
+ 0, G_MAXDOUBLE, 100., G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_W,
+ g_param_spec_double ("w", _("W"), _("Width"),
+ 0, G_MAXDOUBLE, 100., G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_MODEL,
+ g_param_spec_object ("model", "model",
+ "the GogObject this object displays",
+ GOG_OBJECT_TYPE, G_PARAM_WRITABLE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_RENDERER,
+ g_param_spec_object ("renderer", "renderer",
+ "the GogRendererPixbuf being displayed",
+ GOG_RENDERER_PIXBUF_TYPE, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_LOGICAL_WIDTH_PTS,
+ g_param_spec_double ("logical_width_pts", "Logical Width Pts",
+ "Logical width of the drawing area in pts",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_klass, CTRL_FOO_PROP_LOGICAL_HEIGHT_PTS,
+ g_param_spec_double ("logical_height_pts", "Logical Height Pts",
+ "Logical height of the drawing area in pts",
+ 0, G_MAXDOUBLE, 0, G_PARAM_READWRITE));
+}
+
+static void
+gog_control_foocanvas_init (GogControlFooCanvas *ctrl)
+{
+ ctrl->new_h = ctrl->new_w = 0.;
+}
+
+GSF_CLASS (GogControlFooCanvas, gog_control_foocanvas,
+ gog_control_foocanvas_class_init, gog_control_foocanvas_init,
+ FOO_TYPE_CANVAS_GROUP)
+
--- /dev/null
+++ lib/goffice/graph/gog-series.c
@@ -0,0 +1,801 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-graph-data.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-series-impl.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-theme.h>
+#include <goffice/graph/gog-style.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/graph/gog-error-bar.h>
+
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n.h>
+//#include <src/gui-util.h>
+#include <gui-util.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkhseparator.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkspinbutton.h>
+#include <gtk/gtkcheckbutton.h>
+
+#include <string.h>
+
+int gog_series_get_valid_element_index (GogSeries const *series, int old_index, int desired_index);
+
+/*****************************************************************************/
+static GObjectClass *gse_parent_klass;
+
+enum {
+ ELEMENT_PROP_0,
+ ELEMENT_INDEX
+};
+
+static gint element_compare (GogSeriesElement *gse_a, GogSeriesElement *gse_b)
+{
+ return gse_a->index - gse_b->index;
+}
+
+static void
+gog_series_element_set_index (GogSeriesElement *gse, int ind)
+{
+ gse->index = ind;
+ gog_styled_object_apply_theme (&gse->base, gse->base.style);
+ gog_styled_object_style_changed (&gse->base);
+}
+
+static void
+gog_series_element_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogSeriesElement *gse = GOG_SERIES_ELEMENT (obj);
+ GogObject *gobj = GOG_OBJECT (obj);
+
+ switch (param_id) {
+ case ELEMENT_INDEX :
+ gog_series_element_set_index (gse, g_value_get_int (value));
+ if (gobj->parent != NULL) {
+ GogSeries *series = GOG_SERIES (gobj->parent);
+ series->overrides = g_list_remove (series->overrides, gse);
+ series->overrides = g_list_insert_sorted (series->overrides, gse,
+ (GCompareFunc) element_compare);
+ }
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_series_element_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogSeriesElement *gse = GOG_SERIES_ELEMENT (obj);
+
+ switch (param_id) {
+ case ELEMENT_INDEX :
+ g_value_set_int (value, gse->index);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static void
+cb_index_changed (GtkSpinButton *spin_button, GogSeriesElement *element)
+{
+ int index;
+ int value = gtk_spin_button_get_value (spin_button);
+
+ if ((int) element->index == value)
+ return;
+
+ index = gog_series_get_valid_element_index (
+ GOG_SERIES (gog_object_get_parent (GOG_OBJECT (element))),
+ element->index, value);
+
+ if (index != value)
+ gtk_spin_button_set_value (spin_button, index);
+
+ g_object_set (element, "index", (int) index, NULL);
+}
+
+static gpointer
+gog_series_element_editor (GogObject *gobj,
+ GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ static guint series_element_pref_page = 1;
+ GtkWidget *w, *vbox, *spin_button = NULL;
+ gpointer gse_editor = NULL;
+ GogSeriesElementClass *klass = GOG_SERIES_ELEMENT_GET_CLASS (gobj);
+
+ if (klass->gse_editor)
+ gse_editor = (*klass->gse_editor) (gobj, cc);
+
+ if (gse_editor == NULL)
+ return gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, NULL);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ w = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (w), gtk_label_new (_("Index:")),
+ FALSE, FALSE, 0);
+ spin_button = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_button),
+ GOG_SERIES_ELEMENT(gobj)->index);
+ g_signal_connect (G_OBJECT (spin_button),
+ "value_changed",
+ G_CALLBACK (cb_index_changed), gobj);
+
+ gtk_box_pack_start(GTK_BOX (w), spin_button, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (gse_editor), FALSE, FALSE, 0);
+ gtk_widget_show_all (vbox);
+ w = gtk_notebook_new ();
+ gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, w);
+ gtk_notebook_append_page (GTK_NOTEBOOK (w), vbox,
+ gtk_label_new (_("Settings")));
+ gog_style_handle_notebook (w, &series_element_pref_page);
+
+ return w;
+}
+
+static void
+gog_series_element_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ GogSeries const *series = GOG_SERIES (GOG_OBJECT (gso)->parent);
+ GogStyle *parent_style;
+
+ g_return_if_fail (series != NULL);
+
+ parent_style = gog_styled_object_get_style (GOG_STYLED_OBJECT (series));
+ style->interesting_fields = parent_style->interesting_fields;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), GOG_SERIES_ELEMENT (gso)->index, FALSE);
+}
+
+static void
+gog_series_element_class_init (GogSeriesElementClass *klass)
+{
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+ gse_parent_klass = g_type_class_peek_parent (klass);
+
+ gobject_klass->set_property = gog_series_element_set_property;
+ gobject_klass->get_property = gog_series_element_get_property;
+
+ gog_klass->editor = gog_series_element_editor;
+ style_klass->init_style = gog_series_element_init_style;
+
+ gog_klass->use_parent_as_proxy = TRUE;
+
+ g_object_class_install_property (gobject_klass, ELEMENT_INDEX,
+ g_param_spec_int ("index", "index",
+ "Index of the corresponding data element",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT | GOG_PARAM_FORCE_SAVE));
+}
+
+GSF_CLASS (GogSeriesElement, gog_series_element,
+ gog_series_element_class_init, NULL /*gog_series_element_init*/,
+ GOG_STYLED_OBJECT_TYPE)
+
+
+/*****************************************************************************/
+
+static GObjectClass *series_parent_klass;
+
+enum {
+ SERIES_PROP_0,
+ SERIES_HAS_LEGEND
+};
+
+static gboolean
+role_series_element_can_add (GogObject const *parent)
+{
+ GogSeriesClass *klass = GOG_SERIES_GET_CLASS (parent);
+
+ return ((gog_series_get_valid_element_index(GOG_SERIES (parent), -1, 0) >= 0) &&
+ (klass->series_element_type > 0));
+}
+
+static GogObject *
+role_series_element_allocate (GogObject *series)
+{
+ GogSeriesClass *klass = GOG_SERIES_GET_CLASS (series);
+ GType type = klass->series_element_type;
+ GogObject *gse;
+
+ if (type == 0)
+ type = GOG_SERIES_ELEMENT_TYPE;
+
+ gse = g_object_new (type, NULL);
+ if (gse != NULL)
+ gog_series_element_set_index (GOG_SERIES_ELEMENT (gse),
+ gog_series_get_valid_element_index (GOG_SERIES (series), -1, 0));
+ return gse;
+}
+
+static void
+role_series_element_post_add (GogObject *parent, GogObject *child)
+{
+ GogSeries *series = GOG_SERIES (parent);
+ gog_styled_object_set_style (GOG_STYLED_OBJECT (child),
+ gog_styled_object_get_style (GOG_STYLED_OBJECT (parent)));
+ series->overrides = g_list_insert_sorted (series->overrides, child,
+ (GCompareFunc) element_compare);
+}
+
+static void
+role_series_element_pre_remove (GogObject *parent, GogObject *child)
+{
+ GogSeries *series = GOG_SERIES (parent);
+ series->overrides = g_list_remove (series->overrides, child);
+}
+
+static void
+gog_series_finalize (GObject *obj)
+{
+ GogSeries *series = GOG_SERIES (obj);
+
+ if (series->values != NULL) {
+ gog_dataset_finalize (GOG_DATASET (obj));
+ g_free (series->values - 1); /* it was aliased */
+ series->values = NULL;
+ }
+
+ g_list_free (series->overrides);
+
+ (*series_parent_klass->finalize) (obj);
+}
+
+static void
+gog_series_set_property (GObject *obj, guint param_id,
+ GValue const *value, GParamSpec *pspec)
+{
+ GogSeries *series = GOG_SERIES (obj);
+ gboolean b_tmp;
+
+ switch (param_id) {
+ case SERIES_HAS_LEGEND :
+ b_tmp = g_value_get_boolean (value);
+ if (series->has_legend ^ b_tmp) {
+ series->has_legend = b_tmp;
+ if (series->plot != NULL)
+ gog_plot_request_cardinality_update (series->plot);
+ }
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ return; /* NOTE : RETURN */
+ }
+
+ gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
+}
+
+static void
+gog_series_get_property (GObject *obj, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GogSeries *series = GOG_SERIES (obj);
+
+ switch (param_id) {
+ case SERIES_HAS_LEGEND :
+ g_value_set_boolean (value, series->has_legend);
+ break;
+ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+ break;
+ }
+}
+
+static unsigned
+make_dim_editor (GtkTable *table, unsigned row, GtkWidget *editor,
+ char const *name, GogSeriesPriority priority, gboolean is_shared)
+{
+ char *txt = g_strdup_printf (
+ ((priority != GOG_SERIES_REQUIRED) ? "(_%s):" : "_%s:"), _(name));
+ GtkWidget *label = gtk_label_new_with_mnemonic (txt);
+ g_free (txt);
+
+ gtk_table_attach (table, label,
+ 0, 1, row, row+1, GTK_FILL, 0, 0, 0);
+ gtk_table_attach (table, editor,
+ 1, 2, row, row+1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), editor);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+
+ gnm_setup_label_atk (label, editor);
+
+ return row + 1;
+}
+
+static void
+cb_show_in_legend (GtkToggleButton *b, GObject *series)
+{
+ g_object_set (series,
+ "has-legend", gtk_toggle_button_get_active (b),
+ NULL);
+}
+
+static gpointer
+gog_series_editor (GogObject *gobj,
+ GogDataAllocator *dalloc,
+ GnmCmdContext *cc)
+{
+ static guint series_pref_page = 1;
+ GtkWidget *w;
+ GtkTable *table;
+ unsigned i, row = 0;
+ gboolean has_shared = FALSE;
+ GogSeries *series = GOG_SERIES (gobj);
+ GogDataset *set = GOG_DATASET (gobj);
+ GogSeriesDesc const *desc;
+ GogSeriesClass *klass = GOG_SERIES_GET_CLASS (gobj);
+ GogDataType data_type;
+
+ g_return_val_if_fail (series->plot != NULL, NULL);
+
+ /* Are there any shared dimensions */
+ desc = &series->plot->desc.series;
+ for (i = 0; i < desc->num_dim; i++)
+ if (desc->dim[i].is_shared) {
+ has_shared = TRUE;
+ break;
+ }
+
+ w = gtk_table_new (desc->num_dim + (has_shared ? 2 : 1), 2, FALSE);
+ table = GTK_TABLE (w);
+ gtk_table_set_row_spacings (table, 6);
+ gtk_table_set_col_spacings (table, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+
+ row = make_dim_editor (table, row,
+ gog_data_allocator_editor (dalloc, set, -1, GOG_DATA_SCALAR),
+ N_("Name"), TRUE, FALSE);
+
+ /* first the unshared entries */
+ for (i = 0; i < desc->num_dim; i++) {
+ data_type = (desc->dim[i].val_type == GOG_DIM_MATRIX)?
+ GOG_DATA_MATRIX: GOG_DATA_VECTOR;
+ if (!desc->dim[i].is_shared && (desc->dim[i].priority != GOG_SERIES_ERRORS))
+ row = make_dim_editor (table, row,
+ gog_data_allocator_editor (dalloc, set, i, data_type),
+ desc->dim[i].name, desc->dim[i].priority, FALSE);
+ }
+
+ if (has_shared) {
+ gtk_table_attach (table, gtk_hseparator_new (),
+ 0, 2, row, row+1, GTK_FILL, 0, 0, 0);
+ row++;
+ }
+
+ /* then the shared entries */
+ for (i = 0; i < desc->num_dim; i++) {
+ data_type = (desc->dim[i].val_type == GOG_DIM_MATRIX)?
+ GOG_DATA_MATRIX: GOG_DATA_VECTOR;
+ if (desc->dim[i].is_shared)
+ row = make_dim_editor (table, row,
+ gog_data_allocator_editor (dalloc, set, i, data_type),
+ desc->dim[i].name, desc->dim[i].priority, TRUE);
+ }
+
+ gtk_table_attach (table, gtk_hseparator_new (),
+ 0, 2, row, row+1, GTK_FILL, 0, 0, 0);
+ row++;
+ w = gtk_check_button_new_with_mnemonic ("_Show in Legend");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
+ gog_series_has_legend (series));
+ g_signal_connect (G_OBJECT (w),
+ "toggled",
+ G_CALLBACK (cb_show_in_legend), series);
+ gtk_table_attach (table, w,
+ 0, 2, row, row+1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show_all (GTK_WIDGET (table));
+
+ w = gtk_notebook_new ();
+
+ if (klass->populate_editor != NULL)
+ (klass->populate_editor) (GOG_SERIES (set), GTK_NOTEBOOK (w), dalloc, cc);
+
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (w), GTK_WIDGET (table),
+ gtk_label_new (_("Data")));
+ gog_styled_object_editor (GOG_STYLED_OBJECT (gobj), cc, w);
+ gog_style_handle_notebook (w, &series_pref_page);
+ return w;
+}
+
+static void
+gog_series_update (GogObject *obj)
+{
+ GogSeries *series = GOG_SERIES (obj);
+ series->needs_recalc = FALSE;
+}
+
+static void
+gog_series_init_style (GogStyledObject *gso, GogStyle *style)
+{
+ GogSeries const *series = (GogSeries const *)gso;
+ style->interesting_fields = series->plot->desc.series.style_fields;
+ gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
+ style, GOG_OBJECT (gso), series->index, FALSE);
+}
+
+static void
+gog_series_class_init (GogSeriesClass *klass)
+{
+ static GogObjectRole const roles[] = {
+ { N_("Point"), "GogSeriesElement", 0,
+ GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
+ role_series_element_can_add, NULL,
+ role_series_element_allocate,
+ role_series_element_post_add,
+ role_series_element_pre_remove, NULL },
+ };
+ GObjectClass *gobject_klass = (GObjectClass *) klass;
+ GogObjectClass *gog_klass = (GogObjectClass *) klass;
+ GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
+
+ series_parent_klass = g_type_class_peek_parent (klass);
+ gobject_klass->finalize = gog_series_finalize;
+ gobject_klass->set_property = gog_series_set_property;
+ gobject_klass->get_property = gog_series_get_property;
+
+ gog_klass->editor = gog_series_editor;
+ gog_klass->update = gog_series_update;
+ style_klass->init_style = gog_series_init_style;
+ /* series do not have views, so just forward signals from the plot */
+ gog_klass->use_parent_as_proxy = TRUE;
+
+ gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
+
+ g_object_class_install_property (gobject_klass, SERIES_HAS_LEGEND,
+ g_param_spec_boolean ("has-legend", "has-legend",
+ "Should the series show up in legends",
+ TRUE,
+ G_PARAM_READWRITE | GOG_PARAM_PERSISTENT));
+}
+
+static void
+gog_series_init (GogSeries *series)
+{
+ series->is_valid = FALSE;
+ series->has_legend = TRUE;
+ series->plot = NULL;
+ series->values = NULL;
+ series->index = -1;
+}
+
+static void
+gog_series_dataset_dims (GogDataset const *set, int *first, int *last)
+{
+ GogSeries const *series = GOG_SERIES (set);
+ *first = -1;
+ if (series->plot == NULL || series->values == NULL)
+ *last = -2;
+ else
+ *last = (series->plot->desc.series.num_dim - 1);
+}
+
+static GogDatasetElement *
+gog_series_dataset_get_elem (GogDataset const *set, int dim_i)
+{
+ GogSeries const *series = GOG_SERIES (set);
+ g_return_val_if_fail ((int)series->plot->desc.series.num_dim > dim_i, NULL);
+ g_return_val_if_fail (dim_i >= -1, NULL);
+ return series->values + dim_i;
+}
+
+static void
+gog_series_dataset_set_dim (GogDataset *set, int dim_i,
+ GOData *val, GError **err)
+{
+ GogSeriesDesc const *desc;
+ GogSeries *series = GOG_SERIES (set);
+ GogGraph *graph = gog_object_get_graph (GOG_OBJECT (series));
+
+ g_return_if_fail (GOG_PLOT (series->plot) != NULL);
+
+ if (dim_i < 0) {
+ char *name = NULL;
+ if (NULL != series->values[-1].data)
+ name = g_strdup (go_data_scalar_get_str (
+ GO_DATA_SCALAR (series->values[-1].data)));
+ gog_object_set_name (GOG_OBJECT (series), name, err);
+ return;
+ }
+
+ gog_series_check_validity (series);
+
+ /* clone shared dimensions into other series in the plot, and
+ * invalidate if necessary */
+ desc = &series->plot->desc.series;
+ if (desc->dim[dim_i].is_shared) {
+ GSList *ptr = series->plot->series;
+
+ val = series->values[dim_i].data;
+ for (; ptr != NULL ; ptr = ptr->next) {
+ gog_dataset_set_dim_internal (GOG_DATASET (ptr->data),
+ dim_i, val, graph);
+ gog_series_check_validity (GOG_SERIES (ptr->data));
+ }
+ }
+}
+
+static void
+gog_series_dataset_dim_changed (GogDataset *set, int dim_i)
+{
+ GogSeries *series = GOG_SERIES (set);
+ if (dim_i >= 0) {
+ GogSeriesClass *klass = GOG_SERIES_GET_CLASS (set);
+
+ if (!series->needs_recalc) {
+ series->needs_recalc = TRUE;
+ gog_object_emit_changed (GOG_OBJECT (set), FALSE);
+ }
+ if (klass->dim_changed != NULL)
+ (klass->dim_changed) (GOG_SERIES (set), dim_i);
+
+ gog_object_request_update (GOG_OBJECT (set));
+ } else {
+ GOData *name_src = series->values[-1].data;
+ char const *name = (name_src != NULL)
+ ? go_data_scalar_get_str (GO_DATA_SCALAR (name_src)) : NULL;
+ gog_object_set_name (GOG_OBJECT (set), g_strdup (name), NULL);
+ }
+}
+
+static void
+gog_series_dataset_init (GogDatasetClass *iface)
+{
+ iface->get_elem = gog_series_dataset_get_elem;
+ iface->set_dim = gog_series_dataset_set_dim;
+ iface->dims = gog_series_dataset_dims;
+ iface->dim_changed = gog_series_dataset_dim_changed;
+}
+
+GSF_CLASS_FULL (GogSeries, gog_series,
+ gog_series_class_init, gog_series_init,
+ GOG_STYLED_OBJECT_TYPE, 0,
+ GSF_INTERFACE (gog_series_dataset_init, GOG_DATASET_TYPE))
+
+/**
+ * gog_series_get_plot :
+ * @series : #GogSeries
+ *
+ * Returns the possibly NULL plot that contains this series.
+ **/
+GogPlot *
+gog_series_get_plot (GogSeries const *series)
+{
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, NULL);
+ return series->plot;
+}
+
+/**
+ * gog_series_is_valid :
+ * @series : #GogSeries
+ *
+ * Returns the current cached validity. Does not recheck
+ **/
+gboolean
+gog_series_is_valid (GogSeries const *series)
+{
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, FALSE);
+ return series->is_valid;
+}
+
+/**
+ * gog_series_check_validity :
+ * @series : #GogSeries
+ *
+ * Updates the is_valid flag for a series.
+ * This is an internal utility that should not really be necessary for general
+ * usage.
+ **/
+void
+gog_series_check_validity (GogSeries *series)
+{
+ unsigned i;
+ GogSeriesDesc const *desc;
+
+ g_return_if_fail (GOG_SERIES (series) != NULL);
+ g_return_if_fail (GOG_PLOT (series->plot) != NULL);
+
+ desc = &series->plot->desc.series;
+ for (i = series->plot->desc.series.num_dim; i-- > 0; )
+ if (series->values[i].data == NULL &&
+ desc->dim[i].priority == GOG_SERIES_REQUIRED) {
+ series->is_valid = FALSE;
+ return;
+ }
+ series->is_valid = TRUE;
+}
+
+/**
+ * gog_series_has_legend :
+ * @series : #GogSeries
+ *
+ * Returns TRUE if the series has a visible legend entry
+ **/
+gboolean
+gog_series_has_legend (GogSeries const *series)
+{
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, FALSE);
+ return series->has_legend;
+}
+
+/**
+ * gog_series_set_index :
+ * @series : #GogSeries
+ * @index :
+ * @is_manual :
+ *
+ * if @index >= 0 attempt to assign the new index. Auto
+ * indicies (@is_manual == FALSE) will not override the current
+ * index if it is manual. An @index < 0, will reset the index to
+ * automatic and potentially queue a revaluation of the parent
+ * chart's cardinality.
+ **/
+void
+gog_series_set_index (GogSeries *series, int ind, gboolean is_manual)
+{
+ g_return_if_fail (GOG_SERIES (series) != NULL);
+
+ if (ind < 0) {
+ if (series->manual_index && series->plot != NULL)
+ gog_plot_request_cardinality_update (series->plot);
+ series->manual_index = FALSE;
+ return;
+ }
+
+ if (is_manual)
+ series->manual_index = TRUE;
+ else if (series->manual_index)
+ return;
+
+ series->index = ind;
+ gog_styled_object_apply_theme (&series->base, series->base.style);
+ gog_styled_object_style_changed (GOG_STYLED_OBJECT (series));
+}
+
+/**
+ * gog_series_get_name :
+ * @series : #GogSeries
+ *
+ * Returns the _src_ of the name associated with the series
+ * NOTE : this is _NOT_ the actual name
+ * no references are added on the result.
+ **/
+GODataScalar *
+gog_series_get_name (GogSeries const *series)
+{
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, NULL);
+ return GO_DATA_SCALAR (series->values[-1].data);
+}
+
+/**
+ * gog_series_set_name :
+ * @series : #GogSeries
+ * @name_src : #GOData
+ * @err : #Gerror
+ *
+ * Absorbs a ref to @name_src
+ **/
+void
+gog_series_set_name (GogSeries *series, GODataScalar *name_src, GError **err)
+{
+ gog_dataset_set_dim (GOG_DATASET (series),
+ -1, GO_DATA (name_src), NULL);
+}
+
+/**
+ * gog_series_set_dim :
+ * @series : #GogSeries
+ * @dim_i :
+ * @val : #GOData
+ * @err : optional #Gerror pointer
+ *
+ * Absorbs a ref to @val
+ **/
+void
+gog_series_set_dim (GogSeries *series, int dim_i, GOData *val, GError **err)
+{
+ gog_dataset_set_dim (GOG_DATASET (series), dim_i, val, err);
+}
+
+/**
+ * gog_series_num_elements :
+ * @series : #GogSeries
+ *
+ * Returns the number of elements in the series
+ **/
+unsigned
+gog_series_num_elements (GogSeries const *series)
+{
+ return series->num_elements;
+}
+
+GList const *
+gog_series_get_overrides (GogSeries const *series)
+{
+ return series->overrides;
+}
+
+int
+gog_series_get_valid_element_index (GogSeries const *series, int old_index, int desired_index)
+{
+ int index;
+ GList *ptr;
+
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, -1);
+
+ if ((desired_index >= (int) series->num_elements) ||
+ (desired_index < 0))
+ return old_index;
+
+ if (desired_index > old_index)
+ for (ptr = series->overrides; ptr != NULL; ptr = ptr->next) {
+ index = GOG_SERIES_ELEMENT (ptr->data)->index;
+ if (index > desired_index)
+ break;
+ if (index == desired_index)
+ desired_index++;
+ }
+ else
+ for (ptr = g_list_last (series->overrides); ptr != NULL; ptr = ptr->prev) {
+ index = GOG_SERIES_ELEMENT (ptr->data)->index;
+ if (index < desired_index)
+ break;
+ if (index == desired_index)
+ desired_index--;
+ }
+
+ if ((desired_index >= 0) &&
+ (desired_index < (int) series->num_elements))
+ return desired_index;
+
+ return old_index;
+}
+
+GogSeriesElement *
+gog_series_get_element (GogSeries const *series, int index)
+{
+ GList *ptr;
+ GogSeriesElement *element;
+
+ g_return_val_if_fail (GOG_SERIES (series) != NULL, NULL);
+
+ for (ptr = series->overrides; ptr != NULL; ptr = ptr->next) {
+ element = GOG_SERIES_ELEMENT (ptr->data);
+ if ((int) element->index == index)
+ return element;
+ }
+
+ return FALSE;
+}
--- /dev/null
+++ lib/goffice/graph/gog-guru.c
@@ -0,0 +1,1413 @@
+/* vim: set sw=8: */
+
+/*
+ * go-graph-guru.c: The Graph guru
+ *
+ * Copyright (C) 2000-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include <goffice/goffice-config.h>
+
+#include <goffice/graph/gog-guru.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-graph.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-chart.h>
+#include <goffice/graph/gog-plot.h>
+#include <goffice/graph/gog-view.h>
+#include <goffice/graph/gog-plot-engine.h>
+#include <goffice/graph/gog-data-allocator.h>
+#include <goffice/graph/gog-control-foocanvas.h>
+
+#include <glib/gi18n.h>
+#include <gui-util.h>
+
+#include <libxml/parser.h>
+#include <libfoocanvas/foo-canvas.h>
+#include <libfoocanvas/foo-canvas-pixbuf.h>
+#include <libfoocanvas/foo-canvas-rect-ellipse.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtktreestore.h>
+#include <gtk/gtkimage.h>
+#include <gtk/gtkimagemenuitem.h>
+#include <gtk/gtkframe.h>
+#include <gtk/gtkviewport.h>
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtkcellrendererpixbuf.h>
+#include <gtk/gtkstock.h>
+#include <gtk/gtkliststore.h>
+#include <string.h>
+
+typedef struct _GraphGuruState GraphGuruState;
+typedef struct _GraphGuruTypeSelector GraphGuruTypeSelector;
+
+struct _GraphGuruState {
+ GogGraph *graph;
+ GogChart *chart;
+ GogPlot *plot;
+
+ GnmCmdContext *cc;
+ GogDataAllocator *dalloc;
+ GClosure *register_closure;
+
+ /* GUI accessors */
+ GladeXML *gui;
+ GtkWidget *dialog;
+ GtkWidget *button_cancel;
+ GtkWidget *button_navigate;
+ GtkWidget *button_ok;
+ GtkNotebook *steps;
+ GtkWidget *add_menu, *delete_button;
+
+ FooCanvasItem *sample_graph_item;
+
+ GtkContainer *prop_container;
+ GtkTreeSelection *prop_selection;
+ GtkTreeView *prop_view;
+ GtkTreeStore *prop_model;
+ GtkTreeIter prop_iter;
+ GogObject *prop_object;
+
+ GraphGuruTypeSelector *type_selector;
+
+ struct {
+ GtkWidget *inc, *dec, *first, *last, *menu;
+ } prec;
+ /* internal state */
+ int current_page, initial_page;
+ gboolean valid;
+ gboolean updating;
+ gboolean fmt_page_initialized;
+ gboolean editing;
+
+ /* hackish reuse of State as a closure */
+ GogObject *search_target, *new_child;
+};
+
+struct _GraphGuruTypeSelector {
+ GladeXML *gui;
+ GtkWidget *canvas;
+ GtkWidget *sample_button;
+ GtkLabel *label;
+ GtkTreeView *list_view;
+ GtkListStore *model;
+ FooCanvasItem *selector;
+
+ FooCanvasItem *sample_graph_item;
+
+ GraphGuruState *state;
+
+ FooCanvasGroup *graph_group;
+
+ xmlNode const *plots;
+ GogPlotFamily *current_family;
+ GogPlotType *current_type;
+ FooCanvasGroup const *current_family_item;
+ FooCanvasItem const *current_minor_item;
+};
+
+enum {
+ PLOT_FAMILY_TYPE_IMAGE,
+ PLOT_FAMILY_TYPE_NAME,
+ PLOT_FAMILY_TYPE_CANVAS_GROUP,
+ PLOT_FAMILY_NUM_COLUMNS
+};
+enum {
+ PLOT_ATTR_NAME,
+ PLOT_ATTR_OBJECT,
+ PLOT_ATTR_NUM_COLUMNS
+};
+
+#define MINOR_PIXMAP_WIDTH 64
+#define MINOR_PIXMAP_HEIGHT 60
+#define BORDER 5
+
+#define PLOT_TYPE_KEY "plot_type"
+#define FIRST_MINOR_TYPE "first_minor_type"
+#define ROLE_KEY "role"
+#define STATE_KEY "plot_type"
+
+static GdkPixbuf *
+get_pixbuf (char const *image_file)
+{
+ static GHashTable *cache = NULL;
+ GdkPixbuf *pixbuf;
+
+ if (cache != NULL) {
+ pixbuf = g_hash_table_lookup (cache, image_file);
+ if (pixbuf != NULL)
+ return pixbuf;
+ } else
+ cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_object_unref);
+
+ pixbuf = gnumeric_load_pixbuf (image_file);
+ g_hash_table_insert (cache, (gpointer)image_file, pixbuf);
+
+ return pixbuf;
+}
+
+static void
+get_pos (int col, int row, double *x, double *y)
+{
+ *x = (col-1) * (MINOR_PIXMAP_WIDTH + BORDER) + BORDER;
+ *y = (row-1) * (MINOR_PIXMAP_HEIGHT + BORDER) + BORDER;
+}
+
+/*
+ * graph_typeselect_minor :
+ *
+ * @typesel :
+ * @item : A CanvasItem in the selector.
+ *
+ * Moves the typesel::selector overlay above the @item.
+ * Assumes that the item is visible
+ */
+static void
+graph_typeselect_minor (GraphGuruTypeSelector *typesel, FooCanvasItem *item)
+{
+ GraphGuruState *s = typesel->state;
+ GogPlotType *type;
+ double x1, y1, x2, y2;
+ gboolean enable_next_button;
+ GogPlot *plot;
+
+ if (typesel->current_minor_item == item)
+ return;
+
+ type = g_object_get_data (G_OBJECT (item), PLOT_TYPE_KEY);
+
+ g_return_if_fail (type != NULL);
+
+ typesel->current_type = type;
+ typesel->current_minor_item = item;
+ foo_canvas_item_get_bounds (item, &x1, &y1, &x2, &y2);
+ foo_canvas_item_set (FOO_CANVAS_ITEM (typesel->selector),
+ "x1", x1-1., "y1", y1-1.,
+ "x2", x2+1., "y2", y2+1.,
+ NULL);
+ gtk_label_set_text (typesel->label, _(type->description));
+ gtk_widget_set_sensitive (typesel->sample_button, TRUE);
+
+ enable_next_button = (s->plot == NULL);
+
+ plot = gog_plot_new_by_type (type);
+
+ g_return_if_fail (plot != NULL);
+
+ if (s->plot != NULL) {
+ GogObject *obj = GOG_OBJECT (s->plot);
+ gog_object_clear_parent (obj);
+ g_object_unref (obj);
+ }
+
+ gog_object_add_by_name (GOG_OBJECT (s->chart),
+ "Plot", GOG_OBJECT (s->plot = plot));
+#if 0
+ if (s->original_plot != NULL &&
+ !gog_plot_make_similar (s->plot, s->original_plot))
+ return;
+#endif
+
+ if (s->dalloc != NULL)
+ gog_data_allocator_allocate (s->dalloc, s->plot);
+
+ if (s->current_page == 0 && enable_next_button)
+ gtk_widget_set_sensitive (s->button_navigate, TRUE);
+}
+
+static gboolean
+graph_typeselect_minor_x_y (GraphGuruTypeSelector *typesel,
+ double x, double y)
+{
+ FooCanvasItem *item = foo_canvas_get_item_at (
+ FOO_CANVAS (typesel->canvas), x, y);
+
+ if (item != NULL) {
+ if(item != typesel->selector)
+ graph_typeselect_minor (typesel, item);
+ foo_canvas_item_grab_focus (item);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+cb_key_press_event (G_GNUC_UNUSED GtkWidget *wrapper,
+ GdkEventKey *event,
+ GraphGuruTypeSelector *typesel)
+{
+ GtkCornerType corner;
+ int col, row;
+ double x, y;
+ GogPlotType const *type = g_object_get_data (
+ G_OBJECT (typesel->current_minor_item), PLOT_TYPE_KEY);
+
+ g_return_val_if_fail (type != NULL, FALSE);
+
+ col = type->col;
+ row = type->row;
+
+ switch (event->keyval){
+ case GDK_KP_Left: case GDK_Left:
+ corner = GTK_CORNER_BOTTOM_RIGHT;
+ --col;
+ break;
+
+ case GDK_KP_Up: case GDK_Up:
+ corner = GTK_CORNER_BOTTOM_RIGHT;
+ --row;
+ break;
+
+ case GDK_KP_Right: case GDK_Right:
+ corner = GTK_CORNER_TOP_LEFT;
+ ++col;
+ break;
+
+ case GDK_KP_Down: case GDK_Down:
+ corner = GTK_CORNER_TOP_LEFT;
+ ++row;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ get_pos (col, row, &x, &y);
+ graph_typeselect_minor_x_y (typesel, x, y);
+
+ return TRUE;
+}
+
+static gint
+cb_button_press_event (GtkWidget *widget, GdkEventButton *event,
+ GraphGuruTypeSelector *typesel)
+{
+ if (event->button == 1) {
+ FooCanvas *c = FOO_CANVAS (widget);
+ double x, y;
+
+ foo_canvas_window_to_world (c, event->x, event->y, &x, &y);
+
+ graph_typeselect_minor_x_y (typesel, x, y);
+ }
+
+ return FALSE;
+}
+
+static void
+cb_selection_changed (GraphGuruTypeSelector *typesel)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (typesel->list_view);
+ GtkTreeIter iter;
+ FooCanvasItem *item;
+ FooCanvasGroup *group;
+
+ if (typesel->current_family_item != NULL)
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->current_family_item));
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+ gtk_tree_model_get (GTK_TREE_MODEL (typesel->model), &iter,
+ PLOT_FAMILY_TYPE_CANVAS_GROUP, &group,
+ -1);
+
+ foo_canvas_item_show (FOO_CANVAS_ITEM (group));
+ typesel->current_family_item = group;
+
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->selector));
+ item = g_object_get_data (G_OBJECT (group), FIRST_MINOR_TYPE);
+ if (item != NULL)
+ graph_typeselect_minor (typesel, item);
+ foo_canvas_item_show (FOO_CANVAS_ITEM (typesel->selector));
+}
+
+static void
+cb_typesel_sample_plot_resize (FooCanvas *canvas,
+ GtkAllocation *alloc, GraphGuruTypeSelector *typesel)
+{
+ /* Use 96dpi and 50% zoom. Hard code the zoom because it is not
+ * active when sample button is not depressed */
+ if (typesel->sample_graph_item != NULL)
+ foo_canvas_item_set (typesel->sample_graph_item,
+ "w", (double)alloc->width * 2.,
+ "h", (double)alloc->height * 2.,
+ "logical_width_pts", (2. * 72. * (double)alloc->width) / 96.,
+ "logical_height_pts", (2. * 72. * (double)alloc->height) / 96.,
+ NULL);
+}
+
+static void
+cb_sample_pressed (GraphGuruTypeSelector *typesel)
+{
+ if (typesel->current_family_item == NULL)
+ return;
+
+ foo_canvas_set_pixels_per_unit (FOO_CANVAS (typesel->canvas), .5); /* 50% zoom */
+ if (typesel->sample_graph_item == NULL) {
+ GtkAllocation *size = >K_WIDGET (typesel->canvas)->allocation;
+ typesel->sample_graph_item = foo_canvas_item_new (typesel->graph_group,
+ GOG_CONTROL_FOOCANVAS_TYPE,
+ "model", typesel->state->graph,
+ NULL);
+ cb_typesel_sample_plot_resize (FOO_CANVAS (typesel->canvas),
+ size, typesel);
+
+ g_return_if_fail (typesel->sample_graph_item != NULL);
+ }
+
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->current_family_item));
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->selector));
+ foo_canvas_item_show (FOO_CANVAS_ITEM (typesel->graph_group));
+}
+
+static void
+cb_sample_released (GraphGuruTypeSelector *typesel)
+{
+ if (typesel->current_family_item == NULL)
+ return;
+
+ foo_canvas_set_pixels_per_unit (FOO_CANVAS (typesel->canvas), 1.);
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (typesel->graph_group));
+ foo_canvas_item_show (FOO_CANVAS_ITEM (typesel->current_family_item));
+ foo_canvas_item_show (FOO_CANVAS_ITEM (typesel->selector));
+}
+
+typedef struct {
+ GraphGuruTypeSelector *typesel;
+ FooCanvasGroup *group;
+ FooCanvasItem *current_item;
+ GogPlotType *current_type;
+ int col, row;
+} type_list_closure;
+
+static void
+cb_plot_types_init (char const *id, GogPlotType *type,
+ type_list_closure *closure)
+{
+ double x1, y1, w, h;
+ FooCanvasItem *item;
+ int col, row;
+ GdkPixbuf *image = get_pixbuf (type->sample_image_file);
+
+ g_return_if_fail (image != NULL);
+
+ col = type->col;
+ row = type->row;
+ get_pos (col, row, &x1, &y1);
+ w = gdk_pixbuf_get_width (image);
+ if (w > MINOR_PIXMAP_WIDTH)
+ w = MINOR_PIXMAP_WIDTH;
+ h = gdk_pixbuf_get_height (image);
+ if (h > MINOR_PIXMAP_HEIGHT)
+ h = MINOR_PIXMAP_HEIGHT;
+
+ item = foo_canvas_item_new (closure->group,
+ foo_canvas_pixbuf_get_type (),
+ "x", x1, "y", y1,
+ "width", w, "height", h,
+ "pixbuf", image,
+ "point_ignores_alpha", TRUE,
+ NULL);
+ g_object_set_data (G_OBJECT (item), PLOT_TYPE_KEY, (gpointer)type);
+
+ if (closure->current_type == NULL ||
+ closure->row > row ||
+ (closure->row == row && closure->col > col)) {
+ closure->current_type = type;
+ closure->current_item = item;
+ closure->col = col;
+ closure->row = row;
+ }
+}
+
+static void
+cb_plot_families_init (char const *id, GogPlotFamily *family,
+ GraphGuruTypeSelector *typesel)
+{
+ FooCanvasGroup *group;
+ GtkTreeIter iter;
+ type_list_closure closure;
+
+ if (g_hash_table_size (family->types) <= 0)
+ return;
+
+ /* Define a canvas group for all the minor types */
+ group = FOO_CANVAS_GROUP (foo_canvas_item_new (
+ foo_canvas_root (FOO_CANVAS (typesel->canvas)),
+ foo_canvas_group_get_type (),
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+ foo_canvas_item_hide (FOO_CANVAS_ITEM (group));
+
+ gtk_list_store_append (typesel->model, &iter);
+ gtk_list_store_set (typesel->model, &iter,
+ PLOT_FAMILY_TYPE_IMAGE, get_pixbuf (family->sample_image_file),
+ PLOT_FAMILY_TYPE_NAME, _(family->name),
+ PLOT_FAMILY_TYPE_CANVAS_GROUP, group,
+ -1);
+
+ closure.typesel = typesel;
+ closure.group = group;
+ closure.current_type = NULL;
+ closure.current_item = NULL;
+
+ /* Init the list and the canvas group for each family */
+ g_hash_table_foreach (family->types,
+ (GHFunc) cb_plot_types_init, &closure);
+ g_object_set_data (G_OBJECT (group), FIRST_MINOR_TYPE,
+ closure.current_item);
+}
+
+static void
+cb_canvas_realized (GtkLayout *widget)
+{
+ gdk_window_set_back_pixmap (widget->bin_window, NULL, FALSE);
+}
+
+static void
+graph_guru_state_destroy (GraphGuruState *state)
+{
+ g_return_if_fail (state != NULL);
+
+ if (state->graph != NULL) {
+ g_object_unref (state->graph);
+ state->graph = NULL;
+ }
+
+ if (state->gui != NULL) {
+ g_object_unref (state->gui);
+ state->gui = NULL;
+ }
+
+ if (state->register_closure != NULL) {
+ g_closure_unref (state->register_closure);
+ state->register_closure = NULL;
+ }
+
+ state->dialog = NULL;
+ g_free (state);
+}
+
+static void populate_graph_item_list (GogObject *obj, GogObject *select,
+ GraphGuruState *s, GtkTreeIter *parent,
+ gboolean insert);
+
+static void
+cb_graph_guru_add_item (GtkWidget *w, GraphGuruState *s)
+{
+ gog_object_add_by_role (s->prop_object,
+ g_object_get_data (G_OBJECT (w), ROLE_KEY), NULL);
+}
+
+static void
+cb_graph_guru_delete_item (GraphGuruState *s)
+{
+ if (s->prop_object != NULL) {
+ GtkTreeIter iter;
+ GogObject *obj = s->prop_object;
+
+ /* select the parent before we delete */
+ gtk_tree_model_iter_parent (GTK_TREE_MODEL (s->prop_model),
+ &iter, &s->prop_iter);
+ gtk_tree_selection_select_iter (s->prop_selection, &iter);
+ gog_object_clear_parent (obj);
+ g_object_unref (obj);
+ }
+}
+
+static void
+update_prec_menu (GraphGuruState *s, gboolean inc_ok, gboolean dec_ok)
+{
+ gtk_widget_set_sensitive (s->prec.first, inc_ok);
+ gtk_widget_set_sensitive (s->prec.inc, inc_ok);
+ gtk_widget_set_sensitive (s->prec.dec, dec_ok);
+ gtk_widget_set_sensitive (s->prec.last, dec_ok);
+ gtk_widget_set_sensitive (s->prec.menu, dec_ok | inc_ok);
+}
+
+static gboolean
+cb_reordered_find (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ gtk_tree_store_move_after (s->prop_model, &s->prop_iter, iter);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+static void
+reorder (GraphGuruState *s, gboolean inc, gboolean goto_max)
+{
+ gboolean inc_ok, dec_ok;
+ GogObject *after;
+
+ g_return_if_fail (s->search_target == NULL);
+
+ after = gog_object_reorder (s->prop_object, inc, goto_max);
+ if (after != NULL) {
+ s->search_target = after;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_reordered_find, s);
+ s->search_target = NULL;
+ } else
+ gtk_tree_store_move_after (s->prop_model, &s->prop_iter, NULL);
+
+ gog_object_can_reorder (s->prop_object, &inc_ok, &dec_ok);
+ update_prec_menu (s, inc_ok, dec_ok);
+}
+static void cb_prec_first (GraphGuruState *s) { reorder (s, TRUE, TRUE); }
+static void cb_prec_inc (GraphGuruState *s) { reorder (s, TRUE, FALSE); }
+static void cb_prec_dec (GraphGuruState *s) { reorder (s, FALSE, FALSE); }
+static void cb_prec_last (GraphGuruState *s) { reorder (s, FALSE, TRUE); }
+
+struct type_menu_create {
+ GraphGuruState *state;
+ GtkWidget *menu;
+ gboolean non_blank;
+};
+
+
+static gint
+cb_cmp_plot_type (GogPlotType const *a, GogPlotType const *b)
+{
+ if (a->row == b->row)
+ return a->col - b->col;
+ return a->row - b->row;
+}
+
+static void
+cb_plot_type_list (char const *id, GogPlotType *type, GSList **list)
+{
+ *list = g_slist_insert_sorted (*list, type,
+ (GCompareFunc) cb_cmp_plot_type);
+}
+
+static void
+cb_graph_guru_add_plot (GtkWidget *w, GraphGuruState *s)
+{
+ GogPlotType *type = g_object_get_data (G_OBJECT (w), PLOT_TYPE_KEY);
+ GogPlot *plot = gog_plot_new_by_type (type);
+ gog_object_add_by_name (GOG_OBJECT (s->prop_object),
+ "Plot", GOG_OBJECT (plot));
+ /* as a convenience add a series to the newly created plot */
+ gog_object_add_by_name (GOG_OBJECT (plot), "Series", NULL);
+}
+
+static void
+cb_plot_family_menu_create (char const *id, GogPlotFamily *family,
+ struct type_menu_create *closure)
+{
+ GtkWidget *w, *menu;
+ GSList *ptr, *types = NULL;
+ GogPlotType *type;
+
+ if (g_hash_table_size (family->types) <= 0)
+ return;
+
+ menu = gtk_image_menu_item_new_with_label (_(family->name));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu),
+ gtk_image_new_from_pixbuf (
+ get_pixbuf (family->sample_image_file)));
+ gtk_menu_shell_append (GTK_MENU_SHELL (closure->menu), menu);
+ closure->non_blank = TRUE;
+
+ w = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu), w);
+
+ menu = w;
+ g_hash_table_foreach (family->types,
+ (GHFunc) cb_plot_type_list, &types);
+ for (ptr = types ; ptr != NULL ; ptr = ptr->next) {
+ type = ptr->data;
+ w = gtk_image_menu_item_new_with_label (_(type->name));
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (w),
+ gtk_image_new_from_pixbuf (
+ get_pixbuf (type->sample_image_file)));
+ g_object_set_data (G_OBJECT (w), PLOT_TYPE_KEY, type);
+ g_signal_connect (G_OBJECT (w),
+ "activate",
+ G_CALLBACK (cb_graph_guru_add_plot), closure->state);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), w);
+ }
+ g_slist_free (types);
+}
+
+/* return TRUE if there are plot types to add */
+static GtkWidget *
+plot_type_menu_create (GraphGuruState *s)
+{
+ struct type_menu_create closure;
+ closure.state = s;
+ closure.menu = gtk_menu_new ();
+ closure.non_blank = FALSE;
+
+ g_hash_table_foreach ((GHashTable *)gog_plot_families (),
+ (GHFunc) cb_plot_family_menu_create, &closure);
+
+ if (closure.non_blank)
+ return closure.menu;
+ gtk_object_destroy (GTK_OBJECT (closure.menu));
+ return NULL;
+}
+
+static void
+cb_attr_tree_selection_change (GraphGuruState *s)
+{
+ gboolean add_ok = FALSE;
+ gboolean delete_ok = FALSE;
+ gboolean inc_ok = FALSE;
+ gboolean dec_ok = FALSE;
+ GtkTreeModel *model;
+ GogObject *obj = NULL;
+ GtkWidget *w, *editor, *notebook;
+ GtkTreePath *path;
+
+ if (gtk_tree_selection_get_selected (s->prop_selection, &model, &s->prop_iter))
+ gtk_tree_model_get (model, &s->prop_iter,
+ PLOT_ATTR_OBJECT, &obj,
+ -1);
+
+ if (s->prop_object == obj)
+ return;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (s->prop_model), &s->prop_iter);
+ gtk_tree_view_scroll_to_cell (s->prop_view, path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free (path);
+
+ /* remove the old prop page */
+ s->prop_object = obj;
+ w = gtk_bin_get_child (GTK_BIN (s->prop_container));
+ if (w != NULL)
+ gtk_container_remove (s->prop_container, w);
+
+ if (s->prop_object != NULL) {
+ /* Setup up the additions menu */
+ GSList *additions = gog_object_possible_additions (s->prop_object);
+ if (additions != NULL) {
+ GogObjectRole const *role;
+ GSList *ptr;
+ GtkWidget *tmp, *menu;
+
+ menu = gtk_menu_new ();
+ for (ptr = additions ; ptr != NULL ; ptr = ptr->next) {
+ role = ptr->data;
+ /* somewhat hackish, but I do not see a need
+ * for anything more general yet */
+ if (strcmp (role->id, "Plot")) {
+ tmp = gtk_menu_item_new_with_label (_(role->id));
+ g_object_set_data (G_OBJECT (tmp), ROLE_KEY,
+ (gpointer)role);
+ g_signal_connect (G_OBJECT (tmp),
+ "activate",
+ G_CALLBACK (cb_graph_guru_add_item), s);
+ } else {
+ GtkWidget *submenu = plot_type_menu_create (s);
+ if (submenu != NULL) {
+ tmp = gtk_menu_item_new_with_label (_(role->id));
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (tmp), submenu);
+ } else
+ continue;
+ }
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), tmp);
+
+ }
+ add_ok = (additions != NULL);
+ g_slist_free (additions);
+
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (s->add_menu), menu);
+ gtk_widget_show_all (s->add_menu);
+ }
+
+ /* if we ever go back to the typeselector be sure to
+ * add the plot to the last selected chart */
+ s->chart = (GogChart *)
+ gog_object_get_parent_typed (obj, GOG_CHART_TYPE);
+ s->plot = (GogPlot *)
+ gog_object_get_parent_typed (obj, GOG_PLOT_TYPE);
+
+ if (s->plot == NULL) {
+ if (s->chart == NULL && s->graph != NULL) {
+ GSList *charts = gog_object_get_children (GOG_OBJECT (s->graph),
+ gog_object_find_role_by_name (GOG_OBJECT (s->graph), "Chart"));
+ if (charts != NULL && charts->next == NULL)
+ s->chart = charts->data;
+ g_slist_free (charts);
+ }
+ if (s->chart != NULL) {
+ GSList *plots = gog_object_get_children (GOG_OBJECT (s->chart),
+ gog_object_find_role_by_name (GOG_OBJECT (s->chart), "Plot"));
+ if (plots != NULL && plots->next == NULL)
+ s->plot = plots->data;
+ g_slist_free (plots);
+ }
+ }
+ gtk_widget_set_sensitive (s->button_navigate, s->chart != NULL);
+
+ delete_ok = gog_object_is_deletable (s->prop_object);
+ gog_object_can_reorder (obj, &inc_ok, &dec_ok);
+
+ /* create a prefs page for the graph obj */
+ editor = gog_object_get_editor (obj, s->dalloc, s->cc);
+ if (GTK_IS_NOTEBOOK (editor)) {
+ notebook = editor;
+ } else {
+ notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
+ if (editor == NULL)
+ editor = gtk_label_new (NULL); /* dummy widget for empty page */
+ gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook), editor, NULL);
+ gtk_widget_show (editor);
+ }
+ gtk_container_add (s->prop_container, notebook);
+ gtk_widget_show (notebook);
+ }
+
+ gtk_widget_set_sensitive (s->delete_button, delete_ok);
+ gtk_widget_set_sensitive (s->add_menu, add_ok);
+ update_prec_menu (s, inc_ok, dec_ok);
+}
+
+static gboolean
+cb_find_renamed_item (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ s->search_target = NULL;
+ gtk_tree_store_set (s->prop_model, iter,
+ PLOT_ATTR_NAME, gog_object_get_name (obj),
+ -1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+cb_obj_name_changed (GogObject *obj, GraphGuruState *s)
+{
+ s->search_target = obj;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_find_renamed_item, s);
+}
+
+static gboolean
+cb_find_child_added (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ s->search_target = NULL;
+ populate_graph_item_list (s->new_child, s->new_child, s, iter, TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+cb_obj_child_added (GogObject *parent, GogObject *child, GraphGuruState *s)
+{
+ s->search_target = parent;
+ s->new_child = child;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_find_child_added, s);
+ s->new_child = NULL;
+}
+
+static gboolean
+cb_find_child_removed (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+ GtkWidget *w;
+
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ s->search_target = NULL;
+ /* remove the tree element and the prop page */
+ gtk_tree_store_remove (s->prop_model, iter);
+ w = gtk_bin_get_child (GTK_BIN (s->prop_container));
+ if (w != NULL)
+ gtk_container_remove (s->prop_container, w);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+static void
+cb_obj_child_removed (GogObject *parent, GogObject *child, GraphGuruState *s)
+{
+ s->search_target = child;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_find_child_removed, s);
+
+ if (s->chart == (gpointer)child) {
+ s->chart = NULL;
+ s->plot = NULL;
+ gtk_widget_set_sensitive (s->button_navigate, FALSE);
+ } else if (s->plot == (gpointer)child)
+ s->plot = NULL;
+}
+
+static void
+populate_graph_item_list (GogObject *obj, GogObject *select, GraphGuruState *s,
+ GtkTreeIter *parent, gboolean insert)
+{
+ GSList *children, *ptr;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GClosure *closure;
+
+ if (insert) {
+ GogObject *gparent = gog_object_get_parent (obj);
+ gint i = g_slist_index (gparent->children, obj);
+ if (i > 0) {
+ GtkTreeIter sibling;
+ gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (s->prop_model),
+ &sibling, parent, i-1);
+ gtk_tree_store_insert_after (s->prop_model, &iter,
+ parent, &sibling);
+ } else
+ gtk_tree_store_prepend (s->prop_model, &iter, parent);
+ } else
+ gtk_tree_store_append (s->prop_model, &iter, parent);
+
+ gtk_tree_store_set (s->prop_model, &iter,
+ PLOT_ATTR_OBJECT, obj,
+ PLOT_ATTR_NAME, gog_object_get_name (obj),
+ -1);
+
+ /* remove the signal handlers when the guru goes away */
+ closure = g_cclosure_new (G_CALLBACK (cb_obj_name_changed), s, NULL);
+ g_object_watch_closure (G_OBJECT (s->prop_view), closure);
+ g_signal_connect_closure (G_OBJECT (obj),
+ "name-changed",
+ closure, FALSE);
+ closure = g_cclosure_new (G_CALLBACK (cb_obj_child_added), s, NULL);
+ g_object_watch_closure (G_OBJECT (s->prop_view), closure);
+ g_signal_connect_closure (G_OBJECT (obj),
+ "child-added",
+ closure, FALSE);
+ closure = g_cclosure_new (G_CALLBACK (cb_obj_child_removed), s, NULL);
+ g_object_watch_closure (G_OBJECT (s->prop_view), closure);
+ g_signal_connect_closure (G_OBJECT (obj),
+ "child-removed",
+ closure, FALSE);
+
+ children = gog_object_get_children (obj, NULL);
+ for (ptr = children ; ptr != NULL ; ptr = ptr->next)
+ populate_graph_item_list (ptr->data, select, s, &iter, FALSE);
+ g_slist_free (children);
+
+ /* ensure that new items are visible */
+ path = gtk_tree_model_get_path (
+ GTK_TREE_MODEL (s->prop_model), &iter);
+ gtk_tree_view_expand_to_path (s->prop_view, path);
+ gtk_tree_path_free (path);
+
+ if (obj == select)
+ /* select after expanding so that we do not lose selection due
+ * to visibility */
+ gtk_tree_selection_select_iter (s->prop_selection, &iter);
+}
+
+static gboolean
+cb_find_item (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
+ GraphGuruState *s)
+{
+ GogObject *obj;
+
+ gtk_tree_model_get (model, iter, PLOT_ATTR_OBJECT, &obj, -1);
+ if (obj == s->search_target) {
+ gtk_tree_selection_select_iter (s->prop_selection, iter);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint
+cb_canvas_select_item (FooCanvas *canvas, GdkEventButton *event,
+ GraphGuruState *s)
+{
+ GogView *view;
+ GogRenderer *rend;
+ double x, y;
+
+ g_return_val_if_fail (FOO_IS_CANVAS (canvas), FALSE);
+
+ if (canvas->current_item == NULL)
+ return FALSE;
+
+ g_object_get (G_OBJECT (s->sample_graph_item), "renderer", &rend, NULL);
+ g_object_get (G_OBJECT (rend), "view", &view, NULL);
+ foo_canvas_window_to_world (canvas, event->x, event->y, &x, &y);
+ gog_view_info_at_point (view,
+ x * canvas->pixels_per_unit, y * canvas->pixels_per_unit,
+ s->prop_object, &s->search_target, NULL);
+ if (s->search_target == NULL)
+ return FALSE;
+
+ gtk_tree_model_foreach (GTK_TREE_MODEL (s->prop_model),
+ (GtkTreeModelForeachFunc) cb_find_item, s);
+ s->search_target = NULL;
+ return TRUE;
+}
+
+static void
+cb_sample_plot_resize (FooCanvas *canvas,
+ GtkAllocation *alloc, GraphGuruState *s)
+{
+ /* Use 96dpi and zoom */
+ double zoom = 1. / canvas->pixels_per_unit;
+ foo_canvas_item_set (s->sample_graph_item,
+ "w", (double)alloc->width * zoom,
+ "h", (double)alloc->height * zoom,
+ "logical_width_pts", (72. * zoom * (double)alloc->width) / 96.,
+ "logical_height_pts", (72. * zoom * (double)alloc->height) / 96.,
+ NULL);
+}
+
+static void
+graph_guru_init_format_page (GraphGuruState *s)
+{
+ GtkWidget *w;
+ GtkTreeViewColumn *column;
+
+ if (s->fmt_page_initialized)
+ return;
+ s->fmt_page_initialized = TRUE;
+ s->add_menu = glade_xml_get_widget (s->gui, "add_menu");
+ s->delete_button = glade_xml_get_widget (s->gui, "delete");
+ s->prec.menu = glade_xml_get_widget (s->gui, "precedence_menu");
+ s->prec.inc = glade_xml_get_widget (s->gui, "inc_precedence");
+ s->prec.dec = glade_xml_get_widget (s->gui, "dec_precedence");
+ s->prec.first = glade_xml_get_widget (s->gui, "first_precedence");
+ s->prec.last = glade_xml_get_widget (s->gui, "last_precedence");
+
+ g_signal_connect_swapped (G_OBJECT (s->delete_button),
+ "clicked",
+ G_CALLBACK (cb_graph_guru_delete_item), s);
+ g_signal_connect_swapped (G_OBJECT (s->prec.first),
+ "activate",
+ G_CALLBACK (cb_prec_first), s);
+ g_signal_connect_swapped (G_OBJECT (s->prec.inc),
+ "activate",
+ G_CALLBACK (cb_prec_inc), s);
+ g_signal_connect_swapped (G_OBJECT (s->prec.dec),
+ "activate",
+ G_CALLBACK (cb_prec_dec), s);
+ g_signal_connect_swapped (G_OBJECT (s->prec.last),
+ "activate",
+ G_CALLBACK (cb_prec_last), s);
+
+ /* Load up the sample view and make it fill the entire canvas */
+ w = glade_xml_get_widget (s->gui, "sample_canvas");
+ foo_canvas_set_pixels_per_unit (FOO_CANVAS (w), .5); /* 50% zoom */
+ s->sample_graph_item = foo_canvas_item_new (
+ foo_canvas_root (FOO_CANVAS (w)), GOG_CONTROL_FOOCANVAS_TYPE,
+ "model", s->graph,
+ NULL);
+ cb_sample_plot_resize (FOO_CANVAS (w), &w->allocation, s);
+ g_signal_connect (G_OBJECT (w),
+ "size_allocate",
+ G_CALLBACK (cb_sample_plot_resize), s);
+ g_signal_connect_after (G_OBJECT (w),
+ "button_press_event",
+ G_CALLBACK (cb_canvas_select_item), s);
+ gtk_widget_show (w);
+
+ w = glade_xml_get_widget (s->gui, "prop_alignment");
+ s->prop_container = GTK_CONTAINER (w);
+ s->prop_model = gtk_tree_store_new (PLOT_ATTR_NUM_COLUMNS,
+ G_TYPE_STRING, G_TYPE_POINTER);
+ s->prop_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (
+ GTK_TREE_MODEL (s->prop_model)));
+ s->prop_selection = gtk_tree_view_get_selection (s->prop_view);
+ gtk_tree_selection_set_mode (s->prop_selection, GTK_SELECTION_BROWSE);
+ g_signal_connect_swapped (s->prop_selection,
+ "changed",
+ G_CALLBACK (cb_attr_tree_selection_change), s);
+
+ column = gtk_tree_view_column_new_with_attributes (_("Name"),
+ gtk_cell_renderer_text_new (),
+ "text", PLOT_ATTR_NAME, NULL);
+ gtk_tree_view_append_column (s->prop_view, column);
+
+ gtk_tree_view_set_headers_visible (s->prop_view, FALSE);
+
+ gtk_tree_store_clear (s->prop_model);
+ populate_graph_item_list (GOG_OBJECT (s->graph), GOG_OBJECT (s->graph),
+ s, NULL, FALSE);
+ gtk_tree_view_expand_all (s->prop_view);
+
+ w = glade_xml_get_widget (s->gui, "attr_window");
+ gtk_container_add (GTK_CONTAINER (w), GTK_WIDGET (s->prop_view));
+ gtk_widget_show_all (w);
+}
+
+static void
+graph_guru_set_page (GraphGuruState *s, int page)
+{
+ char *name;
+
+ if (s->current_page == page)
+ return;
+
+ switch (page) {
+ case 0: name = _("Step 1 of 2: Select Chart Type");
+ gtk_widget_set_sensitive (s->button_navigate, s->plot != NULL);
+ gtk_button_set_label (GTK_BUTTON (s->button_navigate),
+ GTK_STOCK_GO_FORWARD);
+ break;
+
+ case 1:
+ if (s->initial_page == 0) {
+ name = _("Step 2 of 2: Customize Chart");
+ gtk_widget_set_sensitive (s->button_navigate, s->chart != NULL);
+ gtk_button_set_label (GTK_BUTTON (s->button_navigate),
+ GTK_STOCK_GO_BACK);
+ } else {
+ name = _("Customize Chart");
+ gtk_widget_hide (s->button_navigate);
+ }
+ graph_guru_init_format_page (s);
+ break;
+
+ default:
+ g_warning ("Invalid Chart Guru page");
+ return;
+ }
+
+ s->current_page = page;
+ gtk_notebook_set_current_page (s->steps, page - s->initial_page);
+ gtk_window_set_title (GTK_WINDOW (s->dialog), name);
+}
+
+static void
+cb_graph_guru_clicked (GtkWidget *button, GraphGuruState *s)
+{
+ if (s->dialog == NULL)
+ return;
+
+ if (button == s->button_navigate) {
+ graph_guru_set_page (s, (s->current_page + 1) % 2);
+ return;
+ } else if (button == s->button_ok &&
+ s->register_closure != NULL && s->graph != NULL) {
+ /* Invoking closure */
+ GValue instance_and_params[2];
+ gpointer data = s->register_closure->is_invalid ?
+ NULL : s->register_closure->data;
+
+ instance_and_params[0].g_type = 0;
+ g_value_init (&instance_and_params[0], GOG_GRAPH_TYPE);
+ g_value_set_instance (&instance_and_params[0], s->graph);
+
+ instance_and_params[1].g_type = 0;
+ g_value_init (&instance_and_params[1], G_TYPE_POINTER);
+ g_value_set_pointer (&instance_and_params[1], data);
+
+ g_closure_set_marshal (s->register_closure,
+ g_cclosure_marshal_VOID__POINTER);
+
+ g_closure_invoke (s->register_closure,
+ NULL,
+ 2,
+ instance_and_params,
+ NULL);
+ g_value_unset (instance_and_params + 0);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (s->dialog));
+}
+
+static GtkWidget *
+graph_guru_init_button (GraphGuruState *s, char const *widget_name)
+{
+ GtkWidget *button = glade_xml_get_widget (s->gui, widget_name);
+ g_signal_connect (G_OBJECT (button),
+ "clicked",
+ G_CALLBACK (cb_graph_guru_clicked), s);
+ return button;
+}
+
+static GtkWidget *
+graph_guru_init_ok_button (GraphGuruState *s)
+{
+ GtkButton *button = GTK_BUTTON (glade_xml_get_widget
+ (s->gui, "button_ok"));
+
+ if (s->editing) {
+ gtk_button_set_label (button, GTK_STOCK_APPLY);
+ gtk_button_set_use_stock (button, TRUE);
+ } else {
+ gtk_button_set_use_stock (button, FALSE);
+ gtk_button_set_use_underline (button, TRUE);
+ gtk_button_set_label (button, _("_Insert"));
+ }
+ g_signal_connect (G_OBJECT (button),
+ "clicked",
+ G_CALLBACK (cb_graph_guru_clicked), s);
+ return GTK_WIDGET (button);
+}
+
+static void
+typesel_set_selection_color (GraphGuruTypeSelector *typesel)
+{
+ GtkWidget *w = gtk_entry_new ();
+ GdkColor *color = &w->style->base [GTK_WIDGET_HAS_FOCUS (typesel->canvas)
+ ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE];
+ guint32 select_color = 0;
+
+ select_color |= ((color->red >> 8) & 0xff) << 24;
+ select_color |= ((color->green >> 8) & 0xff) << 16;
+ select_color |= ((color->blue >> 8) & 0xff) << 8;
+ select_color |= 0x40; /* alpha of 25% */
+ foo_canvas_item_set (typesel->selector,
+ "fill_color_rgba", select_color,
+ NULL);
+ gtk_object_destroy (GTK_OBJECT (w));
+}
+
+static GtkWidget *
+graph_guru_type_selector_new (GraphGuruState *s)
+{
+ GtkTreeSelection *selection;
+ GraphGuruTypeSelector *typesel;
+ GtkWidget *selector;
+ GladeXML *gui;
+
+ gui = gnm_glade_xml_new (s->cc, "gog-guru-type-selector.glade", "type_selector", NULL);
+
+ typesel = g_new0 (GraphGuruTypeSelector, 1);
+ typesel->state = s;
+ typesel->current_family_item = NULL;
+ typesel->current_minor_item = NULL;
+ typesel->current_type = NULL;
+ typesel->sample_graph_item = NULL;
+
+ selector = glade_xml_get_widget (gui, "type_selector");
+
+ /* List of family types */
+ typesel->model = gtk_list_store_new (PLOT_FAMILY_NUM_COLUMNS,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_POINTER);
+ typesel->list_view = GTK_TREE_VIEW (glade_xml_get_widget (gui, "type_treeview"));
+ gtk_tree_view_set_model (typesel->list_view, GTK_TREE_MODEL (typesel->model));
+ gtk_tree_view_append_column (typesel->list_view,
+ gtk_tree_view_column_new_with_attributes ("",
+ gtk_cell_renderer_pixbuf_new (),
+ "pixbuf", PLOT_FAMILY_TYPE_IMAGE,
+ NULL));
+ gtk_tree_view_append_column (typesel->list_view,
+ gtk_tree_view_column_new_with_attributes (_("_Plot Type"),
+ gtk_cell_renderer_text_new (),
+ "text", PLOT_FAMILY_TYPE_NAME,
+ NULL));
+ selection = gtk_tree_view_get_selection (typesel->list_view);
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+ g_signal_connect_swapped (selection,
+ "changed",
+ G_CALLBACK (cb_selection_changed), typesel);
+
+ /* Setup an canvas to display the sample image & the sample plot. */
+ typesel->canvas = foo_canvas_new ();
+ typesel->graph_group = FOO_CANVAS_GROUP (foo_canvas_item_new (
+ foo_canvas_root (FOO_CANVAS (typesel->canvas)),
+ foo_canvas_group_get_type (),
+ "x", 0.0,
+ "y", 0.0,
+ NULL));
+ g_object_connect (typesel->canvas,
+ "signal::realize", G_CALLBACK (cb_canvas_realized), typesel,
+ "signal::size_allocate", G_CALLBACK (cb_typesel_sample_plot_resize), typesel,
+ "signal_after::key_press_event", G_CALLBACK (cb_key_press_event), typesel,
+ "signal::button_press_event", G_CALLBACK (cb_button_press_event), typesel,
+ "swapped_signal::focus_in_event", G_CALLBACK (typesel_set_selection_color), typesel,
+ "swapped_signal::focus_out_event", G_CALLBACK (typesel_set_selection_color), typesel,
+ NULL);
+ gtk_widget_set_size_request (typesel->canvas,
+ MINOR_PIXMAP_WIDTH*3 + BORDER*5,
+ MINOR_PIXMAP_HEIGHT*3 + BORDER*5);
+ foo_canvas_scroll_to (FOO_CANVAS (typesel->canvas), 0, 0);
+ gtk_container_add (GTK_CONTAINER (glade_xml_get_widget (gui, "canvas_container")),
+ typesel->canvas);
+
+ /* Init the list and the canvas group for each family */
+ g_hash_table_foreach ((GHashTable *)gog_plot_families (),
+ (GHFunc) cb_plot_families_init, typesel);
+
+ /* The alpha blended selection box */
+ typesel->selector = foo_canvas_item_new (
+ foo_canvas_root (FOO_CANVAS (typesel->canvas)),
+ foo_canvas_rect_get_type (),
+ "outline_color_rgba", 0x000000ff, /* black */
+ "width_pixels", 1,
+ NULL);
+ typesel_set_selection_color (typesel);
+
+ /* Setup the description label */
+ typesel->label = GTK_LABEL (glade_xml_get_widget (gui, "description_label"));
+
+ /* Set up sample button */
+ typesel->sample_button = glade_xml_get_widget (gui, "sample_button");
+ g_signal_connect_swapped (G_OBJECT (typesel->sample_button),
+ "pressed",
+ G_CALLBACK (cb_sample_pressed), typesel);
+ g_signal_connect_swapped (G_OBJECT (typesel->sample_button),
+ "released",
+ G_CALLBACK (cb_sample_released), typesel);
+
+ g_object_set_data_full (G_OBJECT (selector),
+ "state", typesel, (GDestroyNotify) g_free);
+
+ g_object_unref (G_OBJECT (gui));
+
+ return selector;
+}
+
+static gboolean
+graph_guru_init (GraphGuruState *s)
+{
+ s->gui = gnm_glade_xml_new (s->cc, "gog-guru.glade", NULL, NULL);
+ if (s->gui == NULL)
+ return TRUE;
+
+ s->dialog = glade_xml_get_widget (s->gui, "GraphGuru");
+ s->steps = GTK_NOTEBOOK (glade_xml_get_widget (s->gui, "notebook"));
+
+ /* Buttons */
+ s->button_cancel = graph_guru_init_button (s, "button_cancel");
+ s->button_navigate = graph_guru_init_button (s, "button_navigate");
+ s->button_ok = graph_guru_init_ok_button (s);
+
+ gnumeric_init_help_button (
+ glade_xml_get_widget (s->gui, "help_button"),
+ "sect-graphics-plots");
+
+ return FALSE;
+}
+
+/**
+ * dialog_graph_guru
+ * @wb : The workbook to use as a parent window.
+ * @graph : the graph to edit
+ * @page : the page to start on.
+ *
+ * Pop up a graph guru.
+ */
+GtkWidget *
+gog_guru (GogGraph *graph, GogDataAllocator *dalloc,
+ GnmCmdContext *cc, GtkWindow *toplevel,
+ GClosure *closure)
+{
+ int page = (graph != NULL) ? 1 : 0;
+ GraphGuruState *state;
+
+ state = g_new0 (GraphGuruState, 1);
+ state->valid = FALSE;
+ state->updating = FALSE;
+ state->fmt_page_initialized = FALSE;
+ state->editing = (graph != NULL);
+ state->gui = NULL;
+ state->cc = cc;
+ state->dalloc = dalloc;
+ state->current_page = -1;
+ state->register_closure = closure;
+ g_closure_ref (closure);
+
+ if (graph != NULL) {
+ g_return_val_if_fail (IS_GOG_GRAPH (graph), NULL);
+
+ state->graph = gog_graph_dup (graph);
+ state->chart = NULL;
+ state->plot = NULL;
+ } else {
+ state->plot = NULL;
+ state->graph = g_object_new (GOG_GRAPH_TYPE, NULL);
+ state->chart = GOG_CHART (gog_object_add_by_name (
+ GOG_OBJECT (state->graph), "Chart", NULL));
+ }
+
+ if (state->graph == NULL || graph_guru_init (state)) {
+ graph_guru_state_destroy (state);
+ return NULL;
+ }
+
+ /* Ok everything is hooked up. Let-er rip */
+ state->valid = TRUE;
+
+ state->initial_page = page;
+ if (page == 0) {
+ GtkWidget *w = graph_guru_type_selector_new (state);
+ gtk_notebook_prepend_page (state->steps, w, NULL);
+ gtk_widget_show_all (w);
+ }
+
+ graph_guru_set_page (state, page);
+
+ /* a candidate for merging into attach guru */
+ g_object_set_data_full (G_OBJECT (state->dialog),
+ "state", state, (GDestroyNotify) graph_guru_state_destroy);
+ gnumeric_non_modal_dialog (toplevel, GTK_WINDOW (state->dialog));
+ gtk_widget_show (GTK_WIDGET (state->dialog));
+
+ return state->dialog;
+}
+
+#if 0
+gtk_tree_sortable_set_sort_func (store, column, compare_rows,
+ GUINT_TO_POINTER (column), NULL);
+
+for each column of your database table.
+
+int
+compare_rows (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ int column = GPOINTER_TO_UINT (user_data);
+ MyRow *row_a, row_b;
+
+ gtk_tree_model_get (model, DATA_COLUMN, a, &row_a, -1);
+ gtk_tree_model_get (model, DATA_COLUMN, b, &row_b, -1);
+
+ return compare_cells (row_a->cells[column], row_b->cells[column]);
+}
+
+
+#endif
--- /dev/null
+++ lib/goffice/graph/gog-object-xml.c
@@ -0,0 +1,543 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-object-xml.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-object-xml.h>
+#include <goffice/graph/gog-object.h>
+#include <goffice/graph/gog-plot.h>
+#include <goffice/graph/gog-data-set.h>
+#include <goffice/graph/go-data.h>
+#include <goffice/utils/go-color.h>
+
+#include <glade/glade-build.h> /* for the xml utils */
+#include <string.h>
+#include <stdlib.h>
+
+GType
+gog_persist_get_type (void)
+{
+ static GType gog_persist_type = 0;
+
+ if (!gog_persist_type) {
+ static GTypeInfo const gog_persist_info = {
+ sizeof (GogPersistClass), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ gog_persist_type = g_type_register_static (G_TYPE_INTERFACE,
+ "GogPersist", &gog_persist_info, 0);
+ }
+
+ return gog_persist_type;
+}
+
+gboolean
+gog_persist_dom_load (GogPersist *gp, xmlNode *node)
+{
+ g_return_val_if_fail (IS_GOG_PERSIST (gp), FALSE);
+ return GOG_PERSIST_GET_CLASS (gp)->dom_load (gp, node);
+}
+
+void
+gog_persist_dom_save (GogPersist const *gp, xmlNode *parent)
+{
+ g_return_if_fail (IS_GOG_PERSIST (gp));
+ GOG_PERSIST_GET_CLASS (gp)->dom_save (gp, parent);
+}
+void
+gog_persist_sax_save (GogPersist const *gp, GsfXMLOut *output)
+{
+ g_return_if_fail (IS_GOG_PERSIST (gp));
+ GOG_PERSIST_GET_CLASS (gp)->sax_save (gp, output);
+}
+
+static void
+gog_object_set_arg_full (char const *name, char const *val, GogObject *obj, xmlNode *xml_node)
+{
+ GParamSpec *pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name);
+ GType prop_type;
+ GValue res = { 0 };
+ gboolean success = TRUE;
+
+ if (pspec == NULL) {
+ g_warning ("unknown property `%s' for class `%s'",
+ name, G_OBJECT_TYPE_NAME (obj));
+ return;
+ }
+
+ prop_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+ if (val == NULL &&
+ G_TYPE_FUNDAMENTAL (prop_type) != G_TYPE_BOOLEAN) {
+ g_warning ("could not convert NULL to type `%s' for property `%s'",
+ g_type_name (prop_type), pspec->name);
+ return;
+ }
+
+ g_value_init (&res, prop_type);
+ switch (G_TYPE_FUNDAMENTAL (prop_type)) {
+ case G_TYPE_CHAR:
+ g_value_set_char (&res, val[0]);
+ break;
+ case G_TYPE_UCHAR:
+ g_value_set_uchar (&res, (guchar)val[0]);
+ break;
+ case G_TYPE_BOOLEAN:
+ g_value_set_boolean (&res,
+ val == NULL ||
+ g_ascii_tolower (val[0]) == 't' ||
+ g_ascii_tolower (val[0]) == 'y' ||
+ strtol (val, NULL, 0));
+ break;
+ case G_TYPE_INT:
+ g_value_set_int (&res, strtol (val, NULL, 0));
+ break;
+ case G_TYPE_UINT:
+ g_value_set_uint (&res, strtoul (val, NULL, 0));
+ break;
+ case G_TYPE_LONG:
+ g_value_set_long (&res, strtol (val, NULL, 0));
+ break;
+ case G_TYPE_ULONG:
+ g_value_set_ulong (&res, strtoul (val, NULL, 0));
+ break;
+ case G_TYPE_ENUM:
+ g_value_set_enum (&res, glade_enum_from_string (prop_type, val));
+ break;
+ case G_TYPE_FLAGS:
+ g_value_set_flags (&res, glade_flags_from_string (prop_type, val));
+ break;
+ case G_TYPE_FLOAT:
+ g_value_set_float (&res, g_strtod (val, NULL));
+ break;
+ case G_TYPE_DOUBLE:
+ g_value_set_double (&res, g_strtod (val, NULL));
+ break;
+ case G_TYPE_STRING:
+ g_value_set_string (&res, val);
+ break;
+
+ case G_TYPE_OBJECT:
+ if (g_type_is_a (prop_type, G_TYPE_OBJECT)) {
+ xmlChar *type_name;
+ GType type = 0;
+ GObject *val_obj;
+
+ success = FALSE;
+ type_name = xmlGetProp (xml_node,
+ (xmlChar const *) "type");
+ if (type_name != NULL) {
+ type = g_type_from_name (type_name);
+ }
+ xmlFree (type_name);
+ if (type != 0) {
+ val_obj = g_object_new (type, NULL);
+ if (IS_GOG_PERSIST (val_obj) &&
+ gog_persist_dom_load (GOG_PERSIST (val_obj), xml_node)) {
+ g_value_set_object (&res, val_obj);
+ success = TRUE;
+ }
+ g_object_unref (val_obj);
+ }
+ }
+ break;
+
+ default:
+ success = FALSE;
+ }
+
+ if (!success) {
+ g_warning ("could not convert string to type `%s' for property `%s'",
+ g_type_name (prop_type), pspec->name);
+ } else
+ g_object_set_property (G_OBJECT (obj), name, &res);
+ g_value_unset (&res);
+}
+
+void
+gog_object_set_arg (char const *name, char const *val, GogObject *obj)
+{
+ gog_object_set_arg_full (name, val, obj, NULL);
+}
+
+static void
+gog_object_write_property_sax (GogObject const *obj, GParamSpec *pspec, GsfXMLOut *output)
+{
+ GObject *val_obj;
+ GType prop_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+ GValue value = { 0 };
+
+ g_value_init (&value, prop_type);
+ g_object_get_property (G_OBJECT (obj), pspec->name, &value);
+
+ /* No need to save default values */
+ if (!(pspec->flags & GOG_PARAM_FORCE_SAVE) &&
+ g_param_value_defaults (pspec, &value)) {
+ g_value_unset (&value);
+ return;
+ }
+
+ switch (G_TYPE_FUNDAMENTAL (prop_type)) {
+ case G_TYPE_CHAR:
+ case G_TYPE_UCHAR:
+ case G_TYPE_BOOLEAN:
+ case G_TYPE_INT:
+ case G_TYPE_UINT:
+ case G_TYPE_LONG:
+ case G_TYPE_ULONG:
+ case G_TYPE_FLOAT:
+ case G_TYPE_DOUBLE: {
+ GValue str = { 0 };
+ g_value_init (&str, G_TYPE_STRING);
+ g_value_transform (&value, &str);
+ gsf_xml_out_start_element (output, "property");
+ gsf_xml_out_add_cstr_unchecked (output, "name", pspec->name);
+ gsf_xml_out_add_cstr (output, NULL, g_value_get_string (&str));
+ gsf_xml_out_end_element (output); /* </property> */
+ g_value_unset (&str);
+ break;
+ }
+
+ case G_TYPE_STRING: {
+ char const *str = g_value_get_string (&value);
+ if (str != NULL) {
+ gsf_xml_out_start_element (output, "property");
+ gsf_xml_out_add_cstr_unchecked (output, "name", pspec->name);
+ gsf_xml_out_add_cstr (output, NULL, str);
+ gsf_xml_out_end_element (output); /* </property> */
+ }
+ break;
+ }
+
+ case G_TYPE_OBJECT:
+ val_obj = g_value_get_object (&value);
+ if (val_obj != NULL) {
+ if (IS_GOG_PERSIST (val_obj)) {
+ gsf_xml_out_start_element (output, "property");
+ gsf_xml_out_add_cstr_unchecked (output, "name", pspec->name);
+ gog_persist_sax_save (GOG_PERSIST (val_obj), output);
+ gsf_xml_out_end_element (output); /* </property> */
+ } else
+ g_warning ("How are we supposed to persist this ??");
+ }
+ break;
+
+ default:
+ ;
+ }
+ g_value_unset (&value);
+}
+
+static void
+gog_object_write_property (GogObject *obj, GParamSpec *pspec, xmlNode *parent)
+{
+ GObject *val_obj;
+ GType prop_type = G_PARAM_SPEC_VALUE_TYPE (pspec);
+ gboolean success = TRUE;
+ GValue value = { 0 };
+ xmlNode *node;
+
+ g_value_init (&value, prop_type);
+ g_object_get_property (G_OBJECT (obj), pspec->name, &value);
+
+ /* No need to save default values */
+ if (!(pspec->flags & GOG_PARAM_FORCE_SAVE) &&
+ g_param_value_defaults (pspec, &value)) {
+ g_value_unset (&value);
+ return;
+ }
+
+ node = xmlNewDocNode (parent->doc, NULL,
+ (xmlChar const *)"property", NULL);
+
+ switch (G_TYPE_FUNDAMENTAL (prop_type)) {
+ case G_TYPE_CHAR:
+ case G_TYPE_UCHAR:
+ case G_TYPE_BOOLEAN:
+ case G_TYPE_INT:
+ case G_TYPE_UINT:
+ case G_TYPE_LONG:
+ case G_TYPE_ULONG:
+ case G_TYPE_FLOAT:
+ case G_TYPE_DOUBLE: {
+ GValue str = { 0 };
+ g_value_init (&str, G_TYPE_STRING);
+ g_value_transform (&value, &str);
+ xmlNodeSetContent (node, g_value_get_string (&str));
+ g_value_unset (&str);
+ break;
+ }
+
+ case G_TYPE_STRING: {
+ char const *str = g_value_get_string (&value);
+ if (str != NULL)
+ xmlNodeSetContent (node, str);
+ else
+ success = FALSE;
+ break;
+ }
+
+ case G_TYPE_OBJECT:
+ val_obj = g_value_get_object (&value);
+ if (val_obj != NULL) {
+ if (IS_GOG_PERSIST (val_obj)) {
+ gog_persist_dom_save (GOG_PERSIST (val_obj), node);
+ } else
+ g_warning ("How are we supposed to persist this ??");
+ } else
+ success = FALSE;
+ break;
+
+ default:
+ success = FALSE;
+ }
+ g_value_unset (&value);
+
+ if (success) {
+ xmlSetProp (node, (xmlChar const *) "name", pspec->name);
+ xmlAddChild (parent, node);
+ } else
+ xmlFreeNode (node);
+}
+
+static void
+gog_dataset_load (GogDataset *set, xmlNode *node)
+{
+ xmlNode *ptr;
+ xmlChar *id, *val, *type;
+
+ for (ptr = node->xmlChildrenNode ; ptr != NULL ; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL)
+ continue;
+ if (!strcmp (ptr->name, "data"))
+ break;
+ }
+ if (ptr == NULL)
+ return;
+ for (ptr = ptr->xmlChildrenNode ; ptr != NULL ; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL)
+ continue;
+ if (!strcmp (ptr->name, "dimension")) {
+ id = xmlGetProp (ptr, (xmlChar const *) "id");
+ type = xmlGetProp (ptr, (xmlChar const *) "type");
+ val = xmlNodeGetContent (ptr);
+ if (id != NULL && type != NULL && val != NULL) {
+ unsigned dim_id = strtoul (id, NULL, 0);
+ GOData *dat = g_object_new (g_type_from_name (type), NULL);
+ if (dat != NULL && go_data_from_str (dat, val))
+ gog_dataset_set_dim (set, dim_id, dat, NULL);
+ }
+
+ if (id != NULL) xmlFree (id);
+ if (type != NULL) xmlFree (type);
+ if (val != NULL) xmlFree (val);
+ }
+ }
+}
+
+static void
+gog_dataset_sax_save (GogDataset const *set, GsfXMLOut *output)
+{
+ GOData *dat;
+ char *tmp, buffer[10];
+ int i, last;
+
+ gsf_xml_out_start_element (output, "data");
+ gog_dataset_dims (set, &i, &last);
+ for ( ; i <= last ; i++) {
+ dat = gog_dataset_get_dim (set, i);
+ if (dat == NULL)
+ continue;
+
+ gsf_xml_out_start_element (output, "dimension");
+ snprintf (buffer, sizeof buffer, "%d", i);
+ gsf_xml_out_add_cstr (output, "id", buffer);
+ gsf_xml_out_add_cstr (output, "type",
+ G_OBJECT_TYPE_NAME (dat));
+ tmp = go_data_as_str (dat);
+ gsf_xml_out_add_cstr (output, NULL, tmp);
+ g_free (tmp);
+ gsf_xml_out_end_element (output); /* </dimension> */
+ }
+ gsf_xml_out_end_element (output); /* </data> */
+
+}
+static void
+gog_dataset_dom_save (GogDataset *set, xmlNode *parent)
+{
+ xmlNode *node, *child;
+ char *tmp, buffer[10];
+ GOData *dat;
+ int i, last;
+
+ node = xmlNewDocNode (parent->doc, NULL, (xmlChar const *)"data", NULL);
+ gog_dataset_dims (set, &i, &last);
+ for ( ; i <= last ; i++) {
+ dat = gog_dataset_get_dim (set, i);
+ if (dat == NULL)
+ continue;
+
+ tmp = go_data_as_str (dat);
+ child = xmlNewChild (node, NULL,
+ (xmlChar const *) ("dimension"), tmp);
+ g_free (tmp);
+
+ snprintf (buffer, sizeof buffer, "%d", i);
+ xmlSetProp (child, (xmlChar const *) "id", buffer);
+ xmlSetProp (child, (xmlChar const *) "type",
+ G_OBJECT_TYPE_NAME (dat));
+ }
+
+ xmlAddChild (parent, node);
+}
+
+void
+gog_object_write_xml_sax (GogObject const *obj, GsfXMLOut *output)
+{
+ gint n;
+ GParamSpec **props;
+ GSList *ptr;
+
+ gsf_xml_out_start_element (output, "GogObject");
+
+ /* Primary details */
+ if (obj->role != NULL)
+ gsf_xml_out_add_cstr (output, "role", obj->role->id);
+ if (obj->explicitly_typed_role || obj->role == NULL)
+ gsf_xml_out_add_cstr (output, "type", G_OBJECT_TYPE_NAME (obj));
+
+ /* properties */
+ props = g_object_class_list_properties (G_OBJECT_GET_CLASS (obj), &n);
+ while (n-- > 0)
+ if (props[n]->flags & GOG_PARAM_PERSISTENT)
+ gog_object_write_property_sax (obj, props[n], output);
+
+ g_free (props);
+
+ if (IS_GOG_PERSIST (obj)) /* anything special for this class */
+ gog_persist_sax_save (GOG_PERSIST (obj), output);
+ if (IS_GOG_DATASET (obj)) /* convenience to save data */
+ gog_dataset_sax_save (GOG_DATASET (obj), output);
+
+ /* the children */
+ for (ptr = obj->children; ptr != NULL ; ptr = ptr->next)
+ gog_object_write_xml_sax (ptr->data, output);
+
+ gsf_xml_out_end_element (output); /* </GogObject> */
+}
+
+xmlNode *
+gog_object_write_xml (GogObject *obj, xmlDoc *doc)
+{
+ gint n;
+ GParamSpec **props;
+ GSList *ptr;
+ xmlNode *node = xmlNewDocNode (doc, NULL,
+ (xmlChar const *)"GogObject", NULL);
+
+ /* Primary details */
+ if (obj->role != NULL)
+ xmlSetProp (node, (xmlChar const *) "role", obj->role->id);
+ if (obj->explicitly_typed_role || obj->role == NULL)
+ xmlSetProp (node, (xmlChar const *) "type", G_OBJECT_TYPE_NAME (obj));
+
+ /* properties */
+ props = g_object_class_list_properties (G_OBJECT_GET_CLASS (obj), &n);
+ while (n-- > 0)
+ if (props[n]->flags & GOG_PARAM_PERSISTENT)
+ gog_object_write_property (obj, props[n], node);
+
+ g_free (props);
+
+ if (IS_GOG_PERSIST (obj)) /* anything special for this class */
+ gog_persist_dom_save (GOG_PERSIST (obj), node);
+ if (IS_GOG_DATASET (obj)) /* convenience to save data */
+ gog_dataset_dom_save (GOG_DATASET (obj), node);
+
+ /* the children */
+ for (ptr = obj->children; ptr != NULL ; ptr = ptr->next)
+ xmlAddChild (node, gog_object_write_xml (ptr->data, doc));
+
+ return node;
+}
+
+GogObject *
+gog_object_new_from_xml (GogObject *parent, xmlNode *node)
+{
+ xmlChar *role, *name, *val, *type_name;
+ xmlNode *ptr;
+ GogObject *res = NULL;
+ gboolean explicitly_typed_role = FALSE;
+
+ type_name = xmlGetProp (node, (xmlChar const *) "type");
+ if (type_name != NULL) {
+ GType type = g_type_from_name (type_name);
+ if (type == 0) {
+ GogPlot *plot = gog_plot_new_by_name (type_name);
+ res = GOG_OBJECT (plot);
+ } else
+ res = g_object_new (type, NULL);
+ xmlFree (type_name);
+ explicitly_typed_role = TRUE;
+ }
+ role = xmlGetProp (node, (xmlChar const *) "role");
+ if (role == NULL) {
+ g_return_val_if_fail (parent == NULL, NULL);
+ } else {
+ res = gog_object_add_by_name (parent, role, res);
+ xmlFree (role);
+ }
+
+ g_return_val_if_fail (res != NULL, NULL);
+
+ res->explicitly_typed_role = explicitly_typed_role;
+
+ if (IS_GOG_PERSIST (res))
+ gog_persist_dom_load (GOG_PERSIST (res), node);
+ if (IS_GOG_DATASET (res)) /* convenience to save data */
+ gog_dataset_load (GOG_DATASET (res), node);
+
+ for (ptr = node->xmlChildrenNode ; ptr != NULL ; ptr = ptr->next) {
+ if (xmlIsBlankNode (ptr) || ptr->name == NULL)
+ continue;
+ if (!strcmp (ptr->name, "property")) {
+ name = xmlGetProp (ptr, "name");
+ if (name == NULL) {
+ g_warning ("missing name for property entry");
+ continue;
+ }
+ val = xmlNodeGetContent (ptr);
+ gog_object_set_arg_full (name, val, res, ptr);
+ xmlFree (val);
+ xmlFree (name);
+ } else if (!strcmp (ptr->name, "GogObject"))
+ gog_object_new_from_xml (res, ptr);
+ }
+ return res;
+}
+
+void
+go_xml_out_add_color (GsfXMLOut *output, char const *id, GOColor c)
+{
+ char *str = go_color_as_str (c);
+ gsf_xml_out_add_cstr_unchecked (output, id, str);
+ g_free (str);
+}
--- /dev/null
+++ lib/goffice/graph/gog-label.h
@@ -0,0 +1,37 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-label.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_LABEL_H
+#define GOG_LABEL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOG_LABEL_TYPE (gog_label_get_type ())
+#define GOG_LABEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_LABEL_TYPE, GogLabel))
+#define IS_GOG_LABEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_LABEL_TYPE))
+
+GType gog_label_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_LABEL_H */
--- /dev/null
+++ lib/goffice/graph/gog-object-xml.h
@@ -0,0 +1,64 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-object-xml.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_OBJECT_XML_H
+#define GOG_OBJECT_XML_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/utils/goffice-utils.h>
+#include <glib-object.h>
+#include <libxml/tree.h>
+#include <gsf/gsf-libxml.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GogPersist GogPersist;
+
+typedef struct {
+ GTypeInterface base;
+
+ gboolean (*dom_load) (GogPersist *gp, xmlNode *node);
+ void (*dom_save) (GogPersist const *gp, xmlNode *parent);
+ void (*sax_save) (GogPersist const *gp, GsfXMLOut *output);
+} GogPersistClass;
+
+#define GOG_PERSIST_TYPE (gog_persist_get_type ())
+#define GOG_PERSIST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PERSIST_TYPE, GogPersist))
+#define IS_GOG_PERSIST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PERSIST_TYPE))
+#define GOG_PERSIST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_PERSIST_TYPE, GogPersistClass))
+#define IS_GOG_PERSIST_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_PERSIST_TYPE))
+#define GOG_PERSIST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GOG_PERSIST_TYPE, GogPersistClass))
+
+GType gog_persist_get_type (void);
+
+gboolean gog_persist_dom_load (GogPersist *gp, xmlNode *node);
+void gog_persist_dom_save (GogPersist const *gp, xmlNode *parent);
+void gog_persist_sax_save (GogPersist const *gp, GsfXMLOut *output);
+
+void gog_object_set_arg (char const *name, char const *val, GogObject *obj);
+void gog_object_write_xml_sax(GogObject const *obj, GsfXMLOut *output);
+xmlNode *gog_object_write_xml (GogObject *obj, xmlDoc *doc);
+GogObject *gog_object_new_from_xml (GogObject *parent, xmlNode *node);
+
+void go_xml_out_add_color (GsfXMLOut *out, char const *id, GOColor c);
+
+G_END_DECLS
+
+#endif /* GOG_OBJECT_XML_H */
--- /dev/null
+++ lib/goffice/graph/gog-guru.glade
@@ -0,0 +1,426 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkDialog" id="GraphGuru">
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes">GNOME Office Graph</property>
+ <property name="type">GTK_WINDOW_TOPLEVEL</property>
+ <property name="window_position">GTK_WIN_POS_MOUSE</property>
+ <property name="modal">False</property>
+ <property name="default_width">530</property>
+ <property name="default_height">625</property>
+ <property name="resizable">True</property>
+ <property name="destroy_with_parent">False</property>
+ <property name="decorated">True</property>
+ <property name="skip_taskbar_hint">False</property>
+ <property name="skip_pager_hint">False</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+ <property name="has_separator">False</property>
+
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox4">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area4">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+ <child>
+ <widget class="GtkButton" id="help_button">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-help</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-11</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button_cancel">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-cancel</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-6</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button_navigate">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-go-forward</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">2</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="button_ok">
+ <property name="visible">True</property>
+ <property name="can_default">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-ok</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NORMAL</property>
+ <property name="focus_on_click">True</property>
+ <property name="response_id">-5</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkNotebook" id="notebook">
+ <property name="visible">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <property name="tab_pos">GTK_POS_TOP</property>
+ <property name="scrollable">False</property>
+ <property name="enable_popup">False</property>
+
+ <child>
+ <widget class="GtkVPaned" id="v_fmt_pane">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">1</property>
+
+ <child>
+ <widget class="GtkHPaned" id="h_fmt_pane">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="position">1</property>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">6</property>
+ <property name="left_padding">6</property>
+ <property name="right_padding">6</property>
+
+ <child>
+ <widget class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">6</property>
+
+ <child>
+ <widget class="GtkScrolledWindow" id="attr_window">
+ <property name="height_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow_type">GTK_SHADOW_IN</property>
+ <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="homogeneous">False</property>
+ <property name="spacing">0</property>
+
+ <child>
+ <widget class="GtkMenuBar" id="add_menubar">
+ <property name="visible">True</property>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="add_menu">
+ <property name="visible">True</property>
+ <property name="label">gtk-add</property>
+ <property name="use_stock">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="precedence_menu">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">_Order</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image71">
+ <property name="visible">True</property>
+ <property name="stock">gtk-sort-ascending</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkMenu" id="precedence_menu_menu">
+
+ <child>
+ <widget class="GtkImageMenuItem" id="first_precedence">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Push to _back</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image72">
+ <property name="visible">True</property>
+ <property name="stock">gtk-goto-top</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="inc_precedence">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Push b_ackward</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image73">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-up</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="dec_precedence">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Pull f_orward</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image74">
+ <property name="visible">True</property>
+ <property name="stock">gtk-go-down</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="last_precedence">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Pull to _front</property>
+ <property name="use_underline">True</property>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image75">
+ <property name="visible">True</property>
+ <property name="stock">gtk-goto-bottom</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkButton" id="delete">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="label">gtk-remove</property>
+ <property name="use_stock">True</property>
+ <property name="relief">GTK_RELIEF_NONE</property>
+ <property name="focus_on_click">False</property>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">False</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="alignment2">
+ <property name="width_request">200</property>
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">6</property>
+ <property name="left_padding">6</property>
+ <property name="right_padding">6</property>
+
+ <child>
+ <widget class="Custom" id="sample_canvas">
+ <property name="visible">True</property>
+ <property name="creation_function">foo_canvas_new</property>
+ <property name="int1">0</property>
+ <property name="int2">0</property>
+ <property name="last_modification_time">Thu, 03 Apr 2003 17:05:22 GMT</property>
+ </widget>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">False</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">False</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkAlignment" id="prop_alignment">
+ <property name="visible">True</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xscale">1</property>
+ <property name="yscale">1</property>
+ <property name="top_padding">6</property>
+ <property name="bottom_padding">0</property>
+ <property name="left_padding">6</property>
+ <property name="right_padding">6</property>
+
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="shrink">False</property>
+ <property name="resize">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="tab_expand">False</property>
+ <property name="tab_fill">True</property>
+ </packing>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+
+ <child>
+ <placeholder/>
+ </child>
+
+ <child>
+ <widget class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"></property>
+ <property name="use_underline">False</property>
+ <property name="use_markup">False</property>
+ <property name="justify">GTK_JUSTIFY_LEFT</property>
+ <property name="wrap">False</property>
+ <property name="selectable">False</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ <packing>
+ <property name="type">tab</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="padding">0</property>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+</widget>
+
+</glade-interface>
--- /dev/null
+++ lib/goffice/graph/gog-renderer-svg.h
@@ -0,0 +1,35 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-svg.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_RENDERER_SVG_H
+#define GOG_RENDERER_SVG_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <gsf/gsf.h>
+
+G_BEGIN_DECLS
+
+gboolean gog_graph_export_to_svg (GogGraph *graph,
+ GsfOutput *output,
+ double width, double height, double scale);
+
+G_END_DECLS
+
+#endif /* GOG_RENDERER_SVG_H */
--- /dev/null
+++ lib/goffice/graph/gog-error-bar.h
@@ -0,0 +1,81 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-error-bar.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_ERROR_BAR_H
+#define GOG_ERROR_BAR_H
+
+#include <goffice/utils/go-color.h>
+#include "gog-series.h"
+#include "gog-data-set.h"
+#include "gog-axis.h"
+
+typedef enum {
+ GOG_ERROR_BAR_TYPE_NONE,
+ GOG_ERROR_BAR_TYPE_ABSOLUTE,
+ GOG_ERROR_BAR_TYPE_RELATIVE,
+ GOG_ERROR_BAR_TYPE_PERCENT
+} GogErrorBarType;
+
+typedef enum {
+ GOG_ERROR_BAR_DISPLAY_NONE,
+ GOG_ERROR_BAR_DISPLAY_POSITIVE,
+ GOG_ERROR_BAR_DISPLAY_NEGATIVE,
+ GOG_ERROR_BAR_DISPLAY_BOTH
+} GogErrorBarDisplay;
+
+struct _GogErrorBar{
+ GObject base;
+ GogErrorBarType type;
+ GogSeries *series;
+ int dim_i;
+ int error_i;
+ GogErrorBarDisplay display;
+ float width;
+ GogStyle* style;
+};
+
+#define GOG_ERROR_BAR_TYPE (gog_error_bar_get_type ())
+#define GOG_ERROR_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_ERROR_BAR_TYPE, GogErrorBar))
+#define IS_GOG_ERROR_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_ERROR_BAR_TYPE))
+
+GType gog_error_bar_get_type (void);
+
+GogErrorBar *gog_error_bar_dup (GogErrorBar const *bar);
+
+#ifdef WITH_GTK
+gpointer gog_error_bar_prefs (GogSeries *series, char const* property,
+ gboolean horizontal, GogDataAllocator *dalloc,
+ GnmCmdContext *cc);
+#endif
+
+gboolean gog_error_bar_get_bounds (const GogErrorBar *bar, int index,
+ double *min, double *max);
+void gog_error_bar_get_minmax (const GogErrorBar *bar,
+ double *min, double *max);
+void gog_error_bar_render (const GogErrorBar *bar, GogRenderer *rend,
+ GogAxisMap *x_map, GogAxisMap *y_map,
+ double x, double y,
+ double minus,
+ double plus,
+ gboolean horizontal);
+gboolean gog_error_bar_is_visible (GogErrorBar *bar);
+
+#endif
--- /dev/null
+++ lib/goffice/graph/gog-renderer-impl.h
@@ -0,0 +1,99 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-renderer-impl.h :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#ifndef GOG_RENDERER_IMPL_H
+#define GOG_RENDERER_IMPL_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/utils/goffice-utils.h>
+#include <goffice/utils/go-line.h>
+#include <goffice/graph/gog-renderer.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GogViewAllocation area;
+ gpointer data;
+} GogRendererClip;
+
+struct _GogRenderer {
+ GObject base;
+
+ GogGraph *model;
+ GogView *view;
+ double logical_width_pts;
+ double logical_height_pts;
+ float scale, scale_x, scale_y;
+ float zoom;
+
+ GogRendererClip const *cur_clip;
+ GSList *clip_stack;
+
+ GClosure *font_watcher;
+ gboolean needs_update;
+
+ GogStyle const *cur_style;
+ GSList *style_stack;
+
+ ArtVpathDash *line_dash;
+ ArtVpathDash *outline_dash;
+};
+
+typedef struct {
+ GObjectClass base;
+
+ /* Virtuals */
+ void (*font_removed) (GogRenderer *renderer, GOFont const *font);
+ void (*push_style) (GogRenderer *renderer, GogStyle const *style);
+ void (*pop_style) (GogRenderer *renderer);
+
+ void (*clip_push) (GogRenderer *renderer, GogRendererClip *clip);
+ void (*clip_pop) (GogRenderer *renderer, GogRendererClip *clip);
+
+ void (*sharp_path) (GogRenderer *renderer, ArtVpath *path, double line_width);
+
+ void (*draw_path) (GogRenderer *renderer, ArtVpath const *path,
+ GogViewAllocation const *bound);
+ void (*draw_polygon) (GogRenderer *renderer, ArtVpath const *path, gboolean narrow,
+ GogViewAllocation const *bound);
+ void (*draw_text) (GogRenderer *rend, char const *text,
+ GogViewAllocation const *pos, GtkAnchorType anchor,
+ GogViewAllocation *result);
+ void (*draw_marker) (GogRenderer *rend, double x, double y);
+
+ void (*measure_text) (GogRenderer *rend,
+ char const *text, GogViewRequisition *size);
+
+ double (*line_size) (GogRenderer const *rend, double width);
+
+ /* Signals */
+ void (*request_update) (GogRenderer *renderer);
+} GogRendererClass;
+
+#define GOG_RENDERER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOG_RENDERER_TYPE, GogRendererClass))
+#define IS_GOG_RENDERER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_RENDERER_TYPE))
+
+/* protected */
+void gog_renderer_invalidate_size_requests (GogRenderer *rend);
+
+G_END_DECLS
+
+#endif /* GOG_RENDERER_GROUP_IMPL_H */
--- /dev/null
+++ lib/goffice/graph/gog-plot-engine.c
@@ -0,0 +1,453 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-plot-engine.c :
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include <goffice/graph/gog-plot-engine.h>
+#include <goffice/graph/gog-plot-impl.h>
+#include <goffice/graph/gog-theme.h>
+#include <glib/gi18n.h>
+#include <xml-io.h>
+
+#include <gsf/gsf-impl-utils.h>
+
+#include <string.h>
+
+static GSList *refd_plugins;
+
+/***************************************************************************/
+/* Support plot engines in plugins */
+
+#include <plugin-service-impl.h>
+
+#define GOG_PLOT_ENGINE_SERVICE_TYPE (gog_plot_engine_service_get_type ())
+#define GOG_PLOT_ENGINE_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PLOT_ENGINE_SERVICE_TYPE, GogPlotEngineService))
+#define IS_GOG_PLOT_ENGINE_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PLOT_ENGINE_SERVICE_TYPE))
+
+static GType gog_plot_engine_service_get_type (void);
+
+typedef PluginServiceGObjectLoader GogPlotEngineService;
+typedef PluginServiceGObjectLoaderClass GogPlotEngineServiceClass;
+
+static GHashTable *pending_engines = NULL;
+
+static char *
+gog_plot_engine_service_get_description (GnmPluginService *service)
+{
+ return g_strdup (_("Plot Engine"));
+}
+
+static void
+gog_plot_engine_service_class_init (PluginServiceGObjectLoaderClass *gobj_loader_class)
+{
+ GnmPluginServiceClass *ps_class = GPS_CLASS (gobj_loader_class);
+
+ ps_class->get_description = gog_plot_engine_service_get_description;
+
+ gobj_loader_class->pending =
+ pending_engines = g_hash_table_new (g_str_hash, g_str_equal);
+}
+
+GSF_CLASS (GogPlotEngineService, gog_plot_engine_service,
+ gog_plot_engine_service_class_init, NULL,
+ GNM_PLUGIN_SERVICE_GOBJECT_LOADER_TYPE)
+
+GogPlot *
+gog_plot_new_by_name (char const *id)
+{
+ GType type = g_type_from_name (id);
+
+ if (type == 0) {
+ ErrorInfo *err = NULL;
+ GnmPluginService *service =
+ pending_engines
+ ? g_hash_table_lookup (pending_engines, id)
+ : NULL;
+ GnmPlugin *plugin;
+
+ if (!service || !service->is_active)
+ return NULL;
+
+ g_return_val_if_fail (!service->is_loaded, NULL);
+
+ plugin_service_load (service, &err);
+ type = g_type_from_name (id);
+
+ if (err != NULL) {
+ error_info_print (err);
+ error_info_free (err);
+ }
+
+ g_return_val_if_fail (type != 0, NULL);
+
+ /*
+ * The plugin defined a gtype so it must not be unloaded.
+ */
+ plugin = plugin_service_get_plugin (service);
+ refd_plugins = g_slist_prepend (refd_plugins, plugin);
+ g_object_ref (plugin);
+ gnm_plugin_use_ref (plugin);
+ }
+
+ return g_object_new (type, NULL);
+}
+
+/***************************************************************************/
+/* Use a plugin service to define where to find plot types */
+
+#define GOG_PLOT_TYPE_SERVICE_TYPE (gog_plot_type_service_get_type ())
+#define GOG_PLOT_TYPE_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_PLOT_TYPE_SERVICE_TYPE, GogPlotTypeService))
+#define IS_GOG_PLOT_TYPE_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_PLOT_TYPE_SERVICE_TYPE))
+
+GType gog_plot_type_service_get_type (void);
+
+typedef struct {
+ PluginServiceSimple base;
+
+ GSList *families, *types;
+} GogPlotTypeService;
+
+typedef struct{
+ PluginServiceSimpleClass base;
+} GogPlotTypeServiceClass;
+
+static GHashTable *pending_plot_type_files = NULL;
+static GObjectClass *plot_type_parent_klass;
+
+static void
+cb_pending_plot_types_load (char const *path,
+ GogPlotTypeService *service,
+ G_GNUC_UNUSED gpointer ignored)
+{
+ xmlNode *ptr, *prop;
+ xmlDoc *doc = xmlParseFile (path);
+ GogPlotFamily *family = NULL;
+ GogPlotType *type;
+ int col, row;
+ xmlChar *name, *image_file, *description, *engine;
+
+ g_return_if_fail (doc != NULL && doc->xmlRootNode != NULL);
+
+ /* do the families before the types so that they are available */
+ for (ptr = doc->xmlRootNode->xmlChildrenNode; ptr ; ptr = ptr->next)
+ if (!xmlIsBlankNode (ptr) && ptr->name && !strcmp (ptr->name, "Family")) {
+ name = xmlGetProp (ptr, "_name");
+ image_file = xmlGetProp (ptr, "sample_image_file");
+ family = gog_plot_family_register (name, image_file);
+ if (family != NULL)
+ service->families = g_slist_prepend (service->families, family);
+ if (name != NULL) xmlFree (name);
+ if (image_file != NULL) xmlFree (image_file);
+ }
+
+ for (ptr = doc->xmlRootNode->xmlChildrenNode; ptr ; ptr = ptr->next)
+ if (!xmlIsBlankNode (ptr) && ptr->name && !strcmp (ptr->name, "Type")) {
+ name = xmlGetProp (ptr, "family");
+ if (name != NULL) {
+ family = gog_plot_family_by_name (name);
+ xmlFree (name);
+ if (family == NULL)
+ continue;
+ }
+
+ name = xmlGetProp (ptr, "_name");
+ image_file = xmlGetProp (ptr, "sample_image_file");
+ description = xmlGetProp (ptr, "_description");
+ engine = xmlGetProp (ptr, "engine");
+ if (xml_node_get_int (ptr, "col", &col) &&
+ xml_node_get_int (ptr, "row", &row)) {
+ type = gog_plot_type_register (family, col, row,
+ name, image_file, description, engine);
+ if (type != NULL) {
+ service->types = g_slist_prepend (service->types, type);
+ for (prop = ptr->xmlChildrenNode ; prop != NULL ; prop = prop->next)
+ if (!xmlIsBlankNode (prop) &&
+ prop->name && !strcmp (prop->name, "property")) {
+ xmlChar *prop_name = xmlGetProp (prop, "name");
+
+ if (prop_name == NULL) {
+ g_warning ("missing name for property entry");
+ continue;
+ }
+
+ if (type->properties == NULL)
+ type->properties = g_hash_table_new_full (g_str_hash, g_str_equal,
+ xmlFree, xmlFree);
+ g_hash_table_replace (type->properties,
+ prop_name, xmlNodeGetContent (prop));
+ }
+ }
+ }
+ if (name != NULL) xmlFree (name);
+ if (image_file != NULL) xmlFree (image_file);
+ if (description != NULL) xmlFree (description);
+ if (engine != NULL) xmlFree (engine);
+ }
+
+ xmlFreeDoc (doc);
+}
+
+static void
+pending_plot_types_load (void)
+{
+ if (pending_plot_type_files != NULL) {
+ GHashTable *tmp = pending_plot_type_files;
+ pending_plot_type_files = NULL;
+ g_hash_table_foreach (tmp,
+ (GHFunc) cb_pending_plot_types_load, NULL);
+ g_hash_table_destroy (tmp);
+ }
+}
+
+static void
+gog_plot_type_service_read_xml (GnmPluginService *service, xmlNode *tree, ErrorInfo **ret_error)
+{
+ char *path;
+ xmlNode *ptr;
+
+ for (ptr = tree->xmlChildrenNode; ptr != NULL; ptr = ptr->next)
+ if (0 == xmlStrcmp (ptr->name, "file") &&
+ NULL != (path = xmlNodeGetContent (ptr))) {
+ if (!g_path_is_absolute (path)) {
+ char const *dir = gnm_plugin_get_dir_name (
+ plugin_service_get_plugin (service));
+ char *tmp = g_build_filename (dir, path, NULL);
+ g_free (path);
+ path = tmp;
+ }
+ if (pending_plot_type_files == NULL)
+ pending_plot_type_files = g_hash_table_new_full (
+ g_str_hash, g_str_equal, g_free, g_object_unref);
+ g_object_ref (service);
+ g_hash_table_replace (pending_plot_type_files, path, service);
+ }
+}
+
+static char *
+gog_plot_type_service_get_description (GnmPluginService *service)
+{
+ return g_strdup (_("Plot Type"));
+}
+
+static void
+gog_plot_type_service_finalize (GObject *obj)
+{
+ GogPlotTypeService *service = GOG_PLOT_TYPE_SERVICE (obj);
+ GSList *ptr;
+
+ for (ptr = service->families ; ptr != NULL ; ptr = ptr->next) {
+ }
+ g_slist_free (service->families);
+ service->families = NULL;
+
+ for (ptr = service->types ; ptr != NULL ; ptr = ptr->next) {
+ }
+ g_slist_free (service->types);
+ service->types = NULL;
+
+ (plot_type_parent_klass->finalize) (obj);
+}
+
+static void
+gog_plot_type_service_init (GObject *obj)
+{
+ GogPlotTypeService *service = GOG_PLOT_TYPE_SERVICE (obj);
+
+ service->families = NULL;
+ service->types = NULL;
+}
+
+static void
+gog_plot_type_service_class_init (GObjectClass *gobject_klass)
+{
+ GnmPluginServiceClass *ps_class = GPS_CLASS (gobject_klass);
+
+ plot_type_parent_klass = g_type_class_peek_parent (gobject_klass);
+ gobject_klass->finalize = gog_plot_type_service_finalize;
+ ps_class->read_xml = gog_plot_type_service_read_xml;
+ ps_class->get_description = gog_plot_type_service_get_description;
+}
+
+GSF_CLASS (GogPlotTypeService, gog_plot_type_service,
+ gog_plot_type_service_class_init, gog_plot_type_service_init,
+ GNM_PLUGIN_SERVICE_SIMPLE_TYPE)
+
+/***************************************************************************/
+/* Use a plugin service to define themes */
+
+#define GOG_THEME_SERVICE_TYPE (gog_theme_service_get_type ())
+#define GOG_THEME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_THEME_SERVICE_TYPE, GogThemeService))
+#define IS_GOG_THEME_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_THEME_SERVICE_TYPE))
+
+GType gog_theme_service_get_type (void);
+
+typedef PluginServiceSimple GogThemeService;
+typedef PluginServiceSimpleClass GogThemeServiceClass;
+
+static void
+gog_theme_service_read_xml (GnmPluginService *service, xmlNode *tree, ErrorInfo **ret_error)
+{
+ char *path;
+ xmlNode *ptr;
+
+ for (ptr = tree->xmlChildrenNode; ptr != NULL; ptr = ptr->next)
+ if (0 == xmlStrcmp (ptr->name, "file") &&
+ NULL != (path = xmlNodeGetContent (ptr))) {
+ if (!g_path_is_absolute (path)) {
+ char const *dir = gnm_plugin_get_dir_name (
+ plugin_service_get_plugin (service));
+ char *tmp = g_build_filename (dir, path, NULL);
+ g_free (path);
+ path = tmp;
+ }
+ gog_theme_register_file (
+ plugin_service_get_description (service), path);
+ }
+}
+
+static char *
+gog_theme_service_get_description (GnmPluginService *service)
+{
+ return g_strdup (_("Chart Theme"));
+}
+
+static void
+gog_theme_service_class_init (GnmPluginServiceClass *ps_class)
+{
+ ps_class->read_xml = gog_theme_service_read_xml;
+ ps_class->get_description = gog_theme_service_get_description;
+}
+
+GSF_CLASS (GogThemeService, gog_theme_service,
+ gog_theme_service_class_init, NULL,
+ GNM_PLUGIN_SERVICE_SIMPLE_TYPE)
+
+/***************************************************************************/
+
+void
+gog_plugin_services_init (void)
+{
+ plugin_service_define ("plot_engine", &gog_plot_engine_service_get_type);
+ plugin_service_define ("plot_type", &gog_plot_type_service_get_type);
+ plugin_service_define ("chart_theme", &gog_theme_service_get_type);
+}
+
+void
+gog_plugin_services_shutdown (void)
+{
+ g_slist_foreach (refd_plugins, (GFunc)gnm_plugin_use_unref, NULL);
+ g_slist_foreach (refd_plugins, (GFunc)g_object_unref, NULL);
+ g_slist_free (refd_plugins);
+}
+
+/***************************************************************************/
+
+static GHashTable *plot_families = NULL;
+
+static void
+gog_plot_family_free (GogPlotFamily *family)
+{
+ g_free (family->name); family->name = NULL;
+ g_free (family->sample_image_file); family->sample_image_file = NULL;
+ if (family->types) {
+ g_hash_table_destroy (family->types);
+ family->types = NULL;
+ }
+ g_free (family);
+}
+
+static void
+gog_plot_type_free (GogPlotType *type)
+{
+ g_free (type->name);
+ g_free (type->sample_image_file);
+ g_free (type->description);
+ g_free (type->engine);
+ g_free (type);
+}
+
+static void
+create_plot_families (void)
+{
+ if (!plot_families)
+ plot_families = g_hash_table_new_full
+ (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) gog_plot_family_free);
+}
+
+GHashTable const *
+gog_plot_families (void)
+{
+ create_plot_families ();
+ pending_plot_types_load ();
+ return plot_families;
+}
+GogPlotFamily *
+gog_plot_family_by_name (char const *name)
+{
+ create_plot_families ();
+ pending_plot_types_load ();
+ return g_hash_table_lookup (plot_families, name);
+}
+
+GogPlotFamily *
+gog_plot_family_register (char const *name, char const *sample_image_file)
+{
+ GogPlotFamily *res;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (sample_image_file != NULL, NULL);
+
+ create_plot_families ();
+ g_return_val_if_fail (g_hash_table_lookup (plot_families, name) == NULL, NULL);
+
+ res = g_new0 (GogPlotFamily, 1);
+ res->name = g_strdup (name);
+ res->sample_image_file = g_strdup (sample_image_file);
+ res->types = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) gog_plot_type_free);
+ g_hash_table_insert (plot_families, res->name, res);
+
+ return res;
+}
+
+GogPlotType *
+gog_plot_type_register (GogPlotFamily *family, int col, int row,
+ char const *name, char const *sample_image_file,
+ char const *description, char const *engine)
+{
+ GogPlotType *res;
+
+ g_return_val_if_fail (family != NULL, NULL);
+
+ res = g_new0 (GogPlotType, 1);
+ res->name = g_strdup (name);
+ res->sample_image_file = g_strdup (sample_image_file);
+ res->description = g_strdup (description);
+ res->engine = g_strdup (engine);
+
+ res->col = col;
+ res->row = row;
+ res->family = family;
+ g_hash_table_replace (family->types, res->name, res);
+
+ return res;
+}
+
--- /dev/null
+++ lib/goffice/graph/gog-outlined-object.h
@@ -0,0 +1,61 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gog-outlined-object.h : some utility classes for objects with outlines.
+ *
+ * Copyright (C) 2003-2004 Jody Goldberg (jody at gnome.org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#ifndef GOG_OUTLINED_OBJECT_H
+#define GOG_OUTLINED_OBJECT_H
+
+#include <goffice/graph/goffice-graph.h>
+#include <goffice/graph/gog-styled-object.h>
+#include <goffice/graph/gog-view.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ GogStyledObject base;
+ double padding_pts;
+} GogOutlinedObject;
+
+typedef GogStyledObjectClass GogOutlinedObjectClass;
+
+#define GOG_OUTLINED_OBJECT_TYPE (gog_outlined_object_get_type ())
+#define GOG_OUTLINED_OBJECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_OUTLINED_OBJECT_TYPE, GogOutlinedObject))
+#define IS_GOG_OUTLINED_OBJECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_OUTLINED_OBJECT_TYPE))
+
+GType gog_outlined_object_get_type (void);
+double gog_outlined_object_get_pad (GogOutlinedObject const *goo);
+
+/****************************************************************************/
+
+typedef GogView GogOutlinedView;
+typedef struct {
+ GogViewClass base;
+ gboolean call_parent_render;
+} GogOutlinedViewClass;
+
+#define GOG_OUTLINED_VIEW_TYPE (gog_outlined_view_get_type ())
+#define GOG_OUTLINED_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_OUTLINED_VIEW_TYPE, GogOutlinedView))
+#define IS_GOG_OUTLINED_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_OUTLINED_VIEW_TYPE))
+#define GOG_OUTLINED_VIEW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_OUTLINED_VIEW_TYPE, GogOutlinedViewClass))
+
+GType gog_outlined_view_get_type (void);
+
+G_END_DECLS
+
+#endif /* GOG_OUTLINED_VIEW_H */
--- /dev/null
+++ lib/goffice/graph/Makefile.am
@@ -0,0 +1,93 @@
+SUBDIRS = plugins
+
+# SUBDIRS += tests
+## Don't put comment on an assignment line! It can trigger an automake bug.
+
+noinst_LTLIBRARIES = libgoffice-graph.la
+
+AM_CFLAGS = ${GLIB_CFLAGS} ${GSF_CFLAGS} ${ART_CFLAGS} ${GLADE_CFLAGS} ${GNOME_CFLAGS} ${PRINT_CFLAGS}
+
+libgoffice_graph_la_SOURCES = \
+ goffice-graph.h \
+ gog-object.c \
+ gog-object.h \
+ gog-object-xml.c \
+ gog-object-xml.h \
+ gog-styled-object.c \
+ gog-styled-object.h \
+ gog-outlined-object.c \
+ gog-outlined-object.h \
+ gog-view.c \
+ gog-view.h \
+ \
+ gog-graph.c \
+ gog-graph.h \
+ gog-graph-impl.h \
+ gog-chart.c \
+ gog-chart.h \
+ gog-chart-impl.h \
+ \
+ gog-axis.c \
+ gog-axis.h \
+ gog-legend.c \
+ gog-legend.h \
+ gog-label.c \
+ gog-label.h \
+ gog-grid.c \
+ gog-grid.h \
+ gog-grid-line.c \
+ gog-grid-line.h \
+ \
+ gog-style.c \
+ gog-style.h \
+ gog-theme.c \
+ gog-theme.h \
+ \
+ gog-plot.c \
+ gog-plot.h \
+ gog-plot-impl.h \
+ gog-plot-engine.h \
+ gog-plot-engine.c \
+ gog-series.c \
+ gog-series.h \
+ gog-series-impl.h \
+ gog-error-bar.c \
+ gog-error-bar.h \
+ gog-data-allocator.c \
+ gog-data-allocator.h \
+ gog-data-set.c \
+ gog-data-set.h \
+ \
+ go-data.c \
+ go-data.h \
+ go-data-impl.h \
+ go-data-simple.c \
+ go-data-simple.h \
+ \
+ gog-renderer.c \
+ gog-renderer.h \
+ gog-renderer-impl.h \
+ gog-renderer-pixbuf.c \
+ gog-renderer-pixbuf.h \
+ gog-renderer-gnome-print.c \
+ gog-renderer-gnome-print.h \
+ gog-renderer-svg.c \
+ gog-renderer-svg.h
+
+#
+# gog-guru.c \
+# gog-guru.h \
+# gog-control-foocanvas.c \
+# gog-control-foocanvas.h \
+#
+
+#graph_glade_DATA = \
+# gog-guru.glade \
+# gog-guru-type-selector.glade \
+# gog-style-prefs.glade \
+# gog-axis-prefs.glade \
+# gog-error-bar-prefs.glade
+
+# EXTRA_DIST = $(graph_glade_DATA)
+
+include $(srcdir)/../goffice.mk
--- /dev/null
+++ lib/goffice/graph/plugins/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = plot_pie plot_barcol plot_xy plot_radar plot_surface
--- /dev/null
+++ lib/goffice/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, ¶graph, 1);
+ }
+ g_free (paras);
+}
+
+static void
+real_god_text_model_set_paragraph_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GodParagraphAttributes *attributes)
+{
+ guint i;
+ int count = 0;
+ if (text->priv->paragraphs) {
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ int length = strlen (PARAGRAPH(i)->text);
+ if (count >= end)
+ return;
+ if (count + length + 1 > start) {
+ if (PARAGRAPH(i)->para_attributes)
+ g_object_unref (PARAGRAPH(i)->para_attributes);
+ PARAGRAPH(i)->para_attributes = attributes;
+ if (PARAGRAPH(i)->para_attributes)
+ g_object_ref (PARAGRAPH(i)->para_attributes);
+ }
+ count += length + 1;
+ }
+ }
+}
+
+static void
+real_god_text_model_set_pango_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GList *attributes)
+{
+ guint i;
+ int count = 0;
+ if (start == end)
+ return;
+ if (text->priv->paragraphs) {
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ int length = strlen (PARAGRAPH(i)->text);
+ if (length == 0)
+ continue;
+ if (count >= end)
+ return;
+ if (count + length >= start) {
+ int thisstart = MAX (start, count) - count;
+ int thisend = MIN (end, count + length) - count;
+ GList *iterator;
+
+ if (thisstart == thisend)
+ continue;
+
+ if (!PARAGRAPH(i)->char_attributes)
+ PARAGRAPH(i)->char_attributes = pango_attr_list_new ();
+
+ for (iterator = attributes; iterator; iterator = iterator->next) {
+ PangoAttribute *new_attr = pango_attribute_copy (iterator->data);
+ new_attr->start_index = thisstart;
+ new_attr->end_index = thisend;
+ pango_attr_list_insert (PARAGRAPH(i)->char_attributes, new_attr);
+ }
+ }
+ count += length + 1;
+ }
+ }
+}
+
+static void
+real_god_text_model_set_indent (GodTextModel *text,
+ int start,
+ int end,
+ int indent)
+{
+ guint i;
+ int count = 0;
+ if (text->priv->paragraphs) {
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ int length = strlen (PARAGRAPH(i)->text);
+ if (count >= end)
+ return;
+ if (count + length + 1 > start) {
+ PARAGRAPH(i)->indent = indent;
+ }
+ count += length + 1;
+ }
+ }
+}
+
+static void
+real_god_text_model_paragraph_foreach (GodTextModel *text,
+ GodTextModelParagraphForeachCallback callback,
+ gpointer user_data)
+{
+ guint i;
+
+ if (callback == NULL)
+ return;
+ if (text->priv->paragraphs) {
+ for (i = 0; i < text->priv->paragraphs->len; i++) {
+ callback (text, PARAGRAPH(i), user_data);
+ }
+ }
+}
+
+static void
+god_text_model_class_init (GodTextModelClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_text_model_finalize;
+
+ class->get_text = real_god_text_model_get_text;
+ class->set_text = real_god_text_model_set_text;
+
+ class->set_paragraph_attributes = real_god_text_model_set_paragraph_attributes;
+ class->set_pango_attributes = real_god_text_model_set_pango_attributes;
+ class->set_indent = real_god_text_model_set_indent;
+ class->get_default_attributes = NULL;
+ class->paragraph_foreach = real_god_text_model_paragraph_foreach;
+}
+
+GSF_CLASS (GodTextModel, god_text_model,
+ god_text_model_class_init, god_text_model_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-default-attributes.h
@@ -0,0 +1,63 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-default-attributes.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_DEFAULT_ATTRIBUTES_H
+#define GOD_DEFAULT_ATTRIBUTES_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-paragraph-attributes.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DEFAULT_ATTRIBUTES_TYPE (god_default_attributes_get_type ())
+#define GOD_DEFAULT_ATTRIBUTES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DEFAULT_ATTRIBUTES_TYPE, GodDefaultAttributes))
+#define GOD_DEFAULT_ATTRIBUTES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DEFAULT_ATTRIBUTES_TYPE, GodDefaultAttributesClass))
+#define GOD_DEFAULT_ATTRIBUTES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_DEFAULT_ATTRIBUTES_TYPE, GodDefaultAttributesClass))
+#define IS_GOD_DEFAULT_ATTRIBUTES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DEFAULT_ATTRIBUTES_TYPE))
+#define IS_GOD_DEFAULT_ATTRIBUTES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DEFAULT_ATTRIBUTES_TYPE))
+
+typedef struct GodDefaultAttributesPrivate_ GodDefaultAttributesPrivate;
+
+typedef struct {
+ GObject parent;
+ GodDefaultAttributesPrivate *priv;
+} GodDefaultAttributes;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodDefaultAttributesClass;
+
+GType god_default_attributes_get_type (void);
+GodDefaultAttributes *god_default_attributes_new (void);
+
+void god_default_attributes_set_paragraph_attributes (GodDefaultAttributes *attributes,
+ GodParagraphAttributes *paragraph_attributes);
+void god_default_attributes_set_paragraph_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent,
+ GodParagraphAttributes *paragraph_attributes);
+void god_default_attributes_set_pango_attributes (GodDefaultAttributes *attributes,
+ GList *pango_attributes);
+void god_default_attributes_set_pango_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent,
+ GList *pango_attributes);
+
+const GodParagraphAttributes *god_default_attributes_get_paragraph_attributes (GodDefaultAttributes *attributes);
+const GodParagraphAttributes *god_default_attributes_get_paragraph_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent);
+const GList *god_default_attributes_get_pango_attributes (GodDefaultAttributes *attributes);
+const GList *god_default_attributes_get_pango_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent);
+
+
+G_END_DECLS
+
+#endif /* GOD_DEFAULT_ATTRIBUTES_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-renderer-gdk.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *//* vim: set sw=8: */
+#ifndef GOD_DRAWING_RENDERER_GDK_H
+#define GOD_DRAWING_RENDERER_GDK_H
+
+/**
+ * god-drawing-renderer-gdk.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-drawing.h>
+#include <drawing/god-anchor.h>
+#include <gdk/gdkdrawable.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DRAWING_RENDERER_GDK_TYPE (god_drawing_renderer_gdk_get_type ())
+#define GOD_DRAWING_RENDERER_GDK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DRAWING_RENDERER_GDK_TYPE, GodDrawingRendererGdk))
+#define GOD_DRAWING_RENDERER_GDK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DRAWING_RENDERER_GDK_TYPE, GodDrawingRendererGdkClass))
+#define IS_GOD_DRAWING_RENDERER_GDK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DRAWING_RENDERER_GDK_TYPE))
+#define IS_GOD_DRAWING_RENDERER_GDK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DRAWING_RENDERER_GDK_TYPE))
+
+typedef struct GodDrawingRendererGdkPrivate_ GodDrawingRendererGdkPrivate;
+
+typedef struct {
+ GObject parent;
+ GodDrawingRendererGdkPrivate *priv;
+} GodDrawingRendererGdk;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodDrawingRendererGdkClass;
+
+GType god_drawing_renderer_gdk_get_type (void);
+GodDrawingRendererGdk *god_drawing_renderer_gdk_new (void);
+
+/* Return value is reffed. */
+GodDrawing *god_drawing_renderer_gdk_get_drawing (GodDrawingRendererGdk *renderer);
+void god_drawing_renderer_gdk_set_drawing (GodDrawingRendererGdk *renderer,
+ GodDrawing *drawing);
+
+/* Return value is reffed. */
+GdkDrawable *god_drawing_renderer_gdk_get_drawable (GodDrawingRendererGdk *renderer);
+void god_drawing_renderer_gdk_set_drawable (GodDrawingRendererGdk *renderer,
+ GdkDrawable *drawable);
+
+/* Return value is reffed. */
+GdkGC *god_drawing_renderer_gdk_get_gc (GodDrawingRendererGdk *renderer);
+void god_drawing_renderer_gdk_set_gc (GodDrawingRendererGdk *renderer,
+ GdkGC *gc);
+
+/* Return value is reffed. */
+GodAnchor *god_drawing_renderer_gdk_get_extents (GodDrawingRendererGdk *renderer);
+void god_drawing_renderer_gdk_set_extents (GodDrawingRendererGdk *renderer,
+ GodAnchor *extents);
+void god_drawing_renderer_gdk_render (GodDrawingRendererGdk *renderer,
+ GdkRectangle *area);
+
+G_END_DECLS
+
+#endif /* GOD_DRAWING_RENDERER_GDK_H */
--- /dev/null
+++ lib/goffice/drawing/god-default-attributes.c
@@ -0,0 +1,201 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-default-attributes.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-default-attributes.h"
+
+#include <pango/pango-attributes.h>
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+static GObjectClass *parent_class;
+
+struct GodDefaultAttributesPrivate_ {
+ GPtrArray *paragraph_attributes;
+ GPtrArray *pango_attributes;
+};
+
+GodDefaultAttributes *
+god_default_attributes_new (void)
+{
+ GodDefaultAttributes *default_attributes;
+
+ default_attributes = g_object_new (GOD_DEFAULT_ATTRIBUTES_TYPE, NULL);
+
+ return default_attributes;
+}
+
+void
+god_default_attributes_set_paragraph_attributes (GodDefaultAttributes *attributes,
+ GodParagraphAttributes *paragraph_attributes)
+{
+ god_default_attributes_set_paragraph_attributes_for_indent (attributes,
+ 0,
+ paragraph_attributes);
+}
+
+void
+god_default_attributes_set_paragraph_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent,
+ GodParagraphAttributes *paragraph_attributes)
+{
+ if (attributes->priv->paragraph_attributes == NULL)
+ attributes->priv->paragraph_attributes = g_ptr_array_new();
+ if (attributes->priv->paragraph_attributes->len <= indent)
+ g_ptr_array_set_size (attributes->priv->paragraph_attributes, indent + 1);
+ if (g_ptr_array_index (attributes->priv->paragraph_attributes, indent))
+ g_object_unref (g_ptr_array_index (attributes->priv->paragraph_attributes, indent));
+ g_ptr_array_index (attributes->priv->paragraph_attributes, indent) = paragraph_attributes;
+ if (paragraph_attributes)
+ g_object_ref (paragraph_attributes);
+}
+
+void
+god_default_attributes_set_pango_attributes (GodDefaultAttributes *attributes,
+ GList *pango_attributes)
+{
+ god_default_attributes_set_pango_attributes_for_indent (attributes,
+ 0,
+ pango_attributes);
+}
+
+static void
+add_attributes (PangoAttribute *attribute, GList **new_attributes)
+{
+ PangoAttribute *new_attribute = pango_attribute_copy (attribute);
+ new_attribute->start_index = 0;
+ new_attribute->end_index = (guint) -1;
+ *new_attributes = g_list_prepend (*new_attributes, pango_attribute_copy (attribute));
+}
+
+void
+god_default_attributes_set_pango_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent,
+ GList *pango_attributes)
+{
+ GList **pango_attributes_location;
+ if (attributes->priv->pango_attributes == NULL)
+ attributes->priv->pango_attributes = g_ptr_array_new();
+ if (attributes->priv->pango_attributes->len <= indent)
+ g_ptr_array_set_size (attributes->priv->pango_attributes, indent + 1);
+
+ pango_attributes_location = (GList **) &g_ptr_array_index (attributes->priv->pango_attributes, indent);
+
+ g_list_foreach (*pango_attributes_location, (GFunc) pango_attribute_destroy, NULL);
+ g_list_free (*pango_attributes_location);
+ *pango_attributes_location = NULL;
+ g_list_foreach (pango_attributes, (GFunc) add_attributes, pango_attributes_location);
+ *pango_attributes_location = g_list_reverse (*pango_attributes_location);
+}
+
+const GodParagraphAttributes *
+god_default_attributes_get_paragraph_attributes (GodDefaultAttributes *attributes)
+{
+ return god_default_attributes_get_paragraph_attributes_for_indent (attributes,
+ 0);
+}
+
+const GodParagraphAttributes *
+god_default_attributes_get_paragraph_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent)
+{
+ if (attributes->priv->paragraph_attributes == NULL)
+ return NULL;
+ if (attributes->priv->paragraph_attributes->len <= indent)
+ return NULL;
+ return g_ptr_array_index (attributes->priv->paragraph_attributes, indent);
+}
+
+const GList *
+god_default_attributes_get_pango_attributes (GodDefaultAttributes *attributes)
+{
+ return god_default_attributes_get_pango_attributes_for_indent (attributes,
+ 0);
+}
+
+const GList *
+god_default_attributes_get_pango_attributes_for_indent (GodDefaultAttributes *attributes,
+ guint indent)
+{
+ if (attributes->priv->pango_attributes == NULL)
+ return NULL;
+ if (attributes->priv->pango_attributes->len <= indent)
+ return NULL;
+ return g_ptr_array_index (attributes->priv->pango_attributes, indent);
+}
+
+static void
+god_default_attributes_init (GObject *object)
+{
+ GodDefaultAttributes *default_attributes = GOD_DEFAULT_ATTRIBUTES (object);
+ default_attributes->priv = g_new0 (GodDefaultAttributesPrivate, 1);
+ default_attributes->priv->paragraph_attributes = NULL;
+ default_attributes->priv->pango_attributes = NULL;
+}
+
+static void
+maybe_unref (gpointer data, gpointer user_data)
+{
+ if (data)
+ g_object_unref (data);
+}
+
+static void
+free_list (gpointer data, gpointer user_data)
+{
+ g_list_foreach (data, (GFunc) pango_attribute_destroy, NULL);
+}
+
+static void
+god_default_attributes_finalize (GObject *object)
+{
+ GodDefaultAttributes *default_attributes = GOD_DEFAULT_ATTRIBUTES (object);
+
+ g_ptr_array_foreach (default_attributes->priv->paragraph_attributes, maybe_unref, NULL);
+ g_ptr_array_foreach (default_attributes->priv->pango_attributes, free_list, NULL);
+ g_ptr_array_free (default_attributes->priv->paragraph_attributes, TRUE);
+ g_ptr_array_free (default_attributes->priv->pango_attributes, TRUE);
+ g_free (default_attributes->priv);
+ default_attributes->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+god_default_attributes_class_init (GodDefaultAttributesClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_default_attributes_finalize;
+}
+
+GSF_CLASS (GodDefaultAttributes, god_default_attributes,
+ god_default_attributes_class_init, god_default_attributes_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-drawing-view.c
@@ -0,0 +1,142 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-drawing-view.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-drawing-view.h"
+#include <gsf/gsf-impl-utils.h>
+#include <drawing/god-drawing-renderer-gdk.h>
+
+static GObjectClass *parent_class;
+
+struct GodDrawingViewPrivate_ {
+ GodDrawingRendererGdk *renderer;
+};
+
+GodDrawingView *
+god_drawing_view_new (void)
+{
+ GodDrawingView *view;
+
+ view = g_object_new (GOD_DRAWING_VIEW_TYPE, NULL);
+
+ return view;
+}
+
+GodDrawing *
+god_drawing_view_get_drawing (GodDrawingView *view)
+{
+ return god_drawing_renderer_gdk_get_drawing (view->priv->renderer);
+}
+
+void
+god_drawing_view_set_drawing (GodDrawingView *view,
+ GodDrawing *drawing)
+{
+ god_drawing_renderer_gdk_set_drawing (view->priv->renderer, drawing);
+ gtk_widget_queue_draw (GTK_WIDGET (view));
+}
+
+GodAnchor *
+god_drawing_view_get_extents (GodDrawingView *view)
+{
+ return god_drawing_renderer_gdk_get_extents (view->priv->renderer);
+}
+
+void
+god_drawing_view_set_extents (GodDrawingView *view,
+ GodAnchor *extents)
+{
+ god_drawing_renderer_gdk_set_extents (view->priv->renderer, extents);
+ gtk_widget_queue_draw (GTK_WIDGET (view));
+}
+
+static void
+god_drawing_view_init (GObject *object)
+{
+ GodDrawingView *view = GOD_DRAWING_VIEW (object);
+
+ view->priv = g_new0 (GodDrawingViewPrivate, 1);
+ view->priv->renderer = god_drawing_renderer_gdk_new ();
+}
+
+static void
+god_drawing_view_realize (GtkWidget *widget)
+{
+ GodDrawingView *view = GOD_DRAWING_VIEW (widget);
+
+ GTK_WIDGET_CLASS(parent_class)->realize (widget);
+
+ god_drawing_renderer_gdk_set_drawable (view->priv->renderer,
+ widget->window);
+ god_drawing_renderer_gdk_set_gc (view->priv->renderer,
+ widget->style->fg_gc[0]);
+}
+
+static gboolean
+god_drawing_view_expose_event (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ GodDrawingView *view = GOD_DRAWING_VIEW (widget);
+ god_drawing_renderer_gdk_render (view->priv->renderer,
+ &event->area);
+ return TRUE;
+}
+
+static void
+god_drawing_view_dispose (GObject *object)
+{
+ GodDrawingView *view = GOD_DRAWING_VIEW (object);
+
+ if (view->priv == NULL)
+ return;
+
+ if (view->priv->renderer)
+ g_object_unref (view->priv->renderer);
+ g_free (view->priv);
+ view->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_drawing_view_class_init (GodDrawingViewClass *class)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GObjectClass *) class;
+ widget_class = (GtkWidgetClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_drawing_view_dispose;
+
+ widget_class->realize = god_drawing_view_realize;
+ widget_class->expose_event = god_drawing_view_expose_event;
+}
+
+GSF_CLASS (GodDrawingView, god_drawing_view,
+ god_drawing_view_class_init, god_drawing_view_init,
+ GTK_TYPE_DRAWING_AREA)
--- /dev/null
+++ lib/goffice/drawing/god-text-model.h
@@ -0,0 +1,98 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-text-model.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#ifndef GOD_TEXT_MODEL_H
+#define GOD_TEXT_MODEL_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-paragraph-attributes.h>
+#include <drawing/god-default-attributes.h>
+#include <pango/pango-attributes.h>
+
+G_BEGIN_DECLS
+
+#define GOD_TEXT_MODEL_TYPE (god_text_model_get_type ())
+#define GOD_TEXT_MODEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_TEXT_MODEL_TYPE, GodTextModel))
+#define GOD_TEXT_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_TEXT_MODEL_TYPE, GodTextModelClass))
+#define GOD_TEXT_MODEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_TEXT_MODEL_TYPE, GodTextModelClass))
+#define IS_GOD_TEXT_MODEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_TEXT_MODEL_TYPE))
+#define IS_GOD_TEXT_MODEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_TEXT_MODEL_TYPE))
+
+typedef struct GodTextModelPrivate GodTextModelPrivate;
+typedef struct GodTextModel GodTextModel;
+typedef struct GodTextModelClass GodTextModelClass;
+
+typedef struct {
+ char *text;
+ PangoAttrList *char_attributes;
+ GodParagraphAttributes *para_attributes;
+ int indent;
+} GodTextModelParagraph;
+
+typedef void (*GodTextModelParagraphForeachCallback) (GodTextModel *text,
+ GodTextModelParagraph *paragraph,
+ gpointer user_data);
+
+struct GodTextModel {
+ GObject parent;
+ GodTextModelPrivate *priv;
+};
+
+struct GodTextModelClass {
+ GObjectClass parent_class;
+
+ const char *(*get_text) (GodTextModel *text);
+ void (*set_text) (GodTextModel *text, const char *text_value);
+ void (*set_paragraph_attributes) (GodTextModel *text, int start, int end, GodParagraphAttributes *attributes);
+ void (*set_pango_attributes) (GodTextModel *text, int start, int end, GList *attributes);
+ void (*set_indent) (GodTextModel *text, int start, int end, int indent);
+ const GodDefaultAttributes * (*get_default_attributes) (GodTextModel *text);
+ void (*paragraph_foreach) (GodTextModel *text, GodTextModelParagraphForeachCallback callback, gpointer user_data);
+};
+
+GType god_text_model_get_type (void);
+
+/* Set routines*/
+GodTextModel *god_text_model_new (void);
+void god_text_model_set_text (GodTextModel *text,
+ const char *text_value);
+#if 0
+void *god_text_model_append_paragraph (GodTextModel *text,
+ PangoLayout *layout,
+ GodParagraphAttributes *para_attr);
+#endif
+void god_text_model_set_paragraph_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GodParagraphAttributes *para_attr);
+void god_text_model_set_pango_attributes (GodTextModel *text,
+ int start,
+ int end,
+ GList *char_attrs);
+void god_text_model_set_indent (GodTextModel *text,
+ int start,
+ int end,
+ int indent);
+const GodDefaultAttributes *god_text_model_get_default_attributes (GodTextModel *text);
+
+/* Get routines */
+const char *god_text_model_get_text (GodTextModel *text);
+int god_text_model_get_length (GodTextModel *text);
+void god_text_model_paragraph_foreach (GodTextModel *text,
+ GodTextModelParagraphForeachCallback callback,
+ gpointer user_data);
+
+
+G_END_DECLS
+
+#endif /* GOD_TEXT_MODEL_H */
--- /dev/null
+++ lib/goffice/drawing/god-anchor.c
@@ -0,0 +1,122 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-anchor.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-anchor.h"
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodAnchorPrivate_ {
+ GoRect rect;
+};
+
+GodAnchor *
+god_anchor_new (void)
+{
+ GodAnchor *anchor;
+
+ anchor = g_object_new (GOD_ANCHOR_TYPE, NULL);
+
+ return anchor;
+}
+
+void
+god_anchor_get_rect (GodAnchor *anchor,
+ GoRect *rect)
+{
+ if (GOD_ANCHOR_GET_CLASS (anchor)->get_rect) {
+ GOD_ANCHOR_GET_CLASS (anchor)->get_rect (anchor, rect);
+ } else {
+ rect->top = 0;
+ rect->left = 0;
+ rect->bottom = 0;
+ rect->right = 0;
+ }
+}
+
+void
+god_anchor_set_rect (GodAnchor *anchor,
+ const GoRect *rect)
+{
+ if (GOD_ANCHOR_GET_CLASS (anchor)->set_rect)
+ GOD_ANCHOR_GET_CLASS (anchor)->set_rect (anchor, rect);
+}
+
+static void
+god_anchor_init (GObject *object)
+{
+ GodAnchor *anchor = GOD_ANCHOR (object);
+ anchor->priv = g_new0 (GodAnchorPrivate, 1);
+ anchor->priv->rect.top = 0.0;
+ anchor->priv->rect.left = 0.0;
+ anchor->priv->rect.bottom = 0.0;
+ anchor->priv->rect.right = 0.0;
+}
+
+static void
+god_anchor_finalize (GObject *object)
+{
+ GodAnchor *anchor = GOD_ANCHOR (object);
+
+ g_free (anchor->priv);
+ anchor->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+real_god_anchor_get_rect (GodAnchor *anchor,
+ GoRect *rect)
+{
+ *rect = anchor->priv->rect;
+}
+
+static void
+real_god_anchor_set_rect (GodAnchor *anchor,
+ const GoRect *rect)
+{
+ anchor->priv->rect = *rect;
+}
+
+static void
+god_anchor_class_init (GodAnchorClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_anchor_finalize;
+
+ class->get_rect = real_god_anchor_get_rect;
+ class->set_rect = real_god_anchor_set_rect;
+}
+
+GSF_CLASS (GodAnchor, god_anchor,
+ god_anchor_class_init, god_anchor_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-anchor.h
@@ -0,0 +1,55 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-anchor.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#ifndef GOD_ANCHOR_H
+#define GOD_ANCHOR_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <utils/go-units.h>
+
+G_BEGIN_DECLS
+
+#define GOD_ANCHOR_TYPE (god_anchor_get_type ())
+#define GOD_ANCHOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_ANCHOR_TYPE, GodAnchor))
+#define GOD_ANCHOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_ANCHOR_TYPE, GodAnchorClass))
+#define GOD_ANCHOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_ANCHOR_TYPE, GodAnchorClass))
+#define IS_GOD_ANCHOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_ANCHOR_TYPE))
+#define IS_GOD_ANCHOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_ANCHOR_TYPE))
+
+typedef struct GodAnchorPrivate_ GodAnchorPrivate;
+
+typedef struct {
+ GObject parent;
+ GodAnchorPrivate *priv;
+} GodAnchor;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ void (*get_rect) (GodAnchor *anchor, GoRect *rect);
+ void (*set_rect) (GodAnchor *anchor, const GoRect *rect);
+} GodAnchorClass;
+
+GType god_anchor_get_type (void);
+
+GodAnchor *god_anchor_new (void);
+void god_anchor_get_rect (GodAnchor *anchor,
+ GoRect *rect);
+void god_anchor_set_rect (GodAnchor *anchor,
+ const GoRect *anchor_value);
+
+
+
+G_END_DECLS
+
+#endif /* GOD_ANCHOR_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-group.c
@@ -0,0 +1,97 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-drawing-group.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-drawing-group.h"
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodDrawingGroupPrivate_ {
+ GodImageStore *image_store;
+};
+
+static void
+ensure_image_store (GodDrawingGroup *drawing_group)
+{
+ if (drawing_group->priv->image_store == NULL)
+ drawing_group->priv->image_store =
+ god_image_store_new();
+}
+
+GodDrawingGroup *
+god_drawing_group_new (void)
+{
+ GodDrawingGroup *drawing_group;
+
+ drawing_group = g_object_new (GOD_DRAWING_GROUP_TYPE, NULL);
+
+ return drawing_group;
+}
+
+GodImageStore *
+god_drawing_group_get_image_store (GodDrawingGroup *group)
+{
+ ensure_image_store (group);
+ g_object_ref (group->priv->image_store);
+ return group->priv->image_store;
+}
+
+static void
+god_drawing_group_init (GObject *object)
+{
+ GodDrawingGroup *group = GOD_DRAWING_GROUP (object);
+ group->priv = g_new0 (GodDrawingGroupPrivate, 1);
+}
+
+static void
+god_drawing_group_dispose (GObject *object)
+{
+ GodDrawingGroup *group = GOD_DRAWING_GROUP (object);
+
+ if (group->priv->image_store)
+ g_object_unref (group->priv->image_store);
+ g_free (group->priv);
+ group->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_drawing_group_class_init (GodDrawingGroupClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_drawing_group_dispose;
+}
+
+GSF_CLASS (GodDrawingGroup, god_drawing_group,
+ god_drawing_group_class_init, god_drawing_group_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-drawing.h
@@ -0,0 +1,60 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-drawing.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_DRAWING_H
+#define GOD_DRAWING_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-shape.h>
+#include <drawing/god-drawing-group.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DRAWING_TYPE (god_drawing_get_type ())
+#define GOD_DRAWING(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DRAWING_TYPE, GodDrawing))
+#define GOD_DRAWING_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DRAWING_TYPE, GodDrawingClass))
+#define IS_GOD_DRAWING(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DRAWING_TYPE))
+#define IS_GOD_DRAWING_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DRAWING_TYPE))
+
+typedef struct GodDrawingPrivate_ GodDrawingPrivate;
+
+typedef struct {
+ GObject parent;
+ GodDrawingPrivate *priv;
+} GodDrawing;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodDrawingClass;
+
+GType god_drawing_get_type (void);
+GodDrawing *god_drawing_new (void);
+
+/* Return value is reffed. */
+GodShape *god_drawing_get_root_shape (GodDrawing *drawing);
+void god_drawing_set_root_shape (GodDrawing *drawing,
+ GodShape *root_shape);
+
+/* Return value is reffed. */
+GodShape *god_drawing_get_background (GodDrawing *drawing);
+void god_drawing_set_background (GodDrawing *drawing,
+ GodShape *root_shape);
+
+/* Return value is reffed. */
+GodDrawingGroup *god_drawing_get_drawing_group (GodDrawing *drawing);
+void god_drawing_set_drawing_group (GodDrawing *drawing,
+ GodDrawingGroup *drawing_group);
+
+
+G_END_DECLS
+
+#endif /* GOD_DRAWING_H */
--- /dev/null
+++ lib/goffice/drawing/god-paragraph-attributes.h
@@ -0,0 +1,63 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-paragraph-attributes.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_PARAGRAPH_ATTRIBUTES_H
+#define GOD_PARAGRAPH_ATTRIBUTES_H
+
+#include <glib-object.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GOD_PARAGRAPH_ATTRIBUTES_TYPE (god_paragraph_attributes_get_type ())
+#define GOD_PARAGRAPH_ATTRIBUTES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_PARAGRAPH_ATTRIBUTES_TYPE, GodParagraphAttributes))
+#define GOD_PARAGRAPH_ATTRIBUTES_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_PARAGRAPH_ATTRIBUTES_TYPE, GodParagraphAttributesClass))
+#define GOD_PARAGRAPH_ATTRIBUTES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_PARAGRAPH_ATTRIBUTES_TYPE, GodParagraphAttributesClass))
+#define IS_GOD_PARAGRAPH_ATTRIBUTES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_PARAGRAPH_ATTRIBUTES_TYPE))
+#define IS_GOD_PARAGRAPH_ATTRIBUTES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_PARAGRAPH_ATTRIBUTES_TYPE))
+
+typedef struct GodParagraphAttributesPrivate_ GodParagraphAttributesPrivate;
+
+typedef struct {
+ GObject parent;
+ GodParagraphAttributesPrivate *priv;
+} GodParagraphAttributes;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodParagraphAttributesClass;
+
+typedef enum {
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_INDENT = 1 << 0,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_BEFORE = 1 << 1,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_AFTER = 1 << 2,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALIGNMENT = 1 << 3,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_CHARACTER = 1 << 4,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_INDENT = 1 << 5,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_SIZE = 1 << 6,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_FAMILY = 1 << 7,
+ GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALL = ((1 << 8) - 1),
+} GodParagraphAttributesFlags;
+
+typedef enum {
+ GOD_PARAGRAPH_ALIGNMENT_LEFT = 0,
+ GOD_PARAGRAPH_ALIGNMENT_CENTER = 1,
+ GOD_PARAGRAPH_ALIGNMENT_RIGHT = 2,
+ GOD_PARAGRAPH_ALIGNMENT_JUSTIFY = 3
+} GodParagraphAlignment;
+
+GType god_paragraph_attributes_get_type (void);
+GodParagraphAttributes *god_paragraph_attributes_new (void);
+
+
+G_END_DECLS
+
+#endif /* GOD_PARAGRAPH_ATTRIBUTES_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-renderer-gdk.c
@@ -0,0 +1,571 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-drawing-renderer-gdk.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-drawing-renderer-gdk.h"
+#include <gsf/gsf-impl-utils.h>
+#include <gdk/gdk.h>
+#include <string.h>
+#include <math.h>
+
+static GObjectClass *parent_class;
+
+struct GodDrawingRendererGdkPrivate_ {
+ GodDrawing *drawing;
+ GdkDrawable *drawable;
+ GdkGC *gc;
+ GodAnchor *extents;
+ go_unit_t x_units_per_pixel;
+ go_unit_t y_units_per_pixel;
+};
+
+GodDrawingRendererGdk *
+god_drawing_renderer_gdk_new (void)
+{
+ GodDrawingRendererGdk *renderer;
+
+ renderer = g_object_new (GOD_DRAWING_RENDERER_GDK_TYPE, NULL);
+
+ return renderer;
+}
+
+static void
+update_units_per_pixel (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->drawable && renderer->priv->extents) {
+ gint pixel_width, pixel_height;
+ GoRect extent_rect;
+ gdk_drawable_get_size (renderer->priv->drawable, &pixel_width, &pixel_height);
+ god_anchor_get_rect (renderer->priv->extents, &extent_rect);
+
+ renderer->priv->x_units_per_pixel = (extent_rect.right - extent_rect.left) / pixel_width;
+ renderer->priv->y_units_per_pixel = (extent_rect.bottom - extent_rect.top) / pixel_height;
+ } else {
+ renderer->priv->x_units_per_pixel = 0;
+ renderer->priv->y_units_per_pixel = 0;
+ }
+}
+
+GodDrawing *
+god_drawing_renderer_gdk_get_drawing (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->drawing)
+ g_object_ref (renderer->priv->drawing);
+ return renderer->priv->drawing;
+}
+
+void
+god_drawing_renderer_gdk_set_drawing (GodDrawingRendererGdk *renderer,
+ GodDrawing *drawing)
+{
+ if (renderer->priv->drawing)
+ g_object_unref (renderer->priv->drawing);
+ renderer->priv->drawing = drawing;
+ if (renderer->priv->drawing)
+ g_object_ref (renderer->priv->drawing);
+}
+
+GdkDrawable *
+god_drawing_renderer_gdk_get_drawable (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->drawable)
+ g_object_ref (renderer->priv->drawable);
+ return renderer->priv->drawable;
+}
+
+void
+god_drawing_renderer_gdk_set_drawable (GodDrawingRendererGdk *renderer,
+ GdkDrawable *drawable)
+{
+ if (renderer->priv->drawable)
+ g_object_unref (renderer->priv->drawable);
+ renderer->priv->drawable = drawable;
+ if (renderer->priv->drawable)
+ g_object_ref (renderer->priv->drawable);
+ update_units_per_pixel (renderer);
+}
+
+GdkGC *
+god_drawing_renderer_gdk_get_gc (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->gc)
+ g_object_ref (renderer->priv->gc);
+ return renderer->priv->gc;
+}
+
+void
+god_drawing_renderer_gdk_set_gc (GodDrawingRendererGdk *renderer,
+ GdkGC *gc)
+{
+ if (renderer->priv->gc)
+ g_object_unref (renderer->priv->gc);
+ renderer->priv->gc = gc;
+ if (renderer->priv->gc)
+ g_object_ref (renderer->priv->gc);
+ update_units_per_pixel (renderer);
+}
+
+GodAnchor *
+god_drawing_renderer_gdk_get_extents (GodDrawingRendererGdk *renderer)
+{
+ if (renderer->priv->extents)
+ g_object_ref (renderer->priv->extents);
+ return renderer->priv->extents;
+}
+
+void
+god_drawing_renderer_gdk_set_extents (GodDrawingRendererGdk *renderer,
+ GodAnchor *extents)
+{
+ if (renderer->priv->extents)
+ g_object_unref (renderer->priv->extents);
+ renderer->priv->extents = extents;
+ if (renderer->priv->extents)
+ g_object_ref (renderer->priv->extents);
+ update_units_per_pixel (renderer);
+}
+
+static GdkPixbuf *
+get_pixbuf (GodDrawingRendererGdk *renderer,
+ int which_pic)
+{
+ GodDrawingGroup *drawing_group;
+ GdkPixbuf *ret_val = NULL;
+
+ if (which_pic < 0)
+ return NULL;
+
+ drawing_group = god_drawing_get_drawing_group (renderer->priv->drawing);
+ if (drawing_group) {
+ GodImageStore *image_store = god_drawing_group_get_image_store (drawing_group);
+ if (which_pic < god_image_store_get_image_count (image_store)) {
+ GodImage *image = god_image_store_get_image (image_store, which_pic);
+ ret_val = god_image_get_pixbuf (image);
+ g_object_unref (image);
+ }
+ g_object_unref (image_store);
+ g_object_unref (drawing_group);
+ }
+ return ret_val;
+}
+
+typedef struct {
+ GodDrawingRendererGdk *renderer;
+ GdkRectangle *rect;
+ long long y_ofs;
+ const GodDefaultAttributes *default_attributes;
+} DrawTextContext;
+
+#ifdef PANGO_HACK
+static gboolean
+make_absolute (PangoAttribute *attr, gpointer user_data)
+{
+ DrawTextContext *draw_context = user_data;
+ if (attr->klass->type == PANGO_ATTR_SIZE &&
+ ! ((PangoAttrSize *) attr)->absolute) {
+ PangoAttrSize *size_attr = (PangoAttrSize *) attr;
+ size_attr->size = GO_PT_TO_UN ((long long) size_attr->size) / draw_context->renderer->priv->y_units_per_pixel;
+ size_attr->absolute = TRUE;
+ }
+ return FALSE;
+}
+#endif
+
+static void
+draw_text (GodTextModel *text,
+ GodTextModelParagraph *paragraph,
+ gpointer user_data)
+{
+ int height;
+ PangoLayout *layout;
+ DrawTextContext *draw_context = user_data;
+ double space_before = 0;
+ double space_after = 0;
+ double indent = 0;
+ const GList *iterator;
+ PangoAttrList *attributes;
+ GodParagraphAlignment alignment = GOD_PARAGRAPH_ALIGNMENT_LEFT;
+ const GodParagraphAttributes *default_para_attributes;
+ gunichar bullet_character = 0;
+ double bullet_indent = 0;
+ double bullet_size = 1.0;
+ char *bullet_family = NULL;
+ PangoFontDescription *bullet_desc;
+ PangoAttrIterator *attr_iterator;
+
+ if (draw_context->default_attributes) {
+ default_para_attributes = god_default_attributes_get_paragraph_attributes_for_indent ((GodDefaultAttributes *)draw_context->default_attributes, paragraph->indent);
+ if (default_para_attributes) {
+ g_object_get ((GodParagraphAttributes *) default_para_attributes,
+ "space_before", &space_before,
+ "space_after", &space_after,
+ "indent", &indent,
+ "alignment", &alignment,
+ "bullet_character", &bullet_character,
+ "bullet_indent", &bullet_indent,
+ "bullet_size", &bullet_size,
+ "bullet_family", &bullet_family,
+ NULL);
+ }
+ }
+ if (paragraph->para_attributes) {
+ GodParagraphAttributesFlags flags;
+ g_object_get (paragraph->para_attributes,
+ "flags", &flags,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_BEFORE)
+ g_object_get (paragraph->para_attributes,
+ "space_before", &space_before,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_AFTER)
+ g_object_get (paragraph->para_attributes,
+ "space_after", &space_after,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_INDENT)
+ g_object_get (paragraph->para_attributes,
+ "indent", &indent,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALIGNMENT)
+ g_object_get (paragraph->para_attributes,
+ "alignment", &alignment,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_CHARACTER)
+ g_object_get (paragraph->para_attributes,
+ "bullet_character", &bullet_character,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_INDENT)
+ g_object_get (paragraph->para_attributes,
+ "bullet_indent", &bullet_indent,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_SIZE)
+ g_object_get (paragraph->para_attributes,
+ "bullet_size", &bullet_size,
+ NULL);
+ if (flags & GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_FAMILY) {
+ g_free (bullet_family);
+ bullet_family = NULL;
+ g_object_get (paragraph->para_attributes,
+ "bullet_family", &bullet_family,
+ NULL);
+ }
+ }
+ draw_context->y_ofs += space_before;
+
+ layout = pango_layout_new (gdk_pango_context_get_for_screen (gdk_screen_get_default()));
+ pango_layout_set_alignment (layout, alignment == GOD_PARAGRAPH_ALIGNMENT_JUSTIFY ? PANGO_ALIGN_LEFT : alignment);
+ pango_layout_set_width (layout, draw_context->rect->width * PANGO_SCALE);
+ if (strchr (paragraph->text, 0xb)) {
+ int i;
+ char *paragraph_text = g_strdup (paragraph->text);
+ for (i = 0; paragraph_text[i]; i++) {
+ if (paragraph_text[i] == 0xb)
+ paragraph_text[i] = '\r';
+ }
+ pango_layout_set_text (layout, paragraph_text, -1);
+ g_free (paragraph_text);
+ } else {
+ pango_layout_set_text (layout, paragraph->text, -1);
+ }
+ pango_layout_set_auto_dir (layout, FALSE);
+ if (paragraph->char_attributes)
+ attributes = pango_attr_list_copy (paragraph->char_attributes);
+ else
+ attributes = pango_attr_list_new ();
+ if (draw_context->default_attributes) {
+ iterator = god_default_attributes_get_pango_attributes_for_indent ((GodDefaultAttributes *)draw_context->default_attributes, paragraph->indent);
+ for (; iterator; iterator = iterator->next) {
+ PangoAttribute *attr = pango_attribute_copy (iterator->data);
+ attr->start_index = 0;
+ attr->end_index = -1;
+ pango_attr_list_insert_before (attributes, attr);
+ }
+ }
+#ifdef PANGO_HACK
+ pango_attr_list_filter (attributes, make_absolute, draw_context);
+#endif
+ pango_layout_set_attributes (layout, attributes);
+ attr_iterator = pango_attr_list_get_iterator (attributes);
+ bullet_desc = pango_font_description_new ();
+ pango_attr_iterator_get_font (attr_iterator, bullet_desc, NULL, NULL);
+ pango_attr_iterator_destroy (attr_iterator);
+ pango_attr_list_unref (attributes);
+ gdk_draw_layout (draw_context->renderer->priv->drawable,
+ draw_context->renderer->priv->gc,
+ draw_context->rect->x + indent / draw_context->renderer->priv->x_units_per_pixel,
+ draw_context->rect->y + draw_context->y_ofs / draw_context->renderer->priv->y_units_per_pixel,
+ layout);
+
+ pango_layout_get_size (layout, NULL, &height);
+
+ g_object_unref (layout);
+ layout = NULL;
+
+ if (bullet_character != 0 &&
+ bullet_character != 0xe011 &&
+ bullet_size != 0 &&
+ bullet_family != NULL) {
+ char utf8[7];
+ int length;
+ layout = pango_layout_new (gdk_pango_context_get_for_screen (gdk_screen_get_default()));
+ pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
+ length = g_unichar_to_utf8 (bullet_character, utf8);
+ pango_layout_set_text (layout, utf8, length);
+ pango_layout_set_auto_dir (layout, FALSE);
+ pango_font_description_set_size (bullet_desc, pango_font_description_get_size (bullet_desc) * sqrt (bullet_size));
+ pango_font_description_set_family (bullet_desc, bullet_family);
+ pango_layout_set_font_description (layout, bullet_desc);
+
+ gdk_draw_layout (draw_context->renderer->priv->drawable,
+ draw_context->renderer->priv->gc,
+ draw_context->rect->x + bullet_indent / draw_context->renderer->priv->x_units_per_pixel,
+ draw_context->rect->y + draw_context->y_ofs / draw_context->renderer->priv->y_units_per_pixel,
+ layout);
+ pango_font_description_free (bullet_desc);
+ g_object_unref (layout);
+ layout = NULL;
+ }
+
+ draw_context->y_ofs += height * draw_context->renderer->priv->y_units_per_pixel / PANGO_SCALE;
+ draw_context->y_ofs += space_after;
+
+#if 0
+ g_print ("space before: %f\n", space_before);
+ g_print ("space after: %f\n", space_after);
+ g_print ("indent: %f\n", indent);
+ g_print ("x_units: %lld\n", draw_context->renderer->priv->x_units_per_pixel);
+ g_print ("y_units: %lld\n", draw_context->renderer->priv->y_units_per_pixel);
+#endif
+}
+
+static void
+god_drawing_renderer_gdk_render_shape (GodDrawingRendererGdk *renderer,
+ GdkRectangle *area,
+ GodShape *shape)
+{
+ GodAnchor *anchor;
+ GdkRectangle rect;
+ GdkRectangle intersection;
+ GoRect anchor_rect;
+
+ anchor = god_shape_get_anchor (shape);
+ if (anchor) {
+ god_anchor_get_rect (anchor, &anchor_rect);
+ rect.x = anchor_rect.left / renderer->priv->x_units_per_pixel;
+ rect.width = anchor_rect.right / renderer->priv->x_units_per_pixel - rect.x;
+ rect.y = anchor_rect.top / renderer->priv->y_units_per_pixel;
+ rect.height = anchor_rect.bottom / renderer->priv->y_units_per_pixel - rect.y;
+ g_object_unref (anchor);
+ } else {
+ rect.x = 0;
+ rect.y = 0;
+ gdk_drawable_get_size (renderer->priv->drawable, &(rect.width), &(rect.height));
+ }
+
+ if (!gdk_rectangle_intersect (area, &rect, &intersection))
+ return;
+
+ {
+ GodPropertyTable *prop_table;
+ gboolean filled;
+ GodFillType fill_type;
+ GodTextModel *text_model;
+ DrawTextContext *draw_context;
+
+ prop_table = god_shape_get_prop_table (shape);
+ filled = god_property_table_get_flag (prop_table,
+ GOD_PROPERTY_FILLED,
+ TRUE);
+ fill_type = god_property_table_get_int (prop_table,
+ GOD_PROPERTY_FILL_TYPE,
+ GOD_FILL_TYPE_SOLID);
+ if (filled && fill_type == GOD_FILL_TYPE_PICTURE) {
+ int which_pic = god_property_table_get_int (prop_table,
+ GOD_PROPERTY_BLIP_ID,
+ -1);
+ GdkPixbuf *pixbuf = get_pixbuf (renderer, which_pic);
+ if (pixbuf) {
+ GdkPixbuf *to_blit = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (pixbuf),
+ gdk_pixbuf_get_has_alpha (pixbuf),
+ gdk_pixbuf_get_bits_per_sample (pixbuf),
+ intersection.width, intersection.height);
+ double offset_x, offset_y;
+ double scale_x, scale_y;
+
+ scale_x = (double) rect.width / (double) gdk_pixbuf_get_width (pixbuf);
+ scale_y = (double) rect.height / (double) gdk_pixbuf_get_height (pixbuf);
+ offset_x = rect.x - intersection.x;
+ offset_y = rect.y - intersection.y;
+
+ gdk_pixbuf_scale (pixbuf,
+ to_blit,
+ 0,
+ 0,
+ intersection.width,
+ intersection.height,
+ offset_x,
+ offset_y,
+ scale_x,
+ scale_y,
+ GDK_INTERP_HYPER);
+
+
+ gdk_draw_pixbuf (renderer->priv->drawable,
+ renderer->priv->gc,
+ to_blit,
+ 0,
+ 0,
+ intersection.x,
+ intersection.y,
+ intersection.width,
+ intersection.height,
+ GDK_RGB_DITHER_NORMAL,
+ intersection.x,
+ intersection.y);
+
+ g_object_unref (to_blit);
+ g_object_unref (pixbuf);
+ }
+ }
+ if (filled && fill_type == GOD_FILL_TYPE_SOLID) {
+ GdkColor old_color, color;
+ guint32 colornum = god_property_table_get_uint (prop_table,
+ GOD_PROPERTY_FILL_COLOR,
+ 0xffffff);
+ GdkGCValues values;
+
+ gdk_gc_get_values (renderer->priv->gc,
+ &values);
+ old_color = values.foreground;
+
+ color.red = (colornum & 0xff0000) >> 16;
+ color.blue = (colornum & 0xff00) >> 8;
+ color.green = colornum & 0xff;
+
+ color.red = color.red | (color.red << 8);
+ color.blue = color.blue | (color.blue << 8);
+ color.green = color.green + (color.green << 8);
+
+ gdk_gc_set_rgb_fg_color (renderer->priv->gc, &color);
+
+ gdk_draw_rectangle (renderer->priv->drawable,
+ renderer->priv->gc,
+ TRUE,
+ intersection.x,
+ intersection.y,
+ intersection.width,
+ intersection.height);
+
+ gdk_gc_set_foreground (renderer->priv->gc, &old_color);
+ }
+ text_model = god_shape_get_text_model (shape);
+ draw_context = g_new (DrawTextContext, 1);
+ draw_context->renderer = renderer;
+ draw_context->rect = ▭
+ draw_context->y_ofs = 0;
+ draw_context->default_attributes = god_text_model_get_default_attributes (text_model);
+ god_text_model_paragraph_foreach (text_model, draw_text, draw_context);
+ g_object_unref (prop_table);
+ }
+
+ {
+ int i, child_count;
+ GodShape *child;
+ child_count = god_shape_get_child_count (shape);
+ for (i = 0; i < child_count; i++) {
+ child = god_shape_get_child (shape, i);
+ god_drawing_renderer_gdk_render_shape (renderer, area, child);
+ }
+ }
+}
+
+void
+god_drawing_renderer_gdk_render (GodDrawingRendererGdk *renderer,
+ GdkRectangle *area)
+{
+ GodShape *shape;
+
+ update_units_per_pixel (renderer);
+ shape = god_drawing_get_background (renderer->priv->drawing);
+ if (shape) {
+ god_drawing_renderer_gdk_render_shape (renderer,
+ area,
+ shape);
+ g_object_unref (shape);
+ }
+
+ shape = god_drawing_get_root_shape (renderer->priv->drawing);
+ if (shape) {
+ god_drawing_renderer_gdk_render_shape (renderer,
+ area,
+ shape);
+ g_object_unref (shape);
+ }
+}
+
+static void
+god_drawing_renderer_gdk_init (GObject *object)
+{
+ GodDrawingRendererGdk *renderer = GOD_DRAWING_RENDERER_GDK (object);
+
+ renderer->priv = g_new0 (GodDrawingRendererGdkPrivate, 1);
+}
+
+static void
+god_drawing_renderer_gdk_dispose (GObject *object)
+{
+ GodDrawingRendererGdk *renderer = GOD_DRAWING_RENDERER_GDK (object);
+
+ if (renderer->priv == NULL)
+ return;
+
+ if (renderer->priv->drawing)
+ g_object_unref (renderer->priv->drawing);
+ if (renderer->priv->drawable)
+ g_object_unref (renderer->priv->drawable);
+ if (renderer->priv->gc)
+ g_object_unref (renderer->priv->gc);
+ if (renderer->priv->extents)
+ g_object_unref (renderer->priv->extents);
+ g_free (renderer->priv);
+ renderer->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_drawing_renderer_gdk_class_init (GodDrawingRendererGdkClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_drawing_renderer_gdk_dispose;
+}
+
+GSF_CLASS (GodDrawingRendererGdk, god_drawing_renderer_gdk,
+ god_drawing_renderer_gdk_class_init, god_drawing_renderer_gdk_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-shape.c
@@ -0,0 +1,269 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-shape.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-shape.h"
+#include <gsf/gsf-impl-utils.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodShapePrivate_ {
+ GodShape *parent;
+ GPtrArray *children; /* Of type GodShape. */
+ GodPropertyTable *prop_table;
+ GodAnchor *anchor;
+ GodTextModel *text_model;
+};
+
+static GPtrArray*
+g_ptr_array_insert_val (GPtrArray *array,
+ guint index,
+ gpointer data)
+{
+ g_ptr_array_add (array, data);
+ memmove (array->pdata + index + 1,
+ array->pdata + index,
+ array->len - index - 1);
+ g_ptr_array_index (array, index) = data;
+ return array;
+}
+
+static void
+ensure_prop_table (GodShape *shape)
+{
+ if (shape->priv->prop_table == NULL)
+ shape->priv->prop_table =
+ god_property_table_new();
+}
+
+static void
+ensure_text_model (GodShape *shape)
+{
+ if (shape->priv->text_model == NULL)
+ shape->priv->text_model =
+ god_text_model_new();
+}
+
+GodShape *
+god_shape_new (void)
+{
+ GodShape *shape;
+
+ shape = g_object_new (GOD_SHAPE_TYPE, NULL);
+
+ return shape;
+}
+
+void
+god_shape_append_child (GodShape *parent,
+ GodShape *child)
+{
+ god_shape_insert_child (parent, child, -1);
+}
+
+/* pos can be -1 to represent the end. */
+void
+god_shape_insert_child (GodShape *parent,
+ GodShape *child,
+ int pos)
+{
+ g_return_if_fail (parent != NULL);
+ g_return_if_fail (child != NULL);
+ g_return_if_fail (child->priv->parent == NULL);
+
+ if (pos == -1)
+ pos = parent->priv->children->len;
+
+ g_ptr_array_insert_val (parent->priv->children, pos, child);
+ g_object_ref (child);
+ child->priv->parent = parent;
+}
+
+/* pos must be an actual position */
+void
+god_shape_delete_child (GodShape *parent,
+ int pos)
+{
+ GodShape *child = g_ptr_array_remove_index (parent->priv->children, pos);
+ g_object_unref (child);
+}
+
+/* old_pos must be an actual position. new_pos can be -1 to represent the end. */
+void
+god_shape_reorder_child (GodShape *parent,
+ int old_pos,
+ int new_pos)
+{
+ GodShape *child = g_ptr_array_remove_index (parent->priv->children, old_pos);
+ child->priv->parent = NULL;
+ god_shape_insert_child (parent, child, new_pos);
+ g_object_unref (child);
+}
+
+int
+god_shape_get_child_count (GodShape *parent)
+{
+ return parent->priv->children->len;
+}
+
+GodShape *
+god_shape_get_child (GodShape *parent,
+ int pos)
+{
+ GodShape *child;
+
+ g_return_val_if_fail (pos < god_shape_get_child_count (parent), NULL);
+
+ child = g_ptr_array_index (parent->priv->children, pos);
+
+ g_return_val_if_fail (child, NULL);
+
+ g_object_ref (child);
+ return child;
+}
+
+GodPropertyTable *
+god_shape_get_prop_table (GodShape *shape)
+{
+ ensure_prop_table (shape);
+ g_object_ref (shape->priv->prop_table);
+ return shape->priv->prop_table;
+}
+
+void
+god_shape_set_prop_table (GodShape *shape,
+ GodPropertyTable *prop_table)
+{
+ if (shape->priv->prop_table)
+ g_object_unref (shape->priv->prop_table);
+ shape->priv->prop_table = prop_table;
+ if (shape->priv->prop_table)
+ g_object_ref (shape->priv->prop_table);
+}
+
+GodAnchor *
+god_shape_get_anchor (GodShape *shape)
+{
+ if (shape->priv->anchor)
+ g_object_ref (shape->priv->anchor);
+ return shape->priv->anchor;
+}
+
+void
+god_shape_set_anchor (GodShape *shape,
+ GodAnchor *anchor)
+{
+ if (shape->priv->anchor)
+ g_object_unref (shape->priv->anchor);
+ shape->priv->anchor = anchor;
+ if (shape->priv->anchor)
+ g_object_ref (shape->priv->anchor);
+}
+
+GodTextModel *
+god_shape_get_text_model (GodShape *shape)
+{
+ ensure_text_model (shape);
+ g_object_ref (shape->priv->text_model);
+ return shape->priv->text_model;
+}
+
+void
+god_shape_set_text_model (GodShape *shape,
+ GodTextModel *text_model)
+{
+ if (shape->priv->text_model)
+ g_object_unref (shape->priv->text_model);
+ shape->priv->text_model = text_model;
+ if (shape->priv->text_model)
+ g_object_ref (shape->priv->text_model);
+}
+
+const char *
+god_shape_get_text (GodShape *shape)
+{
+ if (shape->priv->text_model)
+ return god_text_model_get_text (shape->priv->text_model);
+ else
+ return NULL;
+}
+
+void
+god_shape_set_text (GodShape *shape,
+ const char *text)
+{
+ ensure_text_model (shape);
+ god_text_model_set_text (shape->priv->text_model, text);
+}
+
+static void
+god_shape_init (GObject *object)
+{
+ GodShape *shape = GOD_SHAPE (object);
+ shape->priv = g_new0 (GodShapePrivate, 1);
+ shape->priv->children = g_ptr_array_new ();
+ shape->priv->parent = NULL;
+}
+
+static void
+god_shape_dispose (GObject *object)
+{
+ GodShape *shape = GOD_SHAPE (object);
+ guint i;
+
+ if (shape->priv == NULL)
+ return;
+
+ for (i = 0; i < shape->priv->children->len; i++)
+ g_object_unref (g_ptr_array_index (shape->priv->children, i));
+ g_ptr_array_free (shape->priv->children, TRUE);
+ if (shape->priv->prop_table)
+ g_object_unref (shape->priv->prop_table);
+ if (shape->priv->anchor)
+ g_object_unref (shape->priv->anchor);
+ if (shape->priv->text_model)
+ g_object_unref (shape->priv->text_model);
+ g_free (shape->priv);
+ shape->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+god_shape_class_init (GodShapeClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->dispose = god_shape_dispose;
+}
+
+GSF_CLASS (GodShape, god_shape,
+ god_shape_class_init, god_shape_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-paragraph-attributes.c
@@ -0,0 +1,249 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-paragraph-attributes.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2002
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-paragraph-attributes.h"
+
+#include <glib/gi18n.h>
+#include <gsf/gsf-impl-utils.h>
+
+static GObjectClass *parent_class;
+
+struct GodParagraphAttributesPrivate_ {
+ GodParagraphAttributesFlags flags;
+ double indent;
+ double space_before;
+ double space_after;
+ GodParagraphAlignment alignment;
+ gunichar bullet_character;
+ double bullet_indent;
+ double bullet_size;
+ char *bullet_family;
+};
+
+enum {
+ PROP_0,
+ PROP_FLAGS,
+ PROP_INDENT,
+ PROP_SPACE_BEFORE,
+ PROP_SPACE_AFTER,
+ PROP_ALIGNMENT,
+ PROP_BULLET_CHARACTER,
+ PROP_BULLET_INDENT,
+ PROP_BULLET_SIZE,
+ PROP_BULLET_FAMILY,
+};
+
+GodParagraphAttributes *
+god_paragraph_attributes_new (void)
+{
+ GodParagraphAttributes *paragraph;
+
+ paragraph = g_object_new (GOD_PARAGRAPH_ATTRIBUTES_TYPE, NULL);
+
+ return paragraph;
+}
+
+static void
+god_paragraph_attributes_init (GObject *object)
+{
+ GodParagraphAttributes *paragraph = GOD_PARAGRAPH_ATTRIBUTES (object);
+ paragraph->priv = g_new0 (GodParagraphAttributesPrivate, 1);
+ paragraph->priv->indent = 0;
+ paragraph->priv->space_before = 0;
+ paragraph->priv->space_after = 0;
+ paragraph->priv->alignment = GOD_PARAGRAPH_ALIGNMENT_LEFT;
+ paragraph->priv->bullet_character = 0;
+ paragraph->priv->bullet_indent = 0;
+ paragraph->priv->bullet_size = 1.0;
+ paragraph->priv->bullet_family = NULL;
+ paragraph->priv->flags = 0;
+}
+
+static void
+god_paragraph_attributes_finalize (GObject *object)
+{
+ GodParagraphAttributes *paragraph = GOD_PARAGRAPH_ATTRIBUTES (object);
+
+ g_free (paragraph->priv->bullet_family);
+ g_free (paragraph->priv);
+ paragraph->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+god_paragraph_attributes_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+ GodParagraphAttributes *paragraph = GOD_PARAGRAPH_ATTRIBUTES (object);
+
+ switch (prop_id) {
+ case PROP_INDENT:
+ paragraph->priv->indent = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_INDENT;
+ break;
+ case PROP_SPACE_BEFORE:
+ paragraph->priv->space_before = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_BEFORE;
+ break;
+ case PROP_SPACE_AFTER:
+ paragraph->priv->space_after = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_SPACE_AFTER;
+ break;
+ case PROP_ALIGNMENT:
+ paragraph->priv->alignment = g_value_get_uint (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALIGNMENT;
+ break;
+ case PROP_BULLET_CHARACTER:
+ paragraph->priv->bullet_character = g_value_get_uint (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_CHARACTER;
+ break;
+ case PROP_BULLET_INDENT:
+ paragraph->priv->bullet_indent = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_INDENT;
+ break;
+ case PROP_BULLET_SIZE:
+ paragraph->priv->bullet_size = g_value_get_double (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_SIZE;
+ break;
+ case PROP_BULLET_FAMILY:
+ g_free (paragraph->priv->bullet_family);
+ paragraph->priv->bullet_family = g_value_dup_string (value);
+ paragraph->priv->flags |= GOD_PARAGRAPH_ATTRIBUTES_FLAGS_BULLET_FAMILY;
+ break;
+ }
+}
+
+static void
+god_paragraph_attributes_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+ GodParagraphAttributes *paragraph = GOD_PARAGRAPH_ATTRIBUTES (object);
+
+ switch (prop_id){
+ case PROP_FLAGS:
+ g_value_set_uint (value, paragraph->priv->flags);
+ break;
+ case PROP_INDENT:
+ g_value_set_double (value, paragraph->priv->indent);
+ break;
+ case PROP_SPACE_BEFORE:
+ g_value_set_double (value, paragraph->priv->space_before);
+ break;
+ case PROP_SPACE_AFTER:
+ g_value_set_double (value, paragraph->priv->space_after);
+ break;
+ case PROP_ALIGNMENT:
+ g_value_set_uint (value, paragraph->priv->alignment);
+ break;
+ case PROP_BULLET_CHARACTER:
+ g_value_set_uint (value, paragraph->priv->bullet_character);
+ break;
+ case PROP_BULLET_INDENT:
+ g_value_set_double (value, paragraph->priv->bullet_indent);
+ break;
+ case PROP_BULLET_SIZE:
+ g_value_set_double (value, paragraph->priv->bullet_size);
+ break;
+ case PROP_BULLET_FAMILY:
+ g_value_set_string (value, paragraph->priv->bullet_family);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+god_paragraph_attributes_class_init (GodParagraphAttributesClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_paragraph_attributes_finalize;
+ object_class->get_property = god_paragraph_attributes_get_property;
+ object_class->set_property = god_paragraph_attributes_set_property;
+
+ g_object_class_install_property (object_class, PROP_FLAGS,
+ g_param_spec_uint ("flags",
+ _( "Flags" ),
+ _( "Flags" ),
+ 0, GOD_PARAGRAPH_ATTRIBUTES_FLAGS_ALL, 0,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class, PROP_INDENT,
+ g_param_spec_double ("indent",
+ _( "Indent" ),
+ _( "Indent" ),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_SPACE_BEFORE,
+ g_param_spec_double ("space_before",
+ _( "Space Before" ),
+ _( "Space Before" ),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_SPACE_AFTER,
+ g_param_spec_double ("space_after",
+ _( "Space After" ),
+ _( "Space After" ),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_ALIGNMENT,
+ g_param_spec_uint ("alignment",
+ _( "Alignment" ),
+ _( "Alignment" ),
+ GOD_PARAGRAPH_ALIGNMENT_LEFT, GOD_PARAGRAPH_ALIGNMENT_JUSTIFY, GOD_PARAGRAPH_ALIGNMENT_LEFT,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_BULLET_CHARACTER,
+ g_param_spec_uint ("bullet_character",
+ _( "Bullet Character" ),
+ _( "Bullet Character" ),
+ 0, 0xffffffff, 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_BULLET_INDENT,
+ g_param_spec_double ("bullet_indent",
+ _( "Bullet Indent" ),
+ _( "Bullet Indent" ),
+ -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_BULLET_SIZE,
+ g_param_spec_double ("bullet_size",
+ _( "Bullet Size" ),
+ _( "Bullet Size" ),
+ 0, G_MAXDOUBLE, 1.0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class, PROP_BULLET_FAMILY,
+ g_param_spec_string ("bullet_family",
+ _( "Bullet Family" ),
+ _( "Bullet Family" ),
+ NULL,
+ G_PARAM_READWRITE));
+}
+
+GSF_CLASS (GodParagraphAttributes, god_paragraph_attributes,
+ god_paragraph_attributes_class_init, god_paragraph_attributes_init,
+ G_TYPE_OBJECT)
--- /dev/null
+++ lib/goffice/drawing/god-image-store.h
@@ -0,0 +1,59 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-image-store.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_IMAGE_STORE_H
+#define GOD_IMAGE_STORE_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-image.h>
+
+G_BEGIN_DECLS
+
+#define GOD_IMAGE_STORE_TYPE (god_image_store_get_type ())
+#define GOD_IMAGE_STORE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_IMAGE_STORE_TYPE, GodImageStore))
+#define GOD_IMAGE_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_IMAGE_STORE_TYPE, GodImageStoreClass))
+#define IS_GOD_IMAGE_STORE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_IMAGE_STORE_TYPE))
+#define IS_GOD_IMAGE_STORE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_IMAGE_STORE_TYPE))
+
+typedef struct GodImageStorePrivate_ GodImageStorePrivate;
+
+typedef struct {
+ GObject parent;
+ GodImageStorePrivate *priv;
+} GodImageStore;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodImageStoreClass;
+
+GType god_image_store_get_type (void);
+GodImageStore *god_image_store_new (void);
+
+/* Tree functions */
+void god_image_store_append_image (GodImageStore *store,
+ GodImage *image);
+void god_image_store_insert_image (GodImageStore *store,
+ GodImage *image,
+ int pos);
+void god_image_store_delete_image (GodImageStore *store,
+ int pos);
+void god_image_store_reorder_image (GodImageStore *store,
+ int old_pos,
+ int new_pos);
+int god_image_store_get_image_count (GodImageStore *store);
+/* Return value is reffed. */
+GodImage *god_image_store_get_image (GodImageStore *store,
+ int pos);
+
+G_END_DECLS
+
+#endif /* GOD_IMAGE_STORE_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-view.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *//* vim: set sw=8: */
+#ifndef GOD_DRAWING_VIEW_H
+#define GOD_DRAWING_VIEW_H
+
+/**
+ * god-drawing-view.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-drawing.h>
+#include <drawing/god-anchor.h>
+#include <gdk/gdkdrawable.h>
+#include <gtk/gtkdrawingarea.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DRAWING_VIEW_TYPE (god_drawing_view_get_type ())
+#define GOD_DRAWING_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DRAWING_VIEW_TYPE, GodDrawingView))
+#define GOD_DRAWING_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DRAWING_VIEW_TYPE, GodDrawingViewClass))
+#define IS_GOD_DRAWING_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DRAWING_VIEW_TYPE))
+#define IS_GOD_DRAWING_VIEW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DRAWING_VIEW_TYPE))
+
+typedef struct GodDrawingViewPrivate_ GodDrawingViewPrivate;
+
+typedef struct {
+ GtkDrawingArea parent;
+ GodDrawingViewPrivate *priv;
+} GodDrawingView;
+
+typedef struct {
+ GtkDrawingAreaClass parent_class;
+} GodDrawingViewClass;
+
+GType god_drawing_view_get_type (void);
+GodDrawingView *god_drawing_view_new (void);
+
+/* Return value is reffed. */
+GodDrawing *god_drawing_view_get_drawing (GodDrawingView *renderer);
+void god_drawing_view_set_drawing (GodDrawingView *renderer,
+ GodDrawing *drawing);
+
+/* Return value is reffed. */
+GodAnchor *god_drawing_view_get_extents (GodDrawingView *renderer);
+void god_drawing_view_set_extents (GodDrawingView *renderer,
+ GodAnchor *extents);
+
+G_END_DECLS
+
+#endif /* GOD_DRAWING_VIEW_H */
--- /dev/null
+++ lib/goffice/drawing/god-property-table.c
@@ -0,0 +1,993 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * god-property-table.c: MS Office Graphic Object support
+ *
+ * Copyright (C) 2000-2004
+ * Jody Goldberg (jody at gnome.org)
+ * Michael Meeks (mmeeks at gnu.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "drawing/god-property-table.h"
+#include <gsf/gsf-impl-utils.h>
+
+#include <gsf/gsf-utils.h>
+#include <stdio.h>
+#include <string.h>
+
+static GObjectClass *parent_class;
+
+struct GodPropertyTablePrivate_ {
+ GHashTable *attrs;
+};
+
+#define GR_END 0x00
+#define GR_MACRO 0x04
+#define GR_COMMAND_BUTTON 0x05
+#define GR_GROUP 0x06
+#define GR_CLIPBOARD_FORMAT 0x07
+#define GR_PICTURE_OPTIONS 0x08
+#define GR_PICTURE_FORMULA 0x09
+#define GR_CHECKBOX_LINK 0x0A
+#define GR_RADIO_BUTTON 0x0B
+#define GR_SCROLLBAR 0x0C
+#define GR_NOTE_STRUCTURE 0x0D
+#define GR_SCROLLBAR_FORMULA 0x0E
+#define GR_GROUP_BOX_DATA 0x0F
+#define GR_EDIT_CONTROL_DATA 0x10
+#define GR_RADIO_BUTTON_DATA 0x11
+#define GR_CHECKBOX_DATA 0x12
+#define GR_LISTBOX_DATA 0x13
+#define GR_CHECKBOX_FORMULA 0x14
+#define GR_COMMON_OBJ_DATA 0x15
+
+static GValue *
+g_value_new (GType type)
+{
+ GValue *value = g_new0 (GValue, 1);
+ g_value_init (value, type);
+ return value;
+}
+
+static void
+g_value_free (gpointer data)
+{
+ GValue *value = data;
+ g_value_unset (value);
+ g_free (value);
+}
+
+void
+god_property_table_set (GodPropertyTable *prop_table, GodPropertyID id, GValue *value)
+{
+ g_hash_table_insert (prop_table->priv->attrs, g_strdup(id), value);
+}
+
+GValue *
+god_property_table_get (GodPropertyTable *prop_table, GodPropertyID id)
+{
+ g_return_val_if_fail (prop_table != NULL, NULL);
+ return g_hash_table_lookup (prop_table->priv->attrs, id);
+}
+
+void
+god_property_table_set_flag (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gboolean val)
+{
+ GValue *value = g_value_new(G_TYPE_BOOLEAN);
+ g_value_set_boolean (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_uint (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ guint32 val)
+{
+ GValue *value = g_value_new(G_TYPE_UINT);
+ g_value_set_uint (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_int (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gint32 val)
+{
+ GValue *value = g_value_new(G_TYPE_INT);
+ g_value_set_int (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_length (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ go_unit_t val)
+{
+ GValue *value = g_value_new(G_TYPE_INT64);
+ g_value_set_int64 (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_pointer (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gpointer val)
+{
+ GValue *value = g_value_new(G_TYPE_POINTER);
+ g_value_set_pointer (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_array (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ GArray *val)
+{
+ GValue *value = g_value_new(G_TYPE_POINTER);
+ g_value_set_pointer (value, val);
+ god_property_table_set (prop_table, id, value);
+}
+
+void
+god_property_table_set_markup (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ PangoAttrList *list)
+{
+ GValue *value = g_value_new (PANGO_TYPE_ATTR_LIST);
+ g_value_set_pointer (value, list);
+ god_property_table_set (prop_table, id, value);
+}
+
+gboolean
+god_property_table_get_flag (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gboolean default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_BOOLEAN (value), default_value);
+
+ return g_value_get_boolean (value);
+}
+
+guint32
+god_property_table_get_uint (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ guint32 default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_UINT (value), default_value);
+
+ return g_value_get_uint (value);
+}
+
+gint32
+god_property_table_get_int (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gint32 default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_INT (value), default_value);
+
+ return g_value_get_int (value);
+}
+
+go_unit_t
+god_property_table_get_length (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ go_unit_t default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_INT64 (value), default_value);
+
+ return g_value_get_int64 (value);
+}
+
+gpointer
+god_property_table_get_pointer (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ gpointer default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_POINTER (value), default_value);
+
+ return g_value_get_pointer (value);
+}
+
+GArray *
+god_property_table_get_array (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ GArray *default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_POINTER (value), default_value);
+
+ return g_value_get_pointer (value);
+}
+
+PangoAttrList *
+god_property_table_get_markup (GodPropertyTable *prop_table,
+ GodPropertyID id,
+ PangoAttrList *default_value)
+{
+ GValue *value;
+
+ g_return_val_if_fail (prop_table != NULL, default_value);
+ value = g_hash_table_lookup (prop_table->priv->attrs, id);
+ if (value == NULL)
+ return default_value;
+
+ g_return_val_if_fail (G_VALUE_HOLDS_POINTER (value), default_value);
+
+ return g_value_get_pointer (value);
+}
+
+GodPropertyTable *
+god_property_table_new (void)
+{
+ GodPropertyTable *prop_table;
+
+ prop_table = g_object_new (GOD_PROPERTY_TABLE_TYPE, NULL);
+
+ return prop_table;
+}
+
+static void
+god_property_table_init (GObject *object)
+{
+ GodPropertyTable *prop_table = GOD_PROPERTY_TABLE (object);
+ prop_table->priv = g_new0 (GodPropertyTablePrivate, 1);
+ prop_table->priv->attrs = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_value_free);
+}
+
+static void
+god_property_table_finalize (GObject *object)
+{
+ GodPropertyTable *prop_table = GOD_PROPERTY_TABLE (object);
+
+ g_hash_table_destroy (prop_table->priv->attrs);
+ g_free (prop_table->priv);
+ prop_table->priv = NULL;
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+god_property_table_class_init (GodPropertyTableClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = god_property_table_finalize;
+}
+
+GSF_CLASS (GodPropertyTable, god_property_table,
+ god_property_table_class_init, god_property_table_init,
+ G_TYPE_OBJECT)
+
+#if 0
+/********************************************************************************/
+
+GODrawingPropertyTable *
+ms_obj_new (GODrawingPropertyTableAttrBag *attrs)
+{
+ GODrawingPropertyTable *obj = g_new0 (GODrawingPropertyTable, 1);
+
+ obj->excel_type = (unsigned)-1; /* Set to undefined */
+ obj->excel_type_name = NULL;
+ obj->id = -1;
+ obj->gnum_obj = NULL;
+ obj->attrs = (attrs != NULL) ? attrs : ms_obj_attr_bag_new ();
+ obj->combo_in_autofilter = FALSE;
+ obj->is_linked = FALSE;
+ obj->comment_pos.col = obj->comment_pos.row = -1;
+
+ return obj;
+}
+
+void
+ms_obj_delete (GODrawingPropertyTable *obj)
+{
+ if (obj) {
+ if (obj->gnum_obj) {
+ g_object_unref (obj->gnum_obj);
+ obj->gnum_obj = NULL;
+ }
+ if (obj->attrs) {
+ ms_obj_attr_bag_destroy (obj->attrs);
+ obj->attrs = NULL;
+ }
+ g_free (obj);
+ }
+}
+
+/* S59EOE.HTM */
+char *
+ms_read_TXO (BiffQuery *q)
+{
+ static char const * const orientations [] = {
+ "Left to right",
+ "Top to Bottom",
+ "Bottom to Top on Side",
+ "Top to Bottom on Side"
+ };
+ static char const * const haligns [] = {
+ "At left", "Horizontaly centered",
+ "At right", "Horizontaly justified"
+ };
+ static char const * const valigns [] = {
+ "At top", "Verticaly centered",
+ "At bottom", "Verticaly justified"
+ };
+
+ guint16 const options = GSF_LE_GET_GUINT16 (q->data);
+ guint16 const orient = GSF_LE_GET_GUINT16 (q->data + 2);
+ guint16 const text_len = GSF_LE_GET_GUINT16 (q->data + 10);
+/* guint16 const num_formats = GSF_LE_GET_GUINT16 (q->data + 12);*/
+ int const halign = (options >> 1) & 0x7;
+ int const valign = (options >> 4) & 0x7;
+ char *text;
+ guint16 peek_op;
+
+ if (text_len == 0)
+ return NULL;
+
+ g_return_val_if_fail (orient <= 3, NULL);
+ g_return_val_if_fail (1 <= halign && halign <= 4, NULL);
+ g_return_val_if_fail (1 <= valign && valign <= 4, NULL);
+
+ if (ms_biff_query_peek_next (q, &peek_op) && peek_op == BIFF_CONTINUE) {
+ ms_biff_query_next (q);
+
+ if ((int)q->length < text_len) {
+ g_warning ("Broken continue in TXO record");
+ text = g_strdup ("Broken continue");
+ } else
+ text = ms_biff_get_chars (q->data + 1, text_len,
+ *(q->data) != 0);
+
+ if (ms_biff_query_peek_next (q, &peek_op) &&
+ peek_op == BIFF_CONTINUE)
+ ms_biff_query_next (q);
+ else
+ g_warning ("Unusual, TXO text with no formatting has 0x%x @ 0x%x", peek_op, q->streamPos);
+ } else {
+ if (text_len > 0)
+ g_warning ("TXO len of %d but no continue", text_len);
+ text = g_strdup ("");
+ }
+
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug > 0) {
+ printf ("{ TextObject\n");
+ printf ("Text '%s'\n", text);
+ printf ("is %s, %s & %s;\n",
+ orientations[orient], haligns[halign], valigns[valign]);
+ printf ("}; /* TextObject */\n");
+ }
+#endif
+ return text;
+}
+
+#ifndef NO_DEBUG_EXCEL
+#define ms_obj_dump(data, len, data_left, name) ms_obj_dump_impl (data, len, data_left, name)
+static void
+ms_obj_dump_impl (guint8 const *data, int len, int data_left, char const *name)
+{
+ if (ms_excel_object_debug < 2)
+ return;
+
+ printf ("{ %s \n", name);
+ if (len+4 > data_left) {
+ printf ("/* invalid length %d (0x%x) > %d(0x%x)*/\n",
+ len+4, len+4, data_left, data_left);
+ len = data_left - 4;
+ }
+ if (ms_excel_object_debug > 2)
+ gsf_mem_dump (data, len+4);
+ printf ("}; /* %s */\n", name);
+}
+#else
+#define ms_obj_dump (data, len, data_left, name)
+#endif
+
+/* S59DAD.HTM */
+static gboolean
+ms_obj_read_pre_biff8_obj (BiffQuery *q, MSContainer *container, GODrawingPropertyTable *obj)
+{
+ guint16 peek_op, tmp, len;
+ guint8 const *data;
+ gboolean const has_fmla = GSF_LE_GET_GUINT16 (q->data+26) != 0;
+
+ /* undocumented */
+ gboolean const has_name = GSF_LE_GET_GUINT16 (q->data+30) != 0;
+
+#if 0
+ guint16 const flags = GSF_LE_GET_GUINT16(q->data+8);
+#endif
+ guint8 *anchor = g_malloc (MS_ANCHOR_SIZE);
+ memcpy (anchor, q->data+8, MS_ANCHOR_SIZE);
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_ptr (MS_OBJ_ATTR_ANCHOR, anchor));
+
+ obj->excel_type = GSF_LE_GET_GUINT16(q->data + 4);
+ obj->id = GSF_LE_GET_GUINT32(q->data + 6);
+
+ switch (obj->excel_type) {
+ case 0: /* group */
+ break;
+ case 1: /* line */
+ tmp = GSF_LE_GET_GUINT8 (q->data+40);
+ if (GSF_LE_GET_GUINT16 (q->data + 10) == 0 &&
+ GSF_LE_GET_GUINT16 (q->data + 14) < 20) {
+ g_warning("%hhu", tmp);
+ }
+ if (GSF_LE_GET_GUINT8 (q->data+38) & 0x0F)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_flag (MS_OBJ_ATTR_ARROW_END));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+34)));
+
+ if (tmp == 1 || tmp == 2)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_H));
+ if (tmp >= 2)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_flag (MS_OBJ_ATTR_FLIP_V));
+ break;
+
+ case 2: /* rectangle */
+ break;
+ case 3: /* oval */
+ break;
+ case 4: /* arc */
+ break;
+ case 5: /* chart */
+ break;
+ case 6: /* textbox */
+ /* gsf_mem_dump (q->data+34, q->length - 34); */
+ if (GSF_LE_GET_GUINT8 (q->data+36) > 0) {
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_flag (MS_OBJ_ATTR_FILLED));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+35)));
+ }
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_FONT_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+34)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+38)));
+
+ /* only pull in the text if it exists */
+ len = GSF_LE_GET_GUINT16 (q->data + 44);
+ if (len > 0) {
+ data = q->data + 70;
+ g_return_val_if_fail ((unsigned)(data - q->data) < q->length, TRUE);
+
+ g_return_val_if_fail (!has_fmla, TRUE); /* how would this happen */
+
+ /* skip the obj name if defined */
+ if (has_name) {
+ data += *data + ((*data & 0x1) ? 1 : 2); /* padding byte */
+ g_return_val_if_fail ((unsigned)(data - q->data) < q->length, TRUE);
+ }
+
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_ptr (MS_OBJ_ATTR_TEXT,
+ g_strndup (data, len)));
+ }
+ break;
+
+ case 7: /* button */
+ break;
+ case 8: /* picture */
+ break;
+ case 9: /* polygon */
+
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_FILL_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+35)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_OUTLINE_COLOR,
+ 0x80000000 | GSF_LE_GET_GUINT8 (q->data+38)));
+
+ if (ms_biff_query_peek_next (q, &peek_op) &&
+ peek_op == BIFF_COORDLIST) {
+ unsigned i, n;
+ guint tmp;
+ GArray *array;
+
+ ms_biff_query_next (q);
+ n = q->length / 2;
+ array = g_array_set_size (
+ g_array_new (FALSE, FALSE, sizeof (double)), n + 2);
+
+ for (i = 0; i < n ; i++) {
+ tmp = GSF_LE_GET_GUINT16 (q->data + 2*i);
+ g_array_index (array, double, i) = (double)tmp/ 16384.;
+ }
+ g_array_index (array, double, i) = g_array_index (array, double, 0);
+ g_array_index (array, double, i+1) = g_array_index (array, double, 1);
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_array (MS_OBJ_ATTR_POLYGON_COORDS, array));
+ }
+ break;
+
+ case 0xB : /* check box */
+ break;
+ case 0xC : /* option button */
+ break;
+ case 0xD : /* edit box */
+ break;
+ case 0xE : /* label */
+ break;
+ case 0xF : /* dialog frame */
+ break;
+ case 0x10 : /* spinner & scrollbar (layout is the same) */
+ case 0x11 :
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_VALUE,
+ GSF_LE_GET_GUINT16 (q->data+48)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MIN,
+ GSF_LE_GET_GUINT16 (q->data+50)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MAX,
+ GSF_LE_GET_GUINT16 (q->data+52)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_INC,
+ GSF_LE_GET_GUINT16 (q->data+54)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_PAGE,
+ GSF_LE_GET_GUINT16 (q->data+56)));
+
+ {
+ GnmExpr const *ref;
+ guint16 len;
+ guint8 const *last = q->data + q->length;
+ guint8 const *ptr = q->data + 64;
+
+ ptr += 1 + *ptr; /* object name */
+ if ((ptr - q->data) & 1) ptr++; /* align on word */
+ if (ptr >= last) break;
+
+ ptr += 2 + GSF_LE_GET_GUINT16 (ptr); /* the macro */
+ if ((ptr - q->data) & 1) ptr++; /* align on word */
+ if (ptr >= last) break;
+
+ len = GSF_LE_GET_GUINT16 (ptr+2); /* the assigned macro */
+ ref = ms_container_parse_expr (container, ptr + 8, len);
+ if (ref != NULL)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
+ }
+ break;
+ case 0x12 : /* list box */
+ break;
+ case 0x13 : /* group box */
+ break;
+ case 0x14 : /* drop down */
+ obj->combo_in_autofilter =
+ (GSF_LE_GET_GUINT16 (q->data + 8) & 0x8000) ? TRUE : FALSE;
+ break;
+ default :
+ ;
+ }
+
+ return FALSE;
+}
+
+/* S59DAD.HTM */
+static gboolean
+ms_obj_read_biff8_obj (BiffQuery *q, MSContainer *container, GODrawingPropertyTable *obj)
+{
+ guint8 *data;
+ gint32 data_len_left;
+ gboolean hit_end = FALSE;
+ gboolean next_biff_record_maybe_imdata = FALSE;
+
+ g_return_val_if_fail (q, TRUE);
+ g_return_val_if_fail (q->ls_op == BIFF_OBJ, TRUE);
+
+ data = q->data;
+ data_len_left = q->length;
+
+#if 0
+ ms_biff_query_dump (q);
+#endif
+
+ /* Scan through the pseudo BIFF substream */
+ while (data_len_left > 0 && !hit_end) {
+ guint16 const record_type = GSF_LE_GET_GUINT16(data);
+
+ /* All the sub-records seem to have this layout
+ * 2001/Mar/29 JEG : liars. Ok not all records have this
+ * layout. Create a list box. It seems to do something
+ * unique. It acts like an end, and has no length specified.
+ */
+ guint16 len = GSF_LE_GET_GUINT16(data+2);
+
+ /* 1st record must be COMMON_OBJ*/
+ g_return_val_if_fail (obj->excel_type >= 0 ||
+ record_type == GR_COMMON_OBJ_DATA,
+ TRUE);
+
+ switch (record_type) {
+ case GR_END:
+ g_return_val_if_fail (len == 0, TRUE);
+ /* ms_obj_dump (data, len, data_len_left, "ObjEnd"); */
+ hit_end = TRUE;
+ break;
+
+ case GR_MACRO :
+ ms_obj_dump (data, len, data_len_left, "MacroObject");
+ break;
+
+ case GR_COMMAND_BUTTON :
+ ms_obj_dump (data, len, data_len_left, "CommandButton");
+ break;
+
+ case GR_GROUP :
+ ms_obj_dump (data, len, data_len_left, "Group");
+ break;
+
+ case GR_CLIPBOARD_FORMAT :
+ ms_obj_dump (data, len, data_len_left, "ClipboardFmt");
+ break;
+
+ case GR_PICTURE_OPTIONS :
+ if (len == 2) {
+ guint16 opt = GSF_LE_GET_GUINT16 (data + 4);
+
+ obj->is_linked = (opt & 0x2) ? TRUE : FALSE;
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug >= 1) {
+ printf ("{ /* PictOpt */\n");
+ printf ("value = %x;\n", opt);
+ printf ("}; /* PictOpt */\n");
+ }
+#endif
+ } else {
+ /* no docs on this so be careful */
+ g_warning ("PictOpt record with size other than 2");
+ }
+
+ next_biff_record_maybe_imdata = TRUE;
+ break;
+
+ case GR_PICTURE_FORMULA :
+ ms_obj_dump (data, len, data_len_left, "PictFormula");
+ break;
+
+ case GR_CHECKBOX_LINK :
+ ms_obj_dump (data, len, data_len_left, "CheckboxLink");
+ break;
+
+ case GR_RADIO_BUTTON :
+ ms_obj_dump (data, len, data_len_left, "RadioButton");
+ break;
+
+ case GR_SCROLLBAR :
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_VALUE,
+ GSF_LE_GET_GUINT16 (data+8)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MIN,
+ GSF_LE_GET_GUINT16 (data+10)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_MAX,
+ GSF_LE_GET_GUINT16 (data+12)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_INC,
+ GSF_LE_GET_GUINT16 (data+14)));
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_uint (MS_OBJ_ATTR_SCROLLBAR_PAGE,
+ GSF_LE_GET_GUINT16 (data+16)));
+ ms_obj_dump (data, len, data_len_left, "ScrollBar");
+ break;
+
+ case GR_NOTE_STRUCTURE :
+ ms_obj_dump (data, len, data_len_left, "Note");
+ break;
+
+ case GR_SCROLLBAR_FORMULA : {
+ guint16 const expr_len = GSF_LE_GET_GUINT16 (data+4);
+ GnmExpr const *ref = ms_container_parse_expr (container, data+10, expr_len);
+ if (ref != NULL)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
+ ms_obj_dump (data, len, data_len_left, "ScrollbarFmla");
+ break;
+ }
+
+ case GR_GROUP_BOX_DATA :
+ ms_obj_dump (data, len, data_len_left, "GroupBoxData");
+ break;
+
+ case GR_EDIT_CONTROL_DATA :
+ ms_obj_dump (data, len, data_len_left, "EditCtrlData");
+ break;
+
+ case GR_RADIO_BUTTON_DATA :
+ ms_obj_dump (data, len, data_len_left, "RadioData");
+ break;
+
+ case GR_CHECKBOX_DATA :
+ ms_obj_dump (data, len, data_len_left, "CheckBoxData");
+ break;
+
+ case GR_LISTBOX_DATA : {
+ /* FIXME : find some docs for this
+ * It seems as if list box data does not conform to
+ * the docs. It acts like an end and has no size.
+ */
+ hit_end = TRUE;
+ len = data_len_left - 4;
+
+ ms_obj_dump (data, len, data_len_left, "ListBoxData");
+ break;
+ }
+
+ case GR_CHECKBOX_FORMULA : {
+ guint16 const expr_len = GSF_LE_GET_GUINT16 (data+4);
+ GnmExpr const *ref = ms_container_parse_expr (container, data+10, expr_len);
+ if (ref != NULL)
+ ms_obj_attr_bag_insert (obj->attrs,
+ ms_obj_attr_new_expr (MS_OBJ_ATTR_LINKED_TO_CELL, ref));
+ ms_obj_dump (data, len, data_len_left, "CheckBoxFmla");
+ break;
+ }
+
+ case GR_COMMON_OBJ_DATA : {
+ guint16 const options =GSF_LE_GET_GUINT16 (data+8);
+
+ /* Multiple objects in 1 record ?? */
+ g_return_val_if_fail (obj->excel_type == -1, TRUE);
+
+ obj->excel_type = GSF_LE_GET_GUINT16(data+4);
+ obj->id = GSF_LE_GET_GUINT16(data+6);
+
+ /* Undocumented. It appears that combos for filters are marked
+ * with flag 0x100
+ */
+ obj->combo_in_autofilter =
+ (obj->excel_type == 0x14) && (options & 0x100);
+
+#ifndef NO_DEBUG_EXCEL
+ /* only print when debug is enabled */
+ if (ms_excel_object_debug == 0)
+ break;
+
+ printf ("OBJECT TYPE = %d\n", obj->excel_type);
+ if (options&0x0001)
+ printf ("Locked;\n");
+ if (options&0x0010)
+ printf ("Printable;\n");
+ if (options&0x2000)
+ printf ("AutoFilled;\n");
+ if (options&0x4000)
+ printf ("AutoLines;\n");
+
+ if (ms_excel_object_debug > 4) {
+ /* According to the docs this should not fail
+ * but there appears to be a flag at 0x200 for
+ * scrollbars and 0x100 for combos
+ * associated with filters.
+ */
+ if ((options & 0x9fee) != 0)
+ printf ("WARNING : Why is option not 0 (%x)\n",
+ options & 0x9fee);
+ }
+#endif
+ }
+ break;
+
+ default:
+ printf ("ERROR : Unknown Obj record 0x%x len 0x%x dll %d;\n",
+ record_type, len, data_len_left);
+ }
+
+ if (data_len_left < len+4)
+ printf ("record len %d (0x%x) > %d\n", len+4, len+4, data_len_left);
+
+ /* FIXME : We need a structure akin to the escher code to do this properly */
+ for (data_len_left -= len+4; data_len_left < 0; ) {
+ guint16 peek_op;
+
+ printf ("deficit of %d\n", data_len_left);
+
+ /* FIXME : what do we expect here ??
+ * I've seen what seem to be embedded drawings
+ * but I am not sure what is embedding what.
+ */
+ if (!ms_biff_query_peek_next (q, &peek_op) ||
+ (peek_op != BIFF_CONTINUE &&
+ peek_op != BIFF_MS_O_DRAWING &&
+ peek_op != BIFF_TXO &&
+ peek_op != BIFF_OBJ)) {
+ printf ("0x%x vs 0x%x\n", q->opcode, peek_op);
+ return TRUE;
+ }
+
+ ms_biff_query_next (q);
+ data_len_left += q->length;
+ printf ("merged in 0x%x with len %d\n", q->opcode, q->length);
+ }
+ data = q->data + q->length - data_len_left;
+ }
+
+ /* The ftEnd record should have been the last */
+ if (data_len_left > 0) {
+ printf("OBJ : unexpected extra data after Object End record;\n");
+ gsf_mem_dump (data, data_len_left);
+ return TRUE;
+ }
+
+ /* Catch underflow too */
+ g_return_val_if_fail (data_len_left == 0, TRUE);
+
+ /* FIXME : Throw away the IMDATA that may follow.
+ * I am not sure when the IMDATA does follow, or how to display it,
+ * but very careful in case it is not there.
+ */
+ if (next_biff_record_maybe_imdata) {
+ guint16 op;
+
+ if (ms_biff_query_peek_next (q, &op) && op == BIFF_IMDATA) {
+ printf ("Reading trailing IMDATA;\n");
+ ms_biff_query_next (q);
+ excel_read_IMDATA (q, FALSE);
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * ms_read_OBJ :
+ * @q : The biff record to start with.
+ * @container : The object's container
+ * @attrs : an OPTIONAL hash of object attributes.
+ */
+void
+ms_read_OBJ (BiffQuery *q, MSContainer *container, GODrawingPropertyTableAttrBag *attrs)
+{
+ static char const * const object_type_names[] = {
+ "Group", /* 0x00 */
+ "Line", /* 0x01 */
+ "Rectangle", /* 0x02 */
+ "Oval", /* 0x03 */
+ "Arc", /* 0x04 */
+ "Chart", /* 0x05 */
+ "TextBox", /* 0x06 */
+ "Button", /* 0x07 */
+ "Picture", /* 0x08 */
+ "Polygon", /* 0x09 */
+ NULL, /* 0x0A */
+ "CheckBox", /* 0x0B */
+ "Option", /* 0x0C */
+ "Edit", /* 0x0D */
+ "Label", /* 0x0E */
+ "Dialog", /* 0x0F */
+ "Spinner", /* 0x10 */
+ "Scroll", /* 0x11 */
+ "List", /* 0x12 */
+ "Group", /* 0x13 */
+ "Combo", /* 0x14 */
+ NULL, NULL, NULL, NULL, /* 0x15 - 0x18 */
+ "Comment", /* 0x19 */
+ NULL, NULL, NULL, NULL, /* 0x1A - 0x1D */
+ "MS Drawing" /* 0x1E */
+ };
+
+ gboolean errors;
+ GODrawingPropertyTable *obj = ms_obj_new (attrs);
+
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug > 0)
+ printf ("{ /* OBJ start */\n");
+#endif
+ errors = (container->ver >= MS_BIFF_V8)
+ ? ms_obj_read_biff8_obj (q, container, obj)
+ : ms_obj_read_pre_biff8_obj (q, container, obj);
+
+ if (errors) {
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug > 0)
+ printf ("}; /* OBJ error 1 */\n");
+#endif
+ ms_obj_delete (obj);
+ return;
+ }
+
+ obj->excel_type_name = NULL;
+ if (obj->excel_type < (int)G_N_ELEMENTS (object_type_names))
+ obj->excel_type_name = object_type_names [obj->excel_type];
+ if (obj->excel_type_name == NULL)
+ obj->excel_type_name = "Unknown";
+
+#ifndef NO_DEBUG_EXCEL
+ if (ms_excel_object_debug > 0) {
+ printf ("Object (%d) is a '%s'\n", obj->id, obj->excel_type_name);
+ printf ("}; /* OBJ end */\n");
+ }
+#endif
+
+ if (container->vtbl->create_obj != NULL)
+ obj->gnum_obj = (*container->vtbl->create_obj) (container, obj);
+
+ /* Chart, There should be a BOF next */
+ if (obj->excel_type == 0x5 &&
+ ms_excel_chart_read_BOF (q, container, obj->gnum_obj)) {
+ ms_obj_delete (obj);
+ return;
+ }
+
+#if 0
+ g_warning ("registered obj %d\n", obj->id);
+#endif
+ ms_container_add_obj (container, obj);
+}
+#endif
--- /dev/null
+++ lib/goffice/drawing/Makefile.am
@@ -0,0 +1,31 @@
+noinst_LTLIBRARIES = libgoffice-drawing.la
+
+AM_CFLAGS = $(GNOME_CFLAGS) $(GSF_CFLAGS)
+
+libgoffice_drawing_la_SOURCES = \
+ god-anchor.c \
+ god-anchor.h \
+ god-default-attributes.c \
+ god-default-attributes.h \
+ god-drawing-group.c \
+ god-drawing-group.h \
+ god-drawing-renderer-gdk.c \
+ god-drawing-renderer-gdk.h \
+ god-drawing-view.c \
+ god-drawing-view.h \
+ god-drawing.c \
+ god-drawing.h \
+ god-image-store.c \
+ god-image-store.h \
+ god-image.c \
+ god-image.h \
+ god-paragraph-attributes.c \
+ god-paragraph-attributes.h \
+ god-property-table.c \
+ god-property-table.h \
+ god-shape.c \
+ god-shape.h \
+ god-text-model.c \
+ god-text-model.h
+
+include $(srcdir)/../goffice.mk
--- /dev/null
+++ lib/goffice/drawing/god-image.h
@@ -0,0 +1,53 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-image.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_IMAGE_H
+#define GOD_IMAGE_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-property-table.h>
+#include <drawing/god-anchor.h>
+#include <drawing/god-text-model.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GOD_IMAGE_TYPE (god_image_get_type ())
+#define GOD_IMAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_IMAGE_TYPE, GodImage))
+#define GOD_IMAGE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_IMAGE_TYPE, GodImageClass))
+#define IS_GOD_IMAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_IMAGE_TYPE))
+#define IS_GOD_IMAGE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_IMAGE_TYPE))
+
+typedef struct GodImagePrivate_ GodImagePrivate;
+
+typedef struct {
+ GObject parent;
+ GodImagePrivate *priv;
+} GodImage;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodImageClass;
+
+GType god_image_get_type (void);
+GodImage *god_image_new (void);
+
+GdkPixbuf *god_image_get_pixbuf (GodImage *image);
+/* Instead of setting the pixbuf, you set the image data. */
+void god_image_set_image_data (GodImage *image,
+ const char *format,
+ const guint8 *data,
+ guint32 length);
+
+G_END_DECLS
+
+#endif /* GOD_IMAGE_H */
--- /dev/null
+++ lib/goffice/drawing/god-drawing-group.h
@@ -0,0 +1,48 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-drawing-group.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+
+#ifndef GOD_DRAWING_GROUP_H
+#define GOD_DRAWING_GROUP_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-image-store.h>
+
+G_BEGIN_DECLS
+
+#define GOD_DRAWING_GROUP_TYPE (god_drawing_group_get_type ())
+#define GOD_DRAWING_GROUP(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_DRAWING_GROUP_TYPE, GodDrawingGroup))
+#define GOD_DRAWING_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_DRAWING_GROUP_TYPE, GodDrawingGroupClass))
+#define GOD_DRAWING_GROUP_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GOD_DRAWING_GROUP_TYPE, GodDrawingGroupClass))
+#define IS_GOD_DRAWING_GROUP(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_DRAWING_GROUP_TYPE))
+#define IS_GOD_DRAWING_GROUP_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_DRAWING_GROUP_TYPE))
+
+typedef struct GodDrawingGroupPrivate_ GodDrawingGroupPrivate;
+
+typedef struct {
+ GObject parent;
+ GodDrawingGroupPrivate *priv;
+} GodDrawingGroup;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodDrawingGroupClass;
+
+GType god_drawing_group_get_type (void);
+
+GodDrawingGroup *god_drawing_group_new (void);
+
+GodImageStore *god_drawing_group_get_image_store (GodDrawingGroup *drawing_group);
+
+G_END_DECLS
+
+#endif /* GOD_DRAWING_GROUP_H */
--- /dev/null
+++ lib/goffice/drawing/god-shape.h
@@ -0,0 +1,80 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * god-shape.h: MS Office Graphic Object support
+ *
+ * Author:
+ * Michael Meeks (michael at ximian.com)
+ * Jody Goldberg (jody at gnome.org)
+ * Christopher James Lahey <clahey at ximian.com>
+ *
+ * (C) 1998-2003 Michael Meeks, Jody Goldberg, Chris Lahey
+ **/
+#ifndef GOD_SHAPE_H
+#define GOD_SHAPE_H
+
+#include <glib-object.h>
+#include <glib.h>
+#include <drawing/god-property-table.h>
+#include <drawing/god-anchor.h>
+#include <drawing/god-text-model.h>
+
+G_BEGIN_DECLS
+
+#define GOD_SHAPE_TYPE (god_shape_get_type ())
+#define GOD_SHAPE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOD_SHAPE_TYPE, GodShape))
+#define GOD_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GOD_SHAPE_TYPE, GodShapeClass))
+#define IS_GOD_SHAPE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOD_SHAPE_TYPE))
+#define IS_GOD_SHAPE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOD_SHAPE_TYPE))
+
+typedef struct GodShapePrivate_ GodShapePrivate;
+
+typedef struct {
+ GObject parent;
+ GodShapePrivate *priv;
+} GodShape;
+
+typedef struct {
+ GObjectClass parent_class;
+} GodShapeClass;
+
+GType god_shape_get_type (void);
+GodShape *god_shape_new (void);
+
+/* Tree functions */
+void god_shape_append_child (GodShape *parent,
+ GodShape *child);
+void god_shape_insert_child (GodShape *parent,
+ GodShape *child,
+ int pos);
+void god_shape_delete_child (GodShape *parent,
+ int pos);
+void god_shape_reorder_child (GodShape *parent,
+ int old_pos,
+ int new_pos);
+int god_shape_get_child_count (GodShape *parent);
+/* Return value is reffed. */
+GodShape *god_shape_get_child (GodShape *parent,
+ int pos);
+
+/* Return value is reffed. */
+GodPropertyTable *god_shape_get_prop_table (GodShape *shape);
+void god_shape_set_prop_table (GodShape *shape,
+ GodPropertyTable *prop_table);
+
+/* Return value is reffed. */
+GodAnchor *god_shape_get_anchor (GodShape *shape);
+void god_shape_set_anchor (GodShape *shape,
+ GodAnchor *anchor);
+
+/* Return value is reffed. */
+GodTextModel *god_shape_get_text_model (GodShape *shape);
+void god_shape_set_text_model (GodShape *shape,
+ GodTextModel *text);
+
+const char *god_shape_get_text (GodShape *shape);
+void god_shape_set_text (GodShape *shape,
+ const char *text_value);
+
+G_END_DECLS
+
+#endif /* GOD_SHAPE_H */
--- /dev/null
+++ lib/goffice/graph/plugins/plot_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