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", ¢er_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