1 /**
2  * ms-chart.c: MS Excel chart support for Gnumeric
3  *
4  * Author:
5  *    Jody Goldberg (jody@gnome.org)
6  *
7  * (C) 1999-2007 Jody Goldberg
8  **/
9 
10 #include <gnumeric-config.h>
11 #include <gnumeric.h>
12 #include "boot.h"
13 #include "excel.h"
14 #include "ms-chart.h"
15 #include "ms-formula-read.h"
16 #include "ms-excel-read.h"
17 #include "ms-excel-write.h"
18 #include "ms-escher.h"
19 #include "ms-formula-write.h"
20 #include <compilation.h>
21 
22 #include <parse-util.h>
23 #include <gnm-format.h>
24 #include <expr.h>
25 #include <value.h>
26 #include <gutils.h>
27 #include <graph.h>
28 #include <style-color.h>
29 #include <sheet-object-graph.h>
30 #include <workbook-view.h>
31 
32 #include <goffice/goffice.h>
33 
34 #include <gsf/gsf-utils.h>
35 #include <math.h>
36 #include <string.h>
37 
38 /* #define NO_DEBUG_EXCEL */
39 #ifndef NO_DEBUG_EXCEL
40 #define d(level, code)	do { if (ms_excel_chart_debug > level) { code } } while (0)
41 #else
42 #define d(level, code)
43 #endif
44 
45 #define reg_show_R2 err_teetop
46 #define reg_order err_num
47 #define reg_intercept err_val
48 #define reg_parent err_parent
49 
50 typedef struct {
51 	struct {
52 		int num_elements;
53 		GOData *data;
54 		GnmValueArray *value;
55 	} data [GOG_MS_DIM_TYPES];
56 	GogSeries *series;
57 	int err_type;
58 	int reg_type;
59 	int err_num; /* also used for order in reg curves */
60 	int err_src;
61 	int err_parent; /* also used for parent in reg curves */
62 	double err_val; /* also used for intercept in reg curves */
63 	double reg_backcast, reg_forecast;
64 	double reg_min, reg_max;
65 	GOData *reg_dims[2];
66 	gboolean err_teetop; /* also used for show R-squared in reg curves */
67 	gboolean reg_show_eq;
68 	gboolean reg_skip_invalid;
69 	GogMSDimType extra_dim;
70 	int chart_group;
71 	gboolean  has_legend;
72 	GOStyle *style;
73 	GHashTable *singletons;
74 	GOLineInterpolation interpolation;
75 } XLChartSeries;
76 
77 typedef struct {
78 	MSContainer	 container;
79 
80 	gboolean        error;
81 	GArray		*stack;
82 	MsBiffVersion	 ver;
83 	guint32		 prev_opcode;
84 
85 	SheetObject	*sog;
86 	GogGraph	*graph;
87 	GogChart	*chart;
88 	GogObject	*legend;
89 	GogPlot		*plot;
90 	GogObject	*label;
91 	GOStyle		*default_plot_style;
92 	GogObject	*axis, *xaxis;
93 	guint8		axislineflags;
94 	GOStyle		*style;
95 	GOStyle		*chartline_style[3];
96 	GOStyle		*dropbar_style;
97 
98 	int		 style_element;
99 	int		 cur_role;
100 	gboolean	 hilo;
101 	gboolean	 dropbar;
102 	guint16		 dropbar_width;
103 
104 	gboolean	 frame_for_grid;
105 	gboolean	 has_a_grid;
106 	gboolean	 is_surface;
107 	gboolean	 is_contour;
108 	gboolean	 has_extra_dataformat;
109 	gboolean	axis_cross_at_max;
110 	double		axis_cross_value;
111 	int		 plot_counter;
112 	XLChartSeries	*currentSeries;
113 	GPtrArray	*series;
114 	char		*text;
115 	guint16		parent_index;
116 	GOLineInterpolation interpolation;
117 } XLChartReadState;
118 
119 typedef struct _XLChartHandler	XLChartHandler;
120 typedef gboolean (*XLChartReader) (XLChartHandler const *handle,
121 				   XLChartReadState *, BiffQuery *q);
122 struct _XLChartHandler {
123 	guint16 const opcode;
124 	guint16 const min_size; /* Minimum across all versions.  */
125 	char const *const name;
126 	XLChartReader const read_fn;
127 };
128 
129 #define BC(n)	xl_chart_ ## n
130 #define BC_R(n)	BC(read_ ## n)
131 
132 static gboolean
check_style(GOStyle * style,char const * object)133 check_style (GOStyle *style, char const *object)
134 {
135 	if (style == NULL) {
136 		g_warning ("File is most likely corrupted.\n"
137 			   "(%s has no associated style.)",
138 			   object);
139 		return FALSE;
140 	}
141 	return TRUE;
142 }
143 
144 static inline MsBiffVersion
BC_R(ver)145 BC_R (ver) (XLChartReadState const *s)
146 {
147 	return s->container.importer->ver;
148 }
149 
150 static XLChartSeries *
excel_chart_series_new(void)151 excel_chart_series_new (void)
152 {
153 	XLChartSeries *series;
154 
155 	series = g_new0 (XLChartSeries, 1);
156 	series->chart_group = -1;
157 	series->has_legend = TRUE;
158 
159 	return series;
160 }
161 
162 static void
excel_chart_series_delete(XLChartSeries * series)163 excel_chart_series_delete (XLChartSeries *series)
164 {
165 	int i;
166 
167 	for (i = GOG_MS_DIM_TYPES; i-- > 0 ; ) {
168 		if (series->data [i].data != NULL)
169 			g_object_unref (series->data[i].data);
170 		if (series->data [i].value != NULL)
171 			value_release ((GnmValue *)(series->data[i].value));
172 	}
173 	if (series->style != NULL)
174 		g_object_unref (series->style);
175 	if (series->singletons != NULL)
176 		g_hash_table_destroy (series->singletons);
177 	g_free (series);
178 }
179 
180 static void
BC_R(get_style)181 BC_R(get_style) (XLChartReadState *s)
182 {
183 	if (s->style == NULL)
184 		s->style = (GOStyle *) gog_style_new ();
185 }
186 
187 static int
BC_R(top_state)188 BC_R(top_state) (XLChartReadState *s, unsigned n)
189 {
190 	g_return_val_if_fail (s != NULL, 0);
191 	XL_CHECK_CONDITION_VAL (s->stack->len >= n+1, 0);
192 	return g_array_index (s->stack, int, s->stack->len-1-n);
193 }
194 
195 static GOColor
BC_R(color)196 BC_R(color) (guint8 const *data, char const *type)
197 {
198 	guint32 const bgr = GSF_LE_GET_GUINT32 (data);
199 	guint16 const r = (bgr >>  0) & 0xff;
200 	guint16 const g = (bgr >>  8) & 0xff;
201 	guint16 const b = (bgr >> 16) & 0xff;
202 
203 	d (1, g_printerr ("%s %02x:%02x:%02x;\n", type, r, g, b););
204 	return GO_COLOR_FROM_RGB (r, g, b);
205 }
206 
207 /****************************************************************************/
208 
209 static gboolean
210 BC_R(3dbarshape)(XLChartHandler const *handle,
211 		 XLChartReadState *s, BiffQuery *q)
212 {
213 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
214 	d (0, {
215 		guint16 const type = GSF_LE_GET_GUINT16 (q->data);
216 		switch (type) {
217 		case 0 : g_printerr ("box"); break;
218 		case 1 : g_printerr ("cylinder"); break;
219 		case 256 : g_printerr ("pyramid"); break;
220 		case 257 : g_printerr ("cone"); break;
221 		default :
222 			   g_printerr ("unknown 3dshape %d\n", type);
223 		}
224 	});
225 
226 	return FALSE;
227 }
228 /****************************************************************************/
229 
230 static gboolean
231 BC_R(3d)(XLChartHandler const *handle,
232 	 XLChartReadState *s, BiffQuery *q)
233 {
234 	guint16 rotation, elevation, distance, height, depth, gap;
235 	guint8 flags, zero;
236 	gboolean use_perspective, cluster, auto_scale, walls_2d;
237 
238 	XL_CHECK_CONDITION_VAL (q->length >= 14, TRUE);
239 	rotation = GSF_LE_GET_GUINT16 (q->data);	/* 0-360 */
240 	elevation = GSF_LE_GET_GUINT16 (q->data+2);	/* -90 - 90 */
241 	distance = GSF_LE_GET_GUINT16 (q->data+4);	/* 0 - 100 */
242 	height = GSF_LE_GET_GUINT16 (q->data+6);
243 	depth = GSF_LE_GET_GUINT16 (q->data+8);
244 	gap = GSF_LE_GET_GUINT16 (q->data+10);
245 	flags = GSF_LE_GET_GUINT8 (q->data+12);
246 	zero = GSF_LE_GET_GUINT8 (q->data+13);
247 
248 	use_perspective = (flags&0x01) ? TRUE :FALSE;
249 	cluster = (flags&0x02) ? TRUE :FALSE;
250 	auto_scale = (flags&0x04) ? TRUE :FALSE;
251 	walls_2d = (flags&0x20) ? TRUE :FALSE;
252 
253 	g_return_val_if_fail (zero == 0, FALSE); /* just warn for now */
254 
255 	if (s->plot == NULL && s->is_surface) {
256 		s->is_contour = elevation == 90 && distance == 0;
257 		if (s->chart != NULL && !s->is_contour) {
258 			GogObject *box = gog_object_get_child_by_name (GOG_OBJECT (s->chart), "3D-Box");
259 			if (!box)
260 				box = gog_object_add_by_name (GOG_OBJECT (s->chart), "3D-Box", NULL);
261 			g_object_set (G_OBJECT (box), "theta", ((elevation > 0)? elevation: -elevation), NULL);
262 			/* FIXME: use other parameters */
263 		}
264 	}
265 	/* at this point, we don't know if data can be converted to a
266 	gnumeric matrix, so we cannot create the plot here. */
267 
268 
269 	d (1, {
270 		g_printerr ("Rot = %hu\n", rotation);
271 		g_printerr ("Elev = %hu\n", elevation);
272 		g_printerr ("Dist = %hu\n", distance);
273 		g_printerr ("Height = %hu\n", height);
274 		g_printerr ("Depth = %hu\n", depth);
275 		g_printerr ("Gap = %hu\n", gap);
276 
277 		if (use_perspective)
278 			g_printerr ("Use perspective;\n");
279 		if (cluster)
280 			g_printerr ("Cluster;\n");
281 		if (auto_scale)
282 			g_printerr ("Auto Scale;\n");
283 		if (walls_2d)
284 			g_printerr ("2D Walls;\n");
285 	});
286 
287 	return FALSE;
288 }
289 
290 /****************************************************************************/
291 
292 static gboolean
BC_R(ai)293 BC_R(ai)(XLChartHandler const *handle,
294 	 XLChartReadState *s, BiffQuery *q)
295 {
296 	guint8 purpose, ref_type, flags, length;
297 	int top_state;
298 
299 	XL_CHECK_CONDITION_VAL (q->length >= 8, TRUE);
300 
301 	purpose = GSF_LE_GET_GUINT8 (q->data);
302 	ref_type = GSF_LE_GET_GUINT8 (q->data + 1);
303 	flags = GSF_LE_GET_GUINT16 (q->data + 2);
304 	length = GSF_LE_GET_GUINT16 (q->data + 6);
305 
306 	top_state = BC_R(top_state) (s, 0);
307 
308 	XL_CHECK_CONDITION_VAL (q->length - 8 >= length, TRUE);
309 
310 	/* ignore these for now */
311 	if (top_state == BIFF_CHART_text) {
312 		GnmExprTop const *texpr;
313 		g_return_val_if_fail (s->label == NULL, FALSE);
314 		s->label = g_object_new (GOG_TYPE_LABEL, NULL);
315 		texpr = ms_container_parse_expr (&s->container,
316 						 q->data+8, length);
317 		if (texpr != NULL) {
318 			Sheet *sheet = ms_container_sheet (s->container.parent);
319 			GOData *data = gnm_go_data_scalar_new_expr (sheet, texpr);
320 
321 			XL_CHECK_CONDITION_VAL (sheet &&
322 						s->label,
323 						(gnm_expr_top_unref (texpr), TRUE));
324 			gog_dataset_set_dim (GOG_DATASET (s->label), 0, data, NULL);
325 		}
326 		return FALSE;
327 	}
328 	else if (top_state == BIFF_CHART_trendlimits) {
329 	}
330 
331 	/* Rest are 0 */
332 	if (flags&0x01) {
333 		GOFormat *fmt = ms_container_get_fmt (&s->container,
334 			GSF_LE_GET_GUINT16 (q->data + 4));
335 		d (2, g_printerr ("Has Custom number format;\n"););
336 		if (fmt != NULL) {
337 			const char *desc = go_format_as_XL (fmt);
338 			d (2, g_printerr ("Format = '%s';\n", desc););
339 
340 			go_format_unref (fmt);
341 		}
342 	} else {
343 		d (2, g_printerr ("Uses number format from data source;\n"););
344 	}
345 
346 	g_return_val_if_fail (purpose < GOG_MS_DIM_TYPES, TRUE);
347 	d (0, {
348 	switch (purpose) {
349 	case GOG_MS_DIM_LABELS :     g_printerr ("Labels;\n"); break;
350 	case GOG_MS_DIM_VALUES :     g_printerr ("Values;\n"); break;
351 	case GOG_MS_DIM_CATEGORIES : g_printerr ("Categories;\n"); break;
352 	case GOG_MS_DIM_BUBBLES :    g_printerr ("Bubbles;\n"); break;
353 	default :
354 		g_printerr ("UKNOWN : purpose (%x)\n", purpose);
355 	}
356 	switch (ref_type) {
357 	case 0 : g_printerr ("Use default categories;\n"); break;
358 	case 1 : g_printerr ("Text/Value entered directly;\n"); g_printerr ("data length = %d\n",length); break;
359 	case 2 : g_printerr ("Linked to Container;\n"); break;
360 	case 4 : g_printerr ("'Error reported' what the heck is this ??;\n"); break;
361 	default :
362 		 g_printerr ("UKNOWN : reference type (%x)\n", ref_type);
363 	}
364 	});
365 
366 	/* (2) == linked to container */
367 	if (ref_type == 2) {
368 		GnmExprTop const *texpr =
369 			ms_container_parse_expr (&s->container,
370 						 q->data+8, length);
371 		if (texpr != NULL) {
372 			Sheet *sheet = ms_container_sheet (s->container.parent);
373 
374 			if (sheet && s->currentSeries)
375 				s->currentSeries->data [purpose].data = (purpose == GOG_MS_DIM_LABELS)
376 					? gnm_go_data_scalar_new_expr (sheet, texpr)
377 					: gnm_go_data_vector_new_expr (sheet, texpr);
378 			else {
379 				gnm_expr_top_unref (texpr);
380 				g_return_val_if_fail (sheet != NULL, FALSE);
381 				g_return_val_if_fail (s->currentSeries != NULL, TRUE);
382 			}
383 		}
384 	} else if (ref_type == 1 && purpose != GOG_MS_DIM_LABELS &&
385 		   s->currentSeries &&
386 		   s->currentSeries->data [purpose].num_elements > 0) {
387 		if (s->currentSeries->data [purpose].value)
388 			g_warning ("Leak?");
389 
390 		s->currentSeries->data[purpose].value = (GnmValueArray *)
391 			value_new_array (1, s->currentSeries->data [purpose].num_elements);
392 	} else {
393 		g_return_val_if_fail (length == 0, TRUE);
394 	}
395 
396 	return FALSE;
397 }
398 
399 /****************************************************************************/
400 
401 static gboolean
BC_R(alruns)402 BC_R(alruns)(XLChartHandler const *handle,
403 	     XLChartReadState *s, BiffQuery *q)
404 {
405 	XL_CHECK_CONDITION_VAL (q->length >= 4, TRUE);
406 #if 0
407 	int length = GSF_LE_GET_GUINT16 (q->data);
408 	guint8 const *in = (q->data + 2);
409 	char *conans = g_new (char, length + 2);
410 	char *out = ans;
411 
412 	for (; --length >= 0 ; in+=4, ++out) {
413 		/*
414 		 * FIXME FIXME FIXME :
415 		 *        - don't toss font info
416 		 *        - Merge streams of the same font together.
417 		 *        - Combine with RTF support once merged
418 		 */
419 		guint32 const elem = GSF_LE_GET_GUINT32 (in);
420 		*out = (char)((elem >> 16) & 0xff);
421 	}
422 	*out = '\0';
423 
424 	/*g_printerr (ans);*/
425 #endif
426 	return FALSE;
427 }
428 
429 /****************************************************************************/
430 
431 static gboolean
BC_R(area)432 BC_R(area)(XLChartHandler const *handle,
433 	   XLChartReadState *s, BiffQuery *q)
434 {
435 	guint16 flags;
436 	char const *type = "normal";
437 	gboolean in_3d;
438 
439 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
440 
441 	flags = GSF_LE_GET_GUINT16 (q->data);
442 	in_3d = (BC_R(ver)(s) >= MS_BIFF_V8 && (flags & 0x04));
443 
444 	g_return_val_if_fail (s->plot == NULL, TRUE);
445 	s->plot = (GogPlot*) gog_plot_new_by_name ("GogAreaPlot");
446 	g_return_val_if_fail (s->plot != NULL, TRUE);
447 
448 	if (flags & 0x02)
449 		type = "as_percentage";
450 	else if (flags & 0x01)
451 		type = "stacked";
452 
453 	g_object_set (G_OBJECT (s->plot),
454 		"type",			type,
455 		"in-3d",		in_3d,
456 		NULL);
457 
458 	d(1, g_printerr ("%s area;", type););
459 	return FALSE;
460 }
461 
462 /****************************************************************************/
463 
464 static gboolean
BC_R(areaformat)465 BC_R(areaformat)(XLChartHandler const *handle,
466 		 XLChartReadState *s, BiffQuery *q)
467 {
468 	guint16 pattern, flags;
469 	gboolean auto_format, invert_if_negative;
470 
471 	XL_CHECK_CONDITION_VAL (q->length >= 12, TRUE);
472 
473 	pattern = GSF_LE_GET_GUINT16 (q->data+8);
474 	flags = GSF_LE_GET_GUINT16 (q->data+10);
475 	auto_format = (flags & 0x01) ? TRUE : FALSE;
476 	invert_if_negative = flags & 0x02;
477 
478 	d (0, {
479 		g_printerr ("pattern = %d;\n", pattern);
480 		if (auto_format)
481 			g_printerr ("Use auto format;\n");
482 		if (invert_if_negative)
483 			g_printerr ("Swap fore and back colours when displaying negatives;\n");
484 	});
485 
486 #if 0
487 	/* 18 */ "5%"
488 #endif
489 	BC_R(get_style) (s);
490 	if (pattern > 0) {
491 		s->style->fill.type = GO_STYLE_FILL_PATTERN;
492 		s->style->fill.invert_if_negative = invert_if_negative;
493 		s->style->fill.pattern.pattern = pattern - 1;
494 		s->style->fill.pattern.fore = BC_R(color) (q->data+0, "AreaFore");
495 		s->style->fill.pattern.back = BC_R(color) (q->data+4, "AreaBack");
496 		if (s->style->fill.pattern.pattern == 0) {
497 			GOColor tmp = s->style->fill.pattern.fore;
498 			s->style->fill.pattern.fore = s->style->fill.pattern.back;
499 			s->style->fill.pattern.back = tmp;
500 			s->style->fill.auto_fore = auto_format;
501 			s->style->fill.auto_back = FALSE;
502 		} else {
503 			s->style->fill.auto_fore = FALSE;
504 			s->style->fill.auto_back = auto_format;
505 		}
506 	} else if (auto_format) {
507 		s->style->fill.type = GO_STYLE_FILL_PATTERN;
508 		s->style->fill.auto_back = TRUE;
509 		s->style->fill.invert_if_negative = invert_if_negative;
510 		s->style->fill.pattern.pattern = 0;
511 		s->style->fill.pattern.fore =
512 		s->style->fill.pattern.back = 0;
513 	} else {
514 		s->style->fill.type = GO_STYLE_FILL_NONE;
515 		s->style->fill.auto_type = FALSE;
516 	}
517 
518 	return FALSE;
519 }
520 
521 /****************************************************************************/
522 
523 static gboolean
BC_R(attachedlabel)524 BC_R(attachedlabel)(XLChartHandler const *handle,
525 		    XLChartReadState *s, BiffQuery *q)
526 {
527 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
528 	d (3,{
529 	guint16 const flags = GSF_LE_GET_GUINT16 (q->data);
530 	gboolean const show_value = (flags&0x01) ? TRUE : FALSE;
531 	gboolean const show_percent = (flags&0x02) ? TRUE : FALSE;
532 	gboolean const show_label_percent = (flags&0x04) ? TRUE : FALSE;
533 	gboolean const smooth_line = (flags&0x08) ? TRUE : FALSE;
534 	gboolean const show_label = (flags&0x10) ? TRUE : FALSE;
535 
536 	if (show_value)
537 		g_printerr ("Show Value;\n");
538 	if (show_percent)
539 		g_printerr ("Show as Percentage;\n");
540 	if (show_label_percent)
541 		g_printerr ("Show as Label Percentage;\n");
542 	if (smooth_line)
543 		g_printerr ("Smooth line;\n");
544 	if (show_label)
545 		g_printerr ("Show the label;\n");
546 
547 	if (BC_R(ver)(s) >= MS_BIFF_V8) {
548 		gboolean const show_bubble_size = (flags&0x20) ? TRUE : FALSE;
549 		if (show_bubble_size)
550 			g_printerr ("Show bubble size;\n");
551 	}
552 	});
553 	return FALSE;
554 }
555 
556 /****************************************************************************/
557 
558 static gboolean
BC_R(axesused)559 BC_R(axesused)(XLChartHandler const *handle,
560 	       XLChartReadState *s, BiffQuery *q)
561 {
562 	guint16 num_axis;
563 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
564 	num_axis = GSF_LE_GET_GUINT16 (q->data);
565 	XL_CHECK_CONDITION_VAL(1 <= num_axis && num_axis <= 2, TRUE);
566 	d (0, g_printerr ("There are %hu axis.\n", num_axis););
567 	return FALSE;
568 }
569 
570 /****************************************************************************/
571 
572 static gboolean
BC_R(axis)573 BC_R(axis)(XLChartHandler const *handle,
574 	   XLChartReadState *s, BiffQuery *q)
575 {
576 	/* only true _most_ of the time.
577 	 * This is 'value axis' and 'catagory axis'
578 	 * which are logical names for X and Y that transpose for bar plots */
579 	static char const *const ms_axis[] = {
580 		"X-Axis", "Y-Axis", "Z-Axis"
581 	};
582 
583 	guint16 axis_type;
584 
585 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
586 
587 	axis_type = GSF_LE_GET_GUINT16 (q->data);
588 
589 	g_return_val_if_fail (axis_type < G_N_ELEMENTS (ms_axis), TRUE);
590 	g_return_val_if_fail (s->axis == NULL, TRUE);
591 
592 	s->axis = gog_object_add_by_name (GOG_OBJECT (s->chart),
593 					  ms_axis [axis_type], NULL);
594 
595 	if (axis_type == 0)
596 		s->xaxis = s->axis;
597 	else if (axis_type == 1) {
598 		if (s->axis_cross_at_max) {
599 			g_object_set (s->axis,
600 				      "pos-str", "high",
601 				      "cross-axis-id", gog_object_get_id (GOG_OBJECT (s->xaxis)),
602 				      NULL);
603 			s->axis_cross_at_max = FALSE;
604 		} else if (!isnan (s->axis_cross_value)) {
605 			GnmValue *value = value_new_float (s->axis_cross_value);
606 			GnmExprTop const *texpr = gnm_expr_top_new_constant (value);
607 			g_object_set (s->axis,
608 				      "pos-str", "cross",
609 				      "cross-axis-id", gog_object_get_id (GOG_OBJECT (s->xaxis)),
610 				      NULL);
611 			gog_dataset_set_dim (GOG_DATASET (s->axis), GOG_AXIS_ELEM_CROSS_POINT,
612 				gnm_go_data_scalar_new_expr (ms_container_sheet (s->container.parent), texpr), NULL);
613 			s->axis_cross_value = go_nan;
614 		}
615 	}
616 
617 	d (0, g_printerr ("This is a %s .\n", ms_axis[axis_type]););
618 	return FALSE;
619 }
620 
621 /****************************************************************************/
622 
623 static gboolean
BC_R(axcext)624 BC_R(axcext)(XLChartHandler const *handle,
625 	     XLChartReadState *s, BiffQuery *q)
626 {
627 	return FALSE;
628 }
629 
630 /****************************************************************************/
631 
632 static gboolean
633 BC_R(lineformat)(XLChartHandler const *handle,
634 		 XLChartReadState *s, BiffQuery *q);
635 static gboolean
BC_R(axislineformat)636 BC_R(axislineformat)(XLChartHandler const *handle,
637 		     XLChartReadState *s, BiffQuery *q)
638 {
639 	guint16 opcode;
640 	guint16 type;
641 
642 	XL_CHECK_CONDITION_VAL (q->length >= 2, FALSE);
643 	type = GSF_LE_GET_GUINT16 (q->data);
644 
645 	d (0, {
646 	g_printerr ("Axisline is ");
647 	switch (type)
648 	{
649 	case 0 : g_printerr ("the axis line.\n"); break;
650 	case 1 : g_printerr ("a major grid along the axis.\n"); break;
651 	case 2 : g_printerr ("a minor grid along the axis.\n"); break;
652 
653 	/* TODO TODO : floor vs wall */
654 	case 3 : g_printerr ("a floor/wall along the axis.\n"); break;
655 	default : g_printerr ("an ERROR.  unknown type (%x).\n", type);
656 	}
657 	});
658 
659 	if (!ms_biff_query_peek_next (q, &opcode) || opcode != BIFF_CHART_lineformat) {
660 		g_warning ("I had hoped that a lineformat would always follow an axislineformat");
661 		return FALSE;
662 	}
663 	ms_biff_query_next (q);
664 	if (BC_R(lineformat)(handle, s, q))
665 		return TRUE;
666 
667 	if (s->axis != NULL)
668 		switch (type) {
669 		case 0:
670 			g_object_set (G_OBJECT (s->axis),
671 				"style", s->style,
672 				NULL);
673 			if (s->axislineflags == 8)
674 				g_object_set (s->axis, "invisible", TRUE, NULL);
675 			else if (q->length >= 10) {
676 				/* deleted axis sets flag here, rather than in TICK */
677 				if (0 == (0x4 & GSF_LE_GET_GUINT16 (q->data+8)))
678 					g_object_set (G_OBJECT (s->axis),
679 						"major-tick-labeled",	FALSE,
680 						NULL);
681 			}
682 			break;
683 		case 1: {
684 			GogObject *GridLine = GOG_OBJECT (g_object_new (GOG_TYPE_GRID_LINE,
685 							NULL));
686 			gog_object_add_by_name (GOG_OBJECT (s->axis), "MajorGrid", GridLine);
687 			if (check_style (s->style, "axis major grid"))
688 				go_styled_object_set_style (GO_STYLED_OBJECT (GridLine), s->style);
689 			break;
690 		}
691 		case 2: {
692 			GogObject *GridLine = GOG_OBJECT (g_object_new (GOG_TYPE_GRID_LINE,
693 							NULL));
694 			gog_object_add_by_name (GOG_OBJECT (s->axis), "MinorGrid", GridLine);
695 			if (check_style (s->style, "axis minor grid"))
696 				go_styled_object_set_style (GO_STYLED_OBJECT (GridLine), s->style);
697 			break;
698 		}
699 		case 3: {
700 			/* in that case, we have an areaformat too */
701 			ms_biff_query_next (q);
702 			if (BC_R(areaformat)(handle, s, q))
703 				return TRUE;
704 			break;
705 		}
706 		default:
707 			break;
708 		}
709 	if (s->style) {
710 		g_object_unref (s->style);
711 		s->style = NULL;
712 	}
713 
714 	return FALSE;
715 }
716 
717 /****************************************************************************/
718 
719 static gboolean
BC_R(axisparent)720 BC_R(axisparent)(XLChartHandler const *handle,
721 		 XLChartReadState *s, BiffQuery *q)
722 {
723 	XL_CHECK_CONDITION_VAL (q->length == 18, TRUE);
724 	/* hmm, what we do here is not conform to the documentation, anyway, we don't do anything useful */
725 	d (1, {
726 	guint16 const index = GSF_LE_GET_GUINT16 (q->data);	/* 1 or 2 */
727 	/* Measured in 1/4000ths of the chart width */
728 	guint32 const x = GSF_LE_GET_GUINT32 (q->data+2);
729 	guint32 const y = GSF_LE_GET_GUINT32 (q->data+6);
730 	guint32 const width = GSF_LE_GET_GUINT32 (q->data+10);
731 	guint32 const height = GSF_LE_GET_GUINT32 (q->data+14);
732 
733 	g_printerr ("Axis # %hu @ %f,%f, X=%f, Y=%f\n",
734 		index, x/4000., y/4000., width/4000., height/4000.);
735 	});
736 	return FALSE;
737 }
738 
739 /****************************************************************************/
740 
741 static gboolean
BC_R(bar)742 BC_R(bar)(XLChartHandler const *handle,
743 	  XLChartReadState *s, BiffQuery *q)
744 {
745 	char const *type = "normal";
746 	int overlap_percentage, gap_percentage;
747 	guint16 flags;
748 	gboolean horizontal, in_3d;
749 
750 	XL_CHECK_CONDITION_VAL (q->length >= 6, TRUE);
751 
752 	overlap_percentage = -GSF_LE_GET_GINT16 (q->data); /* dipsticks */
753 	gap_percentage = GSF_LE_GET_GINT16 (q->data+2);
754 	flags = GSF_LE_GET_GUINT16 (q->data+4);
755 	horizontal = (flags & 0x01) != 0;
756 	in_3d = (BC_R(ver)(s) >= MS_BIFF_V8 && (flags & 0x08));
757 
758 	g_return_val_if_fail (s->plot == NULL, TRUE);
759 	s->plot = (GogPlot*) gog_plot_new_by_name ("GogBarColPlot");
760 	g_return_val_if_fail (s->plot != NULL, TRUE);
761 
762 	if (flags & 0x04)
763 		type = "as_percentage";
764 	else if (flags & 0x02)
765 		type = "stacked";
766 
767 	g_object_set (G_OBJECT (s->plot),
768 		"horizontal",		horizontal,
769 		"type",			type,
770 		"in-3d",		in_3d,
771 		"overlap-percentage",	overlap_percentage,
772 		"gap-percentage",	gap_percentage,
773 		NULL);
774 	d(1, g_printerr ("%s bar with gap = %d, overlap = %d;",
775 		      type, gap_percentage, overlap_percentage););
776 	return FALSE;
777 }
778 
779 /****************************************************************************/
780 
781 static gboolean
BC_R(begin)782 BC_R(begin)(XLChartHandler const *handle,
783 	    XLChartReadState *s, BiffQuery *q)
784 {
785 	d(0, g_printerr ("{\n"););
786 	s->stack = g_array_append_val (s->stack, s->prev_opcode);
787 	return FALSE;
788 }
789 
790 /****************************************************************************/
791 
792 static gboolean
BC_R(boppop)793 BC_R(boppop)(XLChartHandler const *handle,
794 	     XLChartReadState *s, BiffQuery *q)
795 {
796 	XL_CHECK_CONDITION_VAL (q->length >= 18, TRUE);
797 
798 #if 0
799 	guint8 const type = GSF_LE_GET_GUINT8 (q->data); /* 0-2 */
800 	gboolean const use_default_split = (GSF_LE_GET_GUINT8 (q->data+1) == 1);
801 	guint16 const split_type = GSF_LE_GET_GUINT8 (q->data+2); /* 0-3 */
802 #endif
803 
804 	/* KLUDGE : call it a pie for now */
805 	if (NULL == s->plot) {
806 		gboolean const in_3d = (GSF_LE_GET_GUINT16 (q->data+16) == 1);
807 		s->plot = (GogPlot*) gog_plot_new_by_name ("GogPiePlot");
808 		g_return_val_if_fail (s->plot != NULL, TRUE);
809 		g_object_set (G_OBJECT (s->plot), "in-3d", in_3d, NULL);
810 	}
811 
812 	return FALSE;
813 }
814 /****************************************************************************/
815 
816 static gboolean
BC_R(boppopcustom)817 BC_R(boppopcustom)(XLChartHandler const *handle,
818 		   XLChartReadState *s, BiffQuery *q)
819 {
820 #if 0
821 	gint16 const count = GSF_LE_GET_GUINT16 (q->data);
822 	/* TODO TODO : figure out the bitfield array */
823 #endif
824 	return FALSE;
825 }
826 
827 /***************************************************************************/
828 
829 static gboolean
BC_R(catserrange)830 BC_R(catserrange)(XLChartHandler const *handle,
831 		  XLChartReadState *s, BiffQuery *q)
832 {
833 	guint flags;
834 	XL_CHECK_CONDITION_VAL (q->length >= 8, TRUE);
835 	flags = GSF_LE_GET_GUINT16 (q->data + 6);
836 	if (((flags & 2) != 0) ^ ((flags & 4) != 0)) {
837 		if (gog_axis_get_atype (GOG_AXIS (s->axis)) == GOG_AXIS_X)
838 			s->axis_cross_at_max = TRUE;
839 		else if (gog_axis_get_atype (GOG_AXIS (s->axis)) == GOG_AXIS_Y && s->xaxis)
840 			g_object_set (s->xaxis, "pos-str", "high", NULL);
841 		d (1, g_printerr ("Cross over at max value;\n"););
842 	}
843 	return FALSE;
844 }
845 
846 /****************************************************************************/
847 
848 static gboolean
BC_R(chart)849 BC_R(chart)(XLChartHandler const *handle,
850 	    XLChartReadState *s, BiffQuery *q)
851 {
852 	XL_CHECK_CONDITION_VAL (q->length >= 16, TRUE);
853 
854 	d (1, {
855 	/* Fixed point 2 bytes fraction 2 bytes integer */
856 	guint32 const x_pos_fixed = GSF_LE_GET_GUINT32 (q->data + 0);
857 	guint32 const y_pos_fixed = GSF_LE_GET_GUINT32 (q->data + 4);
858 	guint32 const x_size_fixed = GSF_LE_GET_GUINT32 (q->data + 8);
859 	guint32 const y_size_fixed = GSF_LE_GET_GUINT32 (q->data + 12);
860 
861 	/* Measured in points (1/72 of an inch) */
862 	double const x_pos = x_pos_fixed / (65535. * 72.);
863 	double const y_pos = y_pos_fixed / (65535. * 72.);
864 	double const x_size = x_size_fixed / (65535. * 72.);
865 	double const y_size = y_size_fixed / (65535. * 72.);
866 	g_printerr ("Chart @ %g, %g is %g\" x %g\"\n", x_pos, y_pos, x_size, y_size);
867 	});
868 
869 	return FALSE;
870 }
871 
872 /****************************************************************************/
873 
874 static gboolean
BC_R(chartformat)875 BC_R(chartformat)(XLChartHandler const *handle,
876 		  XLChartReadState *s, BiffQuery *q)
877 {
878 	guint16 flags, z_order;
879 
880 	XL_CHECK_CONDITION_VAL (q->length >= 4, TRUE);
881 
882 	flags = GSF_LE_GET_GUINT16 (q->data+16);
883 	z_order = GSF_LE_GET_GUINT16 (q->data+18);
884 
885 	/* always update the counter to keep the index in line with the chart
886 	 * group specifier for series */
887 	s->plot_counter = z_order;
888 
889 	if (s->plot != NULL)
890 		g_object_set (G_OBJECT (s->plot),
891 			      "vary-style-by-element", (flags & 0x01) ? TRUE : FALSE,
892 #if 0
893 			      "index", s->plot_counter
894 			      "stacking-position", z_order
895 #endif
896 			      NULL);
897 
898 	d (0, {
899 		g_printerr ("Z value = %uh\n", z_order);
900 	});
901 
902 	return FALSE;
903 }
904 
905 /****************************************************************************/
906 
907 static gboolean
BC_R(chartformatlink)908 BC_R(chartformatlink)(XLChartHandler const *handle,
909 		      XLChartReadState *s, BiffQuery *q)
910 {
911 	/* ignored */
912 	return FALSE;
913 }
914 
915 /****************************************************************************/
916 
917 static gboolean
BC_R(chartline)918 BC_R(chartline)(XLChartHandler const *handle,
919 		XLChartReadState *s, BiffQuery *q)
920 {
921 	guint16 type;
922 
923 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
924 	type = GSF_LE_GET_GUINT16 (q->data);
925 	XL_CHECK_CONDITION_VAL (type <= 2, FALSE);
926 
927 	if (type == 1)
928 		s->hilo = TRUE;
929 	s->cur_role = type;
930 
931 	d (0, g_printerr ("Use %s lines\n",
932 	     (type == 0) ? "drop" : ((type == 1) ? "hi-lo" : "series")););
933 
934 	return FALSE;
935 }
936 
937 /****************************************************************************/
938 
939 static gboolean
BC_R(clrtclient)940 BC_R(clrtclient)(XLChartHandler const *handle,
941 		 XLChartReadState *s, BiffQuery *q)
942 {
943 	g_printerr ("Undocumented BIFF : clrtclient");
944 	ms_biff_query_dump (q);
945 	return FALSE;
946 }
947 /****************************************************************************/
948 
949 static gboolean
BC_R(dat)950 BC_R(dat)(XLChartHandler const *handle,
951 	  XLChartReadState *s, BiffQuery *q)
952 {
953 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
954 
955 #if 0
956 	gint16 const flags = GSF_LE_GET_GUINT16 (q->data);
957 	gboolean const horiz_border = (flags&0x01) ? TRUE : FALSE;
958 	gboolean const vert_border = (flags&0x02) ? TRUE : FALSE;
959 	gboolean const border = (flags&0x04) ? TRUE : FALSE;
960 	gboolean const series_keys = (flags&0x08) ? TRUE : FALSE;
961 #endif
962 	return FALSE;
963 }
964 /****************************************************************************/
965 
966 static gboolean
BC_R(dataformat)967 BC_R(dataformat)(XLChartHandler const *handle,
968 		 XLChartReadState *s, BiffQuery *q)
969 {
970 	XLChartSeries *series;
971 	guint16 pt_num, series_index, series_index_for_label;
972 
973 	XL_CHECK_CONDITION_VAL (q->length >= 8, TRUE);
974 	pt_num = GSF_LE_GET_GUINT16 (q->data);
975 	series_index = GSF_LE_GET_GUINT16 (q->data+2);
976 	series_index_for_label = GSF_LE_GET_GUINT16 (q->data+4);
977 #if 0
978 	guint16 const excel4_auto_color = GSF_LE_GET_GUINT16 (q->data+6) & 0x01;
979 #endif
980 
981 	if (pt_num == 0 && series_index == 0 && series_index_for_label == 0xfffd)
982 		s->has_extra_dataformat = TRUE;
983 	XL_CHECK_CONDITION_VAL (series_index < s->series->len, TRUE);
984 
985 	series = g_ptr_array_index (s->series, series_index);
986 	XL_CHECK_CONDITION_VAL (series != NULL, TRUE);
987 
988 	if (pt_num == 0xffff) {
989 		s->style_element = -1;
990 		d (0, g_printerr ("All points"););
991 	} else {
992 		s->style_element = pt_num;
993 		d (0, g_printerr ("Point[%hu]", pt_num););
994 	}
995 
996 	d (0, g_printerr (", series=%hu\n", series_index););
997 
998 	return FALSE;
999 }
1000 
1001 /****************************************************************************/
1002 
1003 static gboolean
BC_R(defaulttext)1004 BC_R(defaulttext)(XLChartHandler const *handle,
1005 		  XLChartReadState *s, BiffQuery *q)
1006 {
1007 	guint16	tmp;
1008 
1009 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
1010 	tmp = GSF_LE_GET_GUINT16 (q->data);
1011 
1012 	d (2, g_printerr ("applicability = %hd\n", tmp););
1013 
1014 	/*
1015 	 * 0 == 'show labels' label
1016 	 * 1 == Value and percentage data label
1017 	 * 2 == All text in chart
1018 	 * 3 == Undocumented ??
1019 	 */
1020 	XL_CHECK_CONDITION_VAL (tmp <= 3, TRUE);
1021 	return FALSE;
1022 }
1023 
1024 /****************************************************************************/
1025 
1026 static gboolean
BC_R(dropbar)1027 BC_R(dropbar)(XLChartHandler const *handle,
1028 	      XLChartReadState *s, BiffQuery *q)
1029 {
1030 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
1031 
1032 	/* NOTE : The docs lie.  values > 100 seem legal.  My guess based on
1033 	 * the ui is 500.
1034 	 */
1035 	s->dropbar = TRUE;
1036 	s->dropbar_width = GSF_LE_GET_GUINT16 (q->data);
1037 	d (1, g_printerr ("width=%hu\n",s->dropbar_width););
1038 	return FALSE;
1039 }
1040 
1041 /****************************************************************************/
1042 
1043 static gboolean
BC_R(fbi)1044 BC_R(fbi)(XLChartHandler const *handle,
1045 	  XLChartReadState *s, BiffQuery *q)
1046 {
1047 	guint16 x_basis, y_basis, applied_height, scale_basis, index;
1048 
1049 	XL_CHECK_CONDITION_VAL (q->length >= 10, TRUE);
1050 
1051 	/*
1052 	 * TODO TODO TODO : Work on appropriate scales.
1053 	 * Is any of this useful other than the index ?
1054 	 */
1055 	x_basis = GSF_LE_GET_GUINT16 (q->data);
1056 	y_basis = GSF_LE_GET_GUINT16 (q->data+2);
1057 	applied_height = GSF_LE_GET_GUINT16 (q->data+4);
1058 	scale_basis = GSF_LE_GET_GUINT16 (q->data+6);
1059 	index = GSF_LE_GET_GUINT16 (q->data+8);
1060 
1061 	d (2,
1062 		gsf_mem_dump (q->data, q->length);
1063 		g_printerr ("Font %hu (%hu x %hu) scale=%hu, height=%hu\n",
1064 			index, x_basis, y_basis, scale_basis, applied_height););
1065 	return FALSE;
1066 }
1067 /****************************************************************************/
1068 
1069 static gboolean
BC_R(fontx)1070 BC_R(fontx)(XLChartHandler const *handle,
1071 	    XLChartReadState *s, BiffQuery *q)
1072 {
1073 	ExcelFont const *font;
1074 	GOFont const *gfont;
1075 	guint16 fno;
1076 
1077 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
1078 	fno = GSF_LE_GET_GUINT16 (q->data);
1079 	font = excel_font_get (s->container.importer, fno);
1080 	if (!font)
1081 		return FALSE;
1082 
1083 	gfont = excel_font_get_gofont (font);
1084 	go_font_ref (gfont);
1085 	BC_R(get_style) (s);
1086 	go_style_set_font (s->style, gfont);
1087 	s->style->font.auto_font = FALSE;
1088 	d (2, g_printerr ("apply font %u %s;", fno, go_font_as_str (gfont)););
1089 	return FALSE;
1090 }
1091 
1092 /****************************************************************************/
1093 
1094 static gboolean
BC_R(frame)1095 BC_R(frame)(XLChartHandler const *handle,
1096 	    XLChartReadState *s, BiffQuery *q)
1097 {
1098 #if 0
1099 	guint16 const type = GSF_LE_GET_GUINT16 (q->data);
1100 	guint16 const flags = GSF_LE_GET_GUINT16 (q->data+2);
1101 	gboolean border_shadow, auto_size, auto_pos;
1102 
1103 	g_return_val_if_fail (type == 1 || type ==4, TRUE);
1104 	/* FIXME FIXME FIXME : figure out what other values are */
1105 	border_shadow = (type == 4) ? TRUE : FALSE;
1106 	auto_size = (flags&0x01) ? TRUE : FALSE;
1107 	auto_pos = (flags&0x02) ? TRUE : FALSE;
1108 #endif
1109 
1110 	s->frame_for_grid = (s->prev_opcode == BIFF_CHART_plotarea);
1111 	s->has_a_grid |= s->frame_for_grid;
1112 	d (0, g_printerr (s->frame_for_grid ? "For grid;\n" : "Not for grid;\n"););
1113 
1114 	return FALSE;
1115 }
1116 
1117 /****************************************************************************/
1118 
1119 static GOColor
ms_chart_map_color(XLChartReadState const * s,guint32 raw,guint32 alpha)1120 ms_chart_map_color (XLChartReadState const *s, guint32 raw, guint32 alpha)
1121 {
1122 	GOColor res;
1123 	if ((~0x7ffffff) & raw) {
1124 		GnmColor *c = excel_palette_get (s->container.importer,
1125 			(0x7ffffff & raw));
1126 		res = c->go_color;
1127 		style_color_unref (c);
1128 	} else {
1129 		guint8 r, g, b;
1130 		r = (raw)       & 0xff;
1131 		g = (raw >> 8)  & 0xff;
1132 		b = (raw >> 16) & 0xff;
1133 		res = GO_COLOR_FROM_RGB (r, g, b);
1134 	}
1135 	return res;
1136 }
1137 
1138 static gboolean
BC_R(gelframe)1139 BC_R(gelframe) (XLChartHandler const *handle,
1140 		XLChartReadState *s, BiffQuery *q)
1141 {
1142 	MSObjAttrBag *attrs = ms_escher_parse (q, &s->container, TRUE);
1143 	guint32 type = ms_obj_attr_get_uint (attrs,
1144 		MS_OBJ_ATTR_FILL_TYPE, 0);
1145 	guint32 shade_type = ms_obj_attr_get_uint (attrs,
1146 		MS_OBJ_ATTR_FILL_SHADE_TYPE, 0);
1147 	guint32 fill_color = ms_obj_attr_get_uint (attrs,
1148 		MS_OBJ_ATTR_FILL_COLOR, 0);
1149 	guint32 fill_alpha = ms_obj_attr_get_uint (attrs,
1150 		MS_OBJ_ATTR_FILL_ALPHA, 0x10000);
1151 	guint32 fill_back_color = ms_obj_attr_get_uint (attrs,
1152 		MS_OBJ_ATTR_FILL_BACKGROUND, 0);
1153 	guint32 fill_back_alpha = ms_obj_attr_get_uint (attrs,
1154 		MS_OBJ_ATTR_FILL_BACKGROUND_ALPHA, 0x10000);
1155 	guint32 preset = ms_obj_attr_get_uint (attrs,
1156 		MS_OBJ_ATTR_FILL_PRESET, 0);
1157 
1158 	d (1, g_printerr ("Frame type = %u\n", type););
1159 	/* plot types we do not support that have gradients */
1160 	if (NULL == s->style || type < 5) {
1161 		ms_obj_attr_bag_destroy (attrs);
1162 		return FALSE;
1163 	}
1164 
1165 	s->style->fill.type = GO_STYLE_FILL_GRADIENT;
1166 	s->style->fill.auto_type = FALSE;
1167 	s->style->fill.auto_fore = FALSE;
1168 	s->style->fill.auto_back = FALSE;
1169 	s->style->fill.pattern.fore =
1170 		ms_chart_map_color (s, fill_color, fill_alpha);
1171 
1172 	/* FIXME : make presets the same as 2 color for now */
1173 	if (!(shade_type & 8) || preset > 0) {
1174 		s->style->fill.pattern.back = ms_chart_map_color (s,
1175 			fill_back_color, fill_back_alpha);
1176 	} else {
1177 		double brightness;
1178 		unsigned frac = (fill_back_color >> 16) & 0xff;
1179 
1180 		/**
1181 		 * 0x10 const
1182 		 * 0x00..0xff fraction
1183 		 * 0x02 == bright 0x01 == dark
1184 		 * 0xf0 == const
1185 		 **/
1186 		switch ((fill_back_color & 0xff00)) {
1187 		default :
1188 			g_warning ("looks like our theory of 1-color gradient brightness is incorrect");
1189 		case 0x0100 : brightness = 0. + frac/512.; break;
1190 		case 0x0200 : brightness = 1. - frac/512.; break;
1191 		}
1192 		go_style_set_fill_brightness (s->style, (1. - brightness) * 100.);
1193 		d (1, g_printerr ("%x : frac = %u, flag = 0x%x ::: %f",
1194 			       fill_back_color, frac, fill_back_color & 0xff00, brightness););
1195 	}
1196 
1197 	if (type == 5) { /* Fill from corner */
1198 		/* TODO */
1199 	} else if (type == 6) { /* fill from center */
1200 		/* TODO */
1201 	} else if (type == 7) {
1202 		GOGradientDirection dir = GO_GRADIENT_S_TO_N;
1203 		guint32 angle = ms_obj_attr_get_uint (attrs, MS_OBJ_ATTR_FILL_ANGLE, 0);
1204 		gint32 focus = ms_obj_attr_get_int (attrs, MS_OBJ_ATTR_FILL_FOCUS, 0);
1205 
1206 		if (focus < 0)
1207 			focus = ((focus - 25) / 50) % 4 + 4;
1208 		else
1209 			focus = ((focus + 25) / 50) % 4;
1210 
1211 		switch (angle) {
1212 		default :
1213 			g_warning ("non standard gradient angle %u, using horizontal", angle);
1214 		case 0 : /* horizontal */
1215 			switch (focus) {
1216 			case 0 : dir = GO_GRADIENT_S_TO_N; break;
1217 			case 1 : dir = GO_GRADIENT_N_TO_S_MIRRORED; break;
1218 			case 2 : dir = GO_GRADIENT_N_TO_S; break;
1219 			case 3 : dir = GO_GRADIENT_S_TO_N_MIRRORED; break;
1220 			}
1221 			break;
1222 		case 0xffd30000 : /* diag down (-45 in 16.16 fixed) */
1223 			switch (focus) {
1224 			case 0 : dir = GO_GRADIENT_SE_TO_NW; break;
1225 			case 1 : dir = GO_GRADIENT_SE_TO_NW_MIRRORED; break;
1226 			case 2 : dir = GO_GRADIENT_NW_TO_SE; break;
1227 			case 3 : dir = GO_GRADIENT_NW_TO_SE_MIRRORED; break;
1228 			}
1229 			break;
1230 		case 0xffa60000 : /* vertical (-90 in 16.16 fixed) */
1231 			switch (focus) {
1232 			case 0 : dir = GO_GRADIENT_E_TO_W; break;
1233 			case 1 : dir = GO_GRADIENT_E_TO_W_MIRRORED; break;
1234 			case 2 : dir = GO_GRADIENT_W_TO_E; break;
1235 			case 3 : dir = GO_GRADIENT_W_TO_E_MIRRORED; break;
1236 			}
1237 			break;
1238 		case 0xff790000 : /* diag up (-135 in 16.16 fixed) */
1239 			switch (focus) {
1240 			case 0 : dir = GO_GRADIENT_SE_TO_NW; break;
1241 			case 1 : dir = GO_GRADIENT_SE_TO_NW_MIRRORED; break;
1242 			case 2 : dir = GO_GRADIENT_NW_TO_SE; break;
1243 			case 3 : dir = GO_GRADIENT_NW_TO_SE_MIRRORED; break;
1244 			}
1245 		}
1246 		s->style->fill.gradient.dir = dir;
1247 	}
1248 
1249 	ms_obj_attr_bag_destroy (attrs);
1250 
1251 	return FALSE;
1252 }
1253 
1254 /****************************************************************************/
1255 
1256 static gboolean
BC_R(ifmt)1257 BC_R(ifmt)(XLChartHandler const *handle,
1258 	   XLChartReadState *s, BiffQuery *q)
1259 {
1260 	GOFormat *fmt;
1261 
1262 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
1263 
1264 	fmt = ms_container_get_fmt (&s->container,
1265 		GSF_LE_GET_GUINT16 (q->data));
1266 
1267 	if (fmt != NULL) {
1268 		const char *desc = go_format_as_XL (fmt);
1269 
1270 		if (s->axis != NULL)
1271 			g_object_set (G_OBJECT (s->axis),
1272 				"assigned-format-string-XL", desc,
1273 				NULL);
1274 		d (0, g_printerr ("Format = '%s';\n", desc););
1275 		go_format_unref (fmt);
1276 	}
1277 
1278 	return FALSE;
1279 }
1280 
1281 /****************************************************************************/
1282 
1283 static gboolean
BC_R(legend)1284 BC_R(legend)(XLChartHandler const *handle,
1285 	     XLChartReadState *s, BiffQuery *q)
1286 {
1287 #if 0
1288 	/* Measured in 1/4000ths of the chart width */
1289 	guint32 const x_pos = GSF_LE_GET_GUINT32  (q->data);
1290 	guint32 const y_pos = GSF_LE_GET_GUINT32  (q->data+4);
1291 	guint32 const width = GSF_LE_GET_GUINT32  (q->data+8);
1292 	guint32 const height = GSF_LE_GET_GUINT32 (q->data+12);
1293 	guint8 const spacing = GSF_LE_GET_GUINT8  (q->data+17);
1294 	guint16 const flags = GSF_LE_GET_GUINT16  (q->data+18);
1295 #endif
1296 	guint16 XL_pos;
1297 	GogObjectPosition pos;
1298 
1299 	XL_CHECK_CONDITION_VAL (q->length >= 17, TRUE);
1300 
1301 	XL_pos = GSF_LE_GET_GUINT8 (q->data+16);
1302 
1303 	switch (XL_pos) {
1304 	case 0: pos = GOG_POSITION_S | GOG_POSITION_ALIGN_CENTER; break;
1305 	case 1: pos = GOG_POSITION_N | GOG_POSITION_E; break;
1306 	case 2: pos = GOG_POSITION_N | GOG_POSITION_ALIGN_CENTER; break;
1307 	default :
1308 		g_warning ("Unknown legend position (%d), assuming east.",
1309 			   XL_pos);
1310 	case 3: pos = GOG_POSITION_E | GOG_POSITION_ALIGN_CENTER; break;
1311 	case 4: pos = GOG_POSITION_W | GOG_POSITION_ALIGN_CENTER; break;
1312 	case 7: /* treat floating legends as being on east */
1313 		pos = GOG_POSITION_E | GOG_POSITION_ALIGN_CENTER; break;
1314 	}
1315 
1316 	s->legend = gog_object_add_by_name (GOG_OBJECT (s->chart), "Legend", NULL);
1317 	gog_object_set_position_flags (s->legend, pos, GOG_POSITION_COMPASS | GOG_POSITION_ALIGNMENT);
1318 
1319 #if 0
1320 	g_printerr ("Legend @ %f,%f, X=%f, Y=%f\n",
1321 		x_pos/4000., y_pos/4000., width/4000., height/4000.);
1322 
1323 	/* FIXME : Parse the flags too */
1324 #endif
1325 
1326 	return FALSE;
1327 }
1328 
1329 /****************************************************************************/
1330 
1331 static gboolean
BC_R(legendxn)1332 BC_R(legendxn)(XLChartHandler const *handle,
1333 	       XLChartReadState *s, BiffQuery *q)
1334 {
1335 	guint16 flags;
1336 
1337 	XL_CHECK_CONDITION_VAL (q->length >= 4, TRUE);
1338 
1339 	flags = GSF_LE_GET_GUINT16 (q->data+2);
1340 	if ((flags & 1) && s->currentSeries != NULL)
1341 		s->currentSeries->has_legend = FALSE;
1342 	return FALSE;
1343 }
1344 
1345 /****************************************************************************/
1346 
1347 static gboolean
BC_R(line)1348 BC_R(line)(XLChartHandler const *handle,
1349 	   XLChartReadState *s, BiffQuery *q)
1350 {
1351 	guint16 flags;
1352 	char const *type = "normal";
1353 	gboolean in_3d;
1354 
1355 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
1356 
1357 	flags = GSF_LE_GET_GUINT16 (q->data);
1358 	in_3d = (BC_R(ver)(s) >= MS_BIFF_V8 && (flags & 0x04));
1359 
1360 	g_return_val_if_fail (s->plot == NULL, TRUE);
1361 	s->plot = (GogPlot*) gog_plot_new_by_name ("GogLinePlot");
1362 	g_return_val_if_fail (s->plot != NULL, TRUE);
1363 
1364 	if (flags & 0x02)
1365 		type = "as_percentage";
1366 	else if (flags & 0x01)
1367 		type = "stacked";
1368 
1369 	g_object_set (G_OBJECT (s->plot),
1370 		"type",			type,
1371 		"in-3d",		in_3d,
1372 		NULL);
1373 
1374 	d(1, g_printerr ("%s line;", type););
1375 	return FALSE;
1376 }
1377 
1378 /****************************************************************************/
1379 
1380 static char const *const ms_line_pattern[] = {
1381 	"solid",
1382 	"dashed",
1383 	"dotted",
1384 	"dash dotted",
1385 	"dash dot dotted",
1386 	"invisible",
1387 	"dark gray",
1388 	"medium gray",
1389 	"light gray"
1390 };
1391 
1392 static gboolean
BC_R(lineformat)1393 BC_R(lineformat)(XLChartHandler const *handle,
1394 		 XLChartReadState *s, BiffQuery *q)
1395 {
1396 	guint16 flags;
1397 	guint16 pattern;
1398 
1399 	XL_CHECK_CONDITION_VAL (q->length >= (BC_R(ver)(s) >= MS_BIFF_V8 ? 12 : 10), TRUE);
1400 
1401 	flags = GSF_LE_GET_GUINT16 (q->data + 8);
1402 	pattern = GSF_LE_GET_GUINT16 (q->data + 4);
1403 
1404 	BC_R(get_style) (s);
1405 	switch (GSF_LE_GET_GINT16 (q->data + 6)) {
1406 	default :
1407 	case -1 : s->style->line.width = 0; /* hairline */
1408 		break;
1409 	case  0 : s->style->line.width = 1; /* 'normal' */
1410 		break;
1411 	case  1 : s->style->line.width = 2; /* 'medium' */
1412 		break;
1413 	case  2 : s->style->line.width = 3; /* 'wide' */
1414 		break;
1415 	}
1416 	s->style->line.color      = BC_R(color) (q->data, "LineColor");
1417 	s->style->line.auto_color = s->style->line.auto_dash = (flags & 0x01) ? TRUE : FALSE;
1418 
1419 	d (0, g_printerr ("flags == %hd.\n", flags););
1420 	d (0, g_printerr ("Lines are %f pts wide.\n", s->style->line.width););
1421 	d (0, g_printerr ("Lines have a %s pattern.\n",
1422 		       ms_line_pattern [pattern]););
1423 
1424 	switch (pattern) {
1425 	default:
1426 	case 0:
1427 		s->style->line.dash_type = GO_LINE_SOLID;
1428 		break;
1429 	case 1:
1430 		s->style->line.dash_type = GO_LINE_DASH;
1431 		break;
1432 	case 2:
1433 		s->style->line.dash_type = GO_LINE_DOT;
1434 		break;
1435 	case 3:
1436 		s->style->line.dash_type = GO_LINE_DASH_DOT;
1437 		break;
1438 	case 4:
1439 		s->style->line.dash_type = GO_LINE_DASH_DOT_DOT;
1440 		break;
1441 	case 5:
1442 		s->style->line.dash_type = GO_LINE_NONE;
1443 		break;
1444 /* we don't really support the other styles, although GOStyle would allow that now */
1445 #if 0
1446 	case 6:
1447 		s->style->line.dash_type = GO_LINE_SOLID;
1448 		s->style->line.pattern = GO_PATTERN_GREY25; /* or 75? */
1449 		s->style->line.fore = GO_COLOR_WHITE;
1450 		break;
1451 	case 7:
1452 		s->style->line.dash_type = GO_LINE_SOLID;
1453 		s->style->line.pattern = GO_PATTERN_GREY50;
1454 		s->style->line.fore = GO_COLOR_WHITE;
1455 		break;
1456 	case 8:
1457 		s->style->line.dash_type = GO_LINE_SOLID;
1458 		s->style->line.pattern = GO_PATTERN_GREY75; /* or 25? */
1459 		s->style->line.fore = GO_COLOR_WHITE;
1460 		break;
1461 #endif
1462 	}
1463 
1464 	if (BC_R(ver)(s) >= MS_BIFF_V8 && s->currentSeries != NULL) {
1465 		guint16 const fore = GSF_LE_GET_GUINT16 (q->data + 10);
1466 		d (0, g_printerr ("color index == %hd.\n", fore););
1467 		/* Excel assumes that the color is automatic if it is the same
1468 		as the automatic one whatever the auto flag value is */
1469 		s->style->line.auto_color = (fore == 31 + s->series->len);
1470 	}
1471 
1472 	if (s->prev_opcode == BIFF_CHART_chartline) {
1473 		/* we only support hi-lo lines at the moment */
1474 		if (s->cur_role == 1)
1475 			s->chartline_style[s->cur_role] = s->style;
1476 		else
1477 			g_object_unref (s->style);
1478 		s->style = NULL;
1479 	} else if (s->axis != NULL)
1480 		s->axislineflags = flags;
1481 
1482 	return FALSE;
1483 }
1484 
1485 /****************************************************************************/
1486 
1487 static gboolean
BC_R(markerformat)1488 BC_R(markerformat)(XLChartHandler const *handle,
1489 		   XLChartReadState *s, BiffQuery *q)
1490 {
1491 	static char const *const ms_chart_marker[] = {
1492 		"none", "square", "diamond", "triangle", "x", "star",
1493 		"dow", "std", "circle", "plus"
1494 	};
1495 	static GOMarkerShape const shape_map[] = {
1496 		GO_MARKER_NONE,
1497 		GO_MARKER_SQUARE,
1498 		GO_MARKER_DIAMOND,
1499 		GO_MARKER_TRIANGLE_UP,
1500 		GO_MARKER_X,
1501 		GO_MARKER_ASTERISK,
1502 		GO_MARKER_HALF_BAR,
1503 		GO_MARKER_BAR,
1504 		GO_MARKER_CIRCLE,
1505 		GO_MARKER_CROSS
1506 	};
1507 	GOMarker *marker;
1508 	guint16 shape;
1509 	guint16 flags;
1510 	gboolean auto_marker;
1511 
1512 	XL_CHECK_CONDITION_VAL (q->length >= (BC_R(ver)(s) >= MS_BIFF_V8 ? 20 : 8), TRUE);
1513 
1514 	shape = GSF_LE_GET_GUINT16 (q->data+8);
1515 	flags = GSF_LE_GET_GUINT16 (q->data+10);
1516 	auto_marker = (flags & 0x01) ? TRUE : FALSE;
1517 
1518 	BC_R(get_style) (s);
1519 	marker = go_marker_new ();
1520 
1521 	d (0, g_printerr ("Marker = %s\n", ms_chart_marker [shape]););
1522 	if (shape >= G_N_ELEMENTS (shape_map))
1523 		shape = 1; /* square */
1524 	go_marker_set_shape (marker, shape_map [shape]);
1525 
1526 	go_marker_set_outline_color (marker,
1527 		(flags & 0x20) ? 0 : BC_R(color) (q->data + 0, "MarkerFore"));
1528 	go_marker_set_fill_color (marker,
1529 		(flags & 0x10) ? 0 : BC_R(color) (q->data + 4, "MarkerBack"));
1530 
1531 	s->style->marker.auto_shape = auto_marker;
1532 
1533 	if (BC_R(ver)(s) >= MS_BIFF_V8) {
1534 		guint16 const fore = GSF_LE_GET_GUINT16 (q->data+12);
1535 		guint16 const back = GSF_LE_GET_GUINT16 (q->data+14);
1536 		guint32 const marker_size = GSF_LE_GET_GUINT32 (q->data+16);
1537 		go_marker_set_size (marker, marker_size / 20.);
1538 		d (1, g_printerr ("Marker size : is %f pts\n", marker_size / 20.););
1539 		/* Excel assumes that the color is automatic if it is the same
1540 		as the automatic one whatever the auto flag value is */
1541 		s->style->marker.auto_outline_color = (fore == 31 + s->series->len);
1542 		s->style->marker.auto_fill_color = (back == 31 + s->series->len);
1543 	} else
1544 		s->style->marker.auto_outline_color =
1545 			s->style->marker.auto_fill_color = auto_marker;
1546 
1547 	go_style_set_marker (s->style, marker);
1548 
1549 	return FALSE;
1550 }
1551 
1552 /****************************************************************************/
1553 
1554 static gboolean
BC_R(objectlink)1555 BC_R(objectlink)(XLChartHandler const *handle,
1556 		 XLChartReadState *s, BiffQuery *q)
1557 {
1558 	guint16 purpose;
1559 	GogObject *label = NULL;
1560 
1561 	XL_CHECK_CONDITION_VAL (q->length >= 6, TRUE);
1562 
1563 	purpose = GSF_LE_GET_GUINT16 (q->data);
1564 	if (purpose != 4 && s->text == NULL && s->label == NULL)
1565 		return FALSE;
1566 
1567 	switch (purpose) {
1568 	case 1:
1569 		g_return_val_if_fail (s->chart != NULL, FALSE);
1570 		label = gog_object_add_by_name (GOG_OBJECT (s->chart), "Title", s->label);
1571 		break;
1572 	case 2:
1573 	case 3:
1574 	case 7: {
1575 		GSList *axes;
1576 		GogAxisType t;
1577 
1578 		g_return_val_if_fail (s->chart != NULL, FALSE);
1579 
1580 		switch (purpose) {
1581 		case 2: t = GOG_AXIS_Y; break;
1582 		case 3: t = GOG_AXIS_X; break;
1583 		case 7: t = GOG_AXIS_Z; break;
1584 		default :
1585 			g_warning ("Unknown axis type %d", purpose);
1586 			return FALSE;
1587 		}
1588 		axes = gog_chart_get_axes (s->chart, t);
1589 
1590 		g_return_val_if_fail (axes != NULL, FALSE);
1591 
1592 		label = gog_object_add_by_name (GOG_OBJECT (axes->data), "Label", s->label);
1593 		g_slist_free (axes);
1594 		break;
1595 	}
1596 	case 4:
1597 		break;
1598 	}
1599 
1600 	if (label != NULL) {
1601 		Sheet *sheet = ms_container_sheet (s->container.parent);
1602 		if (sheet != NULL && s->text != NULL) {
1603 			GnmValue *value = value_new_string_nocopy (s->text);
1604 			GnmExprTop const *texpr = gnm_expr_top_new_constant (value);
1605 			gog_dataset_set_dim (GOG_DATASET (label), 0,
1606 					     gnm_go_data_scalar_new_expr (sheet, texpr), NULL);
1607 		}
1608 		s->text = NULL;
1609 		s->label = NULL;
1610 	} else if (s->label) {
1611 		d (2, g_printerr ("We have non imported data for a text field;\n"););
1612 		g_object_unref (s->label);
1613 		s->label = NULL;
1614 	}
1615 
1616 	d (2, {
1617 	guint16 const series_num = GSF_LE_GET_GUINT16 (q->data+2);
1618 	guint16 const pt_num = GSF_LE_GET_GUINT16 (q->data+4);
1619 
1620 	switch (purpose) {
1621 	case 1 : g_printerr ("TEXT is chart title\n"); break;
1622 	case 2 : g_printerr ("TEXT is Y axis title\n"); break;
1623 	case 3 : g_printerr ("TEXT is X axis title\n"); break;
1624 	case 4 : g_printerr ("TEXT is data label for pt %hd in series %hd\n",
1625 			 pt_num, series_num); break;
1626 	case 7 : g_printerr ("TEXT is Z axis title\n"); break;
1627 	default :
1628 		 g_printerr ("ERROR : TEXT is linked to undocumented object\n");
1629 	}});
1630 	if (NULL != label && NULL != s->style)
1631 		go_styled_object_set_style (GO_STYLED_OBJECT (label), s->style);
1632 	return FALSE;
1633 }
1634 
1635 /****************************************************************************/
1636 
1637 static gboolean
BC_R(picf)1638 BC_R(picf)(XLChartHandler const *handle,
1639 	   XLChartReadState *s, BiffQuery *q)
1640 {
1641 	return FALSE;
1642 }
1643 
1644 /****************************************************************************/
1645 
1646 static gboolean
BC_R(pie)1647 BC_R(pie)(XLChartHandler const *handle,
1648 	  XLChartReadState *s, BiffQuery *q)
1649 {
1650 	double initial_angle, center_size;
1651 	guint16 flags;
1652 	gboolean in_3d;
1653 
1654 	XL_CHECK_CONDITION_VAL (q->length >= 6, TRUE);
1655 
1656 	initial_angle = GSF_LE_GET_GUINT16 (q->data);
1657 	center_size = GSF_LE_GET_GUINT16 (q->data+2); /* 0-100 */
1658 	flags = GSF_LE_GET_GUINT16 (q->data+4);
1659 	in_3d = (BC_R(ver)(s) >= MS_BIFF_V8 && (flags & 0x01));
1660 
1661 	g_return_val_if_fail (s->plot == NULL, TRUE);
1662 	s->plot = (GogPlot*) gog_plot_new_by_name ((center_size == 0) ? "GogPiePlot" : "GogRingPlot");
1663 	g_return_val_if_fail (s->plot != NULL, TRUE);
1664 
1665 	g_object_set (G_OBJECT (s->plot),
1666 		"in-3d",		in_3d,
1667 		"initial-angle",	initial_angle,
1668 		NULL);
1669 	if (center_size != 0)
1670 		g_object_set (G_OBJECT (s->plot),
1671 			"center-size",	((double)center_size) / 100.,
1672 			NULL);
1673 
1674 #if 0
1675 	gboolean leader_lines = (BC_R(ver)(s) >= MS_BIFF_V8 && (flags & 0x02));
1676 #endif
1677 
1678 	return FALSE;
1679 }
1680 
1681 /****************************************************************************/
1682 
1683 static gboolean
BC_R(pieformat)1684 BC_R(pieformat)(XLChartHandler const *handle,
1685 		XLChartReadState *s, BiffQuery *q)
1686 {
1687 	unsigned separation;
1688 
1689 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
1690 
1691 	separation = GSF_LE_GET_GUINT16 (q->data); /* 0-500 */
1692 
1693 	/* we only support the default right now.  Also, XL sets this for _all_ types
1694 	 * rather than just pies. */
1695 	if (s->style_element >= 0 && s->style != NULL &&
1696 	    !s->has_extra_dataformat)	/* this is cheesy, figure out what the 0xfffd in dataformat means */
1697 		g_object_set_data (G_OBJECT (s->style),
1698 			"pie-separation", GUINT_TO_POINTER (separation));
1699 	else if (s->plot != NULL &&
1700 		 g_object_class_find_property (G_OBJECT_GET_CLASS (s->plot), "default-separation"))
1701 		g_object_set (G_OBJECT (s->plot),
1702 			"default-separation",	((double) separation) / 100.,
1703 			NULL);
1704 	d (2, g_printerr ("Pie slice(s) are %u %% of diam from center\n",
1705 		       separation););
1706 	return FALSE;
1707 }
1708 
1709 /****************************************************************************/
1710 
1711 static gboolean
BC_R(plotarea)1712 BC_R(plotarea)(XLChartHandler const *handle,
1713 	       XLChartReadState *s, BiffQuery *q)
1714 {
1715 	/* Does nothing.  Should always have a 'FRAME' record following */
1716 	return FALSE;
1717 }
1718 
1719 /****************************************************************************/
1720 
1721 static gboolean
BC_R(plotgrowth)1722 BC_R(plotgrowth)(XLChartHandler const *handle,
1723 		 XLChartReadState *s, BiffQuery *q)
1724 {
1725 	XL_CHECK_CONDITION_VAL (q->length >= 8, TRUE);
1726 	d (2, {
1727 	/* Docs say these are longs
1728 	 * But it appears that only 2 lsb are valid ??
1729 	 */
1730 	gint16 const horiz = GSF_LE_GET_GUINT16 (q->data+2);
1731 	gint16 const vert = GSF_LE_GET_GUINT16 (q->data+6);
1732 
1733 	g_printerr ("Scale H=");
1734 	if (horiz != -1)
1735 		g_printerr ("%u", horiz);
1736 	else
1737 		g_printerr ("Unscaled");
1738 	g_printerr (", V=");
1739 	if (vert != -1)
1740 		g_printerr ("%u", vert);
1741 	else
1742 		g_printerr ("Unscaled");
1743 	});
1744 	return FALSE;
1745 }
1746 /****************************************************************************/
1747 
1748 static gboolean
BC_R(pos)1749 BC_R(pos)(XLChartHandler const *handle,
1750 	  XLChartReadState *s, BiffQuery *q)
1751 {
1752 	switch (BC_R(top_state) (s, 0)) {
1753 	case BIFF_CHART_text :
1754 		d(2, g_printerr ("text pos;"););
1755 		break;
1756 	default :
1757 		;
1758 	}
1759 	return FALSE;
1760 }
1761 
1762 /****************************************************************************/
1763 
1764 static void
set_radial_axes(XLChartReadState * s)1765 set_radial_axes (XLChartReadState *s)
1766 {
1767 	GSList *l, *cur, *contrib, *ptr;
1768 
1769 	l = gog_chart_get_axes (s->chart, GOG_AXIS_X);
1770 	for (cur = l; cur; cur = cur->next) {
1771 		GogObject *axis = cur->data;
1772 
1773 		contrib = g_slist_copy ((GSList *) gog_axis_contributors (GOG_AXIS (axis)));
1774 		gog_axis_clear_contributors (GOG_AXIS (axis));
1775 		if (gog_object_is_deletable (axis))
1776 			gog_object_clear_parent (axis);
1777 		else {
1778 			g_slist_free (contrib);
1779 			continue;
1780 		}
1781 
1782 		g_object_set (G_OBJECT (axis), "type",
1783 			      GOG_AXIS_CIRCULAR, NULL);
1784 		gog_object_add_by_name (GOG_OBJECT (s->chart),
1785 					"Circular-Axis", axis);
1786 		for (ptr= contrib; ptr != NULL; ptr = ptr->next)
1787 			gog_plot_set_axis (GOG_PLOT (ptr->data), GOG_AXIS (axis));
1788 		g_slist_free (contrib);
1789 	}
1790 	g_slist_free (l);
1791 
1792 	l = gog_chart_get_axes (s->chart, GOG_AXIS_Y);
1793 	for (cur = l; cur; cur = cur->next) {
1794 		GogObject *axis = cur->data;
1795 
1796 		contrib = g_slist_copy ((GSList *) gog_axis_contributors (GOG_AXIS (axis)));
1797 		gog_axis_clear_contributors (GOG_AXIS (axis));
1798 		if (gog_object_is_deletable (axis))
1799 			gog_object_clear_parent (axis);
1800 		else {
1801 			g_slist_free (contrib);
1802 			continue;
1803 		}
1804 
1805 		g_object_set (G_OBJECT (axis), "type",
1806 			      GOG_AXIS_RADIAL, NULL);
1807 		gog_object_add_by_name (GOG_OBJECT (s->chart),
1808 					"Radial-Axis", axis);
1809 		for (ptr= contrib; ptr != NULL; ptr = ptr->next)
1810 			gog_plot_set_axis (GOG_PLOT (ptr->data), GOG_AXIS (axis));
1811 		g_slist_free (contrib);
1812 	}
1813 	g_slist_free (l);
1814 }
1815 
1816 static gboolean
BC_R(radar)1817 BC_R(radar)(XLChartHandler const *handle,
1818 	    XLChartReadState *s, BiffQuery *q)
1819 {
1820 	g_return_val_if_fail (s->plot == NULL, TRUE);
1821 
1822 	s->plot = (GogPlot*) gog_plot_new_by_name ("GogRadarPlot");
1823 	/* XL defaults to having markers and does not emit a style record
1824 	 * to define a marker in the default case. */
1825 	if (s->plot)
1826 		g_object_set (G_OBJECT (s->plot), "default-style-has-markers", TRUE, NULL);
1827 
1828 	/* Change axes types */
1829 	set_radial_axes (s);
1830 
1831 	return FALSE;
1832 }
1833 
1834 /****************************************************************************/
1835 
1836 static gboolean
BC_R(radararea)1837 BC_R(radararea)(XLChartHandler const *handle,
1838 		XLChartReadState *s, BiffQuery *q)
1839 {
1840 	g_return_val_if_fail (s->plot == NULL, TRUE);
1841 	s->plot = (GogPlot*) gog_plot_new_by_name ("GogRadarAreaPlot");
1842 
1843 	/* Change axes types */
1844 	set_radial_axes (s);
1845 
1846 	return FALSE;
1847 }
1848 
1849 /****************************************************************************/
1850 
1851 static gboolean
BC_R(sbaseref)1852 BC_R(sbaseref)(XLChartHandler const *handle,
1853 	       XLChartReadState *s, BiffQuery *q)
1854 {
1855 	return FALSE;
1856 }
1857 
1858 /****************************************************************************/
1859 
1860 static gboolean
BC_R(scatter)1861 BC_R(scatter)(XLChartHandler const *handle,
1862 	      XLChartReadState *s, BiffQuery *q)
1863 {
1864 	g_return_val_if_fail (s->plot == NULL, TRUE);
1865 
1866 	if (BC_R(ver)(s) >= MS_BIFF_V8) {
1867 		guint16 flags;
1868 
1869 		XL_CHECK_CONDITION_VAL (q->length >= 6, TRUE);
1870 
1871 		flags = GSF_LE_GET_GUINT16 (q->data + 4);
1872 
1873 		/* Has bubbles */
1874 		if (flags & 0x01) {
1875 			guint16 const size_type = GSF_LE_GET_GUINT16 (q->data+2);
1876 			gboolean in_3d = (flags & 0x04) != 0;
1877 			gboolean show_negatives = (flags & 0x02) != 0;
1878 			gboolean size_as_area = (size_type != 2);
1879 			double scale =  GSF_LE_GET_GUINT16 (q->data) / 100.;
1880 			s->plot = (GogPlot*) gog_plot_new_by_name ("GogBubblePlot");
1881 			g_return_val_if_fail (s->plot != NULL, TRUE);
1882 			g_object_set (G_OBJECT (s->plot),
1883 				"in-3d",		in_3d,
1884 				"show-negatives",	show_negatives,
1885 				"size-as-area",		size_as_area,
1886 				"bubble-scale",	scale,
1887 				NULL);
1888 			d(1, g_printerr ("bubbles;"););
1889 			return FALSE;
1890 		}
1891 	}
1892 
1893 	s->plot = (GogPlot*) gog_plot_new_by_name ("GogXYPlot");
1894 	g_return_val_if_fail (s->plot != NULL, TRUE);
1895 
1896 	d(1, g_printerr ("scatter;"););
1897 	return FALSE;
1898 }
1899 
1900 /****************************************************************************/
1901 
1902 static gboolean
BC_R(serauxerrbar)1903 BC_R(serauxerrbar)(XLChartHandler const *handle,
1904 		   XLChartReadState *s, BiffQuery *q)
1905 {
1906 	guint8 type, src, teetop, num;
1907 	double val;
1908 
1909 	XL_CHECK_CONDITION_VAL (q->length >= 14, TRUE);
1910 
1911 	type = GSF_LE_GET_GUINT8  (q->data);
1912 	src = GSF_LE_GET_GUINT8  (q->data+1);
1913 	teetop = GSF_LE_GET_GUINT8  (q->data+2);
1914 	num = GSF_LE_GET_GUINT16  (q->data+12);
1915 	/* next octet must be 1 */
1916 
1917 	d (1, {
1918 		switch (type) {
1919 		case 1: g_printerr ("type: x-direction plus\n"); break;
1920 		case 2: g_printerr ("type: x-direction minus\n"); break;
1921 		case 3: g_printerr ("type: y-direction plus\n"); break;
1922 		case 4: g_printerr ("type: y-direction minus\n"); break;
1923 		}
1924 		switch (src) {
1925 		case 1: g_printerr ("source: percentage\n"); break;
1926 		case 2: g_printerr ("source: fixed value\n"); break;
1927 		case 3: g_printerr ("source: standard deviation\n"); break;
1928 		case 4: g_printerr ("source: custom\n"); break;
1929 		case 5: g_printerr ("source: standard error\n"); break;
1930 		}
1931 		g_printerr ("%sT-shaped\n", (teetop)? "": "Not ");
1932 		g_printerr ("num values: %d\n", num);
1933 	});
1934 	g_return_val_if_fail (s->currentSeries != NULL, FALSE);
1935 
1936 	s->currentSeries->err_type = type;
1937 	s->currentSeries->err_src = src;
1938 	s->currentSeries->err_teetop = teetop;
1939 	s->currentSeries->err_parent = s->parent_index;
1940 	s->currentSeries->err_num = num;
1941 	if (src > 0 && src < 4) {
1942 		val = GSF_LE_GET_DOUBLE (q->data + 4);
1943 		d(1, g_printerr ("value = %g\n",val););
1944 		s->currentSeries->err_val = val;
1945 	}
1946 	return FALSE;
1947 }
1948 
1949 /****************************************************************************/
1950 
1951 static gboolean
BC_R(serauxtrend)1952 BC_R(serauxtrend)(XLChartHandler const *handle,
1953 		  XLChartReadState *s, BiffQuery *q)
1954 {
1955 	guint8 type, order;
1956 	double intercept, forecast, backcast;
1957 	gboolean equation, r2;
1958 
1959 	XL_CHECK_CONDITION_VAL (q->length >= 28, TRUE);
1960 
1961 	type = GSF_LE_GET_GUINT8  (q->data);
1962 	order = GSF_LE_GET_GUINT8  (q->data+1);
1963 	intercept = GSF_LE_GET_DOUBLE (q->data+2);
1964 	equation = GSF_LE_GET_GUINT8  (q->data+10);
1965 	r2 = GSF_LE_GET_GUINT8  (q->data+11);
1966 	forecast = GSF_LE_GET_DOUBLE (q->data+12);
1967 	backcast = GSF_LE_GET_DOUBLE (q->data+20);
1968 	d (1, {
1969 		switch (type) {
1970 		case 0: g_printerr ("type: polynomial\n"); break;
1971 		case 1: g_printerr ("type: exponential\n"); break;
1972 		case 2: g_printerr ("type: logarithmic\n"); break;
1973 		case 3: g_printerr ("type: power\n"); break;
1974 		case 4: g_printerr ("type: moving average\n"); break;
1975 		}
1976 		g_printerr ("order: %d\n", order);
1977 		g_printerr ("intercept: %g\n", intercept);
1978 		g_printerr ("show equation: %s\n", (equation)? "yes": "no");
1979 		g_printerr ("show R-squared: %s\n", (r2)? "yes": "no");
1980 		g_printerr ("forecast: %g\n", forecast);
1981 		g_printerr ("backcast: %g\n", backcast);
1982 	});
1983 	g_return_val_if_fail (s->currentSeries != NULL, FALSE);
1984 
1985 	s->currentSeries->reg_type = type;
1986 	s->currentSeries->reg_order = order;
1987 	s->currentSeries->reg_show_eq = equation;
1988 	s->currentSeries->reg_show_R2 = r2;
1989 	s->currentSeries->reg_intercept = intercept;
1990 	s->currentSeries->reg_backcast = backcast;
1991 	s->currentSeries->reg_forecast = forecast;
1992 	s->currentSeries->reg_parent = s->parent_index;
1993 	s->currentSeries->reg_skip_invalid = TRUE; /* excel does that, more or less */
1994 	s->currentSeries->reg_min = s->currentSeries->reg_max = go_nan;
1995 	return FALSE;
1996 }
1997 
1998 /****************************************************************************/
1999 
2000 static gboolean
BC_R(serfmt)2001 BC_R(serfmt)(XLChartHandler const *handle,
2002 	     XLChartReadState *s, BiffQuery *q)
2003 {
2004 	guint8 flags;
2005 
2006 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
2007 
2008 	flags = GSF_LE_GET_GUINT8  (q->data);
2009 	if (flags & 1) {
2010 	    if (s->currentSeries)
2011 		s->currentSeries->interpolation = GO_LINE_INTERPOLATION_SPLINE;
2012 	    else
2013 		s->interpolation = GO_LINE_INTERPOLATION_SPLINE;
2014 	}
2015 	d (1, {
2016 		g_printerr ("interpolation: %s\n", (flags & 1)? "spline": "linear");
2017 	});
2018 	return FALSE;
2019 }
2020 
2021 
2022 /****************************************************************************/
2023 
2024 static gboolean
BC_R(trendlimits)2025 BC_R(trendlimits)(XLChartHandler const *handle,
2026 		  XLChartReadState *s, BiffQuery *q)
2027 {
2028 	double min, max;
2029 	gboolean skip_invalid;
2030 
2031 	XL_CHECK_CONDITION_VAL (s->currentSeries, TRUE);
2032 	XL_CHECK_CONDITION_VAL (q->length >= 17, TRUE);
2033 	min = GSF_LE_GET_DOUBLE (q->data);
2034 	max = GSF_LE_GET_DOUBLE (q->data + 8);
2035 	skip_invalid = GSF_LE_GET_GUINT8 (q->data + 16);
2036 
2037 	d (1, {
2038 		g_printerr ("skip invalid data: %s\n", (skip_invalid)? "yes": "no");
2039 		g_printerr ("min: %g\n", min);
2040 		g_printerr ("max: %g\n", max);
2041 	});
2042 	s->currentSeries->reg_min = min;
2043 	s->currentSeries->reg_max = max;
2044 	s->currentSeries->reg_skip_invalid = skip_invalid;
2045 	return FALSE;
2046 }
2047 
2048 /****************************************************************************/
2049 
2050 static void
BC_R(vector_details)2051 BC_R(vector_details)(XLChartReadState *s, BiffQuery *q, XLChartSeries *series,
2052 		     GogMSDimType purpose,
2053 		     int type_offset, int count_offset, char const *name)
2054 {
2055 	XL_CHECK_CONDITION (q->length >= 2 + (unsigned)count_offset);
2056 #if 0
2057 	switch (GSF_LE_GET_GUINT16 (q->data + type_offset)) {
2058 	case 0 : /* date */ break;
2059 	case 1 : /* value */ break;
2060 	case 2 : /* sequences */ break;
2061 	case 3 : /* string */ break;
2062 	}
2063 #endif
2064 
2065 	series->data [purpose].num_elements = GSF_LE_GET_GUINT16 (q->data+count_offset);
2066 	d (0, g_printerr ("%s has %d elements\n",
2067 		       name, series->data [purpose].num_elements););
2068 }
2069 
2070 
2071 static gboolean
BC_R(series)2072 BC_R(series)(XLChartHandler const *handle,
2073 	     XLChartReadState *s, BiffQuery *q)
2074 {
2075 	XLChartSeries *series;
2076 
2077 	XL_CHECK_CONDITION_VAL (s->currentSeries == NULL, TRUE);
2078 
2079 	d (2, g_printerr ("SERIES = %d\n", s->series->len););
2080 
2081 	series = excel_chart_series_new ();
2082 
2083 	/* WARNING : The offsets in the documentation are WRONG.
2084 	 *           Use the sizes instead.
2085 	 */
2086 	BC_R(vector_details) (s, q, series, GOG_MS_DIM_CATEGORIES,
2087 			      0, 4, "Categories");
2088 	BC_R(vector_details) (s, q, series, GOG_MS_DIM_VALUES,
2089 			      2, 6, "Values");
2090 	if (BC_R(ver)(s) >= MS_BIFF_V8)
2091 		BC_R(vector_details) (s, q, series, GOG_MS_DIM_BUBBLES,
2092 				      8, 10, "Bubbles");
2093 
2094 	g_ptr_array_add (s->series, series);
2095 	s->currentSeries = series;
2096 
2097 	return FALSE;
2098 }
2099 
2100 /****************************************************************************/
2101 
2102 static gboolean
BC_R(serieslist)2103 BC_R(serieslist)(XLChartHandler const *handle,
2104 		 XLChartReadState *s, BiffQuery *q)
2105 {
2106 	return FALSE;
2107 }
2108 
2109 /****************************************************************************/
2110 
2111 static gboolean
BC_R(seriestext)2112 BC_R(seriestext)(XLChartHandler const *handle,
2113 		 XLChartReadState *s, BiffQuery *q)
2114 {
2115 	guint16 id;
2116 	int slen;
2117 	char *str;
2118 	GnmValue *value;
2119 
2120 	XL_CHECK_CONDITION_VAL (q->length >= 3, TRUE);
2121 	id = GSF_LE_GET_GUINT16 (q->data);	/* must be 0 */
2122 	slen = GSF_LE_GET_GUINT8 (q->data + 2);
2123 	XL_CHECK_CONDITION_VAL (id == 0, TRUE);
2124 
2125 	if (slen == 0)
2126 		return FALSE;
2127 
2128 	str = excel_biff_text_1 (s->container.importer, q, 2);
2129 	d (2, g_printerr ("'%s';\n", str););
2130 
2131 	if (s->currentSeries != NULL &&
2132 	    s->currentSeries->data [GOG_MS_DIM_LABELS].data == NULL) {
2133 		GnmExprTop const *texpr;
2134 		Sheet *sheet = ms_container_sheet (s->container.parent);
2135 		g_return_val_if_fail (sheet != NULL, FALSE);
2136 		value = value_new_string_nocopy (str);
2137 		texpr = gnm_expr_top_new_constant (value);
2138 		s->currentSeries->data [GOG_MS_DIM_LABELS].data =
2139 			gnm_go_data_scalar_new_expr (sheet, texpr);
2140 	} else if (BC_R(top_state) (s, 0) == BIFF_CHART_text) {
2141 		if (s->text != NULL) {
2142 			g_warning ("multiple seriestext associated with 1 text record ?");
2143 			g_free (str);
2144 		} else
2145 			s->text = str;
2146 	} else
2147 		g_free (str);
2148 
2149 	return FALSE;
2150 }
2151 
2152 /****************************************************************************/
2153 
2154 static gboolean
BC_R(serparent)2155 BC_R(serparent)(XLChartHandler const *handle,
2156 		XLChartReadState *s, BiffQuery *q)
2157 {
2158 	guint16 index;
2159 
2160 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
2161 	index = GSF_LE_GET_GUINT16 (q->data) - 1;
2162 	d (1, g_printerr ("Parent series index is %hd\n", index););
2163 	s->parent_index = index;
2164 
2165 	return FALSE;
2166 }
2167 
2168 /****************************************************************************/
2169 
2170 static gboolean
BC_R(sertocrt)2171 BC_R(sertocrt)(XLChartHandler const *handle,
2172 	       XLChartReadState *s, BiffQuery *q)
2173 {
2174 	guint16 index;
2175 
2176 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
2177 	XL_CHECK_CONDITION_VAL (s->currentSeries != NULL, TRUE);
2178 	index = GSF_LE_GET_GUINT16 (q->data);
2179 
2180 	s->currentSeries->chart_group = index;
2181 
2182 	d (1, g_printerr ("Series chart group index is %hd\n", index););
2183 	return FALSE;
2184 }
2185 
2186 /****************************************************************************/
2187 
2188 typedef enum {
2189 	MS_CHART_BLANK_SKIP		= 0,
2190 	MS_CHART_BLANK_ZERO		= 1,
2191 	MS_CHART_BLANK_INTERPOLATE	= 2,
2192 	MS_CHART_BLANK_MAX		= 3
2193 } MSChartBlank;
2194 static char const *const ms_chart_blank[] = {
2195 	"Skip blanks", "Blanks are zero", "Interpolate blanks"
2196 };
2197 
2198 static gboolean
BC_R(shtprops)2199 BC_R(shtprops)(XLChartHandler const *handle,
2200 	       XLChartReadState *s, BiffQuery *q)
2201 {
2202 	guint16 flags;
2203 	gboolean manual_format, only_plot_visible_cells, dont_size_with_window,
2204 		has_pos_record;
2205 	gboolean ignore_pos_record = FALSE;
2206 	guint8 tmp;
2207 	MSChartBlank blanks;
2208 
2209 	XL_CHECK_CONDITION_VAL (q->length >= 4, TRUE);
2210 	flags = GSF_LE_GET_GUINT16 (q->data);
2211 	manual_format		= (flags&0x01) ? TRUE : FALSE;
2212 	only_plot_visible_cells	= (flags&0x02) ? TRUE : FALSE;
2213 	dont_size_with_window	= (flags&0x04) ? TRUE : FALSE;
2214 	has_pos_record		= (flags&0x08) ? TRUE : FALSE;
2215 	tmp = GSF_LE_GET_GUINT16 (q->data+2);
2216 	g_return_val_if_fail (tmp < MS_CHART_BLANK_MAX, TRUE);
2217 	blanks = tmp;
2218 	d (2, g_printerr ("%s;", ms_chart_blank[blanks]););
2219 
2220 	if (BC_R(ver)(s) >= MS_BIFF_V8)
2221 		ignore_pos_record = (flags&0x10) ? TRUE : FALSE;
2222 
2223 	d (1, {
2224 			g_printerr ("%sesize chart with window.\n",
2225 				    dont_size_with_window ? "Don't r": "R");
2226 
2227 			if (has_pos_record && !ignore_pos_record)
2228 				g_printerr ("There should be a POS record around here soon\n");
2229 
2230 			if (manual_format)
2231 				g_printerr ("Manually formated\n");
2232 			if (only_plot_visible_cells)
2233 				g_printerr ("Only plot visible (to whom?) cells\n");
2234 		});
2235 	return FALSE;
2236 }
2237 
2238 /****************************************************************************/
2239 
2240 static gboolean
BC_R(siindex)2241 BC_R(siindex)(XLChartHandler const *handle,
2242 	      XLChartReadState *s, BiffQuery *q)
2243 {
2244 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
2245 	/* UNDOCUMENTED : Docs says this is long
2246 	 * Biff record is only length 2 */
2247 	s->cur_role = GSF_LE_GET_GUINT16 (q->data);
2248 	d (1, g_printerr ("Series %d is %d\n", s->series->len, s->cur_role););
2249 	return FALSE;
2250 }
2251 /****************************************************************************/
2252 
2253 static gboolean
BC_R(surf)2254 BC_R(surf)(XLChartHandler const *handle,
2255 	   XLChartReadState *s, BiffQuery *q)
2256 {
2257 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
2258 
2259 	/* TODO : implement wireframe (aka use-color) */
2260 #if 0
2261 	/* NOTE: The record is only 2 bytes, so this code is wrong.  */
2262 	guint16 const flags = GSF_LE_GET_GUINT16 (q->data+4);
2263 
2264 	g_return_val_if_fail (s->plot == NULL, TRUE);
2265 	s->plot = gog_plot_new_by_name ("GogContourPlot");
2266 	g_object_set (G_OBJECT (s->plot),
2267 		"use-color",		(flags & 0x01) != 0,
2268 		"in-3d",		(BC_R(ver)(s) >= MS_BIFF_V8 && (flags & 0x02)),
2269 		NULL);
2270 #endif
2271 
2272 	/* at this point, we don't know if it is a contour plot or a surface
2273 	so we defer plot creation until later */
2274 	s->is_surface = TRUE;
2275 
2276 	return FALSE;
2277 }
2278 
2279 /****************************************************************************/
2280 
2281 enum {
2282 	XL_POS_LOW, /* left or top */
2283 	XL_POS_CENTER,
2284 	XL_POS_HIGH, /* right or bottom */
2285 	XL_POS_JUSTIFY /*not supported, just use as an equivalent for center */
2286 };
2287 
2288 static gboolean
BC_R(text)2289 BC_R(text)(XLChartHandler const *handle,
2290 	   XLChartReadState *s, BiffQuery *q)
2291 {
2292 #if 0 /*when we have somewhere to store it */
2293 	static GnmHAlign const haligns[] = { /* typo in docs */
2294 		HALIGN_LEFT, HALIGN_CENTER, HALIGN_RIGHT, HALIGN_JUSTIFY
2295 	};
2296 	static GnmVAlign const valigns[] = {
2297 		VALIGN_TOP, VALIGN_CENTER, VALIGN_BOTTOM, VALIGN_JUSTIFY
2298 	};
2299 	unsigned tmp;
2300 #endif
2301 	XL_CHECK_CONDITION_VAL (q->length >= 8, TRUE);
2302 
2303 	BC_R(get_style) (s);
2304 
2305 #if 0 /*when we have somewhere to store it */
2306 	tmp = GSF_LE_GET_GINT8 (q->data + 0);
2307 	s->style-> .... = haligns[tmp < G_N_ELEMENTS (haligns) ? tmp : 0];
2308 	tmp = GSF_LE_GET_GINT8 (q->data + 1);
2309 	s->style-> .... = valigns[tmp < G_N_ELEMENTS (valigns) ? tmp : 0];
2310 #endif
2311 
2312 	s->style->font.color = BC_R(color) (q->data+4, "Font");
2313 	if (BC_R(ver)(s) >= MS_BIFF_V8 && q->length >= 34) {
2314 		/* this over rides the flags */
2315 		s->style->text_layout.angle = GSF_LE_GET_GINT16 (q->data + 30);
2316 	}
2317 
2318 	d (2, {
2319 	if (s->prev_opcode == BIFF_CHART_defaulttext) {
2320 		g_printerr ("Text follows defaulttext;\n");
2321 	} else switch (BC_R(top_state) (s, 0)) {
2322 	case BIFF_CHART_axisparent :
2323 		g_printerr ("Text follows axis;\n");
2324 		break;
2325 	case BIFF_CHART_chart :
2326 		g_printerr ("Text follows chart;\n");
2327 		break;
2328 	case BIFF_CHART_legend :
2329 		g_printerr ("Text follows legend;\n");
2330 		break;
2331 	default :
2332 		g_printerr ("BIFF ERROR : A Text record follows a %x\n",
2333 			s->prev_opcode);
2334 		g_object_unref (s->style);
2335 		s->style = NULL;
2336 	}};);
2337 
2338 return FALSE;
2339 }
2340 
2341 /****************************************************************************/
2342 
2343 static gboolean
BC_R(tick)2344 BC_R(tick)(XLChartHandler const *handle,
2345 	   XLChartReadState *s, BiffQuery *q)
2346 {
2347 	guint16 major, minor, label, flags;
2348 
2349 	XL_CHECK_CONDITION_VAL (q->length >= 26, TRUE);
2350 
2351 	major = GSF_LE_GET_GUINT8 (q->data);
2352 	minor = GSF_LE_GET_GUINT8 (q->data+1);
2353 	label = GSF_LE_GET_GUINT8 (q->data+2);
2354 	flags = GSF_LE_GET_GUINT16 (q->data+24);
2355 
2356 	if (s->axis != NULL)
2357 		g_object_set (G_OBJECT (s->axis),
2358 			/* cheat until we support different label pos */
2359 			"major-tick-labeled",	(label != 0),
2360 			"major-tick-in",	((major & 1) ? TRUE : FALSE),
2361 			"major-tick-out",	((major >= 2) ? TRUE : FALSE),
2362 			"minor-tick-in",	((minor & 1) ? TRUE : FALSE),
2363 			"minor-tick-out",	((minor >= 2) ? TRUE : FALSE),
2364 			NULL);
2365 	BC_R(get_style) (s);
2366 	if (!(flags & 0x01))
2367 		s->style->font.color = BC_R(color) (q->data+4, "LabelColour");
2368 	s->style->text_layout.auto_angle = flags & 0x20;
2369 	switch (flags & 0x1c) {
2370 	default:
2371 		/* not supported */
2372 	case 0:
2373 		s->style->text_layout.angle = 0.;
2374 		break;
2375 	case 8:
2376 		s->style->text_layout.angle = 90.;
2377 		break;
2378 	case 0x0c:
2379 		s->style->text_layout.angle = -90.;
2380 		break;
2381 	}
2382 
2383 	if ((!(flags & 0x20)) && BC_R(ver)(s) >= MS_BIFF_V8) {
2384 		guint16 trot = GSF_LE_GET_GUINT16 (q->data+28);
2385 		if (trot <= 0x5a)
2386 			s->style->text_layout.angle = trot;
2387 		else if (trot <= 0xb4)
2388 			s->style->text_layout.angle = 90 - (int) trot;
2389 		else
2390 			; // FIXME: not supported for now
2391 	}
2392 
2393 	d (1, {
2394 	switch (major) {
2395 	case 0: g_printerr ("no major tick;\n"); break;
2396 	case 1: g_printerr ("major tick inside axis;\n"); break;
2397 	case 2: g_printerr ("major tick outside axis;\n"); break;
2398 	case 3: g_printerr ("major tick across axis;\n"); break;
2399 	default : g_printerr ("unknown major tick type;\n");
2400 	}
2401 	switch (minor) {
2402 	case 0: g_printerr ("no minor tick;\n"); break;
2403 	case 1: g_printerr ("minor tick inside axis;\n"); break;
2404 	case 2: g_printerr ("minor tick outside axis;\n"); break;
2405 	case 3: g_printerr ("minor tick across axis;\n"); break;
2406 	default : g_printerr ("unknown minor tick type;\n");
2407 	}
2408 	switch (label) {
2409 	case 0: g_printerr ("no tick label;\n"); break;
2410 	case 1: g_printerr ("tick label at low end (NOTE mapped to near axis);\n"); break;
2411 	case 2: g_printerr ("tick label at high end (NOTE mapped to near axis);\n"); break;
2412 	case 3: g_printerr ("tick label near axis;\n"); break;
2413 	default : g_printerr ("unknown tick label position;\n");
2414 	}
2415 
2416 	/*
2417 	if (flags&0x01)
2418 		g_printerr ("Auto tick label colour");
2419 	else
2420 		BC_R(color) (q->data+4, "LabelColour", tick, FALSE);
2421 	*/
2422 
2423 	if (flags&0x02)
2424 		g_printerr ("Auto text background mode\n");
2425 	else
2426 		g_printerr ("background mode = %d\n", (unsigned)GSF_LE_GET_GUINT8 (q->data+3));
2427 
2428 	switch (flags&0x1c) {
2429 	case 0: g_printerr ("no rotation;\n"); break;
2430 	case 4: g_printerr ("top to bottom letters upright;\n"); break;
2431 	case 8: g_printerr ("rotate 90deg counter-clockwise;\n"); break;
2432 	case 0x0c: g_printerr ("rotate 90deg clockwise;\n"); break;
2433 	default : g_printerr ("unknown rotation;\n");
2434 	}
2435 
2436 	if (flags&0x20)
2437 		g_printerr ("Auto rotate;\n");
2438 	});
2439 
2440 	return FALSE;
2441 }
2442 
2443 /****************************************************************************/
2444 
2445 static gboolean
BC_R(units)2446 BC_R(units)(XLChartHandler const *handle,
2447 	    XLChartReadState *s, BiffQuery *q)
2448 {
2449 	guint16 type;
2450 
2451 	XL_CHECK_CONDITION_VAL (q->length >= 2, TRUE);
2452 
2453 	/* Irrelevant */
2454 	type = GSF_LE_GET_GUINT16 (q->data);
2455 	XL_CHECK_CONDITION_VAL (type == 0, TRUE);
2456 
2457 	return FALSE;
2458 }
2459 
2460 /****************************************************************************/
2461 
2462 static void
xl_axis_get_elem(Sheet * sheet,GogObject * axis,unsigned dim,gchar const * name,gboolean flag,guint8 const * data,gboolean log_scale)2463 xl_axis_get_elem (Sheet *sheet, GogObject *axis, unsigned dim, gchar const *name,
2464 		  gboolean flag, guint8 const *data, gboolean log_scale)
2465 {
2466 	GOData *dat;
2467 	if (flag) {
2468 		dat = NULL;
2469 		d (1, g_printerr ("%s = Auto\n", name););
2470 		if (dim == GOG_AXIS_ELEM_CROSS_POINT) {
2471 			GnmValue *value = value_new_float (0.);
2472 			GnmExprTop const *texpr = gnm_expr_top_new_constant (value);
2473 			gog_dataset_set_dim (GOG_DATASET (axis), dim,
2474 			    gnm_go_data_scalar_new_expr (sheet, texpr), NULL);
2475 		    g_object_set (axis, "pos-str", "cross", NULL);
2476 		}
2477 	} else {
2478 		double const val = gsf_le_get_double (data);
2479 		double real_value = (log_scale)? gnm_pow10 (val): val;
2480 		GnmValue *value = value_new_float (real_value);
2481 		GnmExprTop const *texpr = gnm_expr_top_new_constant (value);
2482 		gog_dataset_set_dim (GOG_DATASET (axis), dim,
2483 			gnm_go_data_scalar_new_expr (sheet, texpr), NULL);
2484 		d (1, g_printerr ("%s = %f\n", name, real_value););
2485 	}
2486 }
2487 
2488 static gboolean
BC_R(valuerange)2489 BC_R(valuerange)(XLChartHandler const *handle,
2490 		 XLChartReadState *s, BiffQuery *q)
2491 {
2492 	guint16 flags;
2493 	gboolean log_scale;
2494 	double cross;
2495 	Sheet *sheet = ms_container_sheet (s->container.parent);
2496 
2497 	XL_CHECK_CONDITION_VAL (q->length >= 42, TRUE);
2498 
2499 	flags = GSF_LE_GET_GUINT16 (q->data+40);
2500 	log_scale =  flags & 0x20;
2501 	if (log_scale) {
2502 		g_object_set (s->axis, "map-name", "Log", NULL);
2503 		d (1, g_printerr ("Log scaled;\n"););
2504 	}
2505 
2506 	xl_axis_get_elem (sheet, s->axis, GOG_AXIS_ELEM_MIN,	  "Min Value",		flags&0x01, q->data+ 0, log_scale);
2507 	xl_axis_get_elem (sheet, s->axis, GOG_AXIS_ELEM_MAX,	  "Max Value",		flags&0x02, q->data+ 8, log_scale);
2508 	xl_axis_get_elem (sheet, s->axis, GOG_AXIS_ELEM_MAJOR_TICK,  "Major Increment",	flags&0x04, q->data+16, log_scale);
2509 	xl_axis_get_elem (sheet, s->axis, GOG_AXIS_ELEM_MINOR_TICK,  "Minor Increment",	flags&0x08, q->data+24, log_scale);
2510 	cross = (flags & 0x10)? ((log_scale)? 1.: 0.): ((log_scale)? gnm_pow10 (gsf_le_get_double (q->data + 32)): gsf_le_get_double (q->data + 32));
2511 
2512 	if (flags & 0x40) {
2513 		g_object_set (s->axis, "invert-axis", TRUE, NULL);
2514 		d (1, g_printerr ("Values in reverse order;\n"););
2515 	}
2516 	if (((flags & 0x80) != 0) ^ ((flags & 0x40) != 0)) {
2517 		if (gog_axis_get_atype (GOG_AXIS (s->axis)) == GOG_AXIS_X)
2518 			s->axis_cross_at_max = TRUE;
2519 		else if (gog_axis_get_atype (GOG_AXIS (s->axis)) == GOG_AXIS_Y && s->xaxis)
2520 			g_object_set (s->xaxis,
2521 				      "pos-str", "high",
2522 				      "cross-axis-id", gog_object_get_id (GOG_OBJECT (s->axis)),
2523 				      NULL);
2524 		d (1, g_printerr ("Cross over at max value;\n"););
2525 	} else {
2526 		if (gog_axis_get_atype (GOG_AXIS (s->axis)) == GOG_AXIS_X)
2527 			s->axis_cross_value = cross;
2528 		else if (gog_axis_get_atype (GOG_AXIS (s->axis)) == GOG_AXIS_Y && s->xaxis && !(flags & 0x10)) {
2529 			GnmValue *value = value_new_float (cross);
2530 			GnmExprTop const *texpr = gnm_expr_top_new_constant (value);
2531 			g_object_set (s->xaxis,
2532 				      "pos-str", "cross",
2533 				      "cross-axis-id", gog_object_get_id (GOG_OBJECT (s->axis)),
2534 				      NULL);
2535 			gog_dataset_set_dim (GOG_DATASET (s->xaxis), GOG_AXIS_ELEM_CROSS_POINT,
2536 				gnm_go_data_scalar_new_expr (sheet, texpr), NULL);
2537 		}
2538 		d (1, g_printerr ("Cross over point = %f\n", cross););
2539 	}
2540 
2541 	return FALSE;
2542 }
2543 
2544 /****************************************************************************/
2545 
2546 static void
cb_store_singletons(gpointer indx,GOStyle * style,GogObject * series)2547 cb_store_singletons (gpointer indx, GOStyle *style, GogObject *series)
2548 {
2549 	GogObject *singleton = gog_object_add_by_name (series, "Point", NULL);
2550 	if (singleton != NULL) {
2551 		g_object_set (singleton,
2552 			"index", GPOINTER_TO_UINT (indx),
2553 			"style", style,
2554 			NULL);
2555 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (singleton), "separation")) {
2556 			gpointer sep = g_object_get_data (G_OBJECT (style), "pie-separation");
2557 			g_object_set (singleton,
2558 				"separation", (double)GPOINTER_TO_UINT (sep) / 100.,
2559 				NULL);
2560 		}
2561 	}
2562 }
2563 
2564 static void
xl_axis_swap_elem(GogAxis * a,GogAxis * b,unsigned dim)2565 xl_axis_swap_elem (GogAxis *a, GogAxis *b, unsigned dim)
2566 {
2567 	GOData *a_dat = gog_dataset_get_dim (GOG_DATASET (a), dim);
2568 	GOData *b_dat = gog_dataset_get_dim (GOG_DATASET (b), dim);
2569 
2570 	if (a_dat != NULL) g_object_ref (a_dat);
2571 	if (b_dat != NULL) g_object_ref (b_dat);
2572 	gog_dataset_set_dim (GOG_DATASET (a), dim, b_dat, NULL);
2573 	gog_dataset_set_dim (GOG_DATASET (b), dim, a_dat, NULL);
2574 }
2575 
2576 static void
object_swap_children(GogObject * a,GogObject * b,char const * name)2577 object_swap_children (GogObject *a, GogObject *b, char const *name)
2578 {
2579 	GogObjectRole const *role;
2580 	GSList *children_a, *children_b, *ptr;
2581 	GogObject *obj;
2582 	GOStyle *style;
2583 	role = gog_object_find_role_by_name (a, name);
2584 	g_return_if_fail (role);
2585 	children_a = gog_object_get_children (a, role);
2586 	children_b = gog_object_get_children (b, role);
2587 	ptr = children_a;
2588 	while (ptr) {
2589 		obj = GOG_OBJECT (ptr->data);
2590 		style = go_style_dup (
2591 			go_styled_object_get_style (GO_STYLED_OBJECT (obj)));
2592 		gog_object_clear_parent (obj);
2593 		gog_object_add_by_role (b, role, obj);
2594 		go_styled_object_set_style (GO_STYLED_OBJECT (obj), style);
2595 		g_object_unref (style);
2596 		ptr = ptr->next;
2597 	}
2598 	g_slist_free (children_a);
2599 	ptr = children_b;
2600 	while (ptr) {
2601 		obj = GOG_OBJECT (ptr->data);
2602 		style = go_style_dup (
2603 			go_styled_object_get_style (GO_STYLED_OBJECT (obj)));
2604 		gog_object_clear_parent (obj);
2605 		gog_object_add_by_role (a, role, obj);
2606 		go_styled_object_set_style (GO_STYLED_OBJECT (obj), style);
2607 		g_object_unref (style);
2608 		ptr = ptr->next;
2609 	}
2610 	g_slist_free (children_b);
2611 }
2612 
2613 static gboolean
BC_R(end)2614 BC_R(end)(XLChartHandler const *handle,
2615 	  XLChartReadState *s, BiffQuery *q)
2616 {
2617 	int popped_state;
2618 
2619 	d (0, g_printerr ("}\n"););
2620 
2621 	g_return_val_if_fail (s->stack != NULL, TRUE);
2622 	XL_CHECK_CONDITION_VAL (s->stack->len > 0, TRUE);
2623 
2624 	popped_state = BC_R(top_state) (s, 0);
2625 	s->stack = g_array_remove_index_fast (s->stack, s->stack->len-1);
2626 
2627 	switch (popped_state) {
2628 	case BIFF_CHART_axisparent :
2629 		break;
2630 	case BIFF_CHART_axis :
2631 		if (s->style) {
2632 			g_object_set (s->axis, "style", s->style, NULL);
2633 			g_object_unref (s->style);
2634 			s->style = NULL;
2635 		}
2636 		s->axis = NULL;
2637 		break;
2638 
2639 	case BIFF_CHART_legend :
2640 		if (s->style) {
2641 			g_object_set (s->legend, "style", s->style, NULL);
2642 			g_object_unref (s->style);
2643 			s->style = NULL;
2644 		}
2645 		s->legend = NULL;
2646 		break;
2647 
2648 	case BIFF_CHART_frame :
2649 		if (s->style != NULL) {
2650 			int top_state = BC_R(top_state) (s, 0);
2651 			GogObject *obj = NULL;
2652 			if (top_state == BIFF_CHART_legend)
2653 				obj = s->legend;
2654 			else if (top_state == BIFF_CHART_chart)
2655 				obj = GOG_OBJECT (s->chart);
2656 			else if (s->frame_for_grid) {
2657 				GogGrid *tmp = gog_chart_get_grid (s->chart);
2658 				obj = (tmp == NULL)
2659 					? gog_object_add_by_name (GOG_OBJECT (s->chart), "Backplane", NULL)
2660 					: GOG_OBJECT (tmp);
2661 			}
2662 			if (obj != NULL)
2663 				g_object_set (G_OBJECT (obj),
2664 					"style", s->style,
2665 					NULL);
2666 			g_object_unref (s->style);
2667 			s->style = NULL;
2668 		}
2669 		s->xaxis = NULL;
2670 		s->axis_cross_at_max = FALSE;
2671 		break;
2672 
2673 	case BIFF_CHART_series :
2674 		g_return_val_if_fail (s->currentSeries != NULL, TRUE);
2675 		s->currentSeries = NULL;
2676 		break;
2677 
2678 	case BIFF_CHART_chartformat : {
2679 		unsigned i, j;
2680 		XLChartSeries *eseries;
2681 		GogSeries     *series;
2682 		GOStyle      *style;
2683 		gboolean	   plot_has_lines = FALSE, plot_has_marks = FALSE;
2684 		GnmExprTop const *cat_expr = NULL;
2685 
2686 		/* check series now and create 3d plot if necessary */
2687 		if (s->is_surface) {
2688 			gboolean is_matrix = TRUE, has_labels = FALSE;
2689 			GnmValue *value;
2690 			GnmRange vector;
2691 			gboolean as_col = FALSE; /* makes gcc happy */
2692 			GOData *cur;
2693 			int row_start = 0, col_start = 0, row = 0, col = -1, last = 0;
2694 			GSList *axisY, *axisZ, *l, *contributors, *ptr;
2695 
2696 			/* exchange axis */
2697 			l = axisY = gog_chart_get_axes (s->chart, GOG_AXIS_Y);
2698 			axisZ = gog_chart_get_axes (s->chart, GOG_AXIS_Z);
2699 			while (l) {
2700 				contributors = g_slist_copy ((GSList*) gog_axis_contributors (GOG_AXIS (l->data)));
2701 				gog_axis_clear_contributors (GOG_AXIS (l->data));
2702 				if (gog_object_is_deletable (GOG_OBJECT (l->data)))
2703 					gog_object_clear_parent (GOG_OBJECT (l->data));
2704 				else {
2705 					g_slist_free (contributors);
2706 					continue;
2707 				}
2708 				g_object_set (G_OBJECT (l->data), "type",
2709 					((s->is_contour)? GOG_AXIS_PSEUDO_3D: GOG_AXIS_Z), NULL);
2710 				gog_object_add_by_name (GOG_OBJECT (s->chart),
2711 					((s->is_contour)? "Pseudo-3D-Axis": "Z-Axis"),
2712 					GOG_OBJECT (l->data));
2713 				for (ptr = contributors; ptr != NULL; ptr = ptr->next)
2714 					gog_plot_set_axis (GOG_PLOT (ptr->data), GOG_AXIS (l->data));
2715 				g_slist_free (contributors);
2716 				l = l->next;
2717 			}
2718 			g_slist_free (axisY);
2719 			l = axisZ;
2720 			while (l) {
2721 				contributors = g_slist_copy ((GSList*) gog_axis_contributors (GOG_AXIS (l->data)));
2722 				gog_axis_clear_contributors (GOG_AXIS (l->data));
2723 				if (gog_object_is_deletable (GOG_OBJECT (l->data)))
2724 					gog_object_clear_parent (GOG_OBJECT (l->data));
2725 				else {
2726 					g_slist_free (contributors);
2727 					continue;
2728 				}
2729 				g_object_set (G_OBJECT (l->data), "type", GOG_AXIS_Y, NULL);
2730 				gog_object_add_by_name (GOG_OBJECT (s->chart), "Y-Axis", GOG_OBJECT (l->data));
2731 				for (ptr = contributors; ptr != NULL; ptr = ptr->next)
2732 					gog_plot_set_axis (GOG_PLOT (ptr->data), GOG_AXIS (l->data));
2733 				g_slist_free (contributors);
2734 				l = l->next;
2735 			}
2736 			g_slist_free (axisZ);
2737 
2738 			/* examine the first series to retrieve categories */
2739 			if (s->series->len == 0)
2740 				goto not_a_matrix;
2741 			eseries = g_ptr_array_index (s->series, 0);
2742 			style = eseries->style;
2743 			if (GO_IS_DATA_VECTOR (eseries->data [GOG_MS_DIM_CATEGORIES].data)) {
2744 				cat_expr = gnm_go_data_get_expr (eseries->data [GOG_MS_DIM_CATEGORIES].data);
2745 				if (!gnm_expr_top_is_rangeref (cat_expr))
2746 					goto not_a_matrix;
2747 				if ((value = gnm_expr_top_get_range (cat_expr))) {
2748 					as_col = value->v_range.cell.a.col == value->v_range.cell.b.col;
2749 					row = row_start = value->v_range.cell.a.row;
2750 					col = col_start = value->v_range.cell.a.col;
2751 					if (as_col) {
2752 						col++;
2753 						row_start--;
2754 						last = value->v_range.cell.b.row;
2755 					}
2756 					else {
2757 						if (value->v_range.cell.a.row != value->v_range.cell.b.row) {
2758 							value_release (value);
2759 							goto not_a_matrix;
2760 						}
2761 						row++;
2762 						col_start --;
2763 						last = value->v_range.cell.b.col;
2764 					}
2765 					value_release (value);
2766 				} else
2767 					goto not_a_matrix;
2768 			}
2769 			/* verify that all series are adjacent, have same categories and
2770 			same lengths */
2771 			for (i = 0 ; i < s->series->len; i++ ) {
2772 				GnmExprTop const *texpr;
2773 
2774 				eseries = g_ptr_array_index (s->series, i);
2775 				if (eseries->chart_group != s->plot_counter)
2776 					continue;
2777 				cur = eseries->data [GOG_MS_DIM_VALUES].data;
2778 				if (!cur || !GO_IS_DATA_VECTOR (cur)) {
2779 					is_matrix = FALSE;
2780 					break;
2781 				}
2782 
2783 				texpr = gnm_go_data_get_expr (cur);
2784 				if (!gnm_expr_top_is_rangeref (texpr))
2785 					goto not_a_matrix;
2786 
2787 				value = gnm_expr_top_get_range (texpr);
2788 				if (value) {
2789 					if (col == -1) {
2790 						as_col = value->v_range.cell.a.col == value->v_range.cell.b.col;
2791 						row = row_start = value->v_range.cell.a.row;
2792 						col = col_start = value->v_range.cell.a.col;
2793 						if (as_col) {
2794 							last = value->v_range.cell.b.row;
2795 						}
2796 						else {
2797 							if (value->v_range.cell.a.row != value->v_range.cell.b.row) {
2798 								is_matrix = FALSE;
2799 								value_release (value);
2800 								break;
2801 							}
2802 							last = value->v_range.cell.b.col;
2803 						}
2804 					} else if ((as_col && (value->v_range.cell.a.col != col ||
2805 							value->v_range.cell.b.col != col ||
2806 							value->v_range.cell.a.row != row ||
2807 							value->v_range.cell.b.row != last)) ||
2808 							(! as_col && (value->v_range.cell.a.col != col ||
2809 							value->v_range.cell.b.col != last ||
2810 							value->v_range.cell.a.row != row ||
2811 							value->v_range.cell.b.row != row))) {
2812 						is_matrix = FALSE;
2813 						value_release (value);
2814 						break;
2815 					}
2816 					value_release (value);
2817 				}
2818 
2819 				cur = eseries->data [GOG_MS_DIM_LABELS].data;
2820 				if (cur) {
2821 					if(!GO_IS_DATA_SCALAR (cur)) {
2822 						is_matrix = FALSE;
2823 						break;
2824 					}
2825 					texpr = gnm_go_data_get_expr (cur);
2826 					if (!gnm_expr_top_is_rangeref (texpr))
2827 						goto not_a_matrix;
2828 					value = gnm_expr_top_get_range (texpr);
2829 					if (value) {
2830 						if ((as_col && (value->v_range.cell.a.col != col ||
2831 								value->v_range.cell.a.row != row_start)) ||
2832 								(! as_col && (value->v_range.cell.a.col != col_start ||
2833 								value->v_range.cell.a.row != row))) {
2834 							is_matrix = FALSE;
2835 							value_release (value);
2836 							break;
2837 						}
2838 						value_release (value);
2839 						has_labels = TRUE;
2840 					}
2841 				}
2842 				cur = eseries->data [GOG_MS_DIM_CATEGORIES].data;
2843 				if (cur && cat_expr &&
2844 				    !gnm_expr_top_equal (gnm_go_data_get_expr (cur), cat_expr)) {
2845 					is_matrix = FALSE;
2846 					break;
2847 				}
2848 				if (as_col)
2849 					col++;
2850 				else
2851 					row++;
2852 			}
2853 			if (is_matrix) {
2854 				Sheet *sheet = ms_container_sheet (s->container.parent);
2855 				s->plot = (GogPlot*) gog_plot_new_by_name ((s->is_contour)?
2856 							"GogContourPlot": "GogSurfacePlot");
2857 
2858 				/* build the series */
2859 				series = gog_plot_new_series (s->plot);
2860 				if (style != NULL)
2861 					g_object_set (G_OBJECT (series),
2862 						"style", style,
2863 						NULL);
2864 				if (as_col) {
2865 					g_object_set (G_OBJECT (s->plot),
2866 						"transposed", TRUE,
2867 						NULL);
2868 					col --;
2869 					if (cat_expr != NULL) {
2870 						vector.start.row = row;
2871 						vector.start.col = vector.end.col = col_start;
2872 						vector.end.row = last;
2873 						gog_series_set_dim (series, 1,
2874 							gnm_go_data_vector_new_expr (sheet,
2875 								gnm_expr_top_new_constant (
2876 									value_new_cellrange_r (sheet, &vector))), NULL);
2877 						col_start++;
2878 					}
2879 					if (has_labels) {
2880 						vector.start.col = col_start;
2881 						vector.start.row = vector.end.row = row_start;
2882 						vector.end.col = col;
2883 						gog_series_set_dim (series, 0,
2884 							gnm_go_data_vector_new_expr (sheet,
2885 								gnm_expr_top_new_constant (
2886 									value_new_cellrange_r (sheet, &vector))), NULL);
2887 
2888 						row_start++;
2889 					}
2890 					vector.start.row = row_start;
2891 					vector.end.row = last;
2892 					vector.start.col = col_start;
2893 					vector.end.col = col;
2894 					gog_series_set_dim (series, 2,
2895 						gnm_go_data_matrix_new_expr (sheet,
2896 							gnm_expr_top_new_constant (
2897 								value_new_cellrange_r (sheet, &vector))), NULL);
2898 				} else {
2899 					row--;
2900 					if (cat_expr != NULL) {
2901 						vector.start.col = col;
2902 						vector.start.row = vector.end.row = row_start;
2903 						vector.end.col = last;
2904 						gog_series_set_dim (series, 0,
2905 							gnm_go_data_vector_new_expr (sheet,
2906 								gnm_expr_top_new_constant (
2907 									value_new_cellrange_r (sheet, &vector))), NULL);
2908 						row_start++;
2909 					}
2910 					if (has_labels) {
2911 						vector.start.row = row_start;
2912 						vector.start.col = vector.end.col = col_start;
2913 						vector.end.row = row;
2914 						gog_series_set_dim (series, 1,
2915 							gnm_go_data_vector_new_expr (sheet,
2916 								gnm_expr_top_new_constant (
2917 									value_new_cellrange_r (sheet, &vector))), NULL);
2918 						col_start++;
2919 					}
2920 					vector.start.col = col_start;
2921 					vector.end.col = last;
2922 					vector.start.row = row_start;
2923 					vector.end.row = row;
2924 					gog_series_set_dim (series, 2,
2925 						gnm_go_data_matrix_new_expr (sheet,
2926 							gnm_expr_top_new_constant (
2927 								value_new_cellrange_r (sheet, &vector))), NULL);
2928 				}
2929 			} else {
2930 not_a_matrix:
2931 				s->is_surface = FALSE;
2932 				s->plot = (GogPlot*) gog_plot_new_by_name ((s->is_contour)?
2933 							"XLContourPlot": "XLSurfacePlot");
2934 			}
2935 		}
2936 
2937 		XL_CHECK_CONDITION_VAL (s->plot != NULL, TRUE);
2938 
2939 		/*
2940 		 * Check whether the chart already contains a plot and, if so,
2941 		 * if axis sets are compatible
2942 		 */
2943 		{
2944 			GogAxisSet axis_set = gog_chart_get_axis_set (s->chart);
2945 			GogAxisSet plot_axis_set = G_TYPE_INSTANCE_GET_CLASS ((s->plot), GOG_TYPE_PLOT, GogPlotClass)->axis_set;
2946 
2947 			if (axis_set != GOG_AXIS_SET_UNKNOWN &&
2948 			    (axis_set & GOG_AXIS_SET_FUNDAMENTAL & ~plot_axis_set)) {
2949 				GogAxisType i;
2950 				GogAxisSet j;
2951 
2952 				g_object_unref (s->plot);
2953 				s->plot = NULL;
2954 				if (s->style) {
2955 					g_object_unref (s->style);
2956 					s->style = NULL;
2957 				}
2958 
2959 				/* Now remove all unwanted axes.  */
2960 				for (i = 0, j = 1;
2961 				     i < GOG_AXIS_VIRTUAL;
2962 				     i++, j <<= 1) {
2963 					if ((j & axis_set) == 0) {
2964 						GSList *l = gog_chart_get_axes (s->chart, i), *cur;
2965 						for (cur = l; cur; cur = cur->next) {
2966 							GogObject *axis = cur->data;
2967 							/* first remove contributors otherwise we'll end
2968 							 * with invalid pointers */
2969 							gog_axis_clear_contributors (GOG_AXIS (axis));
2970 							/* remove only if there are no more contributors */
2971 							if (gog_object_is_deletable (axis)) {
2972 								gog_object_clear_parent (axis);
2973 								g_object_unref (axis);
2974 							}
2975 						}
2976 						g_slist_free (l);
2977 					}
2978 
2979 				}
2980 				return TRUE;
2981 			}
2982 		}
2983 
2984 		/* Add _before_ setting styles so theme does not override */
2985 		gog_object_add_by_name (GOG_OBJECT (s->chart),
2986 					"Plot", GOG_OBJECT (s->plot));
2987 
2988 		if (s->default_plot_style != NULL) {
2989 			char const *type = G_OBJECT_TYPE_NAME (s->plot);
2990 			GOStyle const *style = s->default_plot_style;
2991 
2992 			if (type != NULL && style->marker.mark != NULL &&
2993 			    (!strcmp (type, "GogXYPlot") ||
2994 			     !strcmp (type, "GogLinePlot") ||
2995 			     !strcmp (type, "GogRadarPlot"))) {
2996 				plot_has_marks =
2997 					(go_marker_get_shape (style->marker.mark) != GO_MARKER_NONE)
2998 					|| style->marker.auto_shape;
2999 				g_object_set (G_OBJECT (s->plot),
3000 					"default-style-has-markers",
3001 					plot_has_marks,
3002 					NULL);
3003 			}
3004 			if (type != NULL && 0 == strcmp (type, "GogXYPlot")) {
3005 				plot_has_lines = style->line.dash_type != GO_LINE_NONE;
3006 				g_object_set (G_OBJECT (s->plot),
3007 					"default-style-has-lines",
3008 					style->line.width >= 0 &&
3009 					plot_has_lines,
3010 					NULL);
3011 			}
3012 
3013 			g_object_unref (s->default_plot_style);
3014 			s->default_plot_style = NULL;
3015 		}
3016 
3017 		if (!s->is_surface) {
3018 			unsigned k = 0, l = s->series->len, added_plots = 0, n;
3019 			if (l > 0 && s->dropbar) {
3020 				GogObject *plot = GOG_OBJECT (gog_plot_new_by_name ("GogDropBarPlot"));
3021 				if (s->has_extra_dataformat){
3022 					g_object_set (G_OBJECT (plot), "plot-group", "GogStockPlot", NULL);
3023 				}
3024 				l--;
3025 				while (eseries = g_ptr_array_index (s->series, l),
3026 							eseries->chart_group != s->plot_counter)
3027 					if (l != 0)
3028 						l--;
3029 					else {
3030 						eseries = NULL;
3031 							break;
3032 					}
3033 				gog_object_add_by_name (GOG_OBJECT (s->chart), "Plot", plot);
3034 				gog_object_reorder (plot, TRUE, FALSE);
3035 				added_plots++;
3036 				series = gog_plot_new_series (GOG_PLOT (plot));
3037 				g_object_set (plot, "gap_percentage", s->dropbar_width, NULL);
3038 				if (eseries) {
3039 					if (eseries->data [GOG_MS_DIM_CATEGORIES].data != NULL) {
3040 						gog_series_set_XL_dim (series, GOG_MS_DIM_CATEGORIES,
3041 							eseries->data [GOG_MS_DIM_CATEGORIES].data, NULL);
3042 						eseries->data [GOG_MS_DIM_CATEGORIES].data = NULL;
3043 					}
3044 					if (eseries->data [GOG_MS_DIM_VALUES].data != NULL) {
3045 						gog_series_set_XL_dim (series, GOG_MS_DIM_END,
3046 							eseries->data [GOG_MS_DIM_VALUES].data, NULL);
3047 						eseries->data [GOG_MS_DIM_VALUES].data = NULL;
3048 					} else
3049 						eseries->extra_dim = GOG_MS_DIM_END;
3050 				}
3051 				eseries = NULL;
3052 				while (k < l && (eseries = g_ptr_array_index (s->series, k++),
3053 							eseries && eseries->chart_group != s->plot_counter))
3054 					if (k == l) {
3055 						eseries = NULL;
3056 						break;
3057 					}
3058 				if (eseries) {
3059 					if (eseries->data [GOG_MS_DIM_VALUES].data != NULL) {
3060 						gog_series_set_XL_dim (series, GOG_MS_DIM_START,
3061 							eseries->data [GOG_MS_DIM_VALUES].data, NULL);
3062 						eseries->data [GOG_MS_DIM_VALUES].data = NULL;
3063 					} else
3064 						eseries->extra_dim = GOG_MS_DIM_START;
3065 				}
3066 				g_object_set (G_OBJECT (series),
3067 					"style", s->dropbar_style,
3068 					NULL);
3069 				g_object_unref (s->dropbar_style);
3070 				s->dropbar_style = NULL;
3071 			}
3072 			if (l > 0 && s->hilo) {
3073 				GogObject *plot = GOG_OBJECT (gog_plot_new_by_name ("GogMinMaxPlot"));
3074 				if (s->has_extra_dataformat) {
3075 					g_object_set (G_OBJECT (plot), "plot-group", "GogStockPlot", NULL);
3076 					g_object_set (G_OBJECT (s->plot), "plot-group", "GogStockPlot", NULL);
3077 				}
3078 				gog_object_add_by_name (GOG_OBJECT (s->chart), "Plot", plot);
3079 				for (n = 0; n <= added_plots; n++)
3080 					gog_object_reorder (plot, TRUE, FALSE);
3081 				series = gog_plot_new_series (GOG_PLOT (plot));
3082 				eseries = NULL;
3083 				while (k < l && (eseries = g_ptr_array_index (s->series, k++),
3084 							eseries && eseries->chart_group != s->plot_counter))
3085 					if (k == l) {
3086 						eseries = NULL;
3087 						break;
3088 					}
3089 				if (eseries != NULL) {
3090 					if (eseries->data [GOG_MS_DIM_CATEGORIES].data != NULL) {
3091 						gog_series_set_XL_dim (series, GOG_MS_DIM_CATEGORIES,
3092 							eseries->data [GOG_MS_DIM_CATEGORIES].data, NULL);
3093 						eseries->data [GOG_MS_DIM_CATEGORIES].data = NULL;
3094 					}
3095 					if (eseries->data [GOG_MS_DIM_VALUES].data != NULL) {
3096 						gog_series_set_XL_dim (series, GOG_MS_DIM_HIGH,
3097 							eseries->data [GOG_MS_DIM_VALUES].data, NULL);
3098 						eseries->data [GOG_MS_DIM_VALUES].data = NULL;
3099 					} else
3100 						eseries->extra_dim = GOG_MS_DIM_HIGH;
3101 				}
3102 				if (k == s->series->len)
3103 					eseries = NULL;
3104 				else while (eseries = g_ptr_array_index (s->series, k++),
3105 							eseries && eseries->chart_group != s->plot_counter) {
3106 					if (k == s->series->len) {
3107 						eseries = NULL;
3108 						break;
3109 					}
3110 				}
3111 				if (eseries != NULL) {
3112 					if (eseries->data [GOG_MS_DIM_VALUES].data != NULL) {
3113 						gog_series_set_XL_dim (series, GOG_MS_DIM_LOW,
3114 							eseries->data [GOG_MS_DIM_VALUES].data, NULL);
3115 						eseries->data [GOG_MS_DIM_VALUES].data = NULL;
3116 					} else
3117 						eseries->extra_dim = GOG_MS_DIM_LOW;
3118 					if (s->chartline_style[1]) {
3119 						g_object_set (G_OBJECT (series),
3120 								  "style", s->chartline_style[1],
3121 								  NULL);
3122 						g_object_unref (s->chartline_style[1]);
3123 						s->chartline_style[1] = NULL;
3124 					}
3125 				}
3126 			}
3127 			for (i = k ; i < l; i++ ) {
3128 				eseries = g_ptr_array_index (s->series, i);
3129 				if (eseries->chart_group != s->plot_counter)
3130 					continue;
3131 				series = gog_plot_new_series (s->plot);
3132 				for (j = 0 ; j < GOG_MS_DIM_TYPES; j++ )
3133 					if (eseries->data [j].data != NULL) {
3134 						gog_series_set_XL_dim (series, j,
3135 							eseries->data [j].data, NULL);
3136 						eseries->data [j].data = NULL;
3137 					}
3138 				eseries->series = series;
3139 				style = eseries->style;
3140 				if (style != NULL) {
3141 					if (s->hilo || s->dropbar) {
3142 						/* This might be a stock plot, so don't use auto
3143 						styles for lines */
3144 						style->line.auto_dash = FALSE;
3145 						style->marker.auto_shape = FALSE;
3146 						/* These avoid warnings when exporting, do we really care? */
3147 						style->fill.auto_fore = FALSE;
3148 						style->fill.auto_back = FALSE;
3149 					} else {
3150 						if (!plot_has_marks &&
3151 							go_marker_get_shape (style->marker.mark) != GO_MARKER_NONE)
3152 							style->marker.auto_shape = FALSE;
3153 						if (!plot_has_lines && style->line.dash_type != GO_LINE_NONE)
3154 							style->line.auto_dash = FALSE;
3155 					}
3156 					g_object_set (G_OBJECT (series),
3157 						"style", style,
3158 						NULL);
3159 				}
3160 				if (!eseries->has_legend)
3161 					g_object_set (G_OBJECT (series),
3162 						"has-legend", FALSE,
3163 						NULL);
3164 				if (eseries->singletons != NULL)
3165 					g_hash_table_foreach (eseries->singletons,
3166 						(GHFunc) cb_store_singletons, series);
3167 				g_object_set (G_OBJECT (series),
3168 					      "interpolation", go_line_interpolation_as_str (eseries->interpolation),
3169 					      NULL);
3170 			}
3171 		}
3172 		g_object_set (G_OBJECT (s->plot),
3173 			      "interpolation", go_line_interpolation_as_str (s->interpolation),
3174 			      NULL);
3175 		s->interpolation = GO_LINE_INTERPOLATION_LINEAR;
3176 		s->hilo = FALSE;
3177 		s->dropbar = FALSE;
3178 
3179 		/* Vile cheesy hack.
3180 		 * XL stores axis as 'value' and 'category' whereas we use X and Y.
3181 		 * When importing bar plots things are transposed, but we do
3182 		 * not know it until we import the plot.  Swap the contents of the axes */
3183 		if (0 == strcmp (G_OBJECT_TYPE_NAME (s->plot), "GogBarColPlot")) {
3184 			gboolean horizontal;
3185 			g_object_get (s->plot, "horizontal", &horizontal, NULL);
3186 			if (horizontal) {
3187 				GogAxis	*x = gog_plot_get_axis (s->plot, GOG_AXIS_X);
3188 				GogAxis	*y = gog_plot_get_axis (s->plot, GOG_AXIS_Y);
3189 				GOStyle *x_style, *y_style;
3190 				int i;
3191 				if (x != NULL && y!= NULL) {
3192 					/* we only execute that code if both axes really exist, see #702126 */
3193 					for (i = 0 ; i < GOG_AXIS_ELEM_MAX_ENTRY ; i++)
3194 						xl_axis_swap_elem (x, y, i);
3195 					g_object_get (G_OBJECT (x), "style", &x_style, NULL);
3196 					g_object_get (G_OBJECT (y), "style", &y_style, NULL);
3197 					g_object_set (G_OBJECT (y), "style", x_style, NULL);
3198 					g_object_set (G_OBJECT (x), "style", y_style, NULL);
3199 					g_object_unref (x_style);
3200 					g_object_unref (y_style);
3201 					/* we must also exchange children */
3202 					object_swap_children (GOG_OBJECT (x), GOG_OBJECT (y), "Label");
3203 					object_swap_children (GOG_OBJECT (x), GOG_OBJECT (y), "MajorGrid");
3204 					object_swap_children (GOG_OBJECT (x), GOG_OBJECT (y), "MinorGrid");
3205 				}
3206 			}
3207 		}
3208 		if (g_slist_length (s->plot->series) == 0) {
3209 			gog_object_clear_parent (GOG_OBJECT (s->plot));
3210 			g_object_unref (s->plot);
3211 		}		s->plot = NULL;
3212 		break;
3213 	}
3214 
3215 	case BIFF_CHART_dataformat :
3216 		if (s->style == NULL)
3217 			break;
3218 		if (s->currentSeries != NULL) {
3219 			if (s->style_element < 0) {
3220 				g_return_val_if_fail (s->currentSeries->style == NULL, TRUE);
3221 				s->currentSeries->style = s->style;
3222 			} else {
3223 				if (s->currentSeries->singletons == NULL)
3224 					s->currentSeries->singletons = g_hash_table_new_full (
3225 						g_direct_hash, g_direct_equal, NULL, g_object_unref);
3226 				g_hash_table_insert (s->currentSeries->singletons,
3227 					GUINT_TO_POINTER (s->style_element), s->style);
3228 				d (0, g_printerr ("/* STORE singleton style */\n"););
3229 			}
3230 		} else if (s->plot != NULL) {
3231 			g_return_val_if_fail (s->default_plot_style == NULL, TRUE);
3232 			s->default_plot_style = s->style;
3233 		} else
3234 			g_object_unref (s->style);
3235 		s->style = NULL;
3236 		break;
3237 
3238 	case BIFF_CHART_text : {
3239 		gboolean clear_style = TRUE;
3240 		switch (BC_R(top_state) (s, 0)) {
3241 		case BIFF_CHART_legend:
3242 			/* do not destroy the style if it belongs to the
3243 			parent object, applies only to legend at the moment,
3244 			what should be done for DEFAULTEXT? */
3245 			clear_style = FALSE;
3246 			break;
3247 		default:
3248 			break;
3249 		}
3250 		if (s->label) {
3251 			g_object_unref (s->label);
3252 			s->label = NULL;
3253 		}
3254 
3255 		if (clear_style && s->style != NULL) {
3256 			g_object_unref (s->style);
3257 			s->style = NULL;
3258 		}
3259 		g_free (s->text);
3260 		s->text = NULL;
3261 		break;
3262 	}
3263 
3264 	case BIFF_CHART_dropbar :
3265 		if (s->style != NULL) {
3266 			if (s->dropbar_style == NULL) {
3267 				/* automatic styles are very different in that case between
3268 				excel and gnumeric, so set appropriate auto* to FALSE */
3269 				s->style->fill.auto_back = FALSE;
3270 				s->style->fill.auto_fore = FALSE;
3271 				s->dropbar_style = s->style;
3272 			} else
3273 				g_object_unref (s->style);
3274 			s->style = NULL;
3275 		}
3276 		break;
3277 
3278 	case BIFF_CHART_chart : {
3279 		GogPlot *plot = GOG_PLOT (gog_object_get_child_by_name (GOG_OBJECT (s->chart), "Plot"));
3280  		/* check if the chart has an epty title and the plot only one series,
3281 		 * in that case Excel uses the series label as title */
3282 		if (plot && g_slist_length (plot->series) == 1) {
3283 			GogObject *title = gog_object_get_child_by_name (GOG_OBJECT (s->chart), "Title");
3284 			if (title) {
3285 				GOData *dat = gog_dataset_get_dim (GOG_DATASET (title), 0);
3286 				GError *err = NULL;
3287 				if (dat) {
3288 					char *str = go_data_get_scalar_string (dat);
3289 					if (str && *str) {
3290 						g_free (str);
3291 						break;
3292 					}
3293 					g_free (str);
3294 				}
3295 				dat = gog_dataset_get_dim (GOG_DATASET (plot->series->data), -1);
3296 				if (!dat)
3297 					break;
3298 				gog_dataset_set_dim (GOG_DATASET (title), 0, GO_DATA (g_object_ref (dat)), &err);
3299 				if (err)
3300 					g_error_free (err);
3301 			}
3302 		}
3303 	}
3304 	default :
3305 		break;
3306 	}
3307 	return FALSE;
3308 }
3309 
3310 /****************************************************************************/
3311 
3312 static XLChartHandler const *chart_biff_handler[256];
3313 
3314 static void
3315 BC(register_handler)(XLChartHandler const *const handle);
3316 #define BIFF_CHART(name, size) \
3317 {	static XLChartHandler const handle = { \
3318 	BIFF_CHART_ ## name, size, #name, & BC_R(name) }; \
3319 	BC(register_handler)(& handle); \
3320 }
3321 
3322 static void
BC(register_handlers)3323 BC(register_handlers)(void)
3324 {
3325 	static gboolean already_initialized = FALSE;
3326 	int i;
3327 	if (already_initialized)
3328 		return;
3329 	already_initialized = TRUE;
3330 
3331 	/* Init the handles */
3332 	i = G_N_ELEMENTS (chart_biff_handler);
3333 	while (--i >= 0)
3334 		chart_biff_handler[i] = NULL;
3335 
3336 	BIFF_CHART(3dbarshape, 2);	/* Unknown, seems to be 2 */
3337 	BIFF_CHART(3d, 14);
3338 	BIFF_CHART(ai, 8);
3339 	BIFF_CHART(alruns, 2);
3340 	BIFF_CHART(area, 2);
3341 	BIFF_CHART(areaformat, 12);
3342 	BIFF_CHART(attachedlabel, 2);
3343 	BIFF_CHART(axesused, 2);
3344 	BIFF_CHART(axis, 18);
3345 	BIFF_CHART(axcext, 18);
3346 	BIFF_CHART(axislineformat, 2);
3347 	BIFF_CHART(axisparent, 18);
3348 	BIFF_CHART(bar, 6);
3349 	BIFF_CHART(begin, 0);
3350 	BIFF_CHART(boppop, 18);
3351 	BIFF_CHART(boppopcustom, 2);
3352 	BIFF_CHART(catserrange, 8);
3353 	BIFF_CHART(chart, 16);
3354 	BIFF_CHART(chartformat, 20);
3355 	BIFF_CHART(chartformatlink, 0);
3356 	BIFF_CHART(chartline, 2);
3357 	BIFF_CHART(clrtclient, 0);	/* Unknown */
3358 	BIFF_CHART(dat, 2);
3359 	BIFF_CHART(dataformat, 8);
3360 	BIFF_CHART(defaulttext, 2);
3361 	BIFF_CHART(dropbar, 2);
3362 	BIFF_CHART(end, 0);
3363 	BIFF_CHART(fbi, 10);
3364 	BIFF_CHART(fontx, 2);
3365 	BIFF_CHART(frame, 4);
3366 	BIFF_CHART(gelframe, 0);
3367 	BIFF_CHART(ifmt, 2);
3368 	BIFF_CHART(legend, 20);
3369 	BIFF_CHART(legendxn, 4);
3370 	BIFF_CHART(line, 2);
3371 	BIFF_CHART(lineformat, 10);
3372 	BIFF_CHART(markerformat, 12);
3373 	BIFF_CHART(objectlink, 6);
3374 	BIFF_CHART(picf, 14);
3375 	BIFF_CHART(pie, 4);
3376 	BIFF_CHART(pieformat, 2);
3377 	BIFF_CHART(plotarea, 0);
3378 	BIFF_CHART(plotgrowth, 8);
3379 	BIFF_CHART(pos, 20); /* For all states */
3380 	BIFF_CHART(radar, 2);
3381 	BIFF_CHART(radararea, 2);
3382 	BIFF_CHART(sbaseref, 8);
3383 	BIFF_CHART(scatter, 0);
3384 	BIFF_CHART(serauxerrbar, 14);
3385 	BIFF_CHART(serauxtrend, 28);
3386 	BIFF_CHART(serfmt, 2);
3387 	BIFF_CHART(series, 8);
3388 	BIFF_CHART(serieslist, 2);
3389 	BIFF_CHART(seriestext, 3);
3390 	BIFF_CHART(serparent, 2);
3391 	BIFF_CHART(sertocrt, 2);
3392 	BIFF_CHART(shtprops, 3);
3393 	BIFF_CHART(siindex, 2);
3394 	BIFF_CHART(surf, 2);
3395 	BIFF_CHART(text, 26);
3396 	BIFF_CHART(tick, 26);
3397 	BIFF_CHART(units, 2);
3398 	BIFF_CHART(valuerange, 42);
3399 	/* gnumeric specific */
3400 	BIFF_CHART(trendlimits, 17);
3401 }
3402 
3403 /*
3404  *This is a temporary routine.  I wanted each handler in a separate function
3405  *to avoid massive nesting.  While experimenting with the real (vs MS
3406  *documentation) structure of a saved chart, this form offers maximum error
3407  *checking.
3408  */
3409 static void
BC(register_handler)3410 BC(register_handler)(XLChartHandler const *const handle)
3411 {
3412 	unsigned const num_handler = G_N_ELEMENTS (chart_biff_handler);
3413 	guint32 num = handle->opcode & 0xff;
3414 
3415 	if (num >= num_handler)
3416 		g_printerr ("Invalid BIFF_CHART handler (%x)\n", handle->opcode);
3417 	else if (chart_biff_handler[num])
3418 		g_printerr ("Multiple BIFF_CHART handlers for (%x)\n",
3419 			handle->opcode);
3420 	else
3421 		chart_biff_handler[num] = handle;
3422 }
3423 
3424 static gboolean
chart_realize_obj(MSContainer * container,MSObj * obj)3425 chart_realize_obj (MSContainer *container, MSObj *obj)
3426 {
3427 	g_warning ("Dropping nested object");
3428 	return FALSE;
3429 }
3430 
3431 static SheetObject *
chart_create_obj(MSContainer * container,MSObj * obj)3432 chart_create_obj  (MSContainer *container, MSObj *obj)
3433 {
3434 	return (container &&
3435 		container->parent &&
3436 		container->parent->vtbl->create_obj)
3437 		? container->parent->vtbl->create_obj (container->parent, obj)
3438 		: NULL;
3439 }
3440 
3441 static GnmExprTop const *
chart_parse_expr(MSContainer * container,guint8 const * data,int length)3442 chart_parse_expr  (MSContainer *container, guint8 const *data, int length)
3443 {
3444 	return excel_parse_formula (container, NULL, 0, 0,
3445 				    data, length, 0 /* FIXME? */,
3446 				    FALSE, NULL);
3447 }
3448 
3449 static Sheet *
chart_get_sheet(MSContainer const * container)3450 chart_get_sheet (MSContainer const *container)
3451 {
3452 	return ms_container_sheet (container->parent);
3453 }
3454 
3455 static void
xl_chart_import_trend_line(XLChartReadState * state,XLChartSeries * series)3456 xl_chart_import_trend_line (XLChartReadState *state, XLChartSeries *series)
3457 {
3458 	GogTrendLine *rc;
3459 	Sheet *sheet;
3460 	XLChartSeries *parent;
3461 
3462 	XL_CHECK_CONDITION ((unsigned) series->reg_parent < state->series->len);
3463 	parent = g_ptr_array_index (state->series, series->reg_parent);
3464 
3465 	XL_CHECK_CONDITION (parent != NULL && parent->series != NULL);
3466 
3467 	switch (series->reg_type) {
3468 	case 0:
3469 		if (series->reg_order == 1)
3470 			rc = gog_trend_line_new_by_name ("GogLinRegCurve");
3471 		else {
3472 			rc = gog_trend_line_new_by_name ("GogPolynomRegCurve");
3473 			g_object_set (G_OBJECT (rc), "dims", series->reg_order, NULL);
3474 		}
3475 		break;
3476 	case 1:
3477 		rc = gog_trend_line_new_by_name ("GogExpRegCurve");
3478 		break;
3479 	case 2:
3480 		rc = gog_trend_line_new_by_name ("GogLogRegCurve");
3481 		break;
3482 	case 3:
3483 		rc = gog_trend_line_new_by_name ("GogPowerRegCurve");
3484 		break;
3485 	case 4:
3486 		rc = gog_trend_line_new_by_name ("GogMovingAvg");
3487 			g_object_set (G_OBJECT (rc), "span", series->reg_order, "xavg", FALSE, NULL);
3488 		break;
3489 	default:
3490 		g_warning ("Unknown trend line type: %d", series->reg_type);
3491 		rc = NULL;
3492 		break;
3493 	}
3494 	if (rc) {
3495 		if (GOG_IS_REG_CURVE (rc)) {
3496 			sheet = ms_container_sheet (state->container.parent);
3497 			g_object_set (G_OBJECT (rc),
3498 				"affine", series->reg_intercept != 0.,
3499 				"skip-invalid", series->reg_skip_invalid,
3500 				NULL);
3501 			if (sheet) {
3502 				if (series->reg_dims[0]){
3503 					gog_dataset_set_dim (GOG_DATASET (rc), 0, series->reg_dims[0], NULL);
3504 					series->reg_dims[0] = NULL;
3505 				} else if (go_finite (series->reg_min)) {
3506 					GnmValue *value = value_new_float (series->reg_min);
3507 					GnmExprTop const *texpr = gnm_expr_top_new_constant (value);
3508 					GOData *data = gnm_go_data_scalar_new_expr (sheet, texpr);
3509 					gog_dataset_set_dim (GOG_DATASET (rc), 0, data, NULL);
3510 				}
3511 				if (series->reg_dims[1]){
3512 					gog_dataset_set_dim (GOG_DATASET (rc), 1, series->reg_dims[1], NULL);
3513 					series->reg_dims[1] = NULL;
3514 				} else if (go_finite (series->reg_max)) {
3515 					GnmValue *value = value_new_float (series->reg_max);
3516 					GnmExprTop const *texpr = gnm_expr_top_new_constant (value);
3517 					GOData *data = gnm_go_data_scalar_new_expr (sheet, texpr);
3518 					gog_dataset_set_dim (GOG_DATASET (rc), 1, data, NULL);
3519 				}
3520 			}
3521 			if (series->reg_show_eq || series->reg_show_R2) {
3522 				GogObject *obj = gog_object_add_by_name (
3523 					GOG_OBJECT (rc), "Equation", NULL);
3524 				g_object_set (G_OBJECT (obj),
3525 					"show_eq", series->reg_show_eq,
3526 					"show_r2", series->reg_show_R2,
3527 					NULL);
3528 			}
3529 		}
3530 		gog_object_add_by_name (GOG_OBJECT (parent->series),
3531 			"Trend line", GOG_OBJECT (rc));
3532 		if (series->style)
3533 			go_styled_object_set_style (GO_STYLED_OBJECT (rc), series->style);
3534 	}
3535 }
3536 
3537 static void
xl_chart_import_error_bar(XLChartReadState * state,XLChartSeries * series)3538 xl_chart_import_error_bar (XLChartReadState *state, XLChartSeries *series)
3539 {
3540 	unsigned p = series->err_parent;
3541 	XLChartSeries *parent;
3542 	Sheet *sheet;
3543 	int orig_dim;
3544 	GogMSDimType msdim;
3545 	char const *prop_name = (series->err_type < 3)
3546 		? "x-errors" : "y-errors";
3547 	GParamSpec *pspec;
3548 
3549 	XL_CHECK_CONDITION (p < state->series->len);
3550 
3551 	parent = g_ptr_array_index (state->series, p);
3552 	XL_CHECK_CONDITION (parent != NULL && parent->series != NULL);
3553 
3554 	pspec = g_object_class_find_property (
3555 		G_OBJECT_GET_CLASS (parent->series),
3556 		prop_name);
3557 	state->plot = parent->series->plot;
3558 	if (pspec == NULL) {
3559 		pspec = g_object_class_find_property (
3560 			G_OBJECT_GET_CLASS (parent->series), "errors");
3561 		prop_name = (pspec)? "errors": NULL;
3562 		msdim = (series->err_type < 3)
3563 			? GOG_MS_DIM_TYPES + series->err_type
3564 			: GOG_MS_DIM_TYPES + series->err_type - 2;
3565 	} else
3566 		msdim = (series->err_type < 3)
3567 			? GOG_MS_DIM_TYPES + series->err_type + 2
3568 			: GOG_MS_DIM_TYPES + series->err_type - 2;
3569 
3570 	sheet = ms_container_sheet (state->container.parent);
3571 	if (sheet && parent && prop_name) {
3572 		GogErrorBar   *error_bar;
3573 		GOData	      *data;
3574 
3575 		g_object_get (G_OBJECT (parent->series),
3576 			      prop_name, &error_bar,
3577 			      NULL);
3578 		if (!error_bar) {
3579 			error_bar = g_object_new (GOG_TYPE_ERROR_BAR, NULL);
3580 			error_bar->display = GOG_ERROR_BAR_DISPLAY_NONE;
3581 		}
3582 		error_bar->display |= (series->err_type & 1)
3583 			? GOG_ERROR_BAR_DISPLAY_POSITIVE
3584 			: GOG_ERROR_BAR_DISPLAY_NEGATIVE;
3585 		if (!series->err_teetop)
3586 			error_bar->width = 0;
3587 		if (check_style (series->style, "error bar")) {
3588 			g_object_unref (error_bar->style);
3589 			error_bar->style = go_style_dup (series->style);
3590 		}
3591 		switch (series->err_src) {
3592 		case 1: {
3593 			/* percentage */
3594 			GnmExprTop const *texpr =
3595 				gnm_expr_top_new_constant (
3596 					value_new_float (series->err_val));
3597 			error_bar->type = GOG_ERROR_BAR_TYPE_PERCENT;
3598 			data = gnm_go_data_vector_new_expr (sheet, texpr);
3599 			gog_series_set_XL_dim (parent->series, msdim, data, NULL);
3600 			break;
3601 		}
3602 		case 2: {
3603 			/* fixed value */
3604 			GnmExprTop const *texpr =
3605 				gnm_expr_top_new_constant (
3606 					value_new_float (series->err_val));
3607 			error_bar->type = GOG_ERROR_BAR_TYPE_ABSOLUTE;
3608 			data = gnm_go_data_vector_new_expr (sheet, texpr);
3609 			gog_series_set_XL_dim (parent->series, msdim, data, NULL);
3610 			break;
3611 		}
3612 		case 3:
3613 			/* not supported */
3614 			break;
3615 		case 4:
3616 			orig_dim = (series->err_type < 3)
3617 				? GOG_MS_DIM_CATEGORIES
3618 				: GOG_MS_DIM_VALUES;
3619 			error_bar->type = GOG_ERROR_BAR_TYPE_ABSOLUTE;
3620 			if (series->data[orig_dim].data) {
3621 				gog_series_set_XL_dim (parent->series, msdim,
3622 							series->data[orig_dim].data, NULL);
3623 				series->data[orig_dim].data = NULL;
3624 			} else if (series->data[orig_dim].value) {
3625 				GnmExprTop const *texpr =
3626 					gnm_expr_top_new_constant ((GnmValue *)
3627 								   series->data[orig_dim].value);
3628 				series->data[orig_dim].value = NULL;
3629 				data = gnm_go_data_vector_new_expr (sheet, texpr);
3630 				gog_series_set_XL_dim (parent->series, msdim, data, NULL);
3631 			}
3632 			break;
3633 		default:
3634 			/* type 5, standard error is not supported */
3635 			break;
3636 		}
3637 		g_object_set (G_OBJECT (parent->series),
3638 						prop_name, error_bar,
3639 						NULL);
3640 		g_object_unref (error_bar);
3641 	}
3642 }
3643 
3644 static void
ms_excel_chart_read_PROTECT(BiffQuery * q,XLChartReadState * state)3645 ms_excel_chart_read_PROTECT (BiffQuery *q, XLChartReadState *state)
3646 {
3647 	gboolean is_protected;
3648 
3649 	XL_CHECK_CONDITION (q->length >= 2);
3650 	is_protected = (1 == GSF_LE_GET_GUINT16 (q->data));
3651 	d (4, g_printerr ("Chart is%s protected;\n",
3652 			  is_protected ? "" : " not"););
3653 }
3654 
3655 static void
ms_excel_chart_read_NUMBER(BiffQuery * q,XLChartReadState * state,size_t ofs)3656 ms_excel_chart_read_NUMBER (BiffQuery *q, XLChartReadState *state, size_t ofs)
3657 {
3658 	unsigned row, sernum;
3659 	double val;
3660 	XLChartSeries *series;
3661 
3662 	XL_CHECK_CONDITION (q->length >= ofs + 8);
3663 	row = GSF_LE_GET_GUINT16 (q->data);
3664 	sernum = GSF_LE_GET_GUINT16 (q->data + 2);
3665 	val = gsf_le_get_double (q->data + ofs);
3666 
3667 	if (state->series == NULL || state->cur_role < 0)
3668 		return;
3669 	XL_CHECK_CONDITION (state->cur_role < GOG_MS_DIM_TYPES);
3670 	XL_CHECK_CONDITION (sernum < state->series->len);
3671 
3672 	series = g_ptr_array_index (state->series, sernum);
3673 	if (series == NULL)
3674 		return;
3675 
3676 	if (series->data[state->cur_role].value != NULL) {
3677 		XL_CHECK_CONDITION (row < (guint)series->data[state->cur_role].num_elements);
3678 
3679 		value_release (series->data[state->cur_role].value->vals[0][row]);
3680 		series->data[state->cur_role].value->vals[0][row] = value_new_float (val);
3681 	}
3682 	d (10, g_printerr ("series %d, index %d, value %f\n", sernum, row, val););
3683 }
3684 
3685 static void
ms_excel_chart_read_LABEL(BiffQuery * q,XLChartReadState * state)3686 ms_excel_chart_read_LABEL (BiffQuery *q, XLChartReadState *state)
3687 {
3688 	guint16 row, sernum;
3689 	char *label;
3690 	XLChartSeries *series;
3691 
3692 	XL_CHECK_CONDITION (q->length >= 6);
3693 	row = GSF_LE_GET_GUINT16 (q->data + 0);
3694 	sernum = GSF_LE_GET_GUINT16 (q->data + 2);
3695 	/* xf  = GSF_LE_GET_GUINT16 (q->data + 4); */
3696 
3697 	if (state->series == NULL || state->cur_role < 0)
3698 		return;
3699 	XL_CHECK_CONDITION (state->cur_role < GOG_MS_DIM_TYPES);
3700 	XL_CHECK_CONDITION (sernum < state->series->len);
3701 
3702 	series = g_ptr_array_index (state->series, sernum);
3703 	if (series == NULL)
3704 		return;
3705 
3706 	label = excel_biff_text_2 (state->container.importer, q, 6);
3707 	if (label != NULL  &&
3708 	    series->data[state->cur_role].value != NULL) {
3709 		XL_CHECK_CONDITION (row < (guint)series->data[state->cur_role].num_elements);
3710 
3711 		value_release (series->data[state->cur_role].value->vals[0][row]);
3712 		series->data[state->cur_role].value->vals[0][row] = value_new_string (label);
3713 	}
3714 	d (10, {g_printerr ("'%s' row = %d, series = %d\n", label, row, sernum);});
3715 	g_free (label);
3716 }
3717 
3718 
3719 gboolean
ms_excel_chart_read(BiffQuery * q,MSContainer * container,SheetObject * sog,Sheet * full_page)3720 ms_excel_chart_read (BiffQuery *q, MSContainer *container,
3721 		     SheetObject *sog, Sheet *full_page)
3722 {
3723 	static MSContainerClass const vtbl = {
3724 		chart_realize_obj,
3725 		chart_create_obj,
3726 		chart_parse_expr,
3727 		chart_get_sheet,
3728 		NULL
3729 	};
3730 	int const num_handler = G_N_ELEMENTS (chart_biff_handler);
3731 	int i;
3732 	gboolean done = FALSE;
3733 	XLChartReadState state;
3734 
3735 	/* Register the handlers if this is the 1st time through */
3736 	BC(register_handlers)();
3737 
3738 	/* FIXME : create an anchor parser for charts */
3739 	ms_container_init (&state.container, &vtbl,
3740 		container, container->importer);
3741 
3742 	state.stack	    = g_array_new (FALSE, FALSE, sizeof(int));
3743 	state.prev_opcode   = 0xdeadbeef; /* Invalid */
3744 	state.currentSeries = NULL;
3745 	state.series	    = g_ptr_array_new ();
3746 	state.plot_counter  = -1;
3747 	state.has_a_grid    = FALSE;
3748 	state.label	    = NULL;
3749 	state.text	    = NULL;
3750 	state.is_contour	= FALSE;
3751 	state.is_surface	= FALSE;
3752 	state.cur_role = -1;
3753 	state.parent_index = 0;
3754 	state.hilo = FALSE;
3755 	state.dropbar = FALSE;
3756 	state.has_extra_dataformat = FALSE;
3757 	state.axis_cross_at_max = FALSE;
3758 	state.axis_cross_value = go_nan;
3759 	state.xaxis = NULL;
3760 	state.interpolation = GO_LINE_INTERPOLATION_LINEAR;
3761 	state.error = FALSE;
3762 	state.style_element = -1;
3763 
3764 	if (NULL != (state.sog = sog)) {
3765 		state.graph = sheet_object_graph_get_gog (sog);
3766 		state.chart = GOG_CHART (gog_object_add_by_name (GOG_OBJECT (state.graph), "Chart", NULL));
3767 
3768 		if (NULL != full_page) {
3769 			GOStyle *style = (GOStyle *) gog_style_new ();
3770 			style->line.width = 0;
3771 			style->line.dash_type = GO_LINE_NONE;
3772 			style->fill.type = GO_STYLE_FILL_NONE;
3773 			g_object_set (G_OBJECT (state.graph), "style", style, NULL);
3774 			g_object_set (G_OBJECT (state.chart), "style", style, NULL);
3775 			g_object_unref (style);
3776 		}
3777 	} else {
3778 		state.graph = NULL;
3779 		state.chart = NULL;
3780 	}
3781 	state.default_plot_style = NULL;
3782 	state.plot  = NULL;
3783 	state.axis  = NULL;
3784 	state.style = NULL;
3785 	state.legend = NULL;
3786 	for (i = 0; i < 3; i++)
3787 		state.chartline_style[i] = NULL;
3788 	state.dropbar_style = NULL;
3789 
3790 	d (0, g_printerr ("{ /* CHART */\n"););
3791 
3792 	while (!done && ms_biff_query_next (q)) {
3793 		/* Use registered jump table for chart records */
3794 		if ((q->opcode & 0xff00) == 0x1000) {
3795 			int const lsb = q->opcode & 0xff;
3796 			int const begin_end =
3797 				(q->opcode == BIFF_CHART_begin ||
3798 				 q->opcode == BIFF_CHART_end);
3799 
3800 			if (lsb >= num_handler ||
3801 			    !chart_biff_handler [lsb] ||
3802 			    chart_biff_handler  [lsb]->opcode != q->opcode) {
3803 				d (0, {	g_printerr ("Unknown BIFF_CHART record\n");
3804 					ms_biff_query_dump (q);});
3805 			} else {
3806 				XLChartHandler const *const h =
3807 					chart_biff_handler [lsb];
3808 
3809 				if (state.graph	== NULL) {
3810 					g_warning ("File is most like corrupted.\n"
3811 						   "Ignoring spurious chart record (%s).",
3812 						   h->name);
3813 				} else if (q->length < h->min_size) {
3814 					g_warning ("File is most like corrupted.\n"
3815 						   "Ignoring truncated %s record with length %u < %u",
3816 						   h->name, q->length, h->min_size);
3817 				} else {
3818 					d (0, { if (!begin_end)
3819 							g_printerr ("%s(\n", h->name); });
3820 					(void)(*h->read_fn)(h, &state, q);
3821 					d (0, { if (!begin_end)
3822 							g_printerr (");\n"); });
3823 				}
3824 			}
3825 		} else switch (q->opcode) {
3826 		case BIFF_EOF:
3827 			done = TRUE;
3828 			d (0, g_printerr ("}; /* CHART */\n"););
3829 			if (state.stack->len > 0)
3830 				state.error = TRUE;
3831 			break;
3832 
3833 		case BIFF_PROTECT:
3834 			ms_excel_chart_read_PROTECT (q, &state);
3835 			break;
3836 
3837 		case BIFF_BLANK_v0:
3838 		case BIFF_BLANK_v2: /* Stores a missing value in the inline value tables */
3839 			break;
3840 
3841 		case BIFF_NUMBER_v0:
3842 			ms_excel_chart_read_NUMBER (q, &state, 7);
3843 			break;
3844 		case BIFF_NUMBER_v2:
3845 			ms_excel_chart_read_NUMBER (q, &state, 6);
3846 			break;
3847 
3848 		case BIFF_LABEL_v0: break; /* ignore for now */
3849 		case BIFF_LABEL_v2:
3850 			ms_excel_chart_read_LABEL (q, &state);
3851 			break;
3852 
3853 		case BIFF_MS_O_DRAWING:
3854 			ms_escher_parse (q, &state.container, FALSE);
3855 			break;
3856 
3857 		case BIFF_EXTERNCOUNT: /* ignore */ break;
3858 		case BIFF_EXTERNSHEET: /* These cannot be biff8 */
3859 			excel_read_EXTERNSHEET_v7 (q, &state.container);
3860 			break;
3861 
3862 		case BIFF_WINDOW2_v0 :
3863 		case BIFF_WINDOW2_v2 :
3864 			if (full_page != NULL && BC_R(ver)(&state) > MS_BIFF_V2 &&
3865 			    q->length >= 2 &&
3866 			    GSF_LE_GET_GUINT16 (q->data + 0) & 0x0400)
3867 				wb_view_sheet_focus (container->importer->wbv, full_page);
3868 			break;
3869 
3870 		case BIFF_SCL :
3871 			if (full_page != NULL)
3872 				excel_read_SCL (q, full_page);
3873 			break;
3874 
3875 		case BIFF_PLS:		/* Skip for Now */
3876 		case BIFF_DIMENSIONS_v0 :/* Skip for Now */
3877 		case BIFF_DIMENSIONS_v2 :/* Skip for Now */
3878 		case BIFF_HEADER :	/* Skip for Now */
3879 		case BIFF_FOOTER :	/* Skip for Now */
3880 		case BIFF_HCENTER :	/* Skip for Now */
3881 		case BIFF_VCENTER :	/* Skip for Now */
3882 		case BIFF_CODENAME :
3883 		case BIFF_SETUP :
3884 			d (8, g_printerr ("Handled biff %x in chart;\n",
3885 				     q->opcode););
3886 			break;
3887 
3888 		case BIFF_PRINTSIZE: {
3889 #if 0
3890 			/* Undocumented, seems like an enum ?? */
3891 			gint16 const v = GSF_LE_GET_GUINT16 (q->data);
3892 #endif
3893 		}
3894 		break;
3895 
3896 		default :
3897 			excel_unexpected_biff (q, "Chart", ms_excel_chart_debug);
3898 		}
3899 		state.prev_opcode = q->opcode;
3900 	}
3901 
3902 	/* If there was no grid in the stream remove the automatic grid */
3903 	if (!state.error && state.chart != NULL && !state.has_a_grid) {
3904 		GogGrid *grid = gog_chart_get_grid (state.chart);
3905 		if (grid != NULL) {
3906 			gog_object_clear_parent (GOG_OBJECT (grid));
3907 			g_object_unref (grid);
3908 		}
3909 	}
3910 
3911 	for (i = state.series->len; !state.error && i-- > 0 ; ) {
3912 		int j;
3913 		XLChartSeries *series = g_ptr_array_index (state.series, i);
3914 
3915 		if (series != NULL) {
3916 			Sheet *sheet = ms_container_sheet (state.container.parent);
3917 			GOData	      *data;
3918 
3919 			if (series->chart_group < 0 && BC_R(ver)(&state) >= MS_BIFF_V5) {
3920 				/* might be a error bar series or a regression curve */
3921 				if (series->err_type == 0)
3922 					xl_chart_import_trend_line (&state, series);
3923 				else
3924 					xl_chart_import_error_bar (&state, series);
3925 			}
3926 			for (j = GOG_MS_DIM_VALUES ; j < GOG_MS_DIM_TYPES; j++ )
3927 				if (NULL != series->data [j].value) {
3928 					GnmExprTop const *texpr;
3929 
3930 					if (!sheet || !series->series)
3931 						continue;
3932 
3933 					texpr = gnm_expr_top_new_constant
3934 						((GnmValue *)(series->data [j].value));
3935 					series->data [j].value = NULL;
3936 
3937 					data = gnm_go_data_vector_new_expr (sheet, texpr);
3938 					if (series->extra_dim == 0)
3939 						gog_series_set_XL_dim (series->series, j, data, NULL);
3940 					else if (j == GOG_MS_DIM_VALUES)
3941 						gog_series_set_XL_dim (series->series, series->extra_dim, data, NULL);
3942 				}
3943 		}
3944 	}
3945 	/* Cleanup */
3946 	for (i = state.series->len; i-- > 0 ; ) {
3947 		XLChartSeries *series = g_ptr_array_index (state.series, i);
3948 		excel_chart_series_delete (series);
3949 	}
3950 	g_ptr_array_free (state.series, TRUE);
3951 
3952 	g_array_free (state.stack, TRUE);
3953 	ms_container_finalize (&state.container);
3954 
3955 	if (state.error) {
3956 		state.graph = NULL;
3957 		state.chart = NULL;
3958 	}
3959 
3960 	if (state.chart) {
3961 		/* try to replace hidden axes by visible ones when possible */
3962 		GSList *l, *cur;
3963 		GogAxis *hidden, *visible;
3964 		int i;
3965 		for (i = GOG_AXIS_X; i <= GOG_AXIS_Y; i++) {
3966 			hidden = visible = NULL;
3967 			l = gog_chart_get_axes (state.chart, i);
3968 
3969 			for (cur = l; cur; cur = cur->next) {
3970 				gboolean invisible;
3971 				g_object_get (cur->data, "invisible", &invisible, NULL);
3972 				if (invisible)
3973 					hidden = GOG_AXIS (cur->data);
3974 				else
3975 					visible = GOG_AXIS (cur->data);
3976 			}
3977 			g_slist_free (l);
3978 			if (hidden && visible) {
3979 				GSList *l1, *cur1;
3980 
3981 				l1 = g_slist_copy ((GSList *) gog_axis_contributors (hidden));
3982 				for (cur1 = l1; cur1; cur1 = cur1->next) {
3983 					if (GOG_IS_PLOT (cur1->data))
3984 						gog_plot_set_axis (GOG_PLOT (cur1->data), visible);
3985 				}
3986 				g_slist_free (l1);
3987 				/* now reparent the children of the hidden axis */
3988 				l1 = gog_object_get_children (GOG_OBJECT (hidden), NULL);
3989 				for (cur1 = l1; cur1; cur1 = cur1->next) {
3990 					GogObject *obj = GOG_OBJECT (cur1->data);
3991 					GogObjectRole const *role = obj->role;
3992 					gog_object_clear_parent (obj);
3993 					gog_object_set_parent (obj, GOG_OBJECT (visible), role, obj->id);
3994 				}
3995 				g_slist_free (l1);
3996 
3997 				gog_object_clear_parent (GOG_OBJECT (hidden));
3998 				g_object_unref (hidden);
3999 			}
4000 		}
4001 	}
4002 
4003 	if (!state.error && full_page != NULL) {
4004 		static GnmRange const fixed_size = { { 1, 1 }, { 12, 32 } };
4005 		SheetObjectAnchor anchor;
4006 		sheet_object_anchor_init (&anchor, &fixed_size, NULL,
4007 			GOD_ANCHOR_DIR_DOWN_RIGHT, GNM_SO_ANCHOR_TWO_CELLS);
4008 		sheet_object_set_anchor (sog, &anchor);
4009 		sheet_object_set_sheet (sog, full_page);
4010 		g_object_unref (sog);
4011 	}
4012 
4013 	if (state.style != NULL)
4014 		g_object_unref (state.style); /* avoids a leak with corrupted files */
4015 	if (state.default_plot_style != NULL)
4016 		g_object_unref (state.default_plot_style);
4017 
4018 	return state.error;
4019 }
4020 
4021 /* A wrapper which reads and checks the BOF record then calls ms_excel_chart_read */
4022 /**
4023  * ms_excel_chart_read_BOF :
4024  * @q: #BiffQuery
4025  * @container: #MSContainer
4026  * @sog: #SheetObjectGraph
4027  **/
4028 gboolean
ms_excel_chart_read_BOF(BiffQuery * q,MSContainer * container,SheetObject * sog)4029 ms_excel_chart_read_BOF (BiffQuery *q, MSContainer *container, SheetObject *sog)
4030 {
4031 	MsBiffBofData *bof;
4032 	gboolean res = TRUE;
4033 
4034 	/* 1st record must be a valid BOF record */
4035 	g_return_val_if_fail (ms_biff_query_next (q), TRUE);
4036 	bof = ms_biff_bof_data_new (q);
4037 
4038 	g_return_val_if_fail (bof != NULL, TRUE);
4039 	g_return_val_if_fail (bof->type == MS_BIFF_TYPE_Chart, TRUE);
4040 
4041 	/* NOTE : _Ignore_ the verison in the BOF, it lies!
4042 	 * XP saving as 95 will mark the book as biff7
4043 	 * but sheets and charts are marked as biff8, even though they are not
4044 	 * using unicode */
4045 	res = ms_excel_chart_read (q, container, sog, NULL);
4046 
4047 	ms_biff_bof_data_destroy (bof);
4048 	return res;
4049 }
4050 
4051 /***************************************************************************/
4052 
4053 
4054 typedef struct {
4055 	GogAxis *axis [GOG_AXIS_TYPES];
4056 	gboolean transpose, center_ticks;
4057 	GSList  *plots;
4058 } XLAxisSet;
4059 
4060 typedef struct {
4061 	BiffPut		*bp;
4062 	ExcelWriteState *ewb;
4063 	SheetObject	*so;
4064 	GogGraph const	*graph;
4065 	GogObject const	*chart;
4066 	GogView		*root_view;
4067 
4068 	unsigned	 nest_level;
4069 	unsigned cur_series;
4070 	unsigned cur_vis_index;
4071 	unsigned cur_set;
4072 	GSList *pending_series;
4073 	GSList *extra_objects;
4074 	GPtrArray *values[3];
4075 
4076 	/* these are for combining dropbar, minmax and line plots on export */
4077 	GogPlot *line_plot;
4078 	XLAxisSet *line_set;
4079 	gboolean has_dropbar;
4080 	gboolean has_hilow;
4081 	gboolean is_stock;
4082 	GOStyle *dp_style, *hl_style;
4083 	guint16 dp_width;
4084 	GogAxis *primary_axis[GOG_AXIS_TYPES];
4085 } XLChartWriteState;
4086 
4087 typedef struct {
4088 	unsigned series;
4089 	GnmValue const *value;
4090 } XLValue;
4091 
4092 static gint
cb_axis_set_cmp(XLAxisSet const * a,XLAxisSet const * b)4093 cb_axis_set_cmp (XLAxisSet const *a, XLAxisSet const *b)
4094 {
4095 	int i;
4096 	if (a->transpose != b->transpose)
4097 		return TRUE;
4098 	for (i = GOG_AXIS_X; i < GOG_AXIS_TYPES; i++)
4099 		if (a->axis[i] != b->axis[i])
4100 			return TRUE;
4101 	return FALSE;
4102 }
4103 
4104 static void
chart_write_BEGIN(XLChartWriteState * s)4105 chart_write_BEGIN (XLChartWriteState *s)
4106 {
4107 	ms_biff_put_empty (s->bp, BIFF_CHART_begin);
4108 	s->nest_level++;
4109 }
4110 
4111 static void
chart_write_END(XLChartWriteState * s)4112 chart_write_END (XLChartWriteState *s)
4113 {
4114 	g_return_if_fail (s->nest_level > 0);
4115 	s->nest_level--;
4116 	ms_biff_put_empty (s->bp, BIFF_CHART_end);
4117 }
4118 
4119 /* returns the index of an element in the palette that is close to
4120  * the selected color */
4121 static unsigned
chart_write_color(XLChartWriteState * s,guint8 * data,GOColor c)4122 chart_write_color (XLChartWriteState *s, guint8 *data, GOColor c)
4123 {
4124 	guint32 abgr;
4125 	abgr  = GO_COLOR_UINT_R(c);
4126 	abgr |= GO_COLOR_UINT_G(c) << 8;
4127 	abgr |= GO_COLOR_UINT_B(c) << 16;
4128 	GSF_LE_SET_GUINT32 (data, abgr);
4129 
4130 	return palette_get_index (&s->ewb->base, abgr & 0xffffff);
4131 }
4132 
4133 static void
chart_write_AREAFORMAT(XLChartWriteState * s,GOStyle const * style,gboolean disable_auto)4134 chart_write_AREAFORMAT (XLChartWriteState *s, GOStyle const *style, gboolean disable_auto)
4135 {
4136 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_areaformat,
4137 		(s->bp->version >= MS_BIFF_V8) ? 16: 12);
4138 	guint16 fore_index, back_index, pat, flags = 0;
4139 	GOColor fore, back;
4140 
4141 	if (style != NULL) {
4142 		switch (style->fill.type) {
4143 		default :
4144 			g_warning ("invalid fill type, saving as none");
4145 		case GO_STYLE_FILL_IMAGE:
4146 #warning export images
4147 		case GO_STYLE_FILL_NONE:
4148 			pat = 0;
4149 			fore = GO_COLOR_WHITE;
4150 			back = GO_COLOR_WHITE;
4151 			break;
4152 		case GO_STYLE_FILL_PATTERN:
4153 			if ((style->fill.pattern.pattern == GO_PATTERN_SOLID && style->fill.pattern.back == 0)
4154 			    || (style->fill.pattern.pattern == GO_PATTERN_FOREGROUND_SOLID && style->fill.pattern.fore == 0)
4155 			    || (style->fill.pattern.fore == 0 && style->fill.pattern.back == 0)) {
4156 				pat = 0;
4157 				fore = GO_COLOR_WHITE;
4158 				back = GO_COLOR_WHITE;
4159 			} else {
4160 				pat = style->fill.pattern.pattern + 1;
4161 				if (pat == 1) {
4162 					back = style->fill.pattern.fore;
4163 					fore = style->fill.pattern.back;
4164 				} else {
4165 					fore = style->fill.pattern.fore;
4166 					back = style->fill.pattern.back;
4167 				}
4168 			}
4169 			break;
4170 		case GO_STYLE_FILL_GRADIENT:
4171 			pat = 1;
4172 			fore = back = style->fill.pattern.fore;
4173 #warning export gradients
4174 			break;
4175 		}
4176 
4177 		/* As we set only one of auto_fore and auto_back when reading,
4178 		 * we don't need to have both TRUE to set the auto flag here.
4179 		 * See bug #671845 */
4180 		if (style->fill.auto_type && (style->fill.auto_fore || style->fill.auto_back) && !disable_auto)
4181 			flags |= 1;
4182 		if (style->fill.invert_if_negative)
4183 			flags |= 2;
4184 	} else {
4185 		fore = back = 0;
4186 		pat = 0;
4187 		flags = disable_auto ? 0 : 1;
4188 	}
4189 	fore_index = chart_write_color (s, data+0, fore);
4190 	back_index = chart_write_color (s, data+4, back);
4191 	GSF_LE_SET_GUINT16 (data+8,  pat);
4192 	GSF_LE_SET_GUINT16 (data+10, flags);
4193 	if (s->bp->version >= MS_BIFF_V8) {
4194 		GSF_LE_SET_GUINT16 (data+12, fore_index);
4195 		GSF_LE_SET_GUINT16 (data+14, back_index);
4196 	}
4197 
4198 	ms_biff_put_commit (s->bp);
4199 }
4200 
4201 static void
chart_write_LINEFORMAT(XLChartWriteState * s,GOStyleLine const * lstyle,gboolean draw_ticks,gboolean clear_lines_for_null)4202 chart_write_LINEFORMAT (XLChartWriteState *s, GOStyleLine const *lstyle,
4203 			gboolean draw_ticks, gboolean clear_lines_for_null)
4204 {
4205 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_lineformat,
4206 		(s->bp->version >= MS_BIFF_V8) ? 12: 10);
4207 	guint16 w, color_index, pat, flags = 0;
4208 	static guint8 const patterns[] = {5, 0, 2, 3, 4, 4, 2, 1, 1, 1, 3, 4};
4209 
4210 	if (lstyle != NULL) {
4211 		color_index = chart_write_color (s, data, lstyle->color);
4212 		pat = patterns[lstyle->dash_type];
4213 		if (lstyle->width < 0.) {
4214 			w = 0xffff;
4215 			pat = 5;	/* none */
4216 		} else if (lstyle->width <= .5)
4217 			w = 0xffff;	/* hairline */
4218 		else if (lstyle->width <= 1.5)
4219 			w = 0;	/* normal */
4220 		else if (lstyle->width <= 2.5)
4221 			w = 1;	/* medium */
4222 		else
4223 			w = 2;	/* wide */
4224 		/* seems that excel understand auto as solid, so if pattern is not solid
4225 		   do not set the auto flag, see #605043 */
4226 		flags |= (lstyle->auto_color && pat == 0)? 1: 0;
4227 	} else {
4228 		color_index = chart_write_color (s, data, 0);
4229 		if (clear_lines_for_null) {
4230 			pat = 5;
4231 			flags = 8;	/* docs only mention 1, but there is an 8 in there too */
4232 		} else {
4233 			pat = 0;
4234 			flags = 9;	/* docs only mention 1, but there is an 8 in there too */
4235 		}
4236 		w = 0xffff;
4237 	}
4238 	if (draw_ticks)
4239 		flags |= 4;
4240 
4241 	GSF_LE_SET_GUINT16 (data+4, pat);
4242 	GSF_LE_SET_GUINT16 (data+6, w);
4243 	GSF_LE_SET_GUINT16 (data+8, flags);
4244 	if (s->bp->version >= MS_BIFF_V8)
4245 		GSF_LE_SET_GUINT16 (data+10, color_index);
4246 	ms_biff_put_commit (s->bp);
4247 }
4248 
4249 static void
chart_write_SERFMT(XLChartWriteState * s,GOLineInterpolation interpolation)4250 chart_write_SERFMT (XLChartWriteState *s, GOLineInterpolation interpolation)
4251 {
4252     if (interpolation == GO_LINE_INTERPOLATION_SPLINE) {
4253 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_serfmt, 2);
4254 	GSF_LE_SET_GUINT8 (data+0, 1);
4255 	ms_biff_put_commit (s->bp);
4256     }
4257 }
4258 
4259 static void
chart_write_MARKERFORMAT(XLChartWriteState * s,GOStyle const * style,gboolean clear_marks_for_null)4260 chart_write_MARKERFORMAT (XLChartWriteState *s, GOStyle const *style,
4261 			  gboolean clear_marks_for_null)
4262 {
4263 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_markerformat,
4264 		(s->bp->version >= MS_BIFF_V8) ? 20: 12);
4265 	guint16 fore_index, back_index, shape, flags = 0;
4266 	guint32 size;
4267 	GOColor fore, back;
4268 	static int const shape_map[] = {
4269 		0,    /* GO_MARKER_NONE	         */
4270 		1,    /* GO_MARKER_SQUARE	 */
4271 		2,    /* GO_MARKER_DIAMOND	 */
4272 		3,    /* GO_MARKER_TRIANGLE_DOWN */
4273 		3,    /* GO_MARKER_TRIANGLE_UP	 */
4274 		3,    /* GO_MARKER_TRIANGLE_RIGHT*/
4275 		3,    /* GO_MARKER_TRIANGLE_LEFT */
4276 		8,    /* GO_MARKER_CIRCLE	 */
4277 		4,    /* GO_MARKER_X		 */
4278 		9,    /* GO_MARKER_CROSS	 */
4279 		5,    /* GO_MARKER_ASTERISK	 */
4280 		7,    /* GO_MARKER_BAR		 */
4281 		6,    /* GO_MARKER_HALF_BAR	 */
4282 		1,    /* GO_MARKER_BUTTERFLY	 */
4283 		1,    /* GO_MARKER_HOURGLASS	 */
4284 		6     /* GO_MARKER_LEFT_HALF_BAR */
4285 	};
4286 
4287 	if (style != NULL) {
4288 		fore = go_marker_get_outline_color (style->marker.mark);
4289 		back = go_marker_get_fill_color	(style->marker.mark);
4290 		shape = shape_map[go_marker_get_shape (style->marker.mark)];
4291 		size = go_marker_get_size (style->marker.mark) * 20;
4292 		if (style->marker.auto_outline_color &&
4293 		    style->marker.auto_fill_color && style->marker.auto_shape &&
4294 			((size == 100) || (s->bp->version < MS_BIFF_V8)))
4295 			flags |= 1;
4296 		if (fore == 0)
4297 			flags |= 0x20;
4298 		if (back == 0)
4299 			flags |= 0x10;
4300 	} else {
4301 		fore = back = 0;
4302 		if (clear_marks_for_null) {
4303 			shape = flags = 0;
4304 		} else {
4305 			shape = 2;
4306 			flags = 1;
4307 		}
4308 		size = 100;
4309 	}
4310 
4311 	fore_index = chart_write_color (s, data+0, fore);
4312 	back_index = chart_write_color (s, data+4, back);
4313 
4314 	GSF_LE_SET_GUINT16 (data+8,  shape);
4315 	GSF_LE_SET_GUINT16 (data+10, flags);
4316 	if (s->bp->version >= MS_BIFF_V8) {
4317 		/* if s->cur_series is UINT_MAX, we are not saving a series format */
4318 		GSF_LE_SET_GUINT16 (data+12,
4319 			(style && style->marker.auto_outline_color && s->cur_series != UINT_MAX) ?
4320 			(guint16) (32 + s->cur_series): fore_index);
4321 		GSF_LE_SET_GUINT16 (data+14,
4322 			(style && style->marker.auto_outline_color && s->cur_series != UINT_MAX) ?
4323 			32 + s->cur_series: back_index);
4324 		GSF_LE_SET_GUINT32 (data+16, size);
4325 	}
4326 
4327 	ms_biff_put_commit (s->bp);
4328 }
4329 static void
chart_write_PIEFORMAT(XLChartWriteState * s,double separation)4330 chart_write_PIEFORMAT (XLChartWriteState *s, double separation)
4331 {
4332 	gint tmp = separation * 100;
4333 	if (tmp < 0)
4334 		tmp = 0;
4335 	else if (tmp > 500)
4336 		tmp = 500;
4337 	ms_biff_put_2byte (s->bp, BIFF_CHART_pieformat, tmp);
4338 }
4339 
4340 static guint32
map_length(XLChartWriteState * s,double l,gboolean is_horiz)4341 map_length (XLChartWriteState *s, double l, gboolean is_horiz)
4342 {
4343 	double tmp = l / (is_horiz ? s->root_view->allocation.w : s->root_view->allocation.h);
4344 #warning use _tmp_ here when we get the null view in place
4345 	return (unsigned)(4000. * tmp + .5);
4346 }
4347 
4348 static void
chart_write_position(XLChartWriteState * s,GogObject const * obj,guint8 * data,int hpos,int vpos)4349 chart_write_position (XLChartWriteState *s, GogObject const *obj, guint8 *data, int hpos, int vpos)
4350 {
4351 	GogView *view = gog_view_find_child_view  (s->root_view, obj);
4352 	guint32 tmp;
4353 	double l = 0.; /* just to make gcc happy */
4354 
4355 	g_return_if_fail (view != NULL);
4356 
4357 	switch (hpos) {
4358 	case XL_POS_LOW:
4359 		l = view->allocation.x;
4360 		break;
4361 	case XL_POS_HIGH:
4362 		l = view->allocation.x + view->allocation.w;
4363 		break;
4364 	case XL_POS_CENTER:
4365 	case XL_POS_JUSTIFY:
4366 		l = view->allocation.x + view->allocation.w / 2;
4367 		break;
4368 	}
4369 	tmp = map_length (s, l, TRUE);
4370 	GSF_LE_SET_GUINT32 (data + 0, tmp);
4371 	switch (vpos) {
4372 	case XL_POS_LOW:
4373 		l = view->allocation.y;
4374 		break;
4375 	case XL_POS_HIGH:
4376 		l = view->allocation.y + view->allocation.h;
4377 		break;
4378 	case XL_POS_CENTER:
4379 	case XL_POS_JUSTIFY:
4380 		l = view->allocation.y + view->allocation.h / 2;
4381 		break;
4382 	}
4383 	tmp = map_length (s, l, FALSE);
4384 	GSF_LE_SET_GUINT32 (data + 4, tmp);
4385 	tmp = map_length (s, view->allocation.w, TRUE);
4386 	GSF_LE_SET_GUINT32 (data + 8, tmp);
4387 	tmp = map_length (s, view->allocation.h, FALSE);
4388 	GSF_LE_SET_GUINT32 (data + 12, tmp);
4389 }
4390 
4391 #if 0
4392 static void
4393 chart_write_FBI (XLChartWriteState *s, guint16 i, guint16 n)
4394 {
4395 	/* seems to vary from machine to machine */
4396 	static guint8 const fbi[] = { 0xe4, 0x1b, 0xd0, 0x11, 0xc8, 0 };
4397 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_fbi, 10);
4398 	memcpy (data, fbi, sizeof fbi);
4399 	GSF_LE_SET_GUINT16 (data + sizeof fbi + 6, i);
4400 	GSF_LE_SET_GUINT16 (data + sizeof fbi + 8, n);
4401 	ms_biff_put_commit (s->bp);
4402 }
4403 #endif
4404 
4405 static void
chart_write_DATAFORMAT(XLChartWriteState * s,guint16 flag,guint16 indx,guint16 visible_indx)4406 chart_write_DATAFORMAT (XLChartWriteState *s, guint16 flag, guint16 indx, guint16 visible_indx)
4407 {
4408 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_dataformat, 8);
4409 	GSF_LE_SET_GUINT16 (data+0, flag);
4410 	GSF_LE_SET_GUINT16 (data+2, indx);
4411 	GSF_LE_SET_GUINT16 (data+4, visible_indx);
4412 	GSF_LE_SET_GUINT16 (data+6, 0); /* do not use XL 4 autocolor */
4413 	ms_biff_put_commit (s->bp);
4414 }
4415 
4416 static void
chart_write_3d(XLChartWriteState * s,guint16 rotation,guint16 elevation,guint16 distance,guint16 height,guint16 depth,guint16 gap,guint8 flags,guint8 zero)4417 chart_write_3d (XLChartWriteState *s, guint16 rotation, guint16 elevation,
4418 		guint16 distance, guint16 height, guint16 depth, guint16 gap,
4419 		guint8 flags, guint8 zero)
4420 {
4421 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_3d, 14);
4422 	GSF_LE_SET_GUINT16 (data, rotation);
4423 	GSF_LE_SET_GUINT16 (data+2, elevation);
4424 	GSF_LE_SET_GUINT16 (data+4, distance);
4425 	GSF_LE_SET_GUINT16 (data+6, height);
4426 	GSF_LE_SET_GUINT16 (data+8, depth);
4427 	GSF_LE_SET_GUINT16 (data+10, gap);
4428 	GSF_LE_SET_GUINT8 (data+12, flags);
4429 	GSF_LE_SET_GUINT8 (data+13, zero) ;
4430 	ms_biff_put_commit (s->bp);
4431 }
4432 
4433 static void
chart_write_AI(XLChartWriteState * s,GOData const * dim,unsigned n,guint8 ref_type)4434 chart_write_AI (XLChartWriteState *s, GOData const *dim, unsigned n,
4435 		guint8 ref_type)
4436 {
4437 	guint8 buf[8], lendat[2];
4438 	unsigned len;
4439 	GnmExprTop const *texpr = NULL;
4440 	GnmValue const *value = NULL;
4441 	gboolean need_release = FALSE;
4442 
4443 	if (dim != NULL) {
4444 		if (GNM_IS_GO_DATA_SCALAR (dim) || GNM_IS_GO_DATA_VECTOR (dim)) {
4445 			texpr = gnm_go_data_get_expr (dim);
4446 			if ((value = gnm_expr_top_get_range (texpr)) != NULL) {
4447 				GType const t = G_OBJECT_TYPE (dim);
4448 				value_release ((GnmValue*) value);
4449 				value = NULL;
4450 				/* the following condition should always be true */
4451 				if (t == GNM_GO_DATA_SCALAR_TYPE ||
4452 				    t == GNM_GO_DATA_VECTOR_TYPE)
4453 					ref_type = 2;
4454 			} else if ((value = gnm_expr_top_get_constant (texpr)))
4455 				ref_type = 1;
4456 			else /* might be any expression */
4457 				ref_type = 2;
4458 		} else {
4459 			char *str = go_data_serialize (dim, (gpointer)gnm_conventions_default);
4460 			ref_type = 1;
4461 			value = value_new_string (str);
4462 			g_free (str);
4463 			need_release = TRUE;
4464 		}
4465 	}
4466 	ms_biff_put_var_next (s->bp, BIFF_CHART_ai);
4467 	GSF_LE_SET_GUINT8  (buf+0, n);
4468 	GSF_LE_SET_GUINT8  (buf+1, ref_type);
4469 
4470 	/* no custom number format support for a dimension yet */
4471 	GSF_LE_SET_GUINT16 (buf+2, 0);
4472 	GSF_LE_SET_GUINT16 (buf+4, 0);
4473 
4474 	GSF_LE_SET_GUINT16 (buf+6, 0); /* placeholder length */
4475 	ms_biff_put_var_write (s->bp, buf, 8);
4476 
4477 	if (ref_type == 2 && dim) {
4478 		len = excel_write_formula (s->ewb, texpr,
4479 			gnm_go_data_get_sheet (dim),
4480 			0, 0, EXCEL_CALLED_FROM_NAME);
4481 		ms_biff_put_var_seekto (s->bp, 6);
4482 		GSF_LE_SET_GUINT16 (lendat, len);
4483 		ms_biff_put_var_write (s->bp, lendat, 2);
4484 	} else if (ref_type == 1 && value) {
4485 		if (n) {
4486 			XLValue *xlval = g_new0 (XLValue, 1);
4487 			xlval->series = s->cur_series;
4488 			xlval->value = value;
4489 			g_ptr_array_add (s->values[n - 1], xlval);
4490 		} else {
4491 			guint dat[2];
4492 			char *str = (NULL != value && VALUE_IS_STRING (value))
4493 				? value_get_as_string (value)
4494 				: go_data_serialize (dim, (gpointer)gnm_conventions_default);
4495 
4496 			ms_biff_put_commit (s->bp);
4497 			ms_biff_put_var_next (s->bp, BIFF_CHART_seriestext);
4498 			GSF_LE_SET_GUINT16 (dat, 0);
4499 			ms_biff_put_var_write  (s->bp, (guint8*) dat, 2);
4500 			excel_write_string (s->bp, STR_ONE_BYTE_LENGTH, str);
4501 			g_free (str);
4502 		}
4503 		if (need_release)
4504 			value_release ((GnmValue *) value);
4505 	}
4506 
4507 	ms_biff_put_commit (s->bp);
4508 }
4509 
4510 static void
chart_write_text(XLChartWriteState * s,GOData const * src,GOStyledObject const * obj,int purpose)4511 chart_write_text (XLChartWriteState *s, GOData const *src, GOStyledObject const *obj, int purpose)
4512 {
4513 	static guint8 const default_text[] = {
4514 		2,		/* halign = center */
4515 		2,		/* valign = center */
4516 		1, 0,		/* transparent */
4517 		0, 0, 0, 0,	/* black (as rgb) */
4518 
4519 		/* position seems constant ?? */
4520 		0xd6, 0xff, 0xff, 0xff,
4521 		0xbe, 0xff, 0xff, 0xff,
4522 		0, 0, 0, 0,
4523 		0, 0, 0, 0,
4524 
4525 		0xb1, 0,	/* flags 1 */
4526 		/* biff8 specific */
4527 		0, 0,		/* index of color */
4528 		0x10, 0x3d,	/* flags 2 */
4529 		0, 0		/* rotation */
4530 	};
4531 	guint8 *data;
4532 	guint16 color_index = 0x4d;
4533 	unsigned const len = (s->bp->version >= MS_BIFF_V8) ? 32: 26;
4534 	GOStyle *style = (obj)? go_styled_object_get_style (GO_STYLED_OBJECT (obj)): NULL;
4535 
4536 	/* TEXT */
4537 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_text, len);
4538 	memcpy (data, default_text, len);
4539 	if (obj)
4540 		chart_write_position (s, GOG_OBJECT (obj), data+8, XL_POS_CENTER, XL_POS_CENTER);
4541 	if (style != NULL) {
4542 		color_index = chart_write_color (s, data+4, style->font.color);
4543 
4544 	}
4545 	if (s->bp->version >= MS_BIFF_V8) {
4546 		GSF_LE_SET_GUINT16 (data+26, color_index);
4547 	}
4548 	ms_biff_put_commit (s->bp);
4549 
4550 	chart_write_BEGIN (s);
4551 
4552 	/* BIFF_CHART_pos, optional we use auto positioning */
4553 	if (style && !style->font.auto_font)
4554 		ms_biff_put_2byte (s->bp, BIFF_CHART_fontx,
4555 				   excel_font_from_go_font (&s->ewb->base, style->font.font));
4556 
4557 	chart_write_AI (s, src, 0, 1);
4558 	if (obj && purpose) {
4559 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_objectlink, 6);
4560 		GSF_LE_SET_GUINT16 (data, purpose);
4561 		ms_biff_put_commit (s->bp);
4562 	}
4563 
4564 	chart_write_END (s);
4565 }
4566 
4567 static void
store_dim(GogSeries const * series,GogMSDimType t,guint8 * store_type,guint8 * store_count,guint16 default_count)4568 store_dim (GogSeries const *series, GogMSDimType t,
4569 	   guint8 *store_type, guint8 *store_count, guint16 default_count)
4570 {
4571 	int msdim = gog_series_map_XL_dim (series, t);
4572 	GOData *dat = NULL;
4573 	guint16 count, type;
4574 
4575 	if (msdim >= -1)
4576 		dat = gog_dataset_get_dim (GOG_DATASET (series), msdim);
4577 	if (dat == NULL) {
4578 		count = default_count;
4579 		type = 1; /* numeric */
4580 	} else if (GO_IS_DATA_SCALAR (dat)) {
4581 		/* cheesy test to see if the content is strings or numbers */
4582 		double tmp = go_data_scalar_get_value (GO_DATA_SCALAR (dat));
4583 		type = gnm_finite (tmp) ? 1 : 3;
4584 		count = 1;
4585 	} else if (GO_IS_DATA_VECTOR (dat)) {
4586 		count = go_data_vector_get_len (GO_DATA_VECTOR (dat));
4587 		if (count > 0) {
4588 			/* cheesy test to see if the content is strings or numbers */
4589 			double tmp = go_data_vector_get_value (GO_DATA_VECTOR (dat), 0);
4590 			type = gnm_finite (tmp) ? 1 : 3;
4591 		} else
4592 			type = 1;
4593 		if (count > 30000) /* XL limit */
4594 			count = 30000;
4595 	} else {
4596 		g_warning ("How did this happen ?");
4597 		count = 0;
4598 		type = 1; /* numeric */
4599 	}
4600 	GSF_LE_SET_GUINT16 (store_type, type);
4601 	GSF_LE_SET_GUINT16 (store_count, count);
4602 }
4603 
4604 static gboolean
style_is_completely_auto(GOStyle const * style)4605 style_is_completely_auto (GOStyle const *style)
4606 {
4607 	if ((style->interesting_fields & GO_STYLE_FILL)) {
4608 		if (style->fill.type != GO_STYLE_FILL_PATTERN ||
4609 		    !style->fill.auto_back)
4610 			return FALSE;
4611 	}
4612 	if ((style->interesting_fields & (GO_STYLE_OUTLINE | GO_STYLE_LINE)) &&
4613 	    (!style->line.auto_color || !style->line.auto_dash ||
4614 		(style->line.width != 0.)))
4615 		return FALSE;
4616 	if ((style->interesting_fields & GO_STYLE_MARKER)) {
4617 		if (!style->marker.auto_shape ||
4618 		    !style->marker.auto_outline_color ||
4619 		    !style->marker.auto_fill_color)
4620 			return FALSE;
4621 	}
4622 	return TRUE;
4623 }
4624 
4625 static void
chart_write_style(XLChartWriteState * s,GOStyle const * style,guint16 indx,unsigned n,unsigned v,double separation,GOLineInterpolation interpolation)4626 chart_write_style (XLChartWriteState *s, GOStyle const *style,
4627 		   guint16 indx, unsigned n, unsigned v, double separation,
4628 		   GOLineInterpolation interpolation)
4629 {
4630 	chart_write_DATAFORMAT (s, indx, n, v);
4631 	chart_write_BEGIN (s);
4632 	ms_biff_put_2byte (s->bp, BIFF_CHART_3dbarshape, 0); /* box */
4633 	if (!style_is_completely_auto (style) || interpolation == GO_LINE_INTERPOLATION_SPLINE) {
4634 		chart_write_LINEFORMAT (s, &style->line, FALSE, FALSE);
4635 		if ((style->interesting_fields & GO_STYLE_LINE))
4636 			chart_write_SERFMT (s, interpolation);
4637 		chart_write_AREAFORMAT (s, style, FALSE);
4638 		chart_write_PIEFORMAT (s, separation);
4639 		chart_write_MARKERFORMAT (s, style, FALSE);
4640 	}
4641 	chart_write_END (s);
4642 }
4643 
4644 static gboolean
chart_write_error_bar(XLChartWriteState * s,GogErrorBar * bar,unsigned n,unsigned parent,unsigned type)4645 chart_write_error_bar (XLChartWriteState *s, GogErrorBar *bar, unsigned n,
4646 	unsigned parent, unsigned type)
4647 {
4648 	guint8 *data, value;
4649 	gboolean values_as_formula = FALSE;
4650 	GODataVector *vec = GO_DATA_VECTOR ((type & 1)?
4651 		bar->series->values[bar->error_i].data:
4652 		bar->series->values[bar->error_i + 1].data);
4653 	unsigned length,
4654 		series_length = gog_series_num_elements (bar->series);
4655 	int i, imax = (s->bp->version >= MS_BIFF_V8) ?
4656 					GOG_MS_DIM_BUBBLES: GOG_MS_DIM_CATEGORIES;
4657 	double error = 0.;
4658 
4659 	if (bar->type == GOG_ERROR_BAR_TYPE_NONE)
4660 		return FALSE;
4661 	if (!GO_IS_DATA (vec))
4662 		vec = GO_DATA_VECTOR (bar->series->values[bar->error_i].data);
4663 	if (!GO_IS_DATA (vec)) /* if still no data, do not save */
4664 		return FALSE;
4665 	length = go_data_vector_get_len (vec);
4666 	if (length == 1)
4667 		/* whatever the data are, we must save as a constant */
4668 		values_as_formula = FALSE;
4669 	else if (bar->type == GOG_ERROR_BAR_TYPE_ABSOLUTE)
4670 		values_as_formula = TRUE;
4671 
4672 	s->cur_series = n;
4673 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_series,
4674 		(s->bp->version >= MS_BIFF_V8) ? 12: 8);
4675 	GSF_LE_SET_GUINT16 (data+0, 1);
4676 	GSF_LE_SET_GUINT16 (data+4, series_length);
4677 	GSF_LE_SET_GUINT16 (data+2, 1);
4678 	GSF_LE_SET_GUINT16 (data+6, length);
4679 	if (s->bp->version >= MS_BIFF_V8) {
4680 		GSF_LE_SET_GUINT16 (data+8, 1);
4681 		GSF_LE_SET_GUINT16 (data+10, 0);
4682 	}
4683 	ms_biff_put_commit (s->bp);
4684 
4685 	chart_write_BEGIN (s);
4686 	for (i = GOG_MS_DIM_LABELS; i <= imax; i++) {
4687 		if (i == GOG_MS_DIM_VALUES && values_as_formula)
4688 			chart_write_AI (s, GO_DATA (vec), i, 2);
4689 		else {
4690 			data = ms_biff_put_len_next (s->bp, BIFF_CHART_ai, 8);
4691 			GSF_LE_SET_GUINT8  (data+0, i);
4692 			GSF_LE_SET_GUINT8  (data+1, 1);
4693 
4694 			GSF_LE_SET_GUINT16 (data+2, 0);
4695 			GSF_LE_SET_GUINT16 (data+4, 0);
4696 
4697 			GSF_LE_SET_GUINT16 (data+6, 0);
4698 			ms_biff_put_commit (s->bp);
4699 		}
4700 	}
4701 	chart_write_style (s, bar->style, 0xffff, n, 0, 0., GO_LINE_INTERPOLATION_LINEAR);
4702 
4703 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_serparent, 2);
4704 	GSF_LE_SET_GUINT16  (data, parent + 1);
4705 	ms_biff_put_commit (s->bp);
4706 
4707 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_serauxerrbar, 14);
4708 	GSF_LE_SET_GUINT8 (data, type);
4709 	switch (bar->type) {
4710 	case GOG_ERROR_BAR_TYPE_ABSOLUTE:
4711 		if (values_as_formula) {
4712 			value = 4;
4713 		} else {
4714 			error = go_data_vector_get_value (vec, 0);
4715 			value = 2;
4716 		}
4717 		break;
4718 	case GOG_ERROR_BAR_TYPE_PERCENT:
4719 		error = go_data_vector_get_value (vec, 0); /* we lose data :-( */
4720 		value = 1;
4721 		break;
4722 	case GOG_ERROR_BAR_TYPE_RELATIVE:
4723 		error = go_data_vector_get_value (vec, 0) * 100.; /* we lose data :-( */
4724 		value = 1;
4725 		break;
4726 	default:
4727 		g_warning ("unknown error bar type"); /* should not occur */
4728 		error = 0.;
4729 		value = 1;
4730 	}
4731 	GSF_LE_SET_GUINT8 (data+1, value);
4732 	GSF_LE_SET_GUINT8 (data+2, ((bar->width > 0.)? TRUE: FALSE));
4733 	GSF_LE_SET_GUINT8 (data+3, 1);
4734 	GSF_LE_SET_DOUBLE (data+4, error);
4735 	GSF_LE_SET_GUINT16 (data+12, length);
4736 
4737 	ms_biff_put_commit (s->bp);
4738 
4739 	chart_write_END (s);
4740 	return TRUE;
4741 }
4742 
4743 /* the data below are invalid, many other invalid code might be used,
4744 but this is the one xl uses */
4745 static unsigned char invalid_data[8] = {0xff, 0xff, 0xff, 0xff, 0, 1, 0xff, 0xff};
4746 
4747 static gboolean
chart_write_trend_line(XLChartWriteState * s,GogTrendLine * rc,unsigned n,unsigned parent)4748 chart_write_trend_line (XLChartWriteState *s, GogTrendLine *rc, unsigned n, unsigned parent)
4749 {
4750 	guint8 *data, type;
4751 	unsigned order = 0, nb = 96;
4752 	gboolean affine  = FALSE, skip_invalid, show_eq = FALSE, show_R2 = FALSE;
4753 	int i, imax = (s->bp->version >= MS_BIFF_V8) ?
4754 					GOG_MS_DIM_BUBBLES: GOG_MS_DIM_CATEGORIES;
4755 	GogObject *eqn = NULL;
4756 	double min, max;
4757 
4758 	if (0 == strcmp (G_OBJECT_TYPE_NAME (rc), "GogLinRegCurve")) {
4759 		type = 0;
4760 		order = 1;
4761 		nb = 2;
4762 	} else if (0 == strcmp (G_OBJECT_TYPE_NAME (rc), "GogPolynomRegCurve")) {
4763 		type = 0;
4764 		g_object_get (G_OBJECT (rc), "dims", &order, NULL);
4765 	} else if (0 == strcmp (G_OBJECT_TYPE_NAME (rc), "GogExpRegCurve"))
4766 		type = 1;
4767 	else if (0 == strcmp (G_OBJECT_TYPE_NAME (rc), "GogLogRegCurve"))
4768 		type = 2;
4769 	else if (0 == strcmp (G_OBJECT_TYPE_NAME (rc), "GogPowerRegCurve"))
4770 		type = 3;
4771 	else if (0 == strcmp (G_OBJECT_TYPE_NAME (rc), "GogMovingAvg")) {
4772 		type = 4;
4773 		g_object_get (G_OBJECT (rc), "span", &order, NULL);
4774 	} else
4775 		return FALSE;
4776 	s->cur_series = n;
4777 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_series,
4778 		(s->bp->version >= MS_BIFF_V8) ? 12: 8);
4779 	GSF_LE_SET_GUINT16 (data+0, 1);
4780 	GSF_LE_SET_GUINT16 (data+4, nb);
4781 	GSF_LE_SET_GUINT16 (data+2, 1);
4782 	GSF_LE_SET_GUINT16 (data+6, nb);
4783 	if (s->bp->version >= MS_BIFF_V8) {
4784 		GSF_LE_SET_GUINT16 (data+8, 1);
4785 		GSF_LE_SET_GUINT16 (data+10, 0);
4786 	}
4787 	ms_biff_put_commit (s->bp);
4788 
4789 	chart_write_BEGIN (s);
4790 	for (i = GOG_MS_DIM_LABELS; i <= imax; i++) {
4791 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_ai, 8);
4792 		GSF_LE_SET_GUINT8  (data+0, i);
4793 		GSF_LE_SET_GUINT8  (data+1, 1);
4794 
4795 		GSF_LE_SET_GUINT16 (data+2, 0);
4796 		GSF_LE_SET_GUINT16 (data+4, 0);
4797 
4798 		GSF_LE_SET_GUINT16 (data+6, 0);
4799 		ms_biff_put_commit (s->bp);
4800 	}
4801 	chart_write_style (s, GOG_STYLED_OBJECT (rc)->style,
4802 		0xffff, n, 0, 0., GO_LINE_INTERPOLATION_LINEAR);
4803 
4804 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_serparent, 2);
4805 	GSF_LE_SET_GUINT16  (data, parent + 1);
4806 	ms_biff_put_commit (s->bp);
4807 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_serauxtrend, 28);
4808 	GSF_LE_SET_GUINT8  (data+0, type);
4809 	GSF_LE_SET_GUINT8  (data+1, (guint8) order);
4810 	if (GOG_IS_REG_CURVE (rc)) {
4811 		g_object_get (G_OBJECT (rc), "affine", &affine,
4812 				"skip-invalid", &skip_invalid, NULL);
4813 		eqn = gog_object_get_child_by_name (GOG_OBJECT (rc), "Equation");
4814 	}
4815 	if (affine)
4816 		memcpy (data+2, invalid_data, 8);
4817 	else
4818 		GSF_LE_SET_DOUBLE (data+2, 0.);
4819 	if (eqn)
4820 		g_object_get (G_OBJECT (eqn), "show-eq", &show_eq, "show-r2", &show_R2, NULL);
4821 	GSF_LE_SET_GUINT8 (data+10, show_eq);
4822 	GSF_LE_SET_GUINT8 (data+11, show_R2);
4823 	GSF_LE_SET_DOUBLE (data+12, 0.);
4824 	GSF_LE_SET_DOUBLE (data+20, 0.);
4825 	ms_biff_put_commit (s->bp);
4826 
4827 	/* now write our stuff */
4828 	if (GOG_IS_REG_CURVE (rc)) {
4829 		/*
4830 			data+0 == min, #NA if not set
4831 			data+8 == max, #NA if not set
4832 			data+16 == flags (1 if skip invalid)
4833 		*/
4834 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_trendlimits, 17);
4835 		gog_reg_curve_get_bounds (GOG_REG_CURVE (rc), &min, &max);
4836 		if (min > - DBL_MAX)
4837 			GSF_LE_SET_DOUBLE (data+0, min);
4838 		else
4839 			memcpy (data+0, invalid_data, 8);
4840 		if (max < DBL_MAX)
4841 			GSF_LE_SET_DOUBLE (data+8, max);
4842 		else
4843 			memcpy (data+8, invalid_data, 8);
4844 		GSF_LE_SET_GUINT8 (data+16, skip_invalid);
4845 		ms_biff_put_commit (s->bp);
4846 		{
4847 			GOData *dat0 = gog_dataset_get_dim (GOG_DATASET (rc), 0),
4848 				*dat1 = gog_dataset_get_dim (GOG_DATASET (rc), 1);
4849 			gboolean range0, range1;
4850 			GnmValue *val0 = NULL, *val1 = NULL; /* initialized to make gcc happy */
4851 			if (dat0) {
4852 				GnmExprTop const *texpr = gnm_go_data_get_expr (dat0);
4853 				range0 = ((val0 = gnm_expr_top_get_range (texpr)) != NULL);
4854 			} else
4855 				range0 = FALSE;
4856 			if (dat1) {
4857 				GnmExprTop const *texpr = gnm_go_data_get_expr (dat1);
4858 				range1 = ((val1 = gnm_expr_top_get_range (texpr)) != NULL);
4859 			} else
4860 				range1 = FALSE;
4861 			if (range0 || range1) {
4862 				chart_write_BEGIN (s);
4863 				if (range0) {
4864 					value_release (val0);
4865 					chart_write_AI (s, dat0, 0, 2);
4866 				}
4867 				if  (range1) {
4868 					value_release (val1);
4869 					chart_write_AI (s, dat1, 1, 2);
4870 				}
4871 				chart_write_END (s);
4872 			}
4873 		}
4874 	}
4875 
4876 	chart_write_END (s);
4877 	return TRUE;
4878 }
4879 
4880 static int
chart_write_series(XLChartWriteState * s,GogSeries const * series,unsigned n)4881 chart_write_series (XLChartWriteState *s, GogSeries const *series, unsigned n)
4882 {
4883 	static guint8 const default_ref_type[] = { 1, 2, 0, 1 };
4884 	int i, msdim, saved = 1;
4885 	guint8 *data;
4886 	GOData *dat;
4887 	unsigned num_elements = gog_series_num_elements (series);
4888 	GList const *ptr;
4889 	char *interpolation;
4890 
4891 	/* SERIES */
4892 	s->cur_series = n;
4893 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_series,
4894 		(s->bp->version >= MS_BIFF_V8) ? 12: 8);
4895 	store_dim (series, GOG_MS_DIM_CATEGORIES, data+0, data+4, num_elements);
4896 	store_dim (series, GOG_MS_DIM_VALUES, data+2, data+6, num_elements);
4897 	if (s->bp->version >= MS_BIFF_V8) {
4898 		msdim = gog_series_map_XL_dim (series, GOG_MS_DIM_BUBBLES);
4899 		store_dim (series, GOG_MS_DIM_BUBBLES, data+8, data+10,
4900 			   (msdim >= 0) ? num_elements : 0);
4901 	}
4902 	ms_biff_put_commit (s->bp);
4903 
4904 	chart_write_BEGIN (s);
4905 	for (i = GOG_MS_DIM_LABELS; i <= GOG_MS_DIM_BUBBLES; i++) {
4906 		msdim = gog_series_map_XL_dim (series, i);
4907 		if (msdim >= -1)
4908 			dat = gog_dataset_get_dim (GOG_DATASET (series),
4909 				gog_series_map_XL_dim (series, i));
4910 		else
4911 			dat = NULL;
4912 		chart_write_AI (s, dat, i, default_ref_type[i]);
4913 	}
4914 
4915 	g_object_get (G_OBJECT (series), "interpolation", &interpolation, NULL);
4916 	chart_write_style (s, GOG_STYLED_OBJECT (series)->style,
4917 		0xffff, s->cur_series, s->cur_vis_index, 0.,
4918 		go_line_interpolation_from_str (interpolation));
4919 	g_free (interpolation);
4920 	for (ptr = gog_series_get_overrides (series); ptr != NULL ; ptr = ptr->next) {
4921 		double sep = 0;
4922 		if (g_object_class_find_property (
4923 			G_OBJECT_GET_CLASS (ptr->data), "separation"))
4924 			g_object_get (G_OBJECT (ptr->data), "separation", &sep, NULL);
4925 
4926 		chart_write_style (s, GOG_STYLED_OBJECT (ptr->data)->style,
4927 			GOG_SERIES_ELEMENT (ptr->data)->index, s->cur_series,
4928 			s->cur_vis_index, sep, GO_LINE_INTERPOLATION_LINEAR);
4929 	}
4930 	s->cur_vis_index++;
4931 
4932 	ms_biff_put_2byte (s->bp, BIFF_CHART_sertocrt, s->cur_set);
4933 	chart_write_END (s);
4934 	/* now write error bars and regression curves */
4935 	/* Regression curves */
4936 	{
4937 		GSList *cur, *l = gog_object_get_children (GOG_OBJECT (series),
4938 			gog_object_find_role_by_name (GOG_OBJECT (series), "Trend line"));
4939 		cur = l;
4940 		while (cur) {
4941 			if (chart_write_trend_line (s, GOG_TREND_LINE (cur->data),
4942 				n + saved, n))
4943 				saved++;
4944 			cur = cur->next;
4945 		}
4946 		g_slist_free (l);
4947 	}
4948 	/* error bars */
4949 	{
4950 		GogErrorBar *error_bar = NULL;
4951 		GParamSpec *pspec = g_object_class_find_property (
4952 			G_OBJECT_GET_CLASS (series), "errors");
4953 		if (pspec) {
4954 			g_object_get (G_OBJECT (series), "errors", &error_bar, NULL);
4955 			if (error_bar) {
4956 				if ((error_bar->display & GOG_ERROR_BAR_DISPLAY_POSITIVE) &&
4957 					chart_write_error_bar (s, error_bar,
4958 					n + saved, n, 3))
4959 					saved++;
4960 				if ((error_bar->display & GOG_ERROR_BAR_DISPLAY_NEGATIVE) &&
4961 					chart_write_error_bar (s, error_bar,
4962 					n + saved, n, 4))
4963 					saved++;
4964 				g_object_unref (error_bar);
4965 			}
4966 		} else {
4967 			pspec = g_object_class_find_property (
4968 				G_OBJECT_GET_CLASS (series), "x-errors");
4969 			if (pspec) {
4970 				g_object_get (G_OBJECT (series), "x-errors", &error_bar, NULL);
4971 				if (error_bar) {
4972 					if ((error_bar->display & GOG_ERROR_BAR_DISPLAY_POSITIVE) &&
4973 						chart_write_error_bar (s, error_bar,
4974 						n + saved, n, 1))
4975 						saved++;
4976 					if ((error_bar->display & GOG_ERROR_BAR_DISPLAY_NEGATIVE) &&
4977 						chart_write_error_bar (s, error_bar,
4978 						n + saved, n, 2))
4979 						saved++;
4980 					g_object_unref (error_bar);
4981 				}
4982 				/* if we have an x-errors prop, we also have y-errors */
4983 				g_object_get (G_OBJECT (series), "y-errors", &error_bar, NULL);
4984 				if (error_bar) {
4985 					if ((error_bar->display & GOG_ERROR_BAR_DISPLAY_POSITIVE) &&
4986 						chart_write_error_bar (s, error_bar,
4987 						n + saved, n, 3))
4988 						saved++;
4989 					if ((error_bar->display & GOG_ERROR_BAR_DISPLAY_NEGATIVE) &&
4990 						chart_write_error_bar (s, error_bar,
4991 						n + saved, n, 4))
4992 						saved++;
4993 					g_object_unref (error_bar);
4994 				}
4995 			}
4996 		}
4997 	}
4998 	return saved;
4999 }
5000 
5001 static void
chart_write_dummy_style(XLChartWriteState * s,double default_separation,gboolean clear_marks,gboolean clear_lines,GOLineInterpolation interpolation)5002 chart_write_dummy_style (XLChartWriteState *s, double default_separation,
5003 			 gboolean clear_marks, gboolean clear_lines,
5004 			 GOLineInterpolation interpolation)
5005 {
5006 	chart_write_DATAFORMAT (s, 0, 0, 0xfffd);
5007 	chart_write_BEGIN (s);
5008 	ms_biff_put_2byte (s->bp, BIFF_CHART_3dbarshape, 0); /* box */
5009 	chart_write_LINEFORMAT (s, NULL, FALSE, clear_lines);
5010 	chart_write_SERFMT (s, interpolation);
5011 	chart_write_AREAFORMAT (s, NULL, FALSE);
5012 	chart_write_MARKERFORMAT (s, NULL, clear_marks);
5013 	chart_write_PIEFORMAT (s, default_separation);
5014 	chart_write_END (s);
5015 }
5016 
5017 static void
chart_write_frame(XLChartWriteState * s,GogObject const * frame,gboolean calc_size,gboolean disable_auto)5018 chart_write_frame (XLChartWriteState *s, GogObject const *frame,
5019 		   gboolean calc_size, gboolean disable_auto)
5020 {
5021 	GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (frame));
5022 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_frame, 4);
5023 	GSF_LE_SET_GUINT16 (data + 0, 0); /* 0 == std/no border, 4 == shadow */
5024 	GSF_LE_SET_GUINT16 (data + 2, (0x2 | (calc_size ? 1 : 0)));
5025 	ms_biff_put_commit (s->bp);
5026 
5027 	chart_write_BEGIN (s);
5028 	chart_write_LINEFORMAT (s, &style->line, FALSE, FALSE);
5029 	chart_write_AREAFORMAT (s, style, disable_auto);
5030 	chart_write_END (s);
5031 }
5032 
5033 static guint16
xl_axis_set_elem(GogAxis const * axis,unsigned dim,guint16 flag,guint8 * data,gboolean log_scale)5034 xl_axis_set_elem (GogAxis const *axis,
5035 		  unsigned dim, guint16 flag, guint8 *data, gboolean log_scale)
5036 {
5037 	gboolean user_defined = FALSE;
5038 	double val = gog_axis_get_entry (axis, dim, &user_defined);
5039 	if (log_scale)
5040 		val = log10 (val);
5041 	gsf_le_set_double (data, user_defined ? val : 0.);
5042 	return user_defined ? 0 : flag;
5043 }
5044 
5045 static void
chart_write_axis(XLChartWriteState * s,GogAxis const * axis,unsigned i,gboolean centered,gboolean force_catserrange,gboolean cross_at_max,gboolean force_inverted,double cross_at)5046 chart_write_axis (XLChartWriteState *s, GogAxis const *axis,
5047 		unsigned i, gboolean centered, gboolean force_catserrange,
5048 		gboolean cross_at_max, gboolean force_inverted, double cross_at)
5049 {
5050 	gboolean labeled, in, out, inverted = FALSE;
5051 	guint16 tick_color_index, flags = 0;
5052 	guint8 tmp, *data;
5053 
5054 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_axis, 18);
5055 	GSF_LE_SET_GUINT32 (data + 0, i);
5056 	memset (data+2, 0, 16);
5057 	ms_biff_put_commit (s->bp);
5058 
5059 	chart_write_BEGIN (s);
5060 	if ((axis && gog_axis_is_discrete (axis)) || force_catserrange) {
5061 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_catserrange, 8);
5062 
5063 		GSF_LE_SET_GUINT16 (data+0, 1); /* values_axis_crosses_at_cat_index */
5064 		GSF_LE_SET_GUINT16 (data+2, 1); /* frequency_of_label */
5065 		GSF_LE_SET_GUINT16 (data+4, 1); /* frequency_of_tick */
5066 		if (axis)
5067 			g_object_get (G_OBJECT (axis), "invert-axis", &inverted, NULL);
5068 		else
5069 			inverted = force_inverted;
5070 		flags = centered ? 1 : 0; /* bit 0 == cross in middle of cat or between cats
5071 					     bit 1 == enum cross point from max not min */
5072 		if (cross_at_max)
5073 			flags |= 2;
5074 		if (inverted)
5075 			flags |= 0x4; /* cats in reverse order */
5076 		GSF_LE_SET_GUINT16 (data+6, flags);
5077 		ms_biff_put_commit (s->bp);
5078 
5079 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_axcext, 18);
5080 		GSF_LE_SET_GUINT16 (data+ 0, 0); /* min cat ignored if auto */
5081 		GSF_LE_SET_GUINT16 (data+ 2, 0); /* max cat ignored if auto */
5082 		GSF_LE_SET_GUINT16 (data+ 4, 1); /* value of major unit */
5083 		GSF_LE_SET_GUINT16 (data+ 6, 0); /* units of major unit */
5084 		GSF_LE_SET_GUINT16 (data+ 8, 1); /* value of minor unit */
5085 		GSF_LE_SET_GUINT16 (data+10, 0); /* units of minor unit */
5086 		GSF_LE_SET_GUINT16 (data+12, 0); /* base unit */
5087 		GSF_LE_SET_GUINT16 (data+14, 0); /* crossing point */
5088 		GSF_LE_SET_GUINT16 (data+16, 0xef); /*  1 == default min
5089 						     *  2 == default max
5090 						     *  4 == default major unit
5091 						     *  8 == default minor unit
5092 						     * 10 == this is a date axis
5093 						     * 20 == default base
5094 						     * 40 == default cross
5095 						     * 80 == default date settings */
5096 		ms_biff_put_commit (s->bp);
5097 	} else {
5098 		char *scale = NULL;
5099 		gboolean log_scale = FALSE;
5100 
5101 		if (axis != NULL)
5102 			g_object_get (G_OBJECT (axis),
5103 					  "map-name",		&scale,
5104 					  "invert-axis",		&inverted,
5105 					  NULL);
5106 		else
5107 			inverted = force_inverted;
5108 		if (scale != NULL) {
5109 			log_scale = !strcmp (scale, "Log");
5110 			g_free (scale);
5111 		}
5112 
5113 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_valuerange, 42);
5114 		memset (data, 0, 42);
5115 
5116 		if (log_scale)
5117 			flags |= 0x20;
5118 		if (inverted)
5119 			flags |= 0x40;
5120 		if (cross_at_max)
5121 			flags |= 0x80; /* partner crosses at max */
5122 
5123 		flags |= 0x100; /* UNDOCUMENTED */
5124 
5125 		if (axis != NULL) {
5126 			flags |= xl_axis_set_elem (axis, GOG_AXIS_ELEM_MIN,		0x01, data+ 0, log_scale);
5127 			flags |= xl_axis_set_elem (axis, GOG_AXIS_ELEM_MAX,		0x02, data+ 8, log_scale);
5128 			flags |= xl_axis_set_elem (axis, GOG_AXIS_ELEM_MAJOR_TICK,	0x04, data+16, log_scale);
5129 			flags |= xl_axis_set_elem (axis, GOG_AXIS_ELEM_MINOR_TICK,	0x08, data+24, log_scale);
5130 			if (isnan (cross_at) || (log_scale && cross_at == 1.) || (!log_scale && cross_at == 0.))
5131 				/* assume this is the auto case for excel */
5132 				flags |= 0x10;
5133 			else
5134 				gsf_le_set_double (data+32, log_scale ? log10 (cross_at): cross_at);
5135 		} else
5136 			flags |= 0x1f;
5137 		GSF_LE_SET_GUINT16 (data+40, flags);
5138 		ms_biff_put_commit (s->bp);
5139 	}
5140 	if (axis != NULL) {
5141 		GOStyle *style = GOG_STYLED_OBJECT (axis)->style;
5142 		int font;
5143 		GOFormat *fmt = gog_axis_get_format (axis);
5144 		if (fmt) {
5145 			int ifmt = excel_write_add_object_format (s->ewb, fmt);
5146 			data = ms_biff_put_len_next (s->bp, BIFF_CHART_ifmt, 2);
5147 			GSF_LE_SET_GUINT16 (data, ifmt);
5148 			ms_biff_put_commit (s->bp);
5149 		}
5150 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_tick,
5151 			(s->bp->version >= MS_BIFF_V8) ? 30 : 26);
5152 		g_object_get (G_OBJECT (axis),
5153 			"major-tick-labeled",		&labeled,
5154 			"major-tick-in",		&in,
5155 			"major-tick-out",		&out,
5156 			/* "major-tick-size-pts",	(unsupported in XL) */
5157 			/* "minor-tick-size-pts",	(unsupported in XL) */
5158 			NULL);
5159 		tmp = out ? 2 : 0;
5160 		if (in)
5161 			tmp |= 1;
5162 		GSF_LE_SET_GUINT8  (data+0, tmp);
5163 
5164 		g_object_get (G_OBJECT (axis),
5165 			"minor-tick-in",	&in,
5166 			"minor-tick-out",	&out,
5167 			NULL);
5168 		tmp = out ? 2 : 0;
5169 		if (in)
5170 			tmp |= 1;
5171 		GSF_LE_SET_GUINT8  (data+1, tmp);
5172 
5173 		tmp = labeled ? 3 : 0; /* label : 0 == none
5174 					*	  1 == low	(unsupported in gnumeric)
5175 					*	  2 == high	(unsupported in gnumeric)
5176 					*	  3 == beside axis */
5177 		GSF_LE_SET_GUINT8  (data+2, tmp);
5178 		GSF_LE_SET_GUINT8  (data+3, 1); /* background mode : 1 == transparent
5179 						 *		     2 == opaque */
5180 		tick_color_index = chart_write_color (s, data+4, style->font.color); /* tick label color */
5181 		memset (data+8, 0, 16);
5182 		/* if font is black, set the auto color flag, otherwise, don't set */
5183 		flags = (style->font.color == GO_COLOR_BLACK)? 0x03: 0x02;
5184 		if (style->text_layout.auto_angle)
5185 			flags |= 0x20;
5186 		else if (s->bp->version < MS_BIFF_V8) {
5187 			if (style->text_layout.angle < -45)
5188 				flags |= 0x0C;
5189 			else if (style->text_layout.angle > 45)
5190 				flags |= 0x08;
5191 		}
5192 		GSF_LE_SET_GUINT16 (data+24, flags);
5193 		if (s->bp->version >= MS_BIFF_V8) {
5194 			GSF_LE_SET_GUINT16 (data+26, tick_color_index);
5195 			if (style->text_layout.auto_angle)
5196 				GSF_LE_SET_GUINT16 (data+28, 0);
5197 			else if (style->text_layout.angle >= 0)
5198 				GSF_LE_SET_GUINT16 (data+28, (int) style->text_layout.angle);
5199 			else
5200 				GSF_LE_SET_GUINT16 (data+28, 90 - (int) style->text_layout.angle);
5201 		}
5202 		ms_biff_put_commit (s->bp);
5203 		font = excel_font_from_go_font (&s->ewb->base, style->font.font);
5204 		if (font > 0 && !style->font.auto_font)
5205 		    ms_biff_put_2byte (s->bp, BIFF_CHART_fontx, font);
5206 	}
5207 
5208 	ms_biff_put_2byte (s->bp, BIFF_CHART_axislineformat, 0); /* a real axis */
5209 	if (axis != NULL) {
5210 		GogObject *Grid;
5211 		gboolean invisible;
5212 		g_object_get (G_OBJECT (axis), "invisible", &invisible, NULL);
5213 		chart_write_LINEFORMAT (s, (invisible? NULL: &GOG_STYLED_OBJECT (axis)->style->line),
5214 					!invisible, invisible);
5215 		Grid = gog_object_get_child_by_name (GOG_OBJECT (axis), "MajorGrid");
5216 		if (Grid) {
5217 			ms_biff_put_2byte (s->bp, BIFF_CHART_axislineformat, 1);
5218 			chart_write_LINEFORMAT (s, &GOG_STYLED_OBJECT (Grid)->style->line,
5219 						FALSE, FALSE);
5220 		}
5221 		Grid = gog_object_get_child_by_name (GOG_OBJECT (axis), "MinorGrid");
5222 		if (Grid) {
5223 			ms_biff_put_2byte (s->bp, BIFF_CHART_axislineformat, 2);
5224 			chart_write_LINEFORMAT (s, &GOG_STYLED_OBJECT (Grid)->style->line,
5225 						FALSE, FALSE);
5226 		}
5227 	} else {
5228 		GOStyleLine line_style;
5229 		line_style.width = 0.;
5230 		line_style.dash_type = GO_LINE_NONE;
5231 		line_style.auto_dash = FALSE;
5232 		line_style.color = 0;
5233 		line_style.auto_color = FALSE;
5234 		chart_write_LINEFORMAT (s, NULL,
5235 					FALSE, TRUE);
5236 	}
5237 	chart_write_END (s);
5238 }
5239 
5240 static guint16
map_1_5d_type(XLChartWriteState * s,GogPlot const * plot,guint16 stacked,guint16 percentage,guint16 flag_3d)5241 map_1_5d_type (XLChartWriteState *s, GogPlot const *plot,
5242 	       guint16 stacked, guint16 percentage, guint16 flag_3d)
5243 {
5244 	char *type;
5245 	gboolean in_3d = FALSE;
5246 	guint16 res;
5247 
5248 	g_object_get (G_OBJECT (plot), "type", &type, "in-3d", &in_3d, NULL);
5249 
5250 	res = (s->bp->version >= MS_BIFF_V8 && in_3d) ? flag_3d : 0;
5251 
5252 	if (0 == strcmp (type, "stacked"))
5253 		res |= stacked;
5254 	else if (0 == strcmp (type, "as_percentage"))
5255 		res |= (percentage | stacked);
5256 
5257 	g_free (type);
5258 
5259 	return res;
5260 }
5261 
5262 static void
chart_write_plot(XLChartWriteState * s,GogPlot const * plot)5263 chart_write_plot (XLChartWriteState *s, GogPlot const *plot)
5264 {
5265 	guint16 flags = 0;
5266 	guint8 *data;
5267 	char const *type = G_OBJECT_TYPE_NAME (plot);
5268 	gboolean check_lines = FALSE;
5269 	gboolean check_marks = FALSE;
5270 	GOLineInterpolation interpolation;
5271 	char *interp;
5272 
5273 	if (0 == strcmp (type, "GogAreaPlot")) {
5274 		ms_biff_put_2byte (s->bp, BIFF_CHART_area,
5275 			map_1_5d_type (s, plot, 1, 2, 4));
5276 	} else if (0 == strcmp (type, "GogBarColPlot")) {
5277 		gboolean horizontal;
5278 		int overlap_percentage, gap_percentage;
5279 
5280 		g_object_get (G_OBJECT (plot),
5281 			      "horizontal",		&horizontal,
5282 			      "overlap-percentage",	&overlap_percentage,
5283 			      "gap-percentage",		&gap_percentage,
5284 			      NULL);
5285 		if (horizontal)
5286 			flags |= 1;
5287 		flags |= map_1_5d_type (s, plot, 2, 4, 8);
5288 
5289 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_bar, 6);
5290 		GSF_LE_SET_GINT16 (data, -overlap_percentage); /* dipsticks */
5291 		GSF_LE_SET_GINT16 (data+2, gap_percentage);
5292 		GSF_LE_SET_GUINT16 (data+4, flags);
5293 		ms_biff_put_commit (s->bp);
5294 	} else if (0 == strcmp (type, "GogLinePlot")) {
5295 		ms_biff_put_2byte (s->bp, BIFF_CHART_line,
5296 			map_1_5d_type (s, plot, 1, 2, 4));
5297 		check_marks = TRUE;
5298 	} else if (0 == strcmp (type, "GogPiePlot") ||
5299 		   0 == strcmp (type, "GogRingPlot")) {
5300 		gboolean in_3d = FALSE;
5301 		double initial_angle = 0, center_size = 0, default_separation = 0;
5302 		gint16 center = 0;
5303 		g_object_get (G_OBJECT (plot),
5304 			"in-3d",		&in_3d,
5305 			"initial-angle",	&initial_angle,
5306 			"default-separation",	&default_separation,
5307 			NULL);
5308 
5309 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_pie,
5310 			(s->bp->version >= MS_BIFF_V8) ? 6 : 4);
5311 		GSF_LE_SET_GUINT16 (data + 0, (int)initial_angle);
5312 
5313 		if (0 == strcmp (type, "GogRingPlot")) {
5314 			g_object_get (G_OBJECT (plot),
5315 				"center-size",		&center_size,
5316 				NULL);
5317 			center = (int)floor (center_size * 100. + .5);
5318 			if (center < 0)
5319 				center = 0;
5320 			else if (center > 100)
5321 				center = 100;
5322 		} else
5323 			center = 0;
5324 		GSF_LE_SET_GUINT16 (data + 2, center);
5325 		if (s->bp->version >= MS_BIFF_V8 && in_3d)
5326 			flags = 1;
5327 		GSF_LE_SET_GUINT16 (data + 4, flags);
5328 		ms_biff_put_commit (s->bp);
5329 		if (fabs (default_separation) > .005)
5330 			chart_write_dummy_style (s, default_separation, FALSE,
5331 						 FALSE, GO_LINE_INTERPOLATION_LINEAR);
5332 	} else if (0 == strcmp (type, "GogRadarPlot")) {
5333 		ms_biff_put_2byte (s->bp, BIFF_CHART_radar, flags);
5334 		check_marks = TRUE;
5335 	} else if (0 == strcmp (type, "GogRadarAreaPlot")) {
5336 		ms_biff_put_2byte (s->bp, BIFF_CHART_radararea, flags);
5337 	} else if (0 == strcmp (type, "GogBubblePlot") ||
5338 		   0 == strcmp (type, "GogXYPlot")) {
5339 		if (s->bp->version >= MS_BIFF_V8) {
5340 			data = ms_biff_put_len_next (s->bp, BIFF_CHART_scatter, 6);
5341 			if (0 == strcmp (type, "GogXYPlot")) {
5342 				GSF_LE_SET_GUINT16 (data + 0, 100);
5343 				GSF_LE_SET_GUINT16 (data + 2, 1);
5344 				GSF_LE_SET_GUINT16 (data + 4, 0);
5345 				check_marks = check_lines = TRUE;
5346 			} else {
5347 				gboolean show_neg = FALSE, in_3d = FALSE, as_area = TRUE;
5348 				double scale;
5349 				g_object_get (G_OBJECT (plot),
5350 					"show-negatives",	&show_neg,
5351 					"in-3d",		&in_3d,
5352 					"size-as-area",		&as_area,
5353 				        "bubble-scale",		&scale,
5354 					NULL);
5355 				scale *= 100.;
5356 				/* TODO : find accurate size */
5357 				GSF_LE_SET_GUINT16 (data + 0, (guint16) scale);
5358 				GSF_LE_SET_GUINT16 (data + 2, as_area ? 1 : 2);
5359 
5360 				flags = 1;
5361 				if (show_neg)
5362 					flags |= 2;
5363 				if (in_3d)
5364 					flags |= 4;
5365 				GSF_LE_SET_GUINT16 (data + 4, flags);
5366 			}
5367 			ms_biff_put_commit (s->bp);
5368 		} else
5369 			ms_biff_put_empty (s->bp, BIFF_CHART_scatter);
5370 	} else if (0 == strcmp (type, "GogContourPlot") ||
5371 			0 == strcmp (type, "XLContourPlot")) {
5372 		ms_biff_put_2byte (s->bp, BIFF_CHART_surf, 1); /* we always use color fill at the moment */
5373 		chart_write_3d (s, 0, 90, 0, 100, 100, 150, 0x05, 0); /* these are default xl values */
5374 	} else if (0 == strcmp (type, "GogSurfacePlot") ||
5375 			0 == strcmp (type, "XLSurfacePlot")) {
5376 		guint16 rotation = 0, elevation = 90, distance = 0, height = 100, depth = 100, gap = 150;
5377 		guint8 flags = 0x05, zero = 0;
5378 		int psi, theta, phi, fov;
5379 		GogObject *box = gog_object_get_child_by_name (s->chart, "3D-Box");
5380 
5381 		g_object_get (G_OBJECT (box), "psi", &psi, "theta", &theta, "phi", &phi, "fov", &fov, NULL);
5382 		elevation = (guint16) theta; /* FIXME: theta might be as large as 180 */
5383 /* TODO: evaluate the other parameters */
5384 		ms_biff_put_2byte (s->bp, BIFF_CHART_surf, 1); /* we always use color fill at the moment */
5385 		chart_write_3d (s, rotation, elevation, distance, height, depth, gap, flags, zero);
5386 	} else {
5387 		g_warning ("unexpected plot type %s", type);
5388 	}
5389 
5390 	/* be careful ! the XL default is to have lines and markers */
5391 	if (check_marks) {
5392 		g_object_get (G_OBJECT (plot),
5393 			"default-style-has-markers",	&check_marks,
5394 			NULL);
5395 		check_marks = !check_marks;
5396 	}
5397 	if (check_lines) {
5398 		g_object_get (G_OBJECT (plot),
5399 			"default-style-has-lines",	&check_lines,
5400 			NULL);
5401 		check_lines = !check_lines;
5402 	}
5403 
5404 	g_object_get (G_OBJECT (plot), "interpolation", &interp, NULL);
5405 	interpolation = go_line_interpolation_from_str (interp);
5406 	g_free (interp);
5407 	if (check_marks || check_lines || interpolation)
5408 		chart_write_dummy_style (s, 0., check_marks, check_lines, interpolation);
5409 }
5410 
5411 static void
chart_write_CHARTLINE(XLChartWriteState * s,guint16 type,GOStyle * style)5412 chart_write_CHARTLINE (XLChartWriteState *s, guint16 type, GOStyle *style)
5413 {
5414 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_chartline, 2);
5415 	GSF_LE_SET_GUINT16 (data + 0, type);
5416 	ms_biff_put_commit (s->bp);
5417 	chart_write_LINEFORMAT (s, &style->line, FALSE, FALSE);
5418 }
5419 
5420 static void
chart_write_DROPBAR(XLChartWriteState * s)5421 chart_write_DROPBAR (XLChartWriteState *s)
5422 {
5423 	guint8 *data = ms_biff_put_len_next (s->bp, BIFF_CHART_dropbar, 2);
5424 	GSF_LE_SET_GUINT16 (data + 0, s->dp_width);
5425 	ms_biff_put_commit (s->bp);
5426 	chart_write_BEGIN (s);
5427 	chart_write_LINEFORMAT (s, &s->dp_style->line, FALSE, FALSE);
5428 	chart_write_AREAFORMAT (s, s->dp_style, FALSE);
5429 	chart_write_END (s);
5430 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_dropbar, 2);
5431 	GSF_LE_SET_GUINT16 (data + 0, s->dp_width);
5432 	ms_biff_put_commit (s->bp);
5433 	s->dp_style->line.color = 0xffffff00 ^ s->dp_style->line.color;
5434 	s->dp_style->fill.pattern.fore = 0xffffff00 ^ s->dp_style->fill.pattern.fore ;
5435 	s->dp_style->fill.pattern.back = 0xffffff00 ^ s->dp_style->fill.pattern.back ;
5436 	chart_write_BEGIN (s);
5437 	chart_write_LINEFORMAT (s, &s->dp_style->line, FALSE, FALSE);
5438 	chart_write_AREAFORMAT (s, s->dp_style, FALSE);
5439 	chart_write_END (s);
5440 	g_object_unref (s->dp_style);
5441 }
5442 
5443 GNM_BEGIN_KILL_SWITCH_WARNING
5444 static void
chart_write_LEGEND(XLChartWriteState * s,GogObject const * legend)5445 chart_write_LEGEND (XLChartWriteState *s, GogObject const *legend)
5446 {
5447 	GogObjectPosition pos = gog_object_get_position_flags (legend,
5448 		GOG_POSITION_COMPASS | GOG_POSITION_ALIGNMENT);
5449 	guint16 flags = 0x1f;
5450 	guint8  XL_pos;
5451 	guint8 *data;
5452 
5453 	switch (pos) {
5454 	case GOG_POSITION_S | GOG_POSITION_ALIGN_CENTER:	XL_pos = 0; break;
5455 	case GOG_POSITION_N | GOG_POSITION_E:			XL_pos = 1; break;
5456 	case GOG_POSITION_N | GOG_POSITION_ALIGN_CENTER:	XL_pos = 2; break;
5457 
5458 	default :
5459 	case GOG_POSITION_E | GOG_POSITION_ALIGN_CENTER:	XL_pos = 3; break;
5460 	case GOG_POSITION_W | GOG_POSITION_ALIGN_CENTER:	XL_pos = 4; break;
5461 	/* On import we map 'floating' to East, XL_pos = 7; break; */
5462 	}
5463 
5464 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_legend, 20);
5465 	chart_write_position (s, legend, data, XL_POS_LOW, XL_POS_LOW);
5466 	GSF_LE_SET_GUINT8 (data + 16, XL_pos);
5467 	GSF_LE_SET_GUINT8 (data + 17, 1);
5468 	GSF_LE_SET_GUINT16 (data + 18, flags);
5469 
5470 	ms_biff_put_commit (s->bp);
5471 
5472 	chart_write_BEGIN (s);
5473 	/* BIFF_CHART_pos, optional we use auto positioning */
5474 	chart_write_text (s, NULL, GO_STYLED_OBJECT (legend), 0);
5475 	chart_write_END (s);
5476 }
5477 GNM_END_KILL_SWITCH_WARNING
5478 
5479 static void
chart_write_axis_sets(XLChartWriteState * s,GSList * sets)5480 chart_write_axis_sets (XLChartWriteState *s, GSList *sets)
5481 {
5482 	guint16 i = 0, j = 0, nser;
5483 	guint8 *data;
5484 	gboolean x_inverted = FALSE, y_inverted = FALSE;
5485 	GSList *sptr, *pptr;
5486 	XLAxisSet *axis_set;
5487 	GogObject const *legend = gog_object_get_child_by_name (s->chart, "Legend");
5488 	GogObject const *label;
5489 	unsigned num = g_slist_length (sets);
5490 
5491 	if (num == 0)
5492 		return;
5493 	if (num > 2)
5494 		num = 2; /* excel does not support more that 2. */
5495 
5496 	ms_biff_put_2byte (s->bp, BIFF_CHART_axesused, MIN (g_slist_length (sets), 2));
5497 	for (sptr = sets; sptr != NULL ; sptr = sptr->next) {
5498 		data = ms_biff_put_len_next (s->bp, BIFF_CHART_axisparent, 4*4 + 2);
5499 		/* pick arbitrary position, this sort of info is in the view  */
5500 		GSF_LE_SET_GUINT16 (data + 0, i);
5501 		GSF_LE_SET_GUINT32 (data + 2, 400);	/* 10% of 4000th of chart area */
5502 		GSF_LE_SET_GUINT32 (data + 6, 400);
5503 		GSF_LE_SET_GUINT32 (data + 10, 3000);	/* 75% of 4000th of chart area */
5504 		GSF_LE_SET_GUINT32 (data + 14, 3000);
5505 		/* chart_write_position (s, legend, data); */
5506 		ms_biff_put_commit (s->bp);
5507 
5508 		chart_write_BEGIN (s);
5509 		axis_set = sptr->data;
5510 		switch (gog_chart_get_axis_set (GOG_CHART (s->chart))) {
5511 		default :
5512 		case GOG_AXIS_SET_UNKNOWN :
5513 		case GOG_AXIS_SET_NONE :
5514 			break;
5515 		case GOG_AXIS_SET_XY : {
5516 			gboolean x_cross_at_max, y_cross_at_max, inverted,
5517 			x_force_catserrange = FALSE, y_force_catserrange = FALSE;
5518 			double xcross = go_nan, ycross = go_nan;
5519 			char *str;
5520 			if (axis_set->axis[GOG_AXIS_X] != NULL) {
5521 				g_object_get (G_OBJECT (axis_set->axis[GOG_AXIS_X]),
5522 					"pos-str", &str, "invert-axis", &inverted, NULL);
5523 				y_cross_at_max = !strcmp (str, "high");
5524 				x_cross_at_max = inverted;
5525 				if (!strcmp (str, "cross"))
5526 					ycross = gog_axis_get_entry (axis_set->axis[GOG_AXIS_X], GOG_AXIS_ELEM_CROSS_POINT, NULL);
5527 				g_free (str);
5528 			} else {
5529 				g_object_get (G_OBJECT (s->primary_axis[GOG_AXIS_X]),
5530 					"invert-axis", &x_inverted, NULL);
5531 				x_cross_at_max = x_inverted;
5532 				y_cross_at_max = FALSE;
5533 				x_force_catserrange = gog_axis_is_discrete (s->primary_axis[GOG_AXIS_X]);
5534 			}
5535 			if (axis_set->axis[GOG_AXIS_Y] != NULL) {
5536 				g_object_get (G_OBJECT (axis_set->axis[GOG_AXIS_Y]),
5537 					"pos-str", &str, "invert-axis", &inverted, NULL);
5538 				x_cross_at_max ^= !strcmp (str, "high");
5539 				y_cross_at_max ^= inverted;
5540 				if (!strcmp (str, "cross"))
5541 					xcross = gog_axis_get_entry (axis_set->axis[GOG_AXIS_Y], GOG_AXIS_ELEM_CROSS_POINT, NULL);
5542 				g_free (str);
5543 			} else {
5544 				g_object_get (G_OBJECT (s->primary_axis[GOG_AXIS_Y]),
5545 					"pos-str", &str, "invert-axis", &y_inverted, NULL);
5546 				y_cross_at_max ^= y_inverted;
5547 				y_force_catserrange = gog_axis_is_discrete (s->primary_axis[GOG_AXIS_Y]);
5548 				/* What did we want str for?  */
5549 				g_free (str);
5550 			}
5551 
5552 			/* BIFF_CHART_pos, optional we use auto positioning */
5553 			if (axis_set->transpose) {
5554 				chart_write_axis (s, axis_set->axis[GOG_AXIS_Y],
5555 					0, axis_set->center_ticks, y_force_catserrange, y_cross_at_max, y_inverted, ycross);
5556 				chart_write_axis (s, axis_set->axis[GOG_AXIS_X],
5557 					1, TRUE, x_force_catserrange, x_cross_at_max, x_inverted, xcross);
5558 			} else {
5559 				chart_write_axis (s, axis_set->axis[GOG_AXIS_X],
5560 					0, axis_set->center_ticks, x_force_catserrange, x_cross_at_max, x_inverted, xcross);
5561 				chart_write_axis (s, axis_set->axis[GOG_AXIS_Y],
5562 					1, TRUE, y_force_catserrange, y_cross_at_max, y_inverted, ycross);
5563 			}
5564 			break;
5565 		}
5566 		case GOG_AXIS_SET_XY_pseudo_3d :
5567 				chart_write_axis (s, axis_set->axis[GOG_AXIS_X],
5568 					0, FALSE, TRUE, FALSE, FALSE, go_nan);
5569 				chart_write_axis (s, axis_set->axis[GOG_AXIS_PSEUDO_3D],
5570 					1, FALSE, FALSE, FALSE, FALSE, go_nan);
5571 				chart_write_axis (s, axis_set->axis[GOG_AXIS_Y],
5572 					2, FALSE, TRUE, FALSE, FALSE, go_nan);
5573 			break;
5574 		case GOG_AXIS_SET_RADAR :
5575 				chart_write_axis (s, axis_set->axis[GOG_AXIS_CIRCULAR],
5576 					0, FALSE, TRUE, FALSE, FALSE, go_nan);
5577 				chart_write_axis (s, axis_set->axis[GOG_AXIS_RADIAL],
5578 					1, FALSE, FALSE, FALSE, FALSE, go_nan);
5579 			break;
5580 		}
5581 
5582 		if (i == 0) {
5583 			GogObject *grid = gog_object_get_child_by_name (s->chart, "Backplane");
5584 			if (grid != NULL) {
5585 				ms_biff_put_empty (s->bp, BIFF_CHART_plotarea);
5586 				chart_write_frame (s, grid, TRUE, TRUE);
5587 			}
5588 		}
5589 
5590 		for (pptr = axis_set->plots ; pptr != NULL ; pptr = pptr->next, i++) {
5591 			gboolean vary;
5592 			guint16 flags = 0;
5593 
5594 			g_object_get (G_OBJECT (pptr->data),
5595 				      "vary-style-by-element", &vary,
5596 				      NULL);
5597 
5598 			data = ms_biff_put_len_next (s->bp, BIFF_CHART_chartformat, 20);
5599 			memset (data, 0, 16);
5600 			if (vary)
5601 				flags |= 1;
5602 			GSF_LE_SET_GUINT16 (data + 16, flags);
5603 			GSF_LE_SET_GUINT16 (data + 18, i); /* use i as z order for now */
5604 			ms_biff_put_commit (s->bp);
5605 
5606 			chart_write_BEGIN (s);
5607 			chart_write_plot (s, pptr->data);
5608 
5609 			/* BIFF_CHART_chartformatlink documented as unnecessary */
5610 			if (i == 0 && legend != NULL)
5611 				chart_write_LEGEND (s, legend);
5612 			nser = g_slist_length (GOG_PLOT (pptr->data)->series);
5613 			if (i > 0) {
5614 				/* write serieslist */
5615 				int index = 0;
5616 				data = ms_biff_put_len_next (s->bp, BIFF_CHART_serieslist, 2 + 2 * nser);
5617 				GSF_LE_SET_GUINT16 (data, nser);
5618 				while (index < nser) {
5619 					GSF_LE_SET_GUINT16 (data + 2 + 2 * index, j + index);
5620 					index++;
5621 				}
5622 				ms_biff_put_commit (s->bp);
5623 			}
5624 			j += nser;
5625 			if (pptr->data == s->line_plot) {
5626 				if (s->has_dropbar)
5627 					chart_write_DROPBAR (s);
5628 				if (s->has_hilow) {
5629 					chart_write_CHARTLINE (s, 1, s->hl_style);
5630 					g_object_unref (s->hl_style);
5631 				}
5632 				if (s->is_stock) {
5633 					chart_write_DATAFORMAT (s, 0, 0, -3);
5634 					chart_write_BEGIN (s);
5635 					ms_biff_put_2byte (s->bp, BIFF_CHART_3dbarshape, 0); /* box */
5636 					chart_write_LINEFORMAT (s, NULL, FALSE, TRUE);
5637 					chart_write_AREAFORMAT (s, NULL, FALSE);
5638 					chart_write_PIEFORMAT (s, 0.);
5639 					chart_write_MARKERFORMAT (s, NULL, TRUE);
5640 					chart_write_END (s);
5641 					s->has_dropbar = FALSE;
5642 					s->has_hilow = FALSE;
5643 				}
5644 			}
5645 			chart_write_END (s);
5646 		}
5647 		/* Now write axes labels */
5648 		if (axis_set->axis[GOG_AXIS_X] != NULL) {
5649 			label = gog_object_get_child_by_name (GOG_OBJECT (axis_set->axis[GOG_AXIS_X]), "Label");
5650 			if (label) {
5651 				GOData *text = gog_dataset_get_dim (GOG_DATASET (label), 0);
5652 				if (text != NULL) {
5653 					chart_write_text (s, text,
5654 						GO_STYLED_OBJECT (label), 3);
5655 				}
5656 			}
5657 		}
5658 		if (axis_set->axis[GOG_AXIS_Y] != NULL) {
5659 			label = gog_object_get_child_by_name (GOG_OBJECT (axis_set->axis[GOG_AXIS_Y]), "Label");
5660 			if (label) {
5661 				GOData *text = gog_dataset_get_dim (GOG_DATASET (label), 0);
5662 				if (text != NULL) {
5663 					chart_write_text (s, text,
5664 						GO_STYLED_OBJECT (label), 2);
5665 				}
5666 			}
5667 		}
5668 
5669 		chart_write_END (s);
5670 
5671 		g_slist_free (axis_set->plots);
5672 		g_free (axis_set);
5673 	}
5674 	g_slist_free (sets);
5675 }
5676 
5677 static void
chart_write_siindex(XLChartWriteState * s,guint msdim)5678 chart_write_siindex (XLChartWriteState *s, guint msdim)
5679 {
5680 	guint8 *data;
5681 	unsigned i, j, jmax;
5682 	gboolean as_col;
5683 	data = ms_biff_put_len_next (s->bp, BIFF_CHART_siindex, 2);
5684 	GSF_LE_SET_GUINT16 (data, msdim);
5685 	ms_biff_put_commit (s->bp);
5686 	msdim--;
5687 	for (i = 0; i < s->values[msdim]->len; i++) {
5688 		XLValue *xlval = s->values[msdim]->pdata[i];
5689 		if (!VALUE_IS_ARRAY (xlval->value))
5690 			continue;
5691 		as_col = xlval->value->v_array.y > xlval->value->v_array.x;
5692 		jmax = as_col
5693 			? xlval->value->v_array.y
5694 			: xlval->value->v_array.x;
5695 		for (j = 0; j < jmax; j++) {
5696 			GnmValue const* value = as_col
5697 				? xlval->value->v_array.vals[0][j]
5698 				: xlval->value->v_array.vals[j][0];
5699 			switch (value->v_any.type) {
5700 			case VALUE_FLOAT:
5701 				data = ms_biff_put_len_next (s->bp, BIFF_NUMBER_v2, 14);
5702 				GSF_LE_SET_DOUBLE (data + 6, value_get_as_float (value));
5703 				break;
5704 			case VALUE_STRING: {
5705 				guint8 dat[6];
5706 				ms_biff_put_var_next (s->bp, BIFF_LABEL_v2);
5707 				GSF_LE_SET_GUINT16 (dat, j);
5708 				GSF_LE_SET_GUINT16 (dat + 2, i);
5709 				GSF_LE_SET_GUINT16 (dat + 4, 0);
5710 				ms_biff_put_var_write  (s->bp, (guint8*) dat, 6);
5711 				excel_write_string (s->bp, STR_TWO_BYTE_LENGTH,
5712 						    value_peek_string (value));
5713 				ms_biff_put_commit (s->bp);
5714 				continue;
5715 			default:
5716 				break;
5717 			}
5718 			}
5719 			GSF_LE_SET_GUINT16 (data, j);
5720 			GSF_LE_SET_GUINT16 (data + 2, i);
5721 			GSF_LE_SET_GUINT16 (data + 4, 0);
5722 			ms_biff_put_commit (s->bp);
5723 		}
5724 	}
5725 }
5726 
5727 void
ms_excel_chart_write(ExcelWriteState * ewb,SheetObject * so)5728 ms_excel_chart_write (ExcelWriteState *ewb, SheetObject *so)
5729 {
5730 	guint8 *data;
5731 	GogRenderer *renderer;
5732 	XLChartWriteState state;
5733 	unsigned i, num_series = 0;
5734 	GSList const *plots, *series;
5735 	GSList *charts, *sets, *ptr;
5736 	XLAxisSet *axis_set = NULL;
5737 	GogPlot *cur_plot;
5738 	GError *error;
5739 	GogLabel *label;
5740 	double pos[4];
5741 
5742 	state.bp  = ewb->bp;
5743 	state.ewb = ewb;
5744 	state.so  = so;
5745 	state.graph = sheet_object_graph_get_gog (so);
5746 	for (i = 0; i < 3; i++)
5747 		state.values[i] = g_ptr_array_new ();
5748 	state.pending_series = NULL;
5749 	state.extra_objects = NULL;
5750 	state.line_plot = NULL;
5751 	state.line_set = NULL;
5752 	state.has_dropbar = FALSE;
5753 	state.has_hilow = FALSE;
5754 	state.cur_set = 0;
5755 	state.is_stock = FALSE;
5756 	state.cur_vis_index = 0;
5757 
5758 	g_return_if_fail (state.graph != NULL);
5759 
5760 	charts = gog_object_get_children (GOG_OBJECT (state.graph),
5761 		gog_object_find_role_by_name (GOG_OBJECT (state.graph), "Chart"));
5762 
5763 	g_return_if_fail (charts != NULL);
5764 
5765 	/* TODO : handle multiple charts */
5766 	state.chart = charts->data;
5767 	state.nest_level = 0;
5768 	g_slist_free (charts);
5769 
5770 	/* TODO : create a null renderer class for use in sizing things */
5771 	sheet_object_position_pts_get (so, pos);
5772 	renderer  = g_object_new (GOG_TYPE_RENDERER,
5773 				  "model", state.graph,
5774 				  NULL);
5775 	gog_renderer_update (renderer, pos[2] - pos[0], pos[3] - pos[1]);
5776 	g_object_get (G_OBJECT (renderer), "view", &state.root_view, NULL);
5777 
5778 
5779 	excel_write_BOF (state.bp, MS_BIFF_TYPE_Chart);
5780 	ms_biff_put_empty (state.bp, BIFF_HEADER);
5781 	ms_biff_put_empty (state.bp, BIFF_FOOTER);
5782 	ms_biff_put_2byte (state.bp, BIFF_HCENTER, 0);
5783 	ms_biff_put_2byte (state.bp, BIFF_VCENTER, 0);
5784 	/* TODO : maintain this info on import */
5785 	excel_write_SETUP (state.bp, NULL);
5786 	/* undocumented always seems to be 3 */
5787 	ms_biff_put_2byte (state.bp, BIFF_PRINTSIZE, 3);
5788 #if 0 /* do not write these until we know more */
5789 	chart_write_FBI (&state, 0, 0x5);
5790 	chart_write_FBI (&state, 1, 0x6);
5791 #endif
5792 
5793 	ms_biff_put_2byte (state.bp, BIFF_PROTECT, 0);
5794 	ms_biff_put_2byte (state.bp, BIFF_CHART_units, 0);
5795 
5796 #warning be smart about singletons and titles
5797 	data = ms_biff_put_len_next (state.bp, BIFF_CHART_chart, 4*4);
5798 //	chart_write_position (&state, state.chart, data, XL_POS_LOW, XL_POS_LOW);
5799 	GSF_LE_SET_GUINT32 (data + 0, (int) (state.root_view->allocation.x * 65535.));
5800 	GSF_LE_SET_GUINT32 (data + 4, (int) (state.root_view->allocation.y * 65535.));
5801 	GSF_LE_SET_GUINT32 (data + 8, (int) (state.root_view->allocation.w * 65535.));
5802 	GSF_LE_SET_GUINT32 (data + 12, (int) (state.root_view->allocation.h * 65535.));
5803 	ms_biff_put_commit (state.bp);
5804 
5805 	chart_write_BEGIN (&state);
5806 	excel_write_SCL	(state.bp, 1.0, TRUE); /* seems unaffected by zoom */
5807 
5808 	if (state.bp->version >= MS_BIFF_V8) {
5809 		/* zoom does not seem to effect it */
5810 		data = ms_biff_put_len_next (state.bp, BIFF_CHART_plotgrowth, 8);
5811 		GSF_LE_SET_GUINT32 (data + 0, 0x10000);
5812 		GSF_LE_SET_GUINT32 (data + 4, 0x10000);
5813 		ms_biff_put_commit (state.bp);
5814 	}
5815 	chart_write_frame (&state, state.chart, FALSE, FALSE);
5816 	/* collect axis sets */
5817 	sets = NULL;
5818 	for (i = GOG_AXIS_X; i < GOG_AXIS_TYPES; i++)
5819 		state.primary_axis[i] = NULL;
5820 	for (plots = gog_chart_get_plots (GOG_CHART (state.chart)) ; plots != NULL ; plots = plots->next) {
5821 		/* XL cannot handle plots with no data */
5822 		cur_plot = GOG_PLOT (plots->data);
5823 		if (gog_plot_get_series (cur_plot) == NULL) {
5824 			g_warning ("MS Excel cannot handle plots with no data, dropping %s",
5825 				gog_object_get_name (plots->data));
5826 			continue;
5827 		}
5828 
5829 		axis_set = g_new0 (XLAxisSet, 1);
5830 		for (i = GOG_AXIS_X; i < GOG_AXIS_TYPES; i++) {
5831 			axis_set->axis[i] = gog_plot_get_axis (cur_plot, i);
5832 			if (state.primary_axis[i] == NULL)
5833 				state.primary_axis[i] = axis_set->axis[i];
5834 			else if (axis_set->axis[i] == state.primary_axis[i])
5835 				axis_set->axis[i] = NULL; /* write a dummy axis */
5836 		}
5837 
5838 		if (0 == strcmp (G_OBJECT_TYPE_NAME (cur_plot), "GogBarColPlot")) {
5839 			g_object_get (G_OBJECT (plots->data),
5840 				      "horizontal", &axis_set->transpose,
5841 				      NULL);
5842 			axis_set->center_ticks = TRUE;
5843 		} else if (0 == strcmp (G_OBJECT_TYPE_NAME (cur_plot), "GogAreaPlot"))
5844 			axis_set->center_ticks = TRUE;
5845 		else if (0 == strcmp (G_OBJECT_TYPE_NAME (cur_plot), "GogDropBarPlot")) {
5846 			/* The code here is supposed to work on plots imported from excel
5847 			Nothing sure about the ones created in gnumeric */
5848 			/* drop bars concern the first and last series in an xl line plot so
5849 			we must create a new plot and take only one series */
5850 			GogSeries *orig, *first, *last;
5851 			GOStyle *style;
5852 			if (state.has_dropbar) {
5853 				g_free (axis_set);
5854 				continue;
5855 			}
5856 			if (state.line_plot == NULL) {
5857 				state.line_plot = GOG_PLOT (gog_plot_new_by_name ("GogLinePlot"));
5858 				state.line_set = axis_set;
5859 			} else if (cb_axis_set_cmp (axis_set, state.line_set)) {
5860 				/* excel does not support that AFAIK (Jean) */
5861 				g_free (axis_set);
5862 				continue;
5863 			}
5864 			state.has_dropbar = TRUE;
5865 			axis_set->center_ticks = TRUE;
5866 			/* now, take the first series in the plot and split it into two series */
5867 			if (!state.is_stock) {
5868 				char *group_name;
5869 				g_object_get (G_OBJECT (cur_plot), "plot-group", &group_name, NULL);
5870 				if (group_name && !strcmp (group_name, "GogStockPlot"))
5871 					state.is_stock = TRUE;
5872 			}
5873 			orig = GOG_SERIES (gog_plot_get_series (cur_plot)->data);
5874 			state.dp_style = go_style_dup (
5875 					go_styled_object_get_style (GO_STYLED_OBJECT (orig)));
5876 			g_object_get (cur_plot, "gap_percentage", &state.dp_width, NULL);
5877 			first = gog_plot_new_series (state.line_plot);
5878 			gog_series_set_dim (first, -1,
5879 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), -1)),
5880 			&error);
5881 			gog_series_set_dim (first, 0,
5882 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 0)),
5883 			&error);
5884 			gog_series_set_dim (first, 1,
5885 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 1)),
5886 			&error);
5887 			if (!state.is_stock) {
5888 				style = go_styled_object_get_style (GO_STYLED_OBJECT (first));
5889 				/* FIXME: change this code when series lines are available ! */
5890 				style->line.auto_dash = FALSE;
5891 				style->line.auto_color = FALSE;
5892 				style->line.dash_type = GO_LINE_NONE;
5893 				style->marker.auto_shape = FALSE;
5894 				go_marker_set_shape (style->marker.mark, GO_MARKER_NONE);
5895 				style->marker.auto_fill_color = FALSE;
5896 			}
5897 			last = gog_plot_new_series (state.line_plot);
5898 			gog_series_set_dim (last, -1,
5899 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), -1)),
5900 					&error);
5901 			gog_series_set_dim (last, 0,
5902 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 0)),
5903 					&error);
5904 			gog_series_set_dim (last, 1,
5905 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 2)),
5906 					&error);
5907 			if (!state.is_stock) {
5908 				style = go_styled_object_get_style (GO_STYLED_OBJECT (first));
5909 				/* FIXME: change this code when series lines are available ! */
5910 				style->line.auto_dash = FALSE;
5911 				style->line.auto_color = FALSE;
5912 				style->line.dash_type = GO_LINE_NONE;
5913 				style->marker.auto_shape = FALSE;
5914 				go_marker_set_shape (style->marker.mark, GO_MARKER_NONE);
5915 				style->marker.auto_fill_color = FALSE;
5916 			}
5917 			/* now, bring the two series to the begining */
5918 			if (g_slist_length (state.line_plot->series) > 2) {
5919 				state.line_plot->series = g_slist_remove (state.line_plot->series, first);
5920 				state.line_plot->series = g_slist_remove (state.line_plot->series, last);
5921 				state.line_plot->series = g_slist_prepend (state.line_plot->series, last);
5922 				state.line_plot->series = g_slist_prepend (state.line_plot->series, first);
5923 			}
5924 			cur_plot = (state.has_hilow)? NULL: state.line_plot;
5925 		} else if (0 == strcmp (G_OBJECT_TYPE_NAME (cur_plot), "GogMinMaxPlot")) {
5926 			/* same comment as for dropbars */
5927 			GogSeries *orig, *high, *low;
5928 			GOStyle *style;
5929 			if (state.has_hilow) {
5930 				g_free (axis_set);
5931 				continue;
5932 			}
5933 			if (state.line_plot == NULL) {
5934 				state.line_plot = GOG_PLOT (gog_plot_new_by_name ("GogLinePlot"));
5935 				state.line_set = axis_set;
5936 			} else if (cb_axis_set_cmp (axis_set, state.line_set)) {
5937 				/* excel does not support that AFAIK (Jean) */
5938 				g_free (axis_set);
5939 				continue;
5940 			}
5941 			state.has_hilow = TRUE;
5942 			axis_set->center_ticks = TRUE;
5943 			if (!state.is_stock) {
5944 				char *group_name;
5945 				g_object_get (G_OBJECT (cur_plot), "plot-group", &group_name, NULL);
5946 				if (group_name && !strcmp (group_name, "GogStockPlot"))
5947 					state.is_stock = TRUE;
5948 			}
5949 			orig = GOG_SERIES (gog_plot_get_series (cur_plot)->data);
5950 			state.hl_style = go_style_dup (
5951 					go_styled_object_get_style (GO_STYLED_OBJECT (orig)));
5952 			high = gog_plot_new_series (state.line_plot);
5953 			gog_series_set_dim (high, -1,
5954 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), -1)),
5955 			&error);
5956 			gog_series_set_dim (high, 0,
5957 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 0)),
5958 			&error);
5959 			gog_series_set_dim (high, 1,
5960 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 1)),
5961 			&error);
5962 			if (!state.is_stock) {
5963 				style = go_styled_object_get_style (GO_STYLED_OBJECT (high));
5964 				/* FIXME: change this code when series lines are available ! */
5965 				style->line.auto_dash = FALSE;
5966 				style->line.auto_color = FALSE;
5967 				style->line.dash_type = GO_LINE_NONE;
5968 				style->marker.auto_shape = FALSE;
5969 				go_marker_set_shape (style->marker.mark, GO_MARKER_NONE);
5970 				style->marker.auto_fill_color = FALSE;
5971 			}
5972 			low = gog_plot_new_series (state.line_plot);
5973 			gog_series_set_dim (low, -1,
5974 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), -1)),
5975 					&error);
5976 			gog_series_set_dim (low, 0,
5977 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 0)),
5978 					&error);
5979 			gog_series_set_dim (low, 1,
5980 					go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 2)),
5981 					&error);
5982 			if (!state.is_stock) {
5983 				style = go_styled_object_get_style (GO_STYLED_OBJECT (low));
5984 				/* FIXME: change this code when series lines are available ! */
5985 				style->line.auto_dash = FALSE;
5986 				style->line.auto_color = FALSE;
5987 				style->line.dash_type = GO_LINE_NONE;
5988 				style->marker.auto_shape = FALSE;
5989 				go_marker_set_shape (style->marker.mark, GO_MARKER_NONE);
5990 				style->marker.auto_fill_color = FALSE;
5991 			}
5992 			cur_plot = (state.has_dropbar)? NULL: state.line_plot;
5993 		} else if (0 == strcmp (G_OBJECT_TYPE_NAME (cur_plot), "GogLinePlot")) {
5994 			if (state.line_plot != NULL &&
5995 					!cb_axis_set_cmp (axis_set, state.line_set)) {
5996 				GogSeries *orig, *new;
5997 				for (series = gog_plot_get_series (cur_plot); series != NULL;
5998 							series = series->next) {
5999 					orig = GOG_SERIES (series->data);
6000 					new = gog_plot_new_series (state.line_plot);
6001 					gog_series_set_dim (new, -1,
6002 							go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), -1)),
6003 							&error);
6004 					gog_series_set_dim (new, 0,
6005 							go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 0)),
6006 							&error);
6007 					gog_series_set_dim (new, 1,
6008 							go_data_dup (gog_dataset_get_dim (GOG_DATASET (orig), 1)),
6009 							&error);
6010 					go_styled_object_set_style (GO_STYLED_OBJECT (new),
6011 							go_styled_object_get_style (GO_STYLED_OBJECT (orig)));
6012 				}
6013 				cur_plot = NULL;
6014 			}
6015 		}
6016 		if (cur_plot) {
6017 			ptr = g_slist_find_custom (sets, axis_set,
6018 				(GCompareFunc) cb_axis_set_cmp);
6019 			if (ptr != NULL) {
6020 				g_free (axis_set);
6021 				axis_set = ptr->data;
6022 			} else
6023 				sets = g_slist_append (sets, axis_set);
6024 			axis_set->plots = g_slist_append (axis_set->plots, cur_plot);
6025 		}
6026 	}
6027 
6028 	/* line plots with drop bars need to have the series in the correct order ! */
6029 	if (state.line_plot && state.has_dropbar) {
6030 		if (g_slist_length (state.line_plot->series) > 2) {
6031 			gpointer data = g_slist_nth (state.line_plot->series, 1)->data;
6032 			state.line_plot->series = g_slist_remove (state.line_plot->series, data);
6033 			state.line_plot->series = g_slist_append (state.line_plot->series, data);
6034 		}
6035 	}
6036 
6037 	/* dump the associated series (skip any that we are dropping */
6038 	for (ptr = sets; ptr != NULL ; ptr = ptr->next) {
6039 		for (plots = ((XLAxisSet *)ptr->data)->plots ; plots != NULL ; plots = plots->next) {
6040 			/* first test if the plot uses a matrix or not */
6041 			gboolean has_matrix = FALSE;
6042 			GogPlotDesc const *desc = gog_plot_description (GOG_PLOT (plots->data));
6043 			int n = 0, m;
6044 			if (!desc) /* this should not happen, but ... */
6045 				continue;
6046 			for (m = 0; m < (int) desc->series.num_dim; m++)
6047 				if (desc->series.dim[m].val_type == GOG_DIM_MATRIX) {
6048 					n = m;
6049 					has_matrix = TRUE;
6050 					break; /* hopefully there is only one matrix */
6051 				}
6052 			if (!has_matrix) {
6053 				for (series = gog_plot_get_series (plots->data) ; series != NULL ; series = series->next)
6054 					num_series += chart_write_series (&state, series->data, num_series);
6055 			} else if (n == 2) { /* surfaces and countours have the matrix as third data, other
6056 						plot types that might use matrices will probably not be exportable
6057 						to any of excel formats */
6058 				/* we should have only one series there */
6059 				GogSeries *ser = GOG_SERIES (gog_plot_get_series (plots->data)->data);
6060 				/* create an equivalent XLContourPlot and save its series */
6061 				if (ser != NULL) {
6062 					gboolean as_col, s_as_col = FALSE;
6063 					gboolean s_is_rc = FALSE, mat_is_rc;
6064 					GnmExprTop const *stexpr = NULL;
6065 					GnmExprTop const *mattexpr;
6066 					GnmValue const *sval = NULL, *matval;
6067 					GnmValue *val;
6068 					GogSeries *serbuf;
6069 					GnmRange vector, svec;
6070 					int i, j, sn = 0, cur = 0, scur = 0;
6071 					GOData *s, *c, *mat = ser->values[2].data;
6072 					GODataMatrixSize size = go_data_matrix_get_size (GO_DATA_MATRIX (mat));
6073 					GogPlot *plotbuf = (GogPlot*) gog_plot_new_by_name ((0 == strcmp (G_OBJECT_TYPE_NAME (plots->data), "GogContourPlot"))? "XLContourPlot": "XLSurfacePlot");
6074 					Sheet *sheet = sheet_object_get_sheet (so);
6075 					g_object_get (G_OBJECT (plots->data), "transposed", &as_col, NULL);
6076 					mattexpr = gnm_go_data_get_expr (mat);
6077 					mat_is_rc = gnm_expr_top_is_rangeref (mattexpr);
6078 					if (mat_is_rc) {
6079 						matval = gnm_expr_top_get_range (mattexpr);
6080 					} else {
6081 						matval = gnm_expr_top_get_constant (mattexpr);
6082 					}
6083 					if (as_col) {
6084 						c = ser->values[1].data;
6085 						s = ser->values[0].data;
6086 					} else {
6087 						c = ser->values[0].data;
6088 						s = ser->values[1].data;
6089 					}
6090 					if (s) {
6091 						sn = go_data_vector_get_len (GO_DATA_VECTOR (s));
6092 						stexpr = gnm_go_data_get_expr (s);
6093 						s_is_rc = gnm_expr_top_is_rangeref (stexpr);
6094 					}
6095 					if (mat_is_rc) {
6096 						if (as_col) {
6097 							vector.start.row = matval->v_range.cell.a.row;
6098 							vector.end.row = matval->v_range.cell.b.row;
6099 							cur = matval->v_range.cell.a.col;
6100 						} else {
6101 							vector.start.col = matval->v_range.cell.a.col;
6102 							vector.end.col = matval->v_range.cell.b.col;
6103 							cur = matval->v_range.cell.a.row;
6104 						}
6105 					} else {
6106 					}
6107 					if (s) {
6108 						if (s_is_rc) {
6109 							sval = gnm_expr_top_get_range (stexpr);
6110 							s_as_col = sval->v_range.cell.a.col == sval->v_range.cell.b.col;
6111 							if (s_as_col) {
6112 								svec.start.col = svec.end.col = sval->v_range.cell.a.col;
6113 								scur = sval->v_range.cell.a.row;
6114 							} else {
6115 								svec.start.row = svec.end.row = sval->v_range.cell.a.row;
6116 								scur = sval->v_range.cell.a.col;
6117 							}
6118 						} else {
6119 							sval = gnm_expr_top_get_constant (stexpr);
6120 							s_as_col = sval->v_array.y > sval->v_array.x;
6121 						}
6122 					}
6123 					n = (as_col)? size.columns: size.rows;
6124 					if (!mat_is_rc)
6125 						m = (as_col)? size.rows: size.columns;
6126 					else
6127 						m = 0;
6128 					for (i = 0; i < n; i++) {
6129 						serbuf = gog_plot_new_series (plotbuf);
6130 						if (c) {
6131 							g_object_ref (c);
6132 							gog_series_set_dim (serbuf, 0, c, NULL);
6133 						}
6134 						if (s && (i < sn)) {
6135 							if (s_is_rc) {
6136 								if (s_as_col)
6137 									svec.start.row = svec.end.row = scur++;
6138 								else
6139 									svec.start.col = svec.end.col = scur++;
6140 								gog_series_set_dim (serbuf, -1,
6141 									gnm_go_data_scalar_new_expr (sheet,
6142 										gnm_expr_top_new_constant (
6143 											value_new_cellrange_r (sheet, &svec))), NULL);
6144 							} else {
6145 								val = value_dup ((s_as_col)? sval->v_array.vals[0][i]:
6146 											sval->v_array.vals[i][0]);
6147 								gog_series_set_dim (serbuf, -1,
6148 									gnm_go_data_scalar_new_expr (sheet,
6149 										gnm_expr_top_new_constant ( val)), NULL);
6150 							}
6151 						}
6152 						if (mat_is_rc) {
6153 							if (as_col)
6154 								vector.start.col = vector.end.col = cur++;
6155 							else
6156 								vector.start.row = vector.end.row = cur++;
6157 							gog_series_set_dim (serbuf, 1,
6158 								gnm_go_data_vector_new_expr (sheet,
6159 									gnm_expr_top_new_constant (
6160 										value_new_cellrange_r (sheet, &vector))), NULL);
6161 						} else {
6162 							val = value_new_array (m, 1);
6163 							for (j = 0; j < m; j++) {
6164 								value_array_set (val, j, 0,
6165 									value_dup((as_col)? matval->v_array.vals[i][j]:
6166 										matval->v_array.vals[j][i]));
6167 								}
6168 							gog_series_set_dim (serbuf, 1,
6169 								gnm_go_data_vector_new_expr (sheet,
6170 									gnm_expr_top_new_constant (val)), NULL);
6171 						}
6172 					}
6173 					if (mat_is_rc)
6174 						value_release ((GnmValue*) matval);
6175 					if (s && s_is_rc)
6176 						value_release ((GnmValue*) sval);
6177 					for (series = gog_plot_get_series (plotbuf) ; series != NULL ; series = series->next)
6178 						num_series += chart_write_series (&state, series->data, num_series);
6179 					state.extra_objects = g_slist_append (state.extra_objects, plotbuf);
6180 				}
6181 			}
6182 			state.cur_set++;
6183 		}
6184 	}
6185 
6186 	data = ms_biff_put_len_next (state.bp, BIFF_CHART_shtprops, 4);
6187 	GSF_LE_SET_GUINT32 (data + 0, 0xa);
6188 	ms_biff_put_commit (state.bp);
6189 
6190 #warning what do these connect to ?
6191 	for (i = 2; i <= 3; i++) {
6192 		ms_biff_put_2byte (state.bp, BIFF_CHART_defaulttext, i);
6193 		chart_write_text (&state, NULL, NULL, 0);
6194 	}
6195 
6196 	state.cur_series = UINT_MAX;
6197 	chart_write_axis_sets (&state, sets);
6198 
6199 	/* write chart title if any */
6200 	label = GOG_LABEL (gog_object_get_child_by_name (GOG_OBJECT (state.chart), "Title"));
6201 	if (label == NULL)
6202 		/* in that case, try the graph title */
6203 		label = GOG_LABEL (gog_object_get_child_by_name (GOG_OBJECT (state.graph), "Title"));
6204 	if (label != NULL) {
6205 		GOData *text = gog_dataset_get_dim (GOG_DATASET (label), 0);
6206 		if (text != NULL) {
6207 			chart_write_text (&state, text,
6208 				GO_STYLED_OBJECT (label), 1);
6209 		}
6210 	}
6211 
6212 	for (i = 0; i < 3; i++) {
6213 		chart_write_siindex (&state, i + 1);
6214 		g_ptr_array_foreach (state.values[i], (GFunc) g_free, NULL);
6215 		g_ptr_array_free (state.values[i], TRUE);
6216 	}
6217 	g_slist_free_full (state.extra_objects, g_object_unref);
6218 	if (state.line_plot)
6219 		g_object_unref (state.line_plot);
6220 
6221 	chart_write_END (&state);
6222 #if 0 /* they seem optional */
6223 	BIFF_DIMENSIONS
6224 	BIFF_CHART_siindex x num_series ?
6225 #endif
6226 	ms_biff_put_empty (ewb->bp, BIFF_EOF);
6227 
6228 	g_object_unref (state.root_view);
6229 	g_object_unref (renderer);
6230 }
6231