[gnumeric] Improve chart export to ODF



commit 60dc5637404ae0e057a92436fc108c978c305dfc
Author: Andreas J. Guelzow <aguelzow pyrshep ca>
Date:   Wed Jul 15 21:08:53 2009 -0600

    Improve chart export to ODF
    
    2009-07-15 Andreas J. Guelzow <aguelzow pyrshep ca>
    
    	* openoffice-read.c (od_style_prop_chart): handle
    	  chart:reverse-direction, chart:symbol-type,
    	  draw:stroke, chart:lines
    	(oo_chart_axis): in ODF proper the axis style should not affect
    	  the overall graph.
    	(oo_plot_area): handle double series for Gantt charts
    	(oo_plot_area_end): ditto
    	(oo_plot_series): ditto
    	(oo_series_domain): handle dimensions for series:domain
    	(oo_chart_wall): hook-up to the correct element
    	* openoffice-write.c (odf_add_percent): new
    	(odf_write_frame): fix leak
    	(odf_write_series): handle more dimensions
    	(odf_write_gantt_series): new
    	(odf_write_bubble_series): new
    	(odf_write_ring_plot_style): new
    	(odf_write_line_chart_style): new
    	(odf_write_scatter_chart_style): new
    	(odf_write_scatter_series_style): new
    	(odf_write_axis_style): differentiate based on plot type
    	(odf_write_axis): ditto
    	(df_write_plot): handle more plot types

 plugins/openoffice/ChangeLog          |   25 ++
 plugins/openoffice/openoffice-read.c  |  248 ++++++++++++++-------
 plugins/openoffice/openoffice-write.c |  389 ++++++++++++++++++++++++++++++---
 3 files changed, 542 insertions(+), 120 deletions(-)
---
diff --git a/plugins/openoffice/ChangeLog b/plugins/openoffice/ChangeLog
index 2017c9d..8ada888 100644
--- a/plugins/openoffice/ChangeLog
+++ b/plugins/openoffice/ChangeLog
@@ -1,3 +1,28 @@
+2009-07-15 Andreas J. Guelzow <aguelzow pyrshep ca>
+
+	* openoffice-read.c (od_style_prop_chart): handle 
+	  chart:reverse-direction, chart:symbol-type, 
+	  draw:stroke, chart:lines
+	(oo_chart_axis): in ODF proper the axis style should not affect 
+	  the overall graph.
+	(oo_plot_area): handle double series for Gantt charts
+	(oo_plot_area_end): ditto
+	(oo_plot_series): ditto
+	(oo_series_domain): handle dimensions for series:domain
+	(oo_chart_wall): hook-up to the correct element
+	* openoffice-write.c (odf_add_percent): new
+	(odf_write_frame): fix leak
+	(odf_write_series): handle more dimensions
+	(odf_write_gantt_series): new
+	(odf_write_bubble_series): new
+	(odf_write_ring_plot_style): new
+	(odf_write_line_chart_style): new
+	(odf_write_scatter_chart_style): new
+	(odf_write_scatter_series_style): new
+	(odf_write_axis_style): differentiate based on plot type
+	(odf_write_axis): ditto
+	(df_write_plot): handle more plot types
+
 2009-07-13 Andreas J. Guelzow <aguelzow pyrshep ca>
 
 	* openoffice-read.c (od_draw_frame): use table:end-x and 
diff --git a/plugins/openoffice/openoffice-read.c b/plugins/openoffice/openoffice-read.c
index ab26ef1..0a4b437 100644
--- a/plugins/openoffice/openoffice-read.c
+++ b/plugins/openoffice/openoffice-read.c
@@ -121,6 +121,8 @@ typedef enum {
 	OO_PLOT_SCATTER,
 	OO_PLOT_STOCK,
 	OO_PLOT_SURF,
+	OO_PLOT_BUBBLE,
+	OO_PLOT_GANTT,
 	OO_PLOT_UNKNOWN
 } OOPlotType;
 
@@ -160,6 +162,7 @@ typedef struct {
 	int		 src_n_vectors;
 
 	GogSeries	*series;
+	unsigned	 series_count;	/* reset for each plotarea */
 	unsigned	 domain_count;	/* reset for each series */
 	unsigned	 data_pt_count;	/* reset for each series */
 
@@ -2864,7 +2867,7 @@ oo_prop_list_apply (GSList *props, GObject *obj)
 	for (ptr = props; ptr; ptr = ptr->next) {
 		prop = ptr->data;
 		if (NULL != g_object_class_find_property (klass, prop->name))
-			    g_object_set_property (obj, prop->name, &prop->value);
+			g_object_set_property (obj, prop->name, &prop->value);
 	}
 }
 
@@ -2875,6 +2878,9 @@ od_style_prop_chart (GsfXMLIn *xin, xmlChar const **attrs)
 	OOChartStyle *style = state->chart.cur_graph_style;
 	gboolean btmp;
 	int	  tmp;
+	gboolean default_style_has_lines_set = FALSE;
+	gboolean draw_stroke_set = FALSE;
+	gboolean draw_stroke;
 
 	g_return_if_fail (style != NULL);
 
@@ -2889,6 +2895,9 @@ od_style_prop_chart (GsfXMLIn *xin, xmlChar const **attrs)
 			/* This is backwards from my intuition */
 			style->plot_props = g_slist_prepend (style->plot_props,
 				oo_prop_new_bool ("horizontal", btmp));
+		} else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "reverse-direction", &btmp)) {
+			style->axis_props = g_slist_prepend (style->axis_props,
+				oo_prop_new_bool ("invert-axis", btmp));
 		} else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "stacked", &btmp)) {
 			if (btmp)
 				style->plot_props = g_slist_prepend (style->plot_props,
@@ -2903,9 +2912,27 @@ od_style_prop_chart (GsfXMLIn *xin, xmlChar const **attrs)
 		} else if (oo_attr_int (xin, attrs, OO_NS_CHART, "gap-width", &tmp))
 			style->plot_props = g_slist_prepend (style->plot_props,
 				oo_prop_new_int ("gap-percentage", tmp));
-		else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "series-source"))
+		else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "symbol-type"))
+			style->plot_props = g_slist_prepend 
+				(style->plot_props,
+				 oo_prop_new_bool ("default-style-has-markers", 
+						   !attr_eq (attrs[1], "none")));
+		else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "stroke")) {
+			draw_stroke = !attr_eq (attrs[1], "none");
+			draw_stroke_set = TRUE;
+		} else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "lines", &btmp)) {
+			style->plot_props = g_slist_prepend 
+				(style->plot_props,
+				 oo_prop_new_bool ("default-style-has-lines", btmp));
+			default_style_has_lines_set = TRUE;
+		} else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "series-source"))
 			style->src_in_rows = attr_eq (attrs[1], "rows");
 	}
+
+	if (draw_stroke_set && !default_style_has_lines_set)
+		style->plot_props = g_slist_prepend 
+			(style->plot_props,
+			 oo_prop_new_bool ("default-style-has-lines", draw_stroke));
 }
 
 static void
@@ -3309,11 +3336,83 @@ oo_chart_axis (GsfXMLIn *xin, xmlChar const **attrs)
 
 		/* AAARRRGGGHH : why would they do this.  The axis style impact
 		 * the plot ?? */
-		if (NULL != state->chart.plot)
+		if (NULL != state->chart.plot && (state->ver == OOO_VER_1))
 			oo_prop_list_apply (style->plot_props, G_OBJECT (state->chart.plot));
 	}
 }
 
+static int
+gog_series_map_dim (GogSeries const *series, GogMSDimType ms_type)
+{
+	GogSeriesDesc const *desc = &series->plot->desc.series;
+	unsigned i = desc->num_dim;
+
+	if (ms_type == GOG_MS_DIM_LABELS)
+		return -1;
+	while (i-- > 0)
+		if (desc->dim[i].ms_type == ms_type)
+			return i;
+	return -2;
+}
+/* If range == %NULL use an implicit range */
+static void
+oo_plot_assign_dim (GsfXMLIn *xin, xmlChar const *range, GogMSDimType dim_type)
+{
+	OOParseState *state = (OOParseState *)xin->user_state;
+
+	/* force relative to A1, not the containing cell */
+	GnmExprTop const *texpr;
+	GnmParsePos pp;
+	GnmValue *v;
+	int dim;
+
+	if (NULL == state->chart.series)
+		return;
+	dim = gog_series_map_dim (state->chart.series, dim_type);
+	if (dim < -1)
+		return;
+
+	if (NULL != range) {
+		GnmRangeRef ref;
+		char const *ptr = oo_rangeref_parse (&ref, CXML2C (range),
+			parse_pos_init_sheet (&pp, state->pos.sheet));
+		if (ptr == CXML2C (range))
+			return;
+		v = value_new_cellrange (&ref.a, &ref.b, 0, 0);
+#ifdef OO_DEBUG_OBJS
+		g_print ("%d = rangeref (%s)\n", dim, range);
+#endif
+	} else if (NULL != gog_dataset_get_dim (GOG_DATASET (state->chart.series), dim))
+		return;	/* implicit does not overwrite existing */
+	else if (state->chart.src_n_vectors <= 0) {
+		oo_warning (xin,
+			"Not enough data in the supplied range for all the requests");
+		return;
+	} else {
+		v = value_new_cellrange_r (
+			   state->chart.src_sheet,
+			   &state->chart.src_range);
+
+#ifdef OO_DEBUG_OBJS
+		g_print ("%d = implicit (%s)\n", dim, range_as_string (&state->chart.src_range));
+#endif
+
+		state->chart.src_n_vectors--;
+		if (state->chart.src_in_rows)
+			state->chart.src_range.end.row = ++state->chart.src_range.start.row;
+		else
+			state->chart.src_range.end.col = ++state->chart.src_range.start.col;
+	}
+
+	texpr = gnm_expr_top_new_constant (v);
+	if (NULL != texpr)
+		gog_series_set_dim (state->chart.series, dim,
+			(dim_type != GOG_MS_DIM_LABELS)
+			? gnm_go_data_vector_new_expr (state->pos.sheet, texpr)
+			: gnm_go_data_scalar_new_expr (state->pos.sheet, texpr),
+			NULL);
+}
+
 static void
 oo_plot_area (GsfXMLIn *xin, xmlChar const **attrs)
 {
@@ -3345,6 +3444,8 @@ oo_plot_area (GsfXMLIn *xin, xmlChar const **attrs)
 
 	state->chart.src_n_vectors = -1;
 	state->chart.src_in_rows = TRUE;
+	state->chart.series = NULL;
+	state->chart.series_count = 0;
 	if (NULL != source_range_str) {
 		GnmParsePos pp;
 		GnmEvalPos  ep;
@@ -3388,6 +3489,8 @@ oo_plot_area (GsfXMLIn *xin, xmlChar const **attrs)
 	case OO_PLOT_SCATTER:	type = "GogXYPlot";	break;
 	case OO_PLOT_STOCK:	type = "GogMinMaxPlot";	break;
 	case OO_PLOT_SURF:	type = "GogContourPlot"; break;
+	case OO_PLOT_BUBBLE:	type = "GogBubblePlot"; break;
+	case OO_PLOT_GANTT:	type = "GogDropBarPlot"; break;
 	default: return;
 	}
 
@@ -3398,88 +3501,32 @@ oo_plot_area (GsfXMLIn *xin, xmlChar const **attrs)
 		style = l->data;
 		oo_prop_list_apply (style->plot_props, G_OBJECT (state->chart.plot));
 	}
+	if (state->chart.plot_type == OO_PLOT_GANTT) {
+		GogObject *yaxis = gog_object_get_child_by_name (GOG_OBJECT (state->chart.chart), 
+								 "Y-Axis");
+		if (yaxis != NULL) {
+			GValue *val = g_value_init (g_new0 (GValue, 1), G_TYPE_BOOLEAN);
+			g_value_set_boolean (val, TRUE);
+			g_object_set_property (G_OBJECT (yaxis), "invert-axis", val);
+			g_value_unset (val);
+			g_free (val);
+		}
+	}
 }
 
 static void
 oo_plot_area_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
 {
 	OOParseState *state = (OOParseState *)xin->user_state;
+	if (state->chart.series != NULL) {
+		oo_plot_assign_dim (xin, NULL, GOG_MS_DIM_VALUES);
+		state->chart.series = NULL;		
+	}
 	state->chart.plot = NULL;
 	g_slist_free (state->chart.these_plot_styles);
 	state->chart.these_plot_styles = NULL;
 }
 
-static int
-gog_series_map_dim (GogSeries const *series, GogMSDimType ms_type)
-{
-	GogSeriesDesc const *desc = &series->plot->desc.series;
-	unsigned i = desc->num_dim;
-
-	if (ms_type == GOG_MS_DIM_LABELS)
-		return -1;
-	while (i-- > 0)
-		if (desc->dim[i].ms_type == ms_type)
-			return i;
-	return -2;
-}
-/* If range == %NULL use an implicit range */
-static void
-oo_plot_assign_dim (GsfXMLIn *xin, xmlChar const *range, GogMSDimType dim_type)
-{
-	OOParseState *state = (OOParseState *)xin->user_state;
-
-	/* force relative to A1, not the containing cell */
-	GnmExprTop const *texpr;
-	GnmParsePos pp;
-	GnmValue *v;
-	int dim;
-
-	if (NULL == state->chart.series)
-		return;
-	dim = gog_series_map_dim (state->chart.series, dim_type);
-	if (dim < -1)
-		return;
-
-	if (NULL != range) {
-		GnmRangeRef ref;
-		char const *ptr = oo_rangeref_parse (&ref, CXML2C (range),
-			parse_pos_init_sheet (&pp, state->pos.sheet));
-		if (ptr == CXML2C (range))
-			return;
-		v = value_new_cellrange (&ref.a, &ref.b, 0, 0);
-#ifdef OO_DEBUG_OBJS
-		g_print ("%d = rangeref (%s)\n", dim, range);
-#endif
-	} else if (NULL != gog_dataset_get_dim (GOG_DATASET (state->chart.series), dim))
-		return;	/* implicit does not overwrite existing */
-	else if (state->chart.src_n_vectors <= 0) {
-		oo_warning (xin,
-			"Not enough data in the supplied range for all the requests");
-		return;
-	} else {
-		v = value_new_cellrange_r (
-			   state->chart.src_sheet,
-			   &state->chart.src_range);
-
-#ifdef OO_DEBUG_OBJS
-		g_print ("%d = implicit (%s)\n", dim, range_as_string (&state->chart.src_range));
-#endif
-
-		state->chart.src_n_vectors--;
-		if (state->chart.src_in_rows)
-			state->chart.src_range.end.row = ++state->chart.src_range.start.row;
-		else
-			state->chart.src_range.end.col = ++state->chart.src_range.start.col;
-	}
-
-	texpr = gnm_expr_top_new_constant (v);
-	if (NULL != texpr)
-		gog_series_set_dim (state->chart.series, dim,
-			(dim_type != GOG_MS_DIM_LABELS)
-			? gnm_go_data_vector_new_expr (state->pos.sheet, texpr)
-			: gnm_go_data_scalar_new_expr (state->pos.sheet, texpr),
-			NULL);
-}
 
 static void
 oo_plot_series (GsfXMLIn *xin, xmlChar const **attrs)
@@ -3489,14 +3536,28 @@ oo_plot_series (GsfXMLIn *xin, xmlChar const **attrs)
 #ifdef OO_DEBUG_OBJS
 	g_print ("<<<<< Start\n");
 #endif
-	state->chart.series = gog_plot_new_series (state->chart.plot);
+	state->chart.series_count++;
+	if (state->chart.series == NULL)
+		state->chart.series = gog_plot_new_series (state->chart.plot);
 	state->chart.domain_count = 0;
 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
 		if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style"))
 			;
-		else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "values-cell-range-address"))
-			oo_plot_assign_dim (xin, attrs[1], GOG_MS_DIM_VALUES);
-		else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "label-cell-address"))
+		else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "values-cell-range-address")) {
+			int dim;
+			switch (state->chart.plot_type) {
+			case OO_PLOT_GANTT:
+				dim = (state->chart.series_count % 2 == 1) ? GOG_MS_DIM_START : GOG_MS_DIM_END;
+				break;
+			case OO_PLOT_BUBBLE:
+				dim = GOG_MS_DIM_BUBBLES;
+				break;
+			default:
+				dim = GOG_MS_DIM_VALUES;
+				break;
+			}
+			oo_plot_assign_dim (xin, attrs[1], dim);
+		} else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "label-cell-address"))
 			oo_plot_assign_dim (xin, attrs[1], GOG_MS_DIM_LABELS);
 }
 
@@ -3504,8 +3565,16 @@ static void
 oo_plot_series_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
 {
 	OOParseState *state = (OOParseState *)xin->user_state;
-	oo_plot_assign_dim (xin, NULL, GOG_MS_DIM_VALUES);
-	state->chart.series = NULL;
+	
+	switch (state->chart.plot_type) {
+	case OO_PLOT_GANTT:
+		if ((state->chart.series_count % 2) != 0)
+			break;
+	default:
+		oo_plot_assign_dim (xin, NULL, GOG_MS_DIM_VALUES);
+		state->chart.series = NULL;
+		break;
+	}
 #ifdef OO_DEBUG_OBJS
 	g_print (">>>>> end\n");
 #endif
@@ -3521,9 +3590,11 @@ oo_series_domain (GsfXMLIn *xin, xmlChar const **attrs)
 		if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "cell-range-address"))
 			src = attrs[1];
 
-	oo_plot_assign_dim (xin, src, GOG_MS_DIM_CATEGORIES);
+	if (state->chart.plot_type == OO_PLOT_BUBBLE && (state->chart.domain_count == 0))
+		oo_plot_assign_dim (xin, src, GOG_MS_DIM_VALUES);
+	else
+		oo_plot_assign_dim (xin, src, GOG_MS_DIM_CATEGORIES);
 
-/* FIXME : Bubbles */
 	state->chart.domain_count++;
 }
 
@@ -3548,6 +3619,8 @@ oo_chart (GsfXMLIn *xin, xmlChar const **attrs)
 		{ "chart:ring",		OO_PLOT_RING },
 		{ "chart:scatter",	OO_PLOT_SCATTER },
 		{ "chart:stock",	OO_PLOT_STOCK },
+		{ "chart:bubble",	OO_PLOT_BUBBLE },
+		{ "chart:gantt",	OO_PLOT_GANTT },
 		{ NULL,	0 },
 	};
 	OOParseState *state = (OOParseState *)xin->user_state;
@@ -3634,7 +3707,12 @@ static void
 oo_chart_wall (GsfXMLIn *xin, xmlChar const **attrs)
 {
 	OOParseState *state = (OOParseState *)xin->user_state;
-	gog_object_add_by_name (GOG_OBJECT (state->chart.chart), "Backplane", NULL);
+/* 	GOStyle *style = NULL; */
+/* 	GogObject *backplane; */
+
+	/* backplane =  */gog_object_add_by_name (GOG_OBJECT (state->chart.chart), "Backplane", NULL);
+	
+/* 	g_object_get (G_OBJECT (backplane), "style", &style, NULL); */
 }
 
 static void
@@ -4005,8 +4083,8 @@ static GsfXMLInNode const opendoc_content_dtd [] =
 		  GSF_XML_IN_NODE (CHART_SERIES, SERIES_DOMAIN, OO_NS_CHART, "domain", GSF_XML_NO_CONTENT, &oo_series_domain, NULL),
 		  GSF_XML_IN_NODE (CHART_SERIES, SERIES_DATA_PT, OO_NS_CHART, "data-point", GSF_XML_NO_CONTENT, &oo_series_pt, NULL),
 		  GSF_XML_IN_NODE (CHART_SERIES, SERIES_DATA_ERR, OO_NS_CHART, "error-indicator", GSF_XML_NO_CONTENT, NULL, NULL),
-		GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_WALL, OO_NS_CHART, "wall", GSF_XML_NO_CONTENT, NULL, NULL),
-		GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_FLOOR, OO_NS_CHART, "floor", GSF_XML_NO_CONTENT, &oo_chart_wall, NULL),
+		GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_WALL, OO_NS_CHART, "wall", GSF_XML_NO_CONTENT, &oo_chart_wall, NULL),
+		GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_FLOOR, OO_NS_CHART, "floor", GSF_XML_NO_CONTENT, NULL, NULL),
 		GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_AXIS, OO_NS_CHART, "axis", GSF_XML_NO_CONTENT, &oo_chart_axis, NULL),
 		  GSF_XML_IN_NODE (CHART_AXIS, CHART_GRID, OO_NS_CHART, "grid", GSF_XML_NO_CONTENT, &oo_chart_grid, NULL),
 		  GSF_XML_IN_NODE (CHART_AXIS, CHART_AXIS_CAT,   OO_NS_CHART, "categories", GSF_XML_NO_CONTENT, NULL, NULL),
diff --git a/plugins/openoffice/openoffice-write.c b/plugins/openoffice/openoffice-write.c
index a39673b..cf8564a 100644
--- a/plugins/openoffice/openoffice-write.c
+++ b/plugins/openoffice/openoffice-write.c
@@ -382,6 +382,16 @@ odf_add_angle (GsfXMLOut *xml, char const *id, int val)
 	gsf_xml_out_add_int (xml, id, val);
 }
 
+static void
+odf_add_percent (GsfXMLOut *xml, char const *id, double val)
+{
+	GString *str = g_string_new (NULL);
+	
+	g_string_append_printf (str, "%.2f%%", val * 100.);
+	gsf_xml_out_add_cstr_unchecked (xml, id, str->str);
+	g_string_free (str, TRUE);	
+}
+
 
 static void
 odf_add_pt (GsfXMLOut *xml, char const *id, float l)
@@ -2146,6 +2156,7 @@ odf_write_frame (GnmOOExport *state, SheetObject *so)
 	formula = gnm_expr_top_as_string (texpr, &pp, state->conv);
 	gnm_expr_top_unref (texpr);
 	gsf_xml_out_add_cstr (state->xml, TABLE "end-cell-address", odf_strip_brackets (formula));
+	g_free (formula);
 
 	if (IS_SHEET_OBJECT_GRAPH (so)) {
 		char const *name = g_hash_table_lookup (state->objects, so);
@@ -3146,21 +3157,54 @@ odf_write_manifest (GnmOOExport *state, GsfOutput *child)
 }
 
 /**********************************************************************************/
+typedef enum {
+	ODF_BARCOL,
+	ODF_LINE,
+	ODF_AREA,
+	ODF_DROPBAR,
+	ODF_MINMAX,
+	ODF_CIRCLE,
+	ODF_RADAR,
+	ODF_RADARAREA,
+	ODF_RING,
+	ODF_SCATTER,
+	ODF_SURF,
+	ODF_BUBBLE,
+	ODF_STOCK
+} odf_chart_type_t; 
+
 static void
 odf_write_series (GnmOOExport *state, GSList const *series)
 {
 	GnmParsePos pp;
+	int i;
 	parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
 
-	for ( ; NULL != series ; series = series->next) {
+	for (i = 1; NULL != series ; series = series->next, i++) {
 		GOData const *dat = gog_dataset_get_dim (GOG_DATASET (series->data), GOG_MS_DIM_VALUES);
 		if (NULL != dat) {
 			GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
 			if (NULL != texpr) {
 				char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
+				GOData const *cat = gog_dataset_get_dim (GOG_DATASET (series->data), GOG_MS_DIM_LABELS);
 				gsf_xml_out_start_element (state->xml, CHART "series");
 				gsf_xml_out_add_cstr (state->xml, CHART "values-cell-range-address", 
 						      odf_strip_brackets (str));
+				g_free (str);
+				str = g_strdup_printf ("series%i", i);
+				gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
+				g_free (str);
+				if (NULL != cat) {
+					texpr = gnm_go_data_get_expr (cat);	
+					if (NULL != texpr) {
+						str = gnm_expr_top_as_string (texpr, &pp, state->conv);
+						gsf_xml_out_start_element (state->xml, CHART "domain");
+						gsf_xml_out_add_cstr (state->xml, TABLE "cell-range-address", 
+								      odf_strip_brackets (str));
+						gsf_xml_out_end_element (state->xml); /* </chart:domain> */
+						g_free (str);
+					}
+				}
 				gsf_xml_out_end_element (state->xml); /* </chart:series> */
 			}
 		}
@@ -3168,11 +3212,107 @@ odf_write_series (GnmOOExport *state, GSList const *series)
 }
 
 static void
-odf_write_bar_col_plot_style (GnmOOExport *state, GogObject const *chart, GogObject const *plot)
+odf_write_gantt_series (GnmOOExport *state, GSList const *series)
+{
+	GnmParsePos pp;
+	int i;
+	parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
+
+	for (i = 1; NULL != series ; series = series->next, i++) {
+		GOData const *dat = gog_dataset_get_dim (GOG_DATASET (series->data), GOG_MS_DIM_VALUES);
+		if (NULL != dat) {
+			GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
+			if (NULL != texpr) {
+				char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
+				GOData const *cat = gog_dataset_get_dim (GOG_DATASET (series->data), GOG_MS_DIM_LABELS);
+				gsf_xml_out_start_element (state->xml, CHART "series");
+				gsf_xml_out_add_cstr (state->xml, CHART "values-cell-range-address", 
+						      odf_strip_brackets (str));
+				g_free (str);
+				str = g_strdup_printf ("series%i", i);
+				gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
+				g_free (str);
+				if (NULL != cat) {
+					texpr = gnm_go_data_get_expr (cat);	
+					if (NULL != texpr) {
+						str = gnm_expr_top_as_string (texpr, &pp, state->conv);
+						gsf_xml_out_start_element (state->xml, CHART "domain");
+						gsf_xml_out_add_cstr (state->xml, TABLE "cell-range-address", 
+								      odf_strip_brackets (str));
+						gsf_xml_out_end_element (state->xml); /* </chart:domain> */
+						g_free (str);
+					}
+				}
+				gsf_xml_out_end_element (state->xml); /* </chart:series> */
+			}
+		}
+		dat = gog_dataset_get_dim (GOG_DATASET (series->data), GOG_MS_DIM_CATEGORIES);
+		if (NULL != dat) {
+			GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
+			if (NULL != texpr) {
+				char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
+				gsf_xml_out_start_element (state->xml, CHART "series");
+				gsf_xml_out_add_cstr (state->xml, CHART "values-cell-range-address", 
+						      odf_strip_brackets (str));
+				g_free (str);
+				str = g_strdup_printf ("series%i", i);
+				gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
+				g_free (str);
+				gsf_xml_out_end_element (state->xml); /* </chart:series> */
+			}
+		}
+	}
+}
+
+static void
+odf_write_bubble_series (GnmOOExport *state, GSList const *orig_series)
+{
+	GnmParsePos pp;
+	int i;
+	GSList const *series;
+	parse_pos_init (&pp, WORKBOOK (state->wb), NULL, 0,0 );
+
+	gsf_xml_out_start_element (state->xml, CHART "series");
+	for (series = orig_series, i = 0; NULL != series; series = series->next, i++) {
+		GOData const *dat = gog_dataset_get_dim (GOG_DATASET (series->data), 2);
+
+		if (NULL != dat) {
+			GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
+			if (NULL != texpr) {
+				char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
+				gsf_xml_out_add_cstr (state->xml, CHART "values-cell-range-address",
+						      odf_strip_brackets (str));
+				g_free (str);
+				str = g_strdup_printf ("series%i", i);
+				gsf_xml_out_add_cstr (state->xml, CHART "style-name", str);
+				g_free (str);
+				break;
+			}
+		}
+	}
+	for (i = 1; i >= 0; i--) 
+		for (series = orig_series; NULL != series ; series = series->next) {
+			GOData const *dat = gog_dataset_get_dim (GOG_DATASET (series->data), i);
+			if (NULL != dat) {
+				GnmExprTop const *texpr = gnm_go_data_get_expr (dat);
+				if (NULL != texpr) {
+					char *str = gnm_expr_top_as_string (texpr, &pp, state->conv);
+					gsf_xml_out_start_element (state->xml, CHART "domain");
+					gsf_xml_out_add_cstr (state->xml, TABLE "cell-range-address", 
+							      odf_strip_brackets (str));
+					gsf_xml_out_end_element (state->xml); /* </chart:domain> */
+					g_free (str);
+					break;
+				}
+			}
+		}
+	gsf_xml_out_end_element (state->xml); /* </chart:series> */
+}
+
+static void
+odf_write_bar_col_plot_style (GnmOOExport *state, G_GNUC_UNUSED GogObject const *chart, GogObject const *plot)
 {
 	gboolean horizontal = FALSE;
-	if (plot == NULL)
-		return;
 
 	g_object_get (G_OBJECT (plot), "horizontal", &horizontal, NULL);
 	/* Note: horizontal refers to the bars and vertical to the x-axis */
@@ -3180,8 +3320,35 @@ odf_write_bar_col_plot_style (GnmOOExport *state, GogObject const *chart, GogObj
 }
 
 static void
+odf_write_ring_plot_style (GnmOOExport *state, G_GNUC_UNUSED GogObject const *chart, G_GNUC_UNUSED GogObject const *plot)
+{
+	odf_add_percent (state->xml, CHART "hole-size", 0.5);
+}
+
+static void
+odf_write_line_chart_style (GnmOOExport *state, G_GNUC_UNUSED GogObject const *chart, G_GNUC_UNUSED GogObject const *plot)
+{
+	gsf_xml_out_add_cstr (state->xml, CHART "symbol-type", "none");
+}
+
+static void
+odf_write_scatter_chart_style (GnmOOExport *state, G_GNUC_UNUSED GogObject const *chart, G_GNUC_UNUSED GogObject const *plot)
+{
+	gsf_xml_out_add_cstr (state->xml, DRAW "stroke", "none");
+	odf_add_bool (state->xml, CHART "lines", FALSE);
+}
+
+static void
+odf_write_scatter_series_style (GnmOOExport *state, G_GNUC_UNUSED GogObject const *series)
+{
+	gsf_xml_out_add_cstr (state->xml, DRAW "stroke", "none");
+	odf_add_bool (state->xml, CHART "lines", FALSE);
+	gsf_xml_out_add_cstr (state->xml, CHART "symbol-type", "automatic");
+}
+
+static void
 odf_write_axis_style (GnmOOExport *state, GogObject const *chart, char const *axis_role, 
-		      char const *style_label)
+		      char const *style_label, odf_chart_type_t gtype)
 {
 	GogObject const *axis = gog_object_get_child_by_name (chart, axis_role);
 	if (axis != NULL) {
@@ -3194,12 +3361,46 @@ odf_write_axis_style (GnmOOExport *state, GogObject const *chart, char const *ax
 		g_object_get (G_OBJECT (axis), "map-name", &type, NULL);
 		odf_add_bool (state->xml, CHART "logarithmic", 0 != strcmp (type, "Linear"));
 		gsf_xml_out_add_cstr (state->xml, CHART "axis-position", "start");
+		odf_add_bool (state->xml, CHART "display-label", TRUE);
 
 		if (gog_axis_get_bounds (GOG_AXIS (axis), &minima, &maxima)) {
 			gsf_xml_out_add_float (state->xml, CHART "minimum", minima, -1);
 			gsf_xml_out_add_float (state->xml, CHART "maximum", maxima, -1);
 		}
 
+		if (get_gsf_odf_version () > 101)
+			switch (gtype) {
+			case ODF_CIRCLE:
+				odf_add_bool (state->xml, CHART "reverse-direction", TRUE);
+				break;
+			case ODF_RING:
+				odf_add_bool (state->xml, CHART "reverse-direction",
+					      *style_label == 'y' );
+				break;
+			case ODF_DROPBAR:
+				odf_add_bool (state->xml, CHART "reverse-direction", 
+					      *style_label == 'x' && *axis_role == 'Y');
+				break;
+			default:
+				break;
+			}
+		gsf_xml_out_end_element (state->xml); /* </style:chart-properties> */
+		gsf_xml_out_end_element (state->xml); /* </style:style> */
+	} else if (gtype == ODF_CIRCLE || gtype == ODF_RING) {
+		odf_start_style (state->xml, style_label, "chart");
+		gsf_xml_out_start_element (state->xml, STYLE "chart-properties");
+		if (get_gsf_odf_version () > 101)
+			switch (gtype) {
+			case ODF_CIRCLE:
+				odf_add_bool (state->xml, CHART "reverse-direction", TRUE);
+				break;
+			case ODF_RING:
+				odf_add_bool (state->xml, CHART "reverse-direction",
+					      *style_label == 'y' );
+				break;
+			default:
+				break;
+			}
 		gsf_xml_out_end_element (state->xml); /* </style:chart-properties> */
 		gsf_xml_out_end_element (state->xml); /* </style:style> */
 	}
@@ -3207,10 +3408,10 @@ odf_write_axis_style (GnmOOExport *state, GogObject const *chart, char const *ax
 
 static void
 odf_write_axis (GnmOOExport *state, GogObject const *chart, char const *axis_role, char const *style_label,
-	char const *dimension)
+	char const *dimension, odf_chart_type_t gtype)
 {
 	GogObject const *axis = gog_object_get_child_by_name (chart, axis_role);
-	if (axis != NULL) {
+	if (axis != NULL || (gtype == ODF_CIRCLE && *dimension == 'y') || (gtype == ODF_RING)) {
 		gsf_xml_out_start_element (state->xml, CHART "axis");
 		gsf_xml_out_add_cstr (state->xml, CHART "dimension", dimension);
 		gsf_xml_out_add_cstr (state->xml, CHART "style-name", style_label);
@@ -3219,89 +3420,179 @@ odf_write_axis (GnmOOExport *state, GogObject const *chart, char const *axis_rol
 }
 
 static void
-odf_write_plot (GnmOOExport *state, GogObject const *chart, GogObject const *plot)
+odf_write_plot (GnmOOExport *state, SheetObject *so, GogObject const *chart, GogObject const *plot)
 {
-	
-	enum {
-		ODF_BARCOL,
-		ODF_LINE,
-		ODF_AREA,
-		ODF_DROPBAR,
-		ODF_MINMAX,
-		ODF_CIRCLE,
-		ODF_RADAR,
-		ODF_RADARAREA,
-		ODF_RING,
-		ODF_SCATTER,
-		ODF_SURF,
-		ODF_STOCK
-	} gtype;
+	odf_chart_type_t gtype;
 	char const *plot_type = G_OBJECT_TYPE_NAME (plot);
 	char const *odf_plot_type;
+	SheetObjectAnchor const *anchor = sheet_object_get_anchor (so);
+	double res_pts[4] = {0.,0.,0.,0.};
+	double pad = 4.;
+	gboolean horizontal = FALSE;
+	GSList const *series, *l;
+	int i;
+	GogObject *wall = gog_object_get_child_by_name (plot, "Backplane");
 
 	if (0 == strcmp (plot_type, "GogBarColPlot")) {
 		gtype = ODF_BARCOL;
 		odf_plot_type = "chart:bar";
+		pad = 20.;
 	} else if (0 == strcmp (plot_type, "GogLinePlot")) {
 		gtype = ODF_LINE;
 		odf_plot_type = "chart:line";
+		pad = 20.;
 	} else if (0 == strcmp (plot_type, "GogAreaPlot")) {
 		gtype = ODF_AREA;
 		odf_plot_type = "chart:area";
+		pad = 20.;
 	} else if (0 == strcmp (plot_type, "GogDropBarPlot")) {
 		gtype = ODF_DROPBAR;
-		odf_plot_type = "chart:bar";
+		odf_plot_type = "chart:gantt";
+		pad = 20.;
 	} else if (0 == strcmp (plot_type, "GogMinMaxPlot")) {
 		gtype = ODF_STOCK;
 		odf_plot_type = "chart:stock";
+		pad = 10.;
 	} else if (0 == strcmp (plot_type, "GogPiePlot")) {
 		gtype = ODF_CIRCLE;
 		odf_plot_type = "chart:circle";
+		pad = 5.;
 	} else if (0 == strcmp (plot_type, "GogRadarPlot")) {
 		gtype = ODF_RADAR;
 		odf_plot_type = "chart:radar";
+		pad = 10.;
 	} else if (0 == strcmp (plot_type, "GogRadarAreaPlot")) {
 		gtype = ODF_RADARAREA;
 		odf_plot_type = "chart:radararea";
+		pad = 10.;
 	} else if (0 == strcmp (plot_type, "GogRingPlot")) {
 		gtype = ODF_RING;
 		odf_plot_type = "chart:ring";
+		pad = 10.;
 	} else if (0 == strcmp (plot_type, "GogXYPlot")) {
 		gtype = ODF_SCATTER;
 		odf_plot_type = "chart:scatter";
+		pad = 20.;
 	} else if (0 == strcmp (plot_type, "GogContourPlot")) {
 		gtype = ODF_SURF;
 		odf_plot_type = "chart:surface";
+		pad = 20.;
+	} else if (0 == strcmp (plot_type, "GogXYZContourPlot")) {
+		gtype = ODF_SURF;
+		odf_plot_type = "chart:surface";
+		pad = 20.;
+	} else if (0 == strcmp (plot_type, "GogBubblePlot")) {
+		gtype = ODF_BUBBLE;
+		odf_plot_type = "chart:bubble";
+		pad = 20.;
 	} else {
 		g_print ("encountered unknown chart type %s\n", plot_type);
 		gtype = ODF_BARCOL;
-		odf_plot_type = "GogBarColPlot";
+		odf_plot_type = "chart:bar";
 	}
 
+	series = gog_plot_get_series (GOG_PLOT (plot));
+
 	gsf_xml_out_start_element (state->xml, OFFICE "automatic-styles");
-	odf_write_axis_style (state, chart, "Y-Axis", "yaxis");
-	odf_write_axis_style (state, chart, "X-Axis", "xaxis");
+
+	switch (gtype) {
+	case ODF_RADAR:
+		odf_write_axis_style (state, chart, "Radial-Axis", "yaxis", gtype);
+		odf_write_axis_style (state, chart, "Circular-Axis", "xaxis", gtype);
+		break;
+	case ODF_BARCOL:
+	case ODF_DROPBAR:
+		g_object_get (G_OBJECT (plot), "horizontal", &horizontal, NULL);
+		odf_write_axis_style (state, chart, "Y-Axis", horizontal ? "xaxis" : "yaxis", gtype);
+		odf_write_axis_style (state, chart, "X-Axis", horizontal ? "yaxis" : "xaxis", gtype);
+		break;
+	default:
+		odf_write_axis_style (state, chart, "Y-Axis", "yaxis", gtype);
+		odf_write_axis_style (state, chart, "X-Axis", "xaxis", gtype);
+		break;
+	}
+
 	odf_start_style (state->xml, "plotstyle", "chart");
 	gsf_xml_out_start_element (state->xml, STYLE "chart-properties");
+	odf_add_bool (state->xml, CHART "auto-size", TRUE);
 
-	if (gtype == ODF_BARCOL)
-		odf_write_bar_col_plot_style (state, chart, plot);	
+	switch (gtype) {
+	case ODF_SCATTER:
+		odf_write_scatter_chart_style (state, chart, plot);
+		break;
+	case ODF_LINE:
+		odf_write_line_chart_style (state, chart, plot);
+		break;
+	default:
+			break;
+	}
 
 	gsf_xml_out_end_element (state->xml); /* </style:chart-properties> */
 	gsf_xml_out_end_element (state->xml); /* </style:style> */
+
+	odf_start_style (state->xml, "plotarea", "chart");
+	gsf_xml_out_start_element (state->xml, STYLE "chart-properties");
+	odf_add_bool (state->xml, CHART "auto-size", TRUE);
+
+	switch (gtype) {
+	case ODF_BARCOL:
+	case ODF_DROPBAR:
+		odf_write_bar_col_plot_style (state, chart, plot);
+		break;
+	case ODF_RING:
+		odf_write_ring_plot_style (state, chart, plot);
+		break;
+	default:
+			break;
+	}
+
+	gsf_xml_out_end_element (state->xml); /* </style:chart-properties> */
+	gsf_xml_out_end_element (state->xml); /* </style:style> */
+
+	for (l = series, i = 1; l != NULL; l = l->next) {
+		char *name = g_strdup_printf ("series%i", i++);
+		odf_start_style (state->xml, name, "chart");
+		gsf_xml_out_start_element (state->xml, STYLE "chart-properties");
+		odf_add_bool (state->xml, CHART "auto-size", TRUE);
+		switch (gtype) {
+		case ODF_SCATTER:
+			odf_write_scatter_series_style (state, l->data);
+			break;
+		default:
+			break;
+		}
+		gsf_xml_out_end_element (state->xml); /* </style:chart-properties> */
+		gsf_xml_out_end_element (state->xml); /* </style:style> */	
+		g_free (name);
+	}
+
+	if (wall != NULL) {
+		odf_start_style (state->xml, "wallstyle", "chart");
+		gsf_xml_out_start_element (state->xml, STYLE "graphic-properties");
+		gsf_xml_out_add_cstr (state->xml, DRAW "fill", "solid");
+/* 	gnm_xml_out_add_hex_color (state->xml, DRAW "fill-color", GnmColor const *c) */
+		gsf_xml_out_add_cstr (state->xml, DRAW "fill-color", "#D0D0D0");
+		gsf_xml_out_end_element (state->xml); /* </style:graphic-properties> */
+		gsf_xml_out_end_element (state->xml); /* </style:style> */
+	}
+
 	gsf_xml_out_end_element (state->xml); /* </office:automatic-styles> */
 
 	gsf_xml_out_start_element (state->xml, OFFICE "body");
 	gsf_xml_out_start_element (state->xml, OFFICE "chart");
 	gsf_xml_out_start_element (state->xml, CHART "chart");
+
+	sheet_object_anchor_to_pts (anchor, state->sheet, res_pts);
+	odf_add_pt (state->xml, SVG "width", res_pts[2] - res_pts[0] - 2 * pad);
+	odf_add_pt (state->xml, SVG "height", res_pts[3] - res_pts[1] - 2 * pad);
+	
 	if (get_gsf_odf_version () > 101)
 		gsf_xml_out_add_cstr (state->xml, XLINK "href", "..");
 	gsf_xml_out_add_cstr (state->xml, CHART "class", odf_plot_type);
 	gsf_xml_out_add_cstr (state->xml, CHART "style-name", "plotstyle");
 	gsf_xml_out_start_element (state->xml, CHART "plot-area");
-	gsf_xml_out_add_cstr (state->xml, CHART "style-name", "plotstyle");
+	gsf_xml_out_add_cstr (state->xml, CHART "style-name", "plotarea");
 	if (get_gsf_odf_version () <= 101) {
-		GSList const *series = gog_plot_get_series (GOG_PLOT (plot));
 		for ( ; NULL != series ; series = series->next) {
 			GOData const *dat = gog_dataset_get_dim 
 				(GOG_DATASET (series->data), GOG_MS_DIM_VALUES);
@@ -3314,14 +3605,42 @@ odf_write_plot (GnmOOExport *state, GogObject const *chart, GogObject const *plo
 					str = gnm_expr_top_as_string (texpr, &pp, state->conv);
 					gsf_xml_out_add_cstr (state->xml, TABLE "cell-range-address", 
 							      odf_strip_brackets (str));
+					g_free (str);
 					break;
 				}
 			}
 		}
 	}
-	odf_write_axis (state, chart, "Y-Axis", "yaxis", "y");
-	odf_write_axis (state, chart, "X-Axis", "xaxis", "x");
-	odf_write_series (state, gog_plot_get_series (GOG_PLOT (plot)));
+
+	switch (gtype) {
+	case ODF_RADAR:
+		odf_write_axis (state, chart, "Radial-Axis", "yaxis", "y", gtype);
+		odf_write_axis (state, chart, "Circular-Axis", "xaxis", "x", gtype);
+		odf_write_series (state, series);
+		break;
+	case ODF_BUBBLE:
+		odf_write_axis (state, chart, "Y-Axis", "yaxis", "y", gtype);
+		odf_write_axis (state, chart, "X-Axis", "xaxis", "x", gtype);
+		odf_write_bubble_series (state, series);
+		break;
+	case ODF_DROPBAR:
+		odf_write_axis (state, chart, "Y-Axis", "yaxis", "y", gtype);
+		odf_write_axis (state, chart, "X-Axis", "xaxis", "x", gtype);
+		odf_write_gantt_series (state, series);
+		break;
+	default:
+		odf_write_axis (state, chart, "Y-Axis", "yaxis", "y", gtype);
+		odf_write_axis (state, chart, "X-Axis", "xaxis", "x", gtype);
+		odf_write_series (state, series);
+		break;
+	}
+
+	if (wall != NULL) {
+		gsf_xml_out_start_element (state->xml, CHART "wall");
+		odf_add_pt (state->xml, SVG "width", res_pts[2] - res_pts[0] - 2 * pad);
+		gsf_xml_out_add_cstr (state->xml, CHART "style-name", "wallstyle");
+		gsf_xml_out_end_element (state->xml); /* </chart:wall> */
+	}
 	gsf_xml_out_end_element (state->xml); /* </chart:plot_area> */
 	gsf_xml_out_end_element (state->xml); /* </chart:chart> */
 	gsf_xml_out_end_element (state->xml); /* </office:chart> */
@@ -3350,7 +3669,7 @@ odf_write_graph_content (GnmOOExport *state, GsfOutput *child, SheetObject *so)
 		if (chart != NULL) {
 			GogObject const *plot = gog_object_get_child_by_name (GOG_OBJECT (chart), "Plot");
 			if (plot != NULL) 
-				odf_write_plot (state, chart, plot);
+				odf_write_plot (state, so, chart, plot);
 		}
 	}
 	gsf_xml_out_end_element (state->xml); /* </office:document-content> */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]