Code cleanup
[gnumeric.git] / plugins / openoffice / openoffice-read.c
blobfee11b102b24e30dfacd656ae521f6e593838fc0
1 /* vm: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * openoffice-read.c : import open/star calc files
6 * Copyright (C) 2002-2007 Jody Goldberg (jody@gnome.org)
7 * Copyright (C) 2006 Luciano Miguel Wolf (luciano.wolf@indt.org.br)
8 * Copyright (C) 2007 Morten Welinder (terra@gnome.org)
9 * Copyright (C) 2006-2010 Andreas J. Guelzow (aguelzow@pyrshep.ca)
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation; either version 2 of the
14 * License, or (at your option) version 3.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 * USA
27 #include <gnumeric-config.h>
28 #include <gnumeric.h>
30 #include <gnm-plugin.h>
31 #include <workbook-view.h>
32 #include <workbook.h>
33 #include <sheet.h>
34 #include <sheet-view.h>
35 #include <sheet-merge.h>
36 #include <sheet-filter.h>
37 #include <selection.h>
38 #include <ranges.h>
39 #include <cell.h>
40 #include <value.h>
41 #include <expr.h>
42 #include <expr-impl.h>
43 #include <expr-name.h>
44 #include <func.h>
45 #include <parse-util.h>
46 #include <style-color.h>
47 #include <sheet-style.h>
48 #include <mstyle.h>
49 #include <style-border.h>
50 #include <input-msg.h>
51 #include <gnm-format.h>
52 #include <print-info.h>
53 #include <command-context.h>
54 #include <gutils.h>
55 #include <xml-sax.h>
56 #include <sheet-object-cell-comment.h>
57 #include "sheet-object-widget.h"
58 #include <style-conditions.h>
59 #include <gnumeric-conf.h>
60 #include <mathfunc.h>
61 #include <sheet-object-graph.h>
62 #include <sheet-object-image.h>
63 #include <graph.h>
64 #include <gnm-so-filled.h>
65 #include <gnm-so-line.h>
66 #include <gnm-so-path.h>
67 #include <hlink.h>
70 #include <goffice/goffice.h>
72 #include <gsf/gsf-libxml.h>
73 #include <gsf/gsf-input.h>
74 #include <gsf/gsf-infile.h>
75 #include <gsf/gsf-infile-zip.h>
76 #include <gsf/gsf-opendoc-utils.h>
77 #include <gsf/gsf-doc-meta-data.h>
78 #include <gsf/gsf-output-stdio.h>
79 #include <gsf/gsf-utils.h>
80 #include <glib/gi18n-lib.h>
82 #include <string.h>
83 #include <errno.h>
85 GNM_PLUGIN_MODULE_HEADER;
87 #undef OO_DEBUG_OBJS
89 #define CXML2C(s) ((char const *)(s))
90 #define CC2XML(s) ((xmlChar const *)(s))
92 static inline gboolean
93 attr_eq (xmlChar const *a, char const *s)
95 return !strcmp (CXML2C (a), s);
98 enum {
99 OO_SYMBOL_TYPE_AUTO = 1,
100 OO_SYMBOL_TYPE_NONE = 2,
101 OO_SYMBOL_TYPE_NAMED = 3
104 enum {
105 OO_CHART_STYLE_PLOTAREA = 0,
106 OO_CHART_STYLE_SERIES = 1,
107 OO_CHART_STYLE_INHERITANCE = 2
110 enum {
111 OO_FILL_TYPE_UNKNOWN = 0,
112 OO_FILL_TYPE_SOLID,
113 OO_FILL_TYPE_HATCH,
114 OO_FILL_TYPE_GRADIENT,
115 OO_FILL_TYPE_BITMAP,
116 OO_FILL_TYPE_NONE
119 /* Filter Type */
120 typedef enum {
121 OOO_VER_UNKNOWN = -1,
122 OOO_VER_1 = 0,
123 OOO_VER_OPENDOC = 1
124 } OOVer;
125 static struct {
126 char const * const mime_type;
127 int version;
128 } const OOVersions[] = {
129 { "application/vnd.sun.xml.calc", OOO_VER_1 },
130 { "application/vnd.oasis.opendocument.spreadsheet", OOO_VER_OPENDOC },
131 { "application/vnd.oasis.opendocument.spreadsheet-template", OOO_VER_OPENDOC }
134 /* Formula Type */
135 typedef enum {
136 FORMULA_OPENFORMULA = 0,
137 FORMULA_OLD_OPENOFFICE,
138 FORMULA_MICROSOFT,
139 NUM_FORMULAE_SUPPORTED,
140 FORMULA_NOT_SUPPORTED
141 } OOFormula;
143 #define OD_BORDER_THIN 1
144 #define OD_BORDER_MEDIUM 2.5
145 #define OD_BORDER_THICK 5
149 typedef enum {
150 OO_PLOT_AREA,
151 OO_PLOT_BAR,
152 OO_PLOT_CIRCLE,
153 OO_PLOT_LINE,
154 OO_PLOT_RADAR,
155 OO_PLOT_RADARAREA,
156 OO_PLOT_RING,
157 OO_PLOT_SCATTER,
158 OO_PLOT_STOCK,
159 OO_PLOT_CONTOUR,
160 OO_PLOT_BUBBLE,
161 OO_PLOT_GANTT,
162 OO_PLOT_POLAR,
163 OO_PLOT_SCATTER_COLOUR,
164 OO_PLOT_XYZ_SURFACE,
165 OO_PLOT_SURFACE,
166 OO_PLOT_XL_SURFACE,
167 OO_PLOT_XL_CONTOUR,
168 OO_PLOT_BOX,
169 OO_PLOT_UNKNOWN
170 } OOPlotType;
172 typedef enum {
173 OO_STYLE_UNKNOWN,
174 OO_STYLE_CELL,
175 OO_STYLE_COL,
176 OO_STYLE_ROW,
177 OO_STYLE_SHEET,
178 OO_STYLE_GRAPHICS,
179 OO_STYLE_CHART,
180 OO_STYLE_PARAGRAPH,
181 OO_STYLE_TEXT
182 } OOStyleType;
184 typedef struct {
185 GValue value;
186 gchar const *name;
187 } OOProp;
189 typedef struct {
190 GType t;
191 gboolean horizontal;
192 int min;
193 int max;
194 int step;
195 int page_step;
196 char *value;
197 char *value_type;
198 char *current_state;
199 char *linked_cell;
200 char *label;
201 char *implementation;
202 char *source_cell_range;
203 gboolean as_index;
204 } OOControl;
206 typedef struct {
207 char *view_box;
208 char * d;
209 GOArrow *arrow;
210 } OOMarker;
212 typedef struct {
213 gboolean grid; /* graph has grid? */
214 gboolean src_in_rows; /* orientation of graph data: rows or columns */
215 GSList *axis_props; /* axis properties */
216 GSList *plot_props; /* plot properties */
217 GSList *style_props; /* any other properties */
218 GSList *other_props; /* any other properties */
219 GOFormat *fmt;
220 } OOChartStyle;
222 typedef struct {
223 int ref;
224 GnmStyle *style;
225 GSList *styles;
226 GSList *conditions;
227 GSList *bases;
228 } OOCellStyle;
230 typedef struct {
231 GogGraph *graph;
232 GogChart *chart;
233 SheetObject *so;
234 GSList *list; /* used by Stock plot and textbox*/
235 char *name;
237 /* set in plot-area */
238 GogPlot *plot;
239 Sheet *src_sheet;
240 GnmRange src_range;
241 gboolean src_in_rows;
242 int src_n_vectors;
243 GnmRange src_abscissa;
244 gboolean src_abscissa_set;
245 GnmRange src_label;
246 gboolean src_label_set;
248 GogSeries *series;
249 unsigned series_count; /* reset for each plotarea */
250 unsigned domain_count; /* reset for each series */
251 unsigned data_pt_count; /* reset for each series */
252 unsigned x_axis_count; /* reset for each plotarea */
253 unsigned y_axis_count; /* reset for each plotarea */
254 unsigned z_axis_count; /* reset for each plotarea */
256 GogObject *axis;
257 xmlChar *cat_expr;
258 GogObject *regression;
259 GogObject *legend;
261 GnmExprTop const *title_expr;
262 gchar *title_style;
263 gchar *title_position;
264 gboolean title_manual_pos;
265 gchar *title_anchor;
266 double title_x;
267 double title_y;
269 OOChartStyle *cur_graph_style; /* for reading of styles */
271 GSList *saved_graph_styles;
272 GSList *saved_hatches;
273 GSList *saved_dash_styles;
274 GSList *saved_fill_image_styles;
275 GSList *saved_gradient_styles;
277 GHashTable *named_axes;
278 GHashTable *graph_styles;
279 GHashTable *hatches;
280 GHashTable *dash_styles;
281 GHashTable *fill_image_styles;
282 GHashTable *gradient_styles;
283 GHashTable *arrow_markers;
285 OOChartStyle *i_plot_styles[OO_CHART_STYLE_INHERITANCE];
286 /* currently active styles at plot-area, */
287 /* series level*/
288 OOPlotType plot_type;
289 OOPlotType plot_type_default;
290 SheetObjectAnchor anchor; /* anchor to draw the frame (images or graphs) */
291 double frame_offset[4]; /* offset as given in the file */
292 gnm_float width; /* This refers to the ODF frame element */
293 gnm_float height; /* This refers to the ODF frame element */
294 gint z_index;
296 /* Plot Area */
297 gnm_float plot_area_x;
298 gnm_float plot_area_y;
299 gnm_float plot_area_width;
300 gnm_float plot_area_height;
302 /* Legend */
303 gnm_float legend_x;
304 gnm_float legend_y;
305 GogObjectPosition legend_flag;
307 /* Custom Shape */
308 char *cs_type;
309 char *cs_enhanced_path;
310 char *cs_modifiers;
311 char *cs_viewbox;
312 GHashTable *cs_variables;
313 } OOChartInfo;
315 typedef enum {
316 OO_PAGE_BREAK_NONE,
317 OO_PAGE_BREAK_AUTO,
318 OO_PAGE_BREAK_MANUAL
319 } OOPageBreakType;
320 typedef struct {
321 gnm_float size_pts;
322 int count;
323 gboolean manual;
324 OOPageBreakType break_before, break_after;
325 } OOColRowStyle;
326 typedef struct {
327 GnmSheetVisibility visibility;
328 gboolean is_rtl;
329 gboolean tab_color_set;
330 GOColor tab_color;
331 gboolean tab_text_color_set;
332 GOColor tab_text_color;
333 gboolean display_formulas;
334 gboolean hide_col_header;
335 gboolean hide_row_header;
336 char *master_page_name;
337 } OOSheetStyle;
339 typedef struct {
340 GHashTable *settings;
341 GSList *stack;
342 GType type;
343 char *config_item_name;
344 } OOSettings;
346 typedef enum {
347 ODF_ELAPSED_SET_SECONDS = 1 << 0,
348 ODF_ELAPSED_SET_MINUTES = 1 << 1,
349 ODF_ELAPSED_SET_HOURS = 1 << 2
350 } odf_elapsed_set_t;
352 typedef struct {
353 SheetObject *so;
354 double frame_offset[4];
355 gboolean absolute_distance;
356 gint z_index;
357 gchar *control;
358 } object_offset_t;
360 typedef struct _OOParseState OOParseState;
362 typedef struct {
363 gboolean permanent;
364 gboolean p_seen;
365 guint offset;
366 GSList *span_style_stack;
367 GSList *span_style_list;
368 gboolean content_is_simple;
369 GString *gstr;
370 PangoAttrList *attrs;
371 } oo_text_p_t;
373 /* odf_validation <table:name> <val1> */
374 /* odf_validation <table:condition> <of:cell-content-is-in-list("1";"2";"3")> */
375 /* odf_validation <table:display-list> <unsorted> */
376 /* odf_validation <table:base-cell-address> <Tabelle1.A1> */
377 typedef struct {
378 char *condition;
379 char *base_cell_address;
380 gboolean allow_blank;
381 gboolean use_dropdown;
382 OOFormula f_type;
383 ValidationStyle style;
384 gchar *title;
385 gchar *help_title;
386 GString *message;
387 GString *help_message;
388 } odf_validation_t;
390 struct _OOParseState {
391 GOIOContext *context; /* The IOcontext managing things */
392 WorkbookView *wb_view; /* View for the new workbook */
393 OOVer ver; /* Its an OOo v1.0 or v2.0? */
394 gnm_float ver_odf; /* specific ODF version */
395 GsfInfile *zip; /* Reference to the open file, to load graphs and images*/
396 OOChartInfo chart;
397 GSList *chart_list; /* object_offset_t */
398 GnmParsePos pos;
399 int table_n;
400 GnmCellPos extent_data;
401 GnmComment *cell_comment;
402 GnmCell *curr_cell;
403 GnmExprSharer *sharer;
405 int col_inc, row_inc;
406 gboolean content_is_error;
408 GSList *text_p_stack;
409 oo_text_p_t text_p_for_cell;
411 GHashTable *formats;
412 GHashTable *controls;
413 GHashTable *validations;
414 GHashTable *strings;
416 odf_validation_t *cur_validation;
418 struct {
419 GHashTable *cell;
420 GHashTable *cell_datetime;
421 GHashTable *cell_date;
422 GHashTable *cell_time;
423 GHashTable *col;
424 GHashTable *row;
425 GHashTable *sheet;
426 GHashTable *master_pages;
427 GHashTable *page_layouts;
428 GHashTable *text;
429 } styles;
430 struct {
431 OOCellStyle *cells;
432 PangoAttrList *text;
433 OOColRowStyle *col_rows;
434 OOSheetStyle *sheets;
435 gboolean requires_disposal;
436 OOStyleType type;
437 } cur_style;
439 gint h_align_is_valid; /* 0: not set; 1: fix; 2: value-type*/
440 gboolean repeat_content;
441 int text_align, gnm_halign;
443 struct {
444 OOCellStyle *cells;
445 OOColRowStyle *rows;
446 OOColRowStyle *columns;
447 OOChartStyle *graphics;
448 } default_style;
449 GSList *sheet_order;
450 int richtext_len;
451 struct {
452 GString *accum;
453 guint offset;
454 gboolean string_opened;
455 char *name;
456 int magic;
457 gboolean truncate_hour_on_overflow;
458 int elapsed_set; /* using a sum of odf_elapsed_set_t */
459 guint pos_seconds;
460 guint pos_minutes;
461 gboolean percentage;
462 gboolean percent_sign_seen;
463 } cur_format;
464 GSList *conditions;
465 GSList *cond_formats;
466 GnmFilter *filter;
468 GnmConventions *convs[NUM_FORMULAE_SUPPORTED];
469 GHashTable *openformula_namemap;
470 GHashTable *openformula_handlermap;
472 struct {
473 struct {
474 GnmPageBreaks *h, *v;
475 } page_breaks;
477 GnmPrintInformation *cur_pi;
478 GnmPrintHF *cur_hf;
479 char **cur_hf_format;
480 int rep_rows_from;
481 int rep_rows_to;
482 int rep_cols_from;
483 int rep_cols_to;
484 } print;
486 char *object_name; /* also used for table during preparsing */
487 OOControl *cur_control;
489 OOSettings settings;
491 gsf_off_t last_progress_update;
492 char *last_error;
493 gboolean hd_ft_left_warned;
494 gboolean debug;
499 typedef struct {
500 int start;
501 int end;
502 char *style_name;
503 } span_style_info_t;
505 typedef struct {
506 GnmConventions base;
507 OOParseState *state;
508 GsfXMLIn *xin;
509 } ODFConventions;
511 typedef struct {
512 GOColor from;
513 GOColor to;
514 gnm_float brightness;
515 unsigned int dir;
516 } gradient_info_t;
518 typedef struct {
519 Sheet *sheet;
520 int cols;
521 int rows;
522 } sheet_order_t;
524 typedef struct {
525 char const * const name;
526 int val;
527 } OOEnum;
529 static OOEnum const odf_chart_classes[] = {
530 { "chart:area", OO_PLOT_AREA },
531 { "chart:bar", OO_PLOT_BAR },
532 { "chart:circle", OO_PLOT_CIRCLE },
533 { "chart:line", OO_PLOT_LINE },
534 { "chart:radar", OO_PLOT_RADAR },
535 { "chart:filled-radar", OO_PLOT_RADARAREA },
536 { "chart:ring", OO_PLOT_RING },
537 { "chart:scatter", OO_PLOT_SCATTER },
538 { "chart:stock", OO_PLOT_STOCK },
539 { "chart:bubble", OO_PLOT_BUBBLE },
540 { "chart:gantt", OO_PLOT_GANTT },
541 { "chart:surface", OO_PLOT_CONTOUR },
542 { "gnm:polar", OO_PLOT_POLAR },
543 { "gnm:xyz-surface", OO_PLOT_XYZ_SURFACE },
544 { "gnm:scatter-color", OO_PLOT_SCATTER_COLOUR },
545 { "gnm:box", OO_PLOT_BOX },
546 { "gnm:none", OO_PLOT_UNKNOWN },
547 { NULL, 0 },
550 /* Some prototypes */
551 static GsfXMLInNode const * get_dtd (void);
552 static GsfXMLInNode const * get_styles_dtd (void);
553 static void oo_chart_style_free (OOChartStyle *pointer);
554 static OOFormula odf_get_formula_type (GsfXMLIn *xin, char const **str);
555 static char const *odf_strunescape (char const *string, GString *target,
556 G_GNUC_UNUSED GnmConventions const *convs);
557 static void odf_sheet_suggest_size (GsfXMLIn *xin, int *cols, int *rows);
558 static void oo_prop_list_has (GSList *props, gboolean *threed, char const *tag);
561 /* Implementations */
563 static void
564 odf_go_string_append_c_n (GString *target, char c, int n)
566 if (n > 0)
567 go_string_append_c_n (target, c, (gsize) n);
570 static void
571 maybe_update_progress (GsfXMLIn *xin)
573 OOParseState *state = (OOParseState *)xin->user_state;
574 GsfInput *input = gsf_xml_in_get_input (xin);
575 gsf_off_t pos = gsf_input_tell (input);
577 if (pos >= state->last_progress_update + 10000) {
578 go_io_value_progress_update (state->context, pos);
579 state->last_progress_update = pos;
583 static GOErrorInfo *oo_go_error_info_new_vprintf (GOSeverity severity,
584 char const *msg_format, ...)
585 G_GNUC_PRINTF (2, 3);
587 static GOErrorInfo *
588 oo_go_error_info_new_vprintf (GOSeverity severity,
589 char const *msg_format, ...)
591 va_list args;
592 GOErrorInfo *ei;
594 va_start (args, msg_format);
595 ei = go_error_info_new_vprintf (severity, msg_format, args);
596 va_end (args);
598 return ei;
601 static gboolean oo_warning (GsfXMLIn *xin, char const *fmt, ...)
602 G_GNUC_PRINTF (2, 3);
604 static gboolean
605 oo_warning (GsfXMLIn *xin, char const *fmt, ...)
607 OOParseState *state = (OOParseState *)xin->user_state;
608 char *msg;
609 char *detail;
610 va_list args;
612 va_start (args, fmt);
613 detail = g_strdup_vprintf (fmt, args);
614 va_end (args);
616 if (IS_SHEET (state->pos.sheet)) {
617 if (state->pos.eval.col >= 0 && state->pos.eval.row >= 0)
618 msg = g_strdup_printf ("%s!%s",
619 state->pos.sheet->name_quoted,
620 cellpos_as_string (&state->pos.eval));
621 else
622 msg = g_strdup(state->pos.sheet->name_quoted);
623 } else
624 msg = g_strdup (_("General ODF error"));
626 if (0 != go_str_compare (msg, state->last_error)) {
627 GOErrorInfo *ei = oo_go_error_info_new_vprintf
628 (GO_WARNING, "%s", msg);
630 go_io_error_info_set (state->context, ei);
631 g_free (state->last_error);
632 state->last_error = msg;
633 } else
634 g_free (msg);
636 go_error_info_add_details
637 (state->context->info->data,
638 oo_go_error_info_new_vprintf (GO_WARNING, "%s", detail));
640 g_free (detail);
642 return FALSE; /* convenience */
645 static gboolean
646 oo_attr_bool (GsfXMLIn *xin, xmlChar const * const *attrs,
647 int ns_id, char const *name, gboolean *res)
649 g_return_val_if_fail (attrs != NULL, FALSE);
650 g_return_val_if_fail (attrs[0] != NULL, FALSE);
651 g_return_val_if_fail (attrs[1] != NULL, FALSE);
653 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), ns_id, name))
654 return FALSE;
655 *res = (g_ascii_strcasecmp (CXML2C (attrs[1]), "false") &&
656 strcmp (CXML2C (attrs[1]), "0"));
658 return TRUE;
661 static gboolean
662 oo_attr_int (GsfXMLIn *xin, xmlChar const * const *attrs,
663 int ns_id, char const *name, int *res)
665 char *end;
666 long tmp;
668 g_return_val_if_fail (attrs != NULL, FALSE);
669 g_return_val_if_fail (attrs[0] != NULL, FALSE);
670 g_return_val_if_fail (attrs[1] != NULL, FALSE);
672 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), ns_id, name))
673 return FALSE;
675 errno = 0; /* strtol sets errno, but does not clear it. */
676 tmp = strtol (CXML2C (attrs[1]), &end, 10);
677 if (*end || errno != 0 || tmp < INT_MIN || tmp > INT_MAX)
678 return oo_warning (xin, _("Invalid integer '%s', for '%s'"),
679 attrs[1], name);
681 *res = tmp;
682 return TRUE;
685 static gboolean
686 oo_attr_int_range (GsfXMLIn *xin, xmlChar const * const *attrs,
687 int ns_id, char const *name, int *res, int min, int max)
689 int tmp;
690 if (!oo_attr_int (xin, attrs, ns_id, name, &tmp))
691 return FALSE;
692 if (tmp < min || tmp > max) {
693 oo_warning (xin, _("Possible corrupted integer '%s' for '%s'"),
694 attrs[1], name);
695 *res = (tmp < min) ? min : max;
696 return TRUE;
698 *res = tmp;
699 return TRUE;
703 static gboolean
704 oo_attr_font_weight (GsfXMLIn *xin, xmlChar const * const *attrs,
705 int *res)
707 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "font-weight"))
708 return FALSE;
709 if (attr_eq (attrs[1], "bold")) {
710 *res = PANGO_WEIGHT_BOLD;
711 return TRUE;
712 } else if (attr_eq (attrs[1], "normal")) {
713 *res = PANGO_WEIGHT_NORMAL;
714 return TRUE;
716 return oo_attr_int_range (xin, attrs, OO_NS_FO, "font-weight",
717 res, 0, 1000);
721 static gboolean
722 oo_attr_float (GsfXMLIn *xin, xmlChar const * const *attrs,
723 int ns_id, char const *name, gnm_float *res)
725 char *end;
726 gnm_float tmp;
728 g_return_val_if_fail (attrs != NULL, FALSE);
729 g_return_val_if_fail (attrs[0] != NULL, FALSE);
730 g_return_val_if_fail (attrs[1] != NULL, FALSE);
732 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), ns_id, name))
733 return FALSE;
735 tmp = gnm_strto (CXML2C (attrs[1]), &end);
736 if (*end)
737 return oo_warning (xin, _("Invalid attribute '%s', expected number, received '%s'"),
738 name, attrs[1]);
739 *res = tmp;
740 return TRUE;
743 static gboolean
744 oo_attr_percent (GsfXMLIn *xin, xmlChar const * const *attrs,
745 int ns_id, char const *name, gnm_float *res)
747 char *end;
748 gnm_float tmp;
749 const char *val = CXML2C (attrs[1]);
751 g_return_val_if_fail (attrs != NULL, FALSE);
752 g_return_val_if_fail (attrs[0] != NULL, FALSE);
753 g_return_val_if_fail (attrs[1] != NULL, FALSE);
755 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), ns_id, name))
756 return FALSE;
758 tmp = gnm_strto (val, &end);
759 if (end == val || *end != '%' || *(end + 1))
760 return oo_warning (xin,
761 _("Invalid attribute '%s', expected percentage,"
762 " received '%s'"),
763 name, val);
764 *res = tmp / 100;
765 return TRUE;
768 static GnmColor *magic_transparent;
770 static GnmColor *
771 oo_parse_color (GsfXMLIn *xin, xmlChar const *str, char const *name)
773 guint r, g, b;
775 g_return_val_if_fail (str != NULL, NULL);
777 if (3 == sscanf (CXML2C (str), "#%2x%2x%2x", &r, &g, &b))
778 return gnm_color_new_rgb8 (r, g, b);
780 if (0 == strcmp (CXML2C (str), "transparent"))
781 return style_color_ref (magic_transparent);
783 oo_warning (xin, _("Invalid attribute '%s', expected color, received '%s'"),
784 name, str);
785 return NULL;
787 static GnmColor *
788 oo_attr_color (GsfXMLIn *xin, xmlChar const * const *attrs,
789 int ns_id, char const *name)
791 g_return_val_if_fail (attrs != NULL, NULL);
792 g_return_val_if_fail (attrs[0] != NULL, NULL);
794 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), ns_id, name))
795 return NULL;
796 return oo_parse_color (xin, attrs[1], name);
799 static GOLineDashType
800 odf_match_dash_type (OOParseState *state, gchar const *dash_style)
802 GOLineDashType t = go_line_dash_from_str (dash_style);
803 if (t == GO_LINE_NONE) {
804 gpointer res = g_hash_table_lookup
805 (state->chart.dash_styles, dash_style);
806 if (res != NULL)
807 t = GPOINTER_TO_UINT(res);
809 return ((t == GO_LINE_NONE)? GO_LINE_DOT : t );
812 static char const *
813 odf_string_id (OOParseState *state, char const *string)
815 char *id = g_strdup_printf ("str%i", g_hash_table_size (state->strings));
816 g_hash_table_insert (state->strings, id, g_strdup (string));
817 return id;
820 static void
821 odf_apply_style_props (GsfXMLIn *xin, GSList *props, GOStyle *style, gboolean in_chart_context)
823 OOParseState *state = (OOParseState *)xin->user_state;
824 PangoFontDescription *desc;
825 GSList *l;
826 gboolean desc_changed = FALSE;
827 char const *hatch_name = NULL;
828 char const *gradient_name = NULL;
829 char const *fill_image_name = NULL;
830 unsigned int gnm_hatch = 0;
831 int symbol_type = -1, symbol_name = GO_MARKER_DIAMOND;
832 double symbol_height = -1., symbol_width = -1.,
833 stroke_width = -1., gnm_stroke_width = -1.;
834 GOMarker *m;
835 gboolean line_is_not_dash = FALSE;
836 unsigned int fill_type = OO_FILL_TYPE_UNKNOWN;
837 gboolean stroke_colour_set = FALSE;
838 gboolean lines_value = !in_chart_context;
839 gboolean gnm_auto_color_value_set = FALSE;
840 gboolean gnm_auto_color_value = FALSE;
841 gboolean gnm_auto_marker_outline_color_value_set = FALSE;
842 gboolean gnm_auto_marker_outline_color_value = FALSE;
843 gboolean gnm_auto_marker_fill_color_value_set = FALSE;
844 gboolean gnm_auto_marker_fill_color_value = FALSE;
845 gboolean gnm_auto_dash_set = FALSE;
846 gboolean gnm_auto_width_set = FALSE;
847 char const *stroke_dash = NULL;
848 char const *marker_outline_colour = NULL;
849 char const *marker_fill_colour = NULL;
850 gboolean gnm_auto_font_set = FALSE;
851 gboolean gnm_auto_font = FALSE;
852 gboolean gnm_foreground_solid = FALSE;
854 oo_prop_list_has (props, &gnm_foreground_solid, "gnm-foreground-solid");
855 style->line.auto_dash = TRUE;
857 desc = pango_font_description_copy (style->font.font->desc);
858 for (l = props; l != NULL; l = l->next) {
859 OOProp *prop = l->data;
860 if (0 == strcmp (prop->name, "fill")) {
861 char const *val_string = g_value_get_string (&prop->value);
862 if (0 == strcmp (val_string, "solid")) {
863 style->fill.type = GO_STYLE_FILL_PATTERN;
864 style->fill.auto_type = FALSE;
865 style->fill.pattern.pattern = (gnm_foreground_solid) ?
866 GO_PATTERN_FOREGROUND_SOLID : GO_PATTERN_SOLID;
867 fill_type = OO_FILL_TYPE_SOLID;
868 } else if (0 == strcmp (val_string, "hatch")) {
869 style->fill.type = GO_STYLE_FILL_PATTERN;
870 style->fill.auto_type = FALSE;
871 fill_type = OO_FILL_TYPE_HATCH;
872 } else if (0 == strcmp (val_string, "gradient")) {
873 style->fill.type = GO_STYLE_FILL_GRADIENT;
874 style->fill.auto_type = FALSE;
875 fill_type = OO_FILL_TYPE_GRADIENT;
876 } else if (0 == strcmp (val_string, "bitmap")) {
877 style->fill.type = GO_STYLE_FILL_IMAGE;
878 style->fill.auto_type = FALSE;
879 fill_type = OO_FILL_TYPE_BITMAP;
880 } else { /* "none" */
881 style->fill.type = GO_STYLE_FILL_NONE;
882 style->fill.auto_type = FALSE;
883 fill_type = OO_FILL_TYPE_NONE;
885 } else if (0 == strcmp (prop->name, "fill-color")) {
886 GdkRGBA rgba;
887 gchar const *color = g_value_get_string (&prop->value);
888 if (gdk_rgba_parse (&rgba, color)) {
889 guint a;
890 if (gnm_foreground_solid) {
891 a = GO_COLOR_UINT_A (style->fill.pattern.fore);
892 go_color_from_gdk_rgba (&rgba, &style->fill.pattern.fore);
893 style->fill.auto_fore = FALSE;
894 style->fill.pattern.fore = GO_COLOR_CHANGE_A (style->fill.pattern.fore, a);
895 } else {
896 a = GO_COLOR_UINT_A (style->fill.pattern.back);
897 go_color_from_gdk_rgba (&rgba, &style->fill.pattern.back);
898 style->fill.auto_back = FALSE;
899 style->fill.pattern.back = GO_COLOR_CHANGE_A (style->fill.pattern.back, a);
902 }else if (0 == strcmp (prop->name, "opacity")) {
903 guint a = 255 * g_value_get_double (&prop->value);
904 style->fill.pattern.back = GO_COLOR_CHANGE_A (style->fill.pattern.back, a);
905 } else if (0 == strcmp (prop->name, "stroke-color")) {
906 GdkRGBA rgba;
907 gchar const *color = g_value_get_string (&prop->value);
908 if (gdk_rgba_parse (&rgba, color)) {
909 style->line.fore = go_color_from_gdk_rgba (&rgba, &style->line.color);
910 style->line.auto_color = FALSE;
911 style->line.auto_fore = FALSE;
912 style->line.pattern = GO_PATTERN_SOLID;
913 stroke_colour_set = TRUE;
915 } else if (0 == strcmp (prop->name, "marker-outline-colour")) {
916 marker_outline_colour = g_value_get_string (&prop->value);
917 } else if (0 == strcmp (prop->name, "marker-fill-colour")) {
918 marker_fill_colour = g_value_get_string (&prop->value);
919 } else if (0 == strcmp (prop->name, "lines")) {
920 lines_value = g_value_get_boolean (&prop->value);
921 } else if (0 == strcmp (prop->name, "gnm-auto-color")) {
922 gnm_auto_color_value_set = TRUE;
923 gnm_auto_color_value = g_value_get_boolean (&prop->value);
924 } else if (0 == strcmp (prop->name, "gnm-auto-marker-outline-colour")) {
925 gnm_auto_marker_outline_color_value_set = TRUE;
926 gnm_auto_marker_outline_color_value = g_value_get_boolean (&prop->value);
927 } else if (0 == strcmp (prop->name, "gnm-auto-marker-fill-colour")) {
928 gnm_auto_marker_fill_color_value_set = TRUE;
929 gnm_auto_marker_fill_color_value = g_value_get_boolean (&prop->value);
930 } else if (0 == strcmp (prop->name, "color")) {
931 GdkRGBA rgba;
932 gchar const *color = g_value_get_string (&prop->value);
933 if (gdk_rgba_parse (&rgba, color)) {
934 go_color_from_gdk_rgba (&rgba, &style->font.color);
935 style->font.auto_color = FALSE;
937 } else if (0 == strcmp (prop->name, "gnm-auto-dash")) {
938 gnm_auto_dash_set = TRUE;
939 style->line.auto_dash = g_value_get_boolean (&prop->value);
940 } else if (0 == strcmp (prop->name, "fill-gradient-name"))
941 gradient_name = g_value_get_string (&prop->value);
942 else if (0 == strcmp (prop->name, "fill-hatch-name"))
943 hatch_name = g_value_get_string (&prop->value);
944 else if (0 == strcmp (prop->name, "fill-image-name"))
945 fill_image_name = g_value_get_string (&prop->value);
946 else if (0 == strcmp (prop->name, "gnm-pattern"))
947 gnm_hatch = g_value_get_int (&prop->value);
948 else if (0 == strcmp (prop->name, "text-rotation-angle")) {
949 int angle = g_value_get_int (&prop->value);
950 if (angle > 180)
951 angle -= 360;
952 go_style_set_text_angle (style, angle);
953 } else if (0 == strcmp (prop->name, "font-size")) {
954 pango_font_description_set_size
955 (desc, PANGO_SCALE * g_value_get_double
956 (&prop->value));
957 desc_changed = TRUE;
958 } else if (0 == strcmp (prop->name, "font-weight")) {
959 pango_font_description_set_weight
960 (desc, g_value_get_int (&prop->value));
961 desc_changed = TRUE;
962 } else if (0 == strcmp (prop->name, "font-variant")) {
963 pango_font_description_set_variant
964 (desc, g_value_get_int (&prop->value));
965 desc_changed = TRUE;
966 } else if (0 == strcmp (prop->name, "font-style")) {
967 pango_font_description_set_style
968 (desc, g_value_get_int (&prop->value));
969 desc_changed = TRUE;
970 } else if (0 == strcmp (prop->name, "font-stretch-pango")) {
971 pango_font_description_set_stretch
972 (desc, g_value_get_int (&prop->value));
973 desc_changed = TRUE;
974 } else if (0 == strcmp (prop->name, "font-gravity-pango")) {
975 pango_font_description_set_gravity
976 (desc, g_value_get_int (&prop->value));
977 desc_changed = TRUE;
978 } else if (0 == strcmp (prop->name, "font-family")) {
979 pango_font_description_set_family
980 (desc, g_value_get_string (&prop->value));
981 desc_changed = TRUE;
982 } else if (0 == strcmp (prop->name, "stroke")) {
983 if (0 == strcmp (g_value_get_string (&prop->value), "solid")) {
984 style->line.dash_type = GO_LINE_SOLID;
985 if (!gnm_auto_dash_set)
986 style->line.auto_dash = FALSE;
987 line_is_not_dash = TRUE;
988 } else if (0 == strcmp (g_value_get_string (&prop->value), "dash")) {
989 if (!gnm_auto_dash_set)
990 style->line.auto_dash = FALSE;
991 line_is_not_dash = FALSE;
992 } else {
993 style->line.dash_type = GO_LINE_NONE;
994 if (!gnm_auto_dash_set)
995 style->line.auto_dash = FALSE;
996 line_is_not_dash = TRUE;
998 } else if (0 == strcmp (prop->name, "stroke-dash"))
999 stroke_dash = g_value_get_string (&prop->value);
1000 else if (0 == strcmp (prop->name, "symbol-type"))
1001 symbol_type = g_value_get_int (&prop->value);
1002 else if (0 == strcmp (prop->name, "symbol-name"))
1003 symbol_name = g_value_get_int (&prop->value);
1004 else if (0 == strcmp (prop->name, "symbol-height"))
1005 symbol_height = g_value_get_double (&prop->value);
1006 else if (0 == strcmp (prop->name, "symbol-width"))
1007 symbol_width = g_value_get_double (&prop->value);
1008 else if (0 == strcmp (prop->name, "stroke-width"))
1009 stroke_width = g_value_get_double (&prop->value);
1010 else if (0 == strcmp (prop->name, "gnm-stroke-width"))
1011 gnm_stroke_width = g_value_get_double (&prop->value);
1012 else if (0 == strcmp (prop->name, "gnm-auto-width")) {
1013 gnm_auto_width_set = TRUE;
1014 style->line.auto_width = g_value_get_boolean (&prop->value);
1015 } else if (0 == strcmp (prop->name, "repeat"))
1016 style->fill.image.type = g_value_get_int (&prop->value);
1017 else if (0 == strcmp (prop->name, "gnm-auto-type"))
1018 style->fill.auto_type = g_value_get_boolean (&prop->value);
1019 else if (0 == strcmp (prop->name, "gnm-auto-font")) {
1020 gnm_auto_font_set = TRUE;
1021 gnm_auto_font = g_value_get_boolean (&prop->value);
1025 if (desc_changed)
1026 go_style_set_font_desc (style, desc);
1027 else
1028 pango_font_description_free (desc);
1029 style->font.auto_font = gnm_auto_font_set ? gnm_auto_font : !desc_changed;
1032 * Stroke colour is tricky: if we have lines, that is what it
1033 * refers to. Otherwise it refers to markers.
1035 if (!gnm_auto_color_value_set)
1036 gnm_auto_color_value = !stroke_colour_set;
1038 style->line.auto_color = (lines_value ? gnm_auto_color_value : TRUE);
1040 if (gnm_stroke_width >= 0)
1041 style->line.width = gnm_stroke_width;
1042 else if (stroke_width == 0.) {
1043 style->line.width = 0.;
1044 style->line.dash_type = GO_LINE_NONE;
1045 } else if (stroke_width > 0)
1046 style->line.width = stroke_width;
1047 else
1048 style->line.width = 0;
1049 if (!gnm_auto_width_set)
1050 style->line.auto_width = FALSE;
1052 if (stroke_dash != NULL && !line_is_not_dash)
1053 style->line.dash_type = odf_match_dash_type (state, stroke_dash);
1055 switch (fill_type) {
1056 case OO_FILL_TYPE_HATCH:
1057 if (hatch_name != NULL) {
1058 GOPattern *pat = g_hash_table_lookup
1059 (state->chart.hatches, hatch_name);
1060 if (pat == NULL)
1061 oo_warning (xin, _("Unknown hatch name \'%s\'"
1062 " encountered!"), hatch_name);
1063 else {
1064 style->fill.pattern.fore = pat->fore;
1065 style->fill.auto_fore = gnm_auto_color_value_set && gnm_auto_color_value;
1066 style->fill.pattern.pattern = (gnm_hatch > 0) ?
1067 gnm_hatch : pat->pattern;
1069 } else oo_warning (xin, _("Hatch fill without hatch name "
1070 "encountered!"));
1071 break;
1072 case OO_FILL_TYPE_GRADIENT:
1073 if (gradient_name != NULL) {
1074 gradient_info_t *info = g_hash_table_lookup
1075 (state->chart.gradient_styles, gradient_name);
1076 if (info == NULL)
1077 oo_warning (xin, _("Unknown gradient name \'%s\'"
1078 " encountered!"), gradient_name);
1079 else {
1080 style->fill.auto_fore = FALSE;
1081 style->fill.auto_back = FALSE;
1082 style->fill.pattern.back = info->from;
1083 style->fill.pattern.fore = info->to;
1084 style->fill.gradient.dir = info->dir;
1085 style->fill.gradient.brightness = -1.0;
1086 if (info->brightness >= 0)
1087 go_style_set_fill_brightness
1088 (style, info->brightness);
1090 } else oo_warning (xin, _("Gradient fill without gradient "
1091 "name encountered!"));
1092 break;
1093 case OO_FILL_TYPE_BITMAP:
1094 if (fill_image_name != NULL) {
1095 char const *href = g_hash_table_lookup
1096 (state->chart.fill_image_styles, fill_image_name);
1097 if (href == NULL)
1098 oo_warning (xin, _("Unknown image fill name \'%s\'"
1099 " encountered!"), fill_image_name);
1100 else {
1101 GsfInput *input;
1102 char *href_complete;
1103 char **path;
1105 if (strncmp (href, "./", 2) == 0)
1106 href += 2;
1107 if (strncmp (href, "/", 1) == 0) {
1108 oo_warning (xin, _("Invalid absolute file "
1109 "specification \'%s\' "
1110 "encountered."), href);
1111 break;
1114 href_complete = g_strconcat (state->object_name,
1115 "/", href, NULL);
1116 path = g_strsplit (href_complete, "/", -1);
1117 input = gsf_infile_child_by_aname
1118 (state->zip, (const char **) path);
1119 g_strfreev (path);
1120 if (input == NULL)
1121 oo_warning (xin, _("Unable to open \'%s\'."),
1122 href_complete);
1123 else {
1124 gsf_off_t len = gsf_input_size (input);
1125 guint8 const *data = gsf_input_read (input, len, NULL);
1126 GOImage *image = go_image_new_from_data (NULL, data, len, NULL, NULL);
1127 if (image) {
1128 g_clear_object (&style->fill.image.image);
1129 style->fill.image.image = image;
1130 } else {
1131 oo_warning (xin, _("Unable to load "
1132 "the file \'%s\'."),
1133 href_complete);
1136 g_object_unref (input);
1138 g_free (href_complete);
1140 } else oo_warning (xin, _("Image fill without image "
1141 "name encountered!"));
1142 break;
1143 default:
1144 break;
1147 switch (symbol_type) {
1148 case OO_SYMBOL_TYPE_AUTO:
1149 m = go_marker_new ();
1150 style->marker.auto_shape = TRUE;
1151 break;
1152 case OO_SYMBOL_TYPE_NONE:
1153 style->marker.auto_shape = FALSE;
1154 m = go_marker_new ();
1155 go_marker_set_shape (m, GO_MARKER_NONE);
1156 break;
1157 case OO_SYMBOL_TYPE_NAMED:
1158 style->marker.auto_shape = FALSE;
1159 m = go_marker_new ();
1160 go_marker_set_shape (m, symbol_name);
1161 break;
1162 default:
1163 m = NULL;
1164 break;
1166 if (m) {
1167 gboolean dshm;
1169 if (symbol_type != OO_SYMBOL_TYPE_NONE) {
1170 /* Inherit line colour. */
1171 go_marker_set_fill_color (m, style->line.color);
1172 if (marker_fill_colour != NULL) {
1173 GOColor color;
1174 GdkRGBA rgba;
1175 if (gdk_rgba_parse (&rgba, marker_fill_colour)) {
1176 go_color_from_gdk_rgba (&rgba, &color);
1177 go_marker_set_fill_color (m, color);
1180 go_marker_set_fill_color (m, style->line.color);
1181 style->marker.auto_fill_color = gnm_auto_marker_fill_color_value_set ?
1182 gnm_auto_marker_fill_color_value : gnm_auto_color_value;
1183 if (marker_outline_colour == NULL)
1184 go_marker_set_outline_color (m, style->line.color);
1185 else {
1186 GOColor color;
1187 GdkRGBA rgba;
1188 if (gdk_rgba_parse (&rgba, marker_outline_colour)) {
1189 go_color_from_gdk_rgba (&rgba, &color);
1190 go_marker_set_outline_color (m, color);
1191 } else
1192 go_marker_set_outline_color (m, style->line.color);
1194 style->marker.auto_outline_color = gnm_auto_marker_outline_color_value_set ?
1195 gnm_auto_marker_outline_color_value : gnm_auto_color_value;
1198 if (symbol_height >= 0. || symbol_width >= 0.) {
1199 double size;
1200 /* If we have only one dimension, use that for the other */
1201 if (symbol_width < 0) symbol_width = symbol_height;
1202 if (symbol_height < 0) symbol_height = symbol_width;
1204 size = (symbol_height + symbol_width + 1) / 2;
1205 size = MIN (size, G_MAXINT);
1207 go_marker_set_size (m, (int)size);
1210 if (gnm_object_has_readable_prop (state->chart.plot,
1211 "default-style-has-markers",
1212 G_TYPE_BOOLEAN,
1213 &dshm) &&
1214 !dshm &&
1215 go_marker_get_shape (m) == GO_MARKER_NONE) {
1216 style->marker.auto_shape = TRUE;
1219 go_style_set_marker (style, m);
1223 /* returns pts */
1224 static char const *
1225 oo_parse_spec_distance (char const *str, gnm_float *pts)
1227 double num;
1228 char *end = NULL;
1230 num = go_strtod (str, &end);
1231 if (CXML2C (str) != end) {
1232 if (0 == strncmp (end, "mm", 2)) {
1233 num = GO_CM_TO_PT (num/10.);
1234 end += 2;
1235 } else if (0 == strncmp (end, "m", 1)) {
1236 num = GO_CM_TO_PT (num*100.);
1237 end ++;
1238 } else if (0 == strncmp (end, "km", 2)) {
1239 num = GO_CM_TO_PT (num*100000.);
1240 end += 2;
1241 } else if (0 == strncmp (end, "cm", 2)) {
1242 num = GO_CM_TO_PT (num);
1243 end += 2;
1244 } else if (0 == strncmp (end, "pt", 2)) {
1245 end += 2;
1246 } else if (0 == strncmp (end, "pc", 2)) { /* pica 12pt == 1 pica */
1247 num /= 12.;
1248 end += 2;
1249 } else if (0 == strncmp (end, "ft", 2)) {
1250 num = GO_IN_TO_PT (num*12.);
1251 end += 2;
1252 } else if (0 == strncmp (end, "mi", 2)) {
1253 num = GO_IN_TO_PT (num*63360.);
1254 end += 2;
1255 } else if (0 == strncmp (end, "inch", 4)) {
1256 num = GO_IN_TO_PT (num);
1257 end += 4;
1258 } else if (0 == strncmp (end, "in", 2)) {
1259 num = GO_IN_TO_PT (num);
1260 end += 2;
1261 } else
1262 return GINT_TO_POINTER(1);
1263 } else return NULL;
1265 *pts = num;
1266 return end;
1270 /* returns pts */
1271 static char const *
1272 oo_parse_distance (GsfXMLIn *xin, xmlChar const *str,
1273 char const *name, gnm_float *pts)
1275 char const *end = NULL;
1277 g_return_val_if_fail (str != NULL, NULL);
1279 if (0 == strncmp (CXML2C (str), "none", 4)) {
1280 *pts = 0;
1281 return CXML2C (str) + 4;
1284 end = oo_parse_spec_distance (CXML2C (str), pts);
1286 if (end == GINT_TO_POINTER(1)) {
1287 oo_warning (xin, _("Invalid attribute '%s', unknown unit '%s'"),
1288 name, str);
1289 return NULL;
1291 if (end == NULL) {
1292 oo_warning (xin, _("Invalid attribute '%s', expected distance, received '%s'"),
1293 name, str);
1294 return NULL;
1296 return end;
1299 /* returns pts */
1300 static char const *
1301 oo_attr_distance (GsfXMLIn *xin, xmlChar const * const *attrs,
1302 int ns_id, char const *name, gnm_float *pts)
1304 g_return_val_if_fail (attrs != NULL, NULL);
1305 g_return_val_if_fail (attrs[0] != NULL, NULL);
1306 g_return_val_if_fail (attrs[1] != NULL, NULL);
1308 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), ns_id, name))
1309 return NULL;
1310 return oo_parse_distance (xin, attrs[1], name, pts);
1313 static gboolean
1314 oo_attr_percent_or_distance (GsfXMLIn *xin, xmlChar const * const *attrs,
1315 int ns_id, char const *name, gnm_float *res,
1316 gboolean *found_percent)
1318 char *end;
1319 gnm_float tmp;
1321 g_return_val_if_fail (attrs != NULL, FALSE);
1322 g_return_val_if_fail (attrs[0] != NULL, FALSE);
1323 g_return_val_if_fail (attrs[1] != NULL, FALSE);
1324 g_return_val_if_fail (res != NULL, FALSE);
1325 g_return_val_if_fail (found_percent != NULL, FALSE);
1327 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), ns_id, name))
1328 return FALSE;
1330 tmp = gnm_strto (CXML2C (attrs[1]), &end);
1331 if (*end != '%' || *(end + 1)) {
1332 *found_percent = FALSE;
1333 return (NULL != oo_parse_distance (xin, attrs[1], name, res));
1335 *res = tmp / 100;
1336 *found_percent = TRUE;
1337 return TRUE;
1340 static char const *
1341 oo_parse_angle (GsfXMLIn *xin, xmlChar const *str,
1342 char const *name, int *angle)
1344 gnm_float num;
1345 char *end = NULL;
1347 g_return_val_if_fail (str != NULL, NULL);
1349 num = gnm_strto (CXML2C (str), &end);
1350 if (CXML2C (str) != end) {
1351 if (*end == '\0') {
1352 num = gnm_fmod (num, 360);
1353 } else if (0 == strncmp (end, "deg", 3)) {
1354 num = gnm_fmod (num, 360);
1355 end += 3;
1356 } else if (0 == strncmp (end, "grad", 4)) {
1357 num = gnm_fmod (num, 400);
1358 num = num * 10. / 9.;
1359 end += 4;
1360 } else if (0 == strncmp (end, "rad", 3)) {
1361 num = gnm_fmod (num, 2 * M_PIgnum);
1362 num = num * 180. / M_PIgnum;
1363 end += 3;
1364 } else {
1365 oo_warning (xin, _("Invalid attribute '%s', unknown unit '%s'"),
1366 name, str);
1367 return NULL;
1369 } else {
1370 oo_warning (xin, _("Invalid attribute '%s', expected angle, received '%s'"),
1371 name, str);
1372 return NULL;
1375 num = gnm_fake_round (num);
1376 if (gnm_abs (num) >= 360)
1377 num = 0;
1379 *angle = (int)num;
1381 return end;
1384 /* returns degree */
1385 static char const *
1386 oo_attr_angle (GsfXMLIn *xin, xmlChar const * const *attrs,
1387 int ns_id, char const *name, int *deg)
1389 g_return_val_if_fail (attrs != NULL, NULL);
1390 g_return_val_if_fail (attrs[0] != NULL, NULL);
1391 g_return_val_if_fail (attrs[1] != NULL, NULL);
1393 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), ns_id, name))
1394 return NULL;
1395 return oo_parse_angle (xin, attrs[1], name, deg);
1398 static gboolean
1399 odf_attr_range (GsfXMLIn *xin, xmlChar const * const *attrs, Sheet *sheet, GnmRange *res)
1401 int flags = 0;
1403 g_return_val_if_fail (attrs != NULL, FALSE);
1405 for (; attrs[0] && attrs[1] ; attrs += 2)
1406 if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT, "start-col", &res->start.col, 0, gnm_sheet_get_last_col(sheet)))
1407 flags |= 0x1;
1408 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT, "start-row", &res->start.row, 0, gnm_sheet_get_last_row(sheet)))
1409 flags |= 0x2;
1410 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT, "end-col", &res->end.col, 0, gnm_sheet_get_last_col(sheet)))
1411 flags |= 0x4;
1412 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT, "end-row", &res->end.row, 0, gnm_sheet_get_last_row(sheet)))
1413 flags |= 0x8;
1414 else
1415 return FALSE;
1417 return flags == 0xf;
1420 static gboolean
1421 oo_attr_enum (GsfXMLIn *xin, xmlChar const * const *attrs,
1422 int ns_id, char const *name, OOEnum const *enums, int *res)
1424 g_return_val_if_fail (attrs != NULL, FALSE);
1425 g_return_val_if_fail (attrs[0] != NULL, FALSE);
1426 g_return_val_if_fail (attrs[1] != NULL, FALSE);
1428 if (!gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), ns_id, name))
1429 return FALSE;
1431 for (; enums->name != NULL ; enums++)
1432 if (!strcmp (enums->name, CXML2C (attrs[1]))) {
1433 *res = enums->val;
1434 return TRUE;
1436 return oo_warning (xin, _("Invalid attribute '%s', unknown enum value '%s'"),
1437 name, attrs[1]);
1440 static gboolean
1441 oo_cellref_check_for_err (GnmCellRef *ref, char const **start)
1443 if (g_str_has_prefix (*start, "$#REF!")) {
1444 ref->sheet = invalid_sheet;
1445 *start += 6;
1446 return TRUE;
1448 if (g_str_has_prefix (*start, "#REF!")) {
1449 ref->sheet = invalid_sheet;
1450 *start += 5;
1451 return TRUE;
1453 return FALSE;
1456 static char const *
1457 oo_cellref_parse (GnmCellRef *ref, char const *start, GnmParsePos const *pp,
1458 gchar **foreign_sheet)
1460 char const *tmp, *ptr = start;
1461 GnmSheetSize const *ss;
1462 GnmSheetSize ss_max = { GNM_MAX_COLS, GNM_MAX_ROWS};
1463 Sheet *sheet;
1465 if (*ptr != '.') {
1466 char *name, *accum;
1468 /* ignore abs vs rel for sheets */
1469 if (*ptr == '$')
1470 ptr++;
1472 /* From the spec :
1473 * SheetName ::= [^\. ']+ | "'" ([^'] | "''")+ "'" */
1474 if ('\'' == *ptr) {
1475 tmp = ++ptr;
1476 two_quotes :
1477 /* missing close paren */
1478 if (NULL == (tmp = strchr (tmp, '\'')))
1479 return start;
1481 /* two in a row is the escape for a single */
1482 if (tmp[1] == '\'') {
1483 tmp += 2;
1484 goto two_quotes;
1487 /* If a name is quoted the entire named must be quoted */
1488 if (tmp[1] != '.')
1489 return start;
1491 accum = name = g_alloca (tmp-ptr+1);
1492 while (ptr != tmp)
1493 if ('\'' == (*accum++ = *ptr++))
1494 ptr++;
1495 *accum = '\0';
1496 ptr += 2;
1497 } else {
1498 if (NULL == (tmp = strchr (ptr, '.')))
1499 return start;
1500 name = g_alloca (tmp-ptr+1);
1501 strncpy (name, ptr, tmp-ptr);
1502 name[tmp-ptr] = '\0';
1503 ptr = tmp + 1;
1506 if (name[0] == 0)
1507 return start;
1509 if (foreign_sheet != NULL) {
1510 /* This is a reference to a foreign workbook */
1511 *foreign_sheet = g_strdup (name);
1512 ref->sheet = NULL;
1513 } else {
1514 /* We have seen instances of ODF files generated by */
1515 /* Libreoffice referring internally to table included */
1516 /* inside charts. See */
1517 /* https://bugzilla.gnome.org/show_bug.cgi?id=698388 */
1518 /* Since all true sheets have been created during */
1519 /* preparsing, this reference should just be invalid! */
1521 ref->sheet = workbook_sheet_by_name (pp->wb, name);
1522 if (ref->sheet == NULL)
1523 ref->sheet = invalid_sheet;
1525 } else {
1526 ptr++; /* local ref */
1527 ref->sheet = NULL;
1530 tmp = col_parse (ptr, &ss_max, &ref->col, &ref->col_relative);
1531 if (!tmp && !oo_cellref_check_for_err (ref, &ptr))
1532 return start;
1533 if (tmp)
1534 ptr = tmp;
1535 tmp = row_parse (ptr, &ss_max, &ref->row, &ref->row_relative);
1536 if (!tmp && !oo_cellref_check_for_err (ref, &ptr))
1537 return start;
1538 if (tmp)
1539 ptr = tmp;
1541 if (ref->sheet == invalid_sheet)
1542 return ptr;
1544 sheet = eval_sheet (ref->sheet, pp->sheet);
1545 ss = gnm_sheet_get_size (sheet);
1547 if (foreign_sheet == NULL && (ss->max_cols <= ref->col || ss->max_rows <= ref->row)) {
1548 int new_cols = ref->col + 1, new_rows = ref->row + 1;
1549 GOUndo * goundo;
1550 gboolean err;
1552 odf_sheet_suggest_size (NULL, &new_cols, &new_rows);
1553 goundo = gnm_sheet_resize (sheet, new_cols, new_rows, NULL, &err);
1554 if (goundo) g_object_unref (goundo);
1556 ss = gnm_sheet_get_size (sheet);
1557 if (ss->max_cols <= ref->col || ss->max_rows <= ref->row)
1558 return start;
1561 if (ref->col_relative)
1562 ref->col -= pp->eval.col;
1563 if (ref->row_relative)
1564 ref->row -= pp->eval.row;
1566 return ptr;
1569 static char const *
1570 odf_parse_external (char const *start, gchar **external,
1571 GnmConventions const *convs)
1573 char const *ptr;
1574 GString *str;
1576 /* Source ::= "'" IRI "'" "#" */
1577 if (*start != '\'')
1578 return start;
1579 str = g_string_new (NULL);
1580 ptr = odf_strunescape (start, str, convs);
1582 if (ptr == NULL || *ptr != '#') {
1583 g_string_free (str, TRUE);
1584 return start;
1587 *external = g_string_free (str, FALSE);
1588 return (ptr + 1);
1591 static char const *
1592 oo_rangeref_parse (GnmRangeRef *ref, char const *start, GnmParsePos const *pp,
1593 GnmConventions const *convs)
1595 char const *ptr;
1596 char const *ptr2;
1597 char *external = NULL;
1598 char *external_sheet_1 = NULL;
1599 char *external_sheet_2 = NULL;
1600 ODFConventions *oconv = (ODFConventions *)convs;
1602 ptr = odf_parse_external (start, &external, convs);
1604 ptr2 = oo_cellref_parse (&ref->a, ptr, pp,
1605 external ? &external_sheet_1 : NULL);
1606 if (ptr == ptr2)
1607 return start;
1608 ptr = ptr2;
1610 if (*ptr == ':') {
1611 ptr2 = oo_cellref_parse (&ref->b, ptr+1, pp,
1612 external ? &external_sheet_2 : NULL);
1613 if (ptr2 == ptr + 1)
1614 ref->b = ref->a;
1615 else
1616 ptr = ptr2;
1617 } else
1618 ref->b = ref->a;
1620 if (ref->b.sheet == invalid_sheet)
1621 ref->a.sheet = invalid_sheet;
1623 if (external != NULL) {
1624 Workbook *wb = pp->wb, *ext_wb;
1625 Workbook *ref_wb = wb ? wb : pp->sheet->workbook;
1627 ext_wb = (*convs->input.external_wb) (convs, ref_wb, external);
1628 if (ext_wb == NULL) {
1629 if (oconv != NULL)
1630 oo_warning (oconv->xin,
1631 _("Ignoring reference to unknown "
1632 "external workbook '%s'"),
1633 external);
1634 ref->a.sheet = invalid_sheet;
1635 } else {
1636 if (external_sheet_1 != NULL)
1637 ref->a.sheet = workbook_sheet_by_name
1638 (ext_wb, external_sheet_1);
1639 else
1640 ref->a.sheet = workbook_sheet_by_index
1641 (ext_wb, 0);
1642 if (external_sheet_2 != NULL)
1643 ref->b.sheet = workbook_sheet_by_name
1644 (ext_wb, external_sheet_1);
1645 else
1646 ref->b.sheet = NULL;
1648 g_free (external);
1649 g_free (external_sheet_1);
1650 g_free (external_sheet_2);
1652 return ptr;
1655 static char const *
1656 oo_expr_rangeref_parse (GnmRangeRef *ref, char const *start, GnmParsePos const *pp,
1657 GnmConventions const *convs)
1659 char const *ptr;
1660 if (start[0] == '[' && start[1] != ']') {
1661 if (strncmp (start, "[#REF!]", 7) == 0) {
1662 ref->a.sheet = invalid_sheet;
1663 return start + 7;
1665 ptr = oo_rangeref_parse (ref, start+1, pp, convs);
1666 if (*ptr == ']')
1667 return ptr + 1;
1669 return start;
1673 static char const *
1674 odf_strunescape (char const *string, GString *target,
1675 G_GNUC_UNUSED GnmConventions const *convs)
1677 /* Constant strings are surrounded by double-quote characters */
1678 /* (QUOTATION MARK, U+0022); a literal double-quote character '"'*/
1679 /* (QUOTATION MARK, U+0022) as */
1680 /* string content is escaped by duplicating it. */
1682 char quote = *string++;
1683 size_t oldlen = target->len;
1685 /* This should be UTF-8 safe as long as quote is ASCII. */
1686 do {
1687 while (*string != quote) {
1688 if (*string == '\0')
1689 goto error;
1690 g_string_append_c (target, *string);
1691 string++;
1693 string++;
1694 if (*string == quote)
1695 g_string_append_c (target, quote);
1696 } while (*string++ == quote);
1697 return --string;
1699 error:
1700 g_string_truncate (target, oldlen);
1701 return NULL;
1704 /* Handle formatted text inside text:p */
1705 static void
1706 odf_text_p_add_text (OOParseState *state, char const *str)
1708 oo_text_p_t *ptr;
1710 g_return_if_fail (state->text_p_stack != NULL);
1711 ptr = state->text_p_stack->data;
1713 if (ptr->gstr) {
1714 g_string_append (ptr->gstr, str);
1715 } else
1716 ptr->gstr = g_string_new (str);
1719 typedef struct {
1720 guint start;
1721 guint end;
1722 PangoAttrList *attrs;
1723 } odf_text_p_apply_style_t;
1725 static gboolean
1726 odf_text_p_apply_pango_attribute (PangoAttribute *attribute, gpointer ptr)
1728 odf_text_p_apply_style_t *data = ptr;
1729 PangoAttribute *attr = pango_attribute_copy (attribute);
1731 attr->start_index = data->start;
1732 attr->end_index = data->end;
1734 pango_attr_list_change (data->attrs, attr);
1736 return FALSE;
1739 static void
1740 odf_text_p_apply_style (OOParseState *state,
1741 PangoAttrList *attrs,
1742 int start, int end)
1744 oo_text_p_t *ptr;
1745 odf_text_p_apply_style_t data;
1747 if (attrs == NULL)
1748 return;
1750 g_return_if_fail (state->text_p_stack != NULL);
1751 ptr = state->text_p_stack->data;
1753 if (ptr->attrs == NULL)
1754 ptr->attrs = pango_attr_list_new ();
1756 data.start = start;
1757 data.end = end;
1758 data.attrs = ptr->attrs;
1760 pango_attr_list_filter (attrs, odf_text_p_apply_pango_attribute, &data);
1763 static void
1764 odf_push_text_p (OOParseState *state, gboolean permanent)
1766 oo_text_p_t *ptr;
1768 if (permanent) {
1769 ptr = &(state->text_p_for_cell);
1770 if (ptr->gstr)
1771 g_string_truncate (ptr->gstr, 0);
1772 if (ptr->attrs) {
1773 pango_attr_list_unref (ptr->attrs);
1774 ptr->attrs = NULL;
1776 } else {
1777 ptr = g_new0 (oo_text_p_t, 1);
1778 ptr->permanent = FALSE;
1779 ptr->content_is_simple = TRUE;
1781 ptr->p_seen = FALSE;
1782 ptr->offset = 0;
1783 ptr->span_style_stack = NULL;
1784 ptr->span_style_list = NULL;
1785 state->text_p_stack = g_slist_prepend (state->text_p_stack, ptr);
1788 static void
1789 odf_pop_text_p (OOParseState *state)
1791 oo_text_p_t *ptr;
1792 GSList *link = state->text_p_stack;
1794 g_return_if_fail (state->text_p_stack != NULL);
1796 ptr = link->data;
1797 g_slist_free (ptr->span_style_stack);
1798 /* ptr->span_style_list should be NULL. If it isn't something went wrong and we are leaking here! */
1799 g_slist_free_full (ptr->span_style_list, g_free);
1800 ptr->span_style_stack = NULL;
1801 ptr->span_style_list = NULL;
1802 if (!ptr->permanent) {
1803 if (ptr->gstr)
1804 g_string_free (ptr->gstr, TRUE);
1805 if (ptr->attrs)
1806 pango_attr_list_unref (ptr->attrs);
1807 g_free (ptr);
1810 state->text_p_stack = g_slist_remove_link (state->text_p_stack, link);
1811 g_slist_free_1 (link);
1814 static void
1815 odf_text_content_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
1817 OOParseState *state = (OOParseState *)xin->user_state;
1818 oo_text_p_t *ptr = state->text_p_stack->data;
1820 if (ptr->p_seen)
1821 odf_text_p_add_text (state, "\n");
1822 else
1823 ptr->p_seen = TRUE;
1826 static void
1827 odf_text_content_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
1829 OOParseState *state = (OOParseState *)xin->user_state;
1830 oo_text_p_t *ptr;
1831 GSList *list = NULL, *l;
1833 g_return_if_fail ( state->text_p_stack != NULL);
1834 ptr = state->text_p_stack->data;
1836 g_return_if_fail (ptr != NULL);
1837 g_return_if_fail (xin->content != NULL);
1839 if (strlen (xin->content->str) > ptr->offset)
1840 odf_text_p_add_text
1841 (state, xin->content->str + ptr->offset);
1842 ptr->offset = 0;
1843 l = list = g_slist_reverse(ptr->span_style_list);
1844 while (l != NULL) {
1845 span_style_info_t *ssi = l->data;
1846 if (ssi != NULL) {
1847 int start = ssi->start;
1848 int end = ssi->end;
1849 char *style_name = ssi->style_name;
1850 if (style_name != NULL && end > 0 && end > start) {
1851 PangoAttrList *attrs = g_hash_table_lookup (state->styles.text, style_name);
1852 if (attrs == NULL)
1853 oo_warning (xin, _("Unknown text style with name \"%s\" encountered!"), style_name);
1854 else
1855 odf_text_p_apply_style (state, attrs, start, end);
1857 g_free (style_name);
1858 g_free (ssi);
1860 l = l->next;
1862 g_slist_free (list);
1863 ptr->span_style_list = NULL;
1866 static void
1867 odf_text_span_start (GsfXMLIn *xin, xmlChar const **attrs)
1869 OOParseState *state = (OOParseState *)xin->user_state;
1870 oo_text_p_t *ptr = state->text_p_stack->data;
1872 if (ptr->content_is_simple) {
1873 span_style_info_t *ssi = g_new0 (span_style_info_t, 1);
1875 if (xin->content->str != NULL && *xin->content->str != 0) {
1876 odf_text_p_add_text (state, xin->content->str + ptr->offset);
1877 ptr->offset = strlen (xin->content->str);
1880 ssi->start = ((ptr->gstr) ? ptr->gstr->len : 0);
1882 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1883 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TEXT, "style-name"))
1884 ssi->style_name = g_strdup (attrs[1]);
1886 ptr->span_style_stack = g_slist_prepend (ptr->span_style_stack, ssi);
1887 ptr->span_style_list = g_slist_prepend (ptr->span_style_list, ssi);
1891 static void
1892 odf_text_span_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
1894 OOParseState *state = (OOParseState *)xin->user_state;
1895 oo_text_p_t *ptr = state->text_p_stack->data;
1897 if (ptr->content_is_simple) {
1898 int end;
1899 span_style_info_t *ssi = NULL;
1901 g_return_if_fail (ptr->span_style_stack != NULL);
1903 if (xin->content->str != NULL && *xin->content->str != 0) {
1904 odf_text_p_add_text (state, xin->content->str + ptr->offset);
1905 ptr->offset = strlen (xin->content->str);
1908 end = ((ptr->gstr) ? ptr->gstr->len : 0);
1910 ssi = ptr->span_style_stack->data;
1911 ptr->span_style_stack = g_slist_delete_link (ptr->span_style_stack,
1912 ptr->span_style_stack);
1913 if (ssi != NULL)
1914 ssi->end = end;
1918 static void
1919 odf_text_special (GsfXMLIn *xin, int count, char const *sym)
1921 OOParseState *state = (OOParseState *)xin->user_state;
1922 oo_text_p_t *ptr = state->text_p_stack->data;
1924 if (ptr->content_is_simple) {
1925 if (xin->content->str != NULL && *xin->content->str != 0) {
1926 odf_text_p_add_text (state, xin->content->str + ptr->offset);
1927 ptr->offset = strlen (xin->content->str);
1930 if (count == 1)
1931 odf_text_p_add_text (state, sym);
1932 else if (count > 0) {
1933 gchar *space = g_strnfill (count, *sym);
1934 odf_text_p_add_text (state, space);
1935 g_free (space);
1940 static void
1941 odf_text_space (GsfXMLIn *xin, xmlChar const **attrs)
1943 int count = 1;
1945 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1946 if (oo_attr_int_range (xin, attrs, OO_NS_TEXT, "c", &count, 0, INT_MAX))
1948 odf_text_special (xin, count, " ");
1951 static void
1952 odf_text_symbol (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
1954 odf_text_special (xin, 1, xin->node->user_data.v_str);
1957 /* End of handle formatted text inside text:p */
1960 static void
1961 oo_sheet_style_free (OOSheetStyle *style)
1963 if (style) {
1964 g_free (style->master_page_name);
1965 g_free (style);
1969 typedef struct {
1970 GHashTable *orig2fixed;
1971 GHashTable *fixed2orig;
1972 OOParseState *state;
1973 GnmNamedExpr *nexpr;
1974 char const *nexpr_name;
1975 } odf_fix_expr_names_t;
1977 static odf_fix_expr_names_t *
1978 odf_fix_expr_names_t_new (OOParseState *state)
1980 odf_fix_expr_names_t *fen = g_new (odf_fix_expr_names_t, 1);
1982 fen->fixed2orig = g_hash_table_new (g_str_hash, g_str_equal);
1983 fen->orig2fixed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1984 fen->state = state;
1985 fen->nexpr = NULL;
1986 fen->nexpr_name = NULL;
1988 return fen;
1991 static void
1992 odf_fix_expr_names_t_free (odf_fix_expr_names_t *fen)
1994 g_hash_table_unref (fen->fixed2orig);
1995 g_hash_table_unref (fen->orig2fixed);
1996 g_free (fen);
1999 static void
2000 odf_fix_expr_names_t_add (odf_fix_expr_names_t *fen, char const *orig, char *fixed)
2002 char *orig_c = g_strdup (orig);
2003 g_hash_table_insert (fen->orig2fixed, orig_c, fixed);
2004 g_hash_table_insert (fen->fixed2orig, fixed, orig_c);
2007 static gboolean
2008 odf_fix_en_validate (char const *name, odf_fix_expr_names_t *fen)
2010 if (!expr_name_validate (name))
2011 return FALSE;
2012 if (NULL != g_hash_table_lookup (fen->fixed2orig, name))
2013 return FALSE;
2015 WORKBOOK_FOREACH_SHEET
2016 (fen->state->pos.wb, sheet,
2018 GnmParsePos pp;
2019 parse_pos_init_sheet (&pp, sheet);
2020 if (expr_name_lookup (&pp, name))
2021 return FALSE;
2024 return TRUE;
2027 static void
2028 odf_fix_en_collect (G_GNUC_UNUSED gconstpointer key_,
2029 GnmNamedExpr *nexpr, odf_fix_expr_names_t *fen)
2031 GString *str;
2032 gchar *here;
2033 const char *name = expr_name_name (nexpr);
2035 if (expr_name_validate (name))
2036 return;
2037 if (NULL != g_hash_table_lookup (fen->orig2fixed, name))
2038 return;
2039 str = g_string_new (name);
2041 for (here = str->str; *here; here = g_utf8_next_char (here)) {
2042 if (!g_unichar_isalnum (g_utf8_get_char (here)) &&
2043 here[0] != '_') {
2044 int i, limit = g_utf8_next_char (here) - here;
2045 for (i = 0; i<limit;i++)
2046 here[i] = '_';
2049 while (!odf_fix_en_validate (str->str, fen))
2050 g_string_append_c (str, '_');
2051 odf_fix_expr_names_t_add (fen, name, g_string_free (str, FALSE));
2054 static void
2055 odf_fix_en_find (G_GNUC_UNUSED gconstpointer key,
2056 GnmNamedExpr *nexpr, odf_fix_expr_names_t *fen)
2058 if (strcmp (expr_name_name (nexpr), fen->nexpr_name) == 0)
2059 fen->nexpr = nexpr;
2062 static void
2063 odf_fix_en_apply (const char *orig, const char *fixed, odf_fix_expr_names_t *fen)
2065 int i = 0;
2067 g_return_if_fail (orig != NULL);
2068 g_return_if_fail (fixed != NULL);
2069 g_return_if_fail (fen != NULL);
2071 fen->nexpr_name = orig;
2073 while (i++ < 1000) {
2074 fen->nexpr = NULL;
2075 workbook_foreach_name (fen->state->pos.wb, FALSE,
2076 (GHFunc)odf_fix_en_find, fen);
2078 if (fen->nexpr == NULL)
2079 return;
2081 expr_name_set_name (fen->nexpr, fixed);
2088 * When we initialy validate names we have to accept every ODF name
2089 * in odf_fix_expr_names we fix them.
2094 static void
2095 odf_fix_expr_names (OOParseState *state)
2097 odf_fix_expr_names_t *fen = odf_fix_expr_names_t_new (state);
2099 workbook_foreach_name (state->pos.wb, FALSE,
2100 (GHFunc)odf_fix_en_collect, fen);
2101 g_hash_table_foreach (fen->orig2fixed, (GHFunc)odf_fix_en_apply, fen);
2103 odf_fix_expr_names_t_free (fen);
2107 * odf_expr_name_validate:
2108 * @name: tentative name
2110 * returns TRUE if the given name is valid, FALSE otherwise.
2112 * We are accepting names here that contain periods or look like addresses.
2113 * They need to be replaced when we have finished parsing the file since
2114 * they are not allowed inside Gnumeric.
2116 static gboolean
2117 odf_expr_name_validate (const char *name)
2119 const char *p;
2120 GnmValue *v;
2122 g_return_val_if_fail (name != NULL, FALSE);
2124 if (name[0] == 0)
2125 return FALSE;
2127 v = value_new_from_string (VALUE_BOOLEAN, name, NULL, TRUE);
2128 if (!v)
2129 v = value_new_from_string (VALUE_BOOLEAN, name, NULL, FALSE);
2130 if (v) {
2131 value_release (v);
2132 return FALSE;
2135 /* Hmm... Now what? */
2136 if (!g_unichar_isalpha (g_utf8_get_char (name)) &&
2137 name[0] != '_')
2138 return FALSE;
2140 for (p = name; *p; p = g_utf8_next_char (p)) {
2141 if (!g_unichar_isalnum (g_utf8_get_char (p)) &&
2142 p[0] != '_' && p[0] != '.')
2143 return FALSE;
2146 return TRUE;
2150 * This is ugly! And it's ods' fault.
2152 * On one hand we have function names like ORG.GNUMERIC.LOG2, on the
2153 * other we have inter-sheet name references Sheet2.localname
2154 * The former is a name, the latter is a sheet name, a sheetsep, and
2155 * a name.
2157 * To resolve that, we look ahead for a '('.
2159 static char const *
2160 odf_name_parser (char const *str, GnmConventions const *convs)
2162 gunichar uc = g_utf8_get_char (str);
2163 const char *firstdot = NULL;
2164 int dotcount = 0;
2166 if (!g_unichar_isalpha (uc) && uc != '_' && uc != '\\')
2167 return NULL;
2169 do {
2170 str = g_utf8_next_char (str);
2171 uc = g_utf8_get_char (str);
2173 if (uc == '.') {
2174 if (dotcount++ == 0)
2175 firstdot = str;
2177 } while (g_unichar_isalnum (uc) ||
2178 (uc == '_' || uc == '?' || uc == '\\' || uc == '.'));
2180 if (dotcount == 1 && convs->sheet_name_sep == '.') {
2181 const char *p = str;
2183 while (g_unichar_isspace (g_utf8_get_char (p)))
2184 p = g_utf8_next_char (p);
2186 if (*p != '(')
2187 return firstdot;
2190 return str;
2193 static GnmExpr const *
2194 oo_func_map_in (GnmConventions const *convs, Workbook *scope,
2195 char const *name, GnmExprList *args);
2197 static GnmConventions *
2198 oo_conventions_new (OOParseState *state, GsfXMLIn *xin)
2200 GnmConventions *conv = gnm_conventions_new_full
2201 (sizeof (ODFConventions));
2202 ODFConventions *oconv = (ODFConventions *)conv;
2203 conv->decode_ampersands = TRUE;
2204 conv->exp_is_left_associative = TRUE;
2206 conv->intersection_char = '!';
2207 conv->union_char = '~';
2208 conv->decimal_sep_dot = TRUE;
2209 conv->range_sep_colon = TRUE;
2210 conv->arg_sep = ';';
2211 conv->array_col_sep = ';';
2212 conv->array_row_sep = '|';
2213 conv->input.string = odf_strunescape;
2214 conv->input.func = oo_func_map_in;
2215 conv->input.range_ref = oo_expr_rangeref_parse;
2216 conv->input.name = odf_name_parser;
2217 conv->input.name_validate = odf_expr_name_validate;
2218 conv->sheet_name_sep = '.';
2219 oconv->state = state;
2220 oconv->xin = xin;
2222 return conv;
2225 static void
2226 oo_load_convention (OOParseState *state, GsfXMLIn *xin, OOFormula type)
2228 GnmConventions *convs;
2230 g_return_if_fail (state->convs[type] == NULL);
2232 switch (type) {
2233 case FORMULA_MICROSOFT:
2234 convs = gnm_xml_io_conventions ();
2235 convs->exp_is_left_associative = TRUE;
2236 break;
2237 case FORMULA_OLD_OPENOFFICE:
2238 convs = oo_conventions_new (state, xin);
2239 convs->sheet_name_sep = '!'; /* Note that we are using this also as a marker*/
2240 /* in the function handlers */
2241 break;
2242 case FORMULA_OPENFORMULA:
2243 default:
2244 convs = oo_conventions_new (state, xin);
2245 break;
2248 state->convs[type] = convs;
2251 static GnmExprTop const *
2252 oo_expr_parse_str_try (GsfXMLIn *xin, char const *str,
2253 GnmParsePos const *pp, GnmExprParseFlags flags,
2254 OOFormula type, GnmParseError *perr)
2256 OOParseState *state = (OOParseState *)xin->user_state;
2258 if (state->convs[type] == NULL)
2259 oo_load_convention (state, xin, type);
2260 return gnm_expr_parse_str (str, pp, flags | GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_INVALID,
2261 state->convs[type], perr);
2264 static GnmExprTop const *
2265 oo_expr_parse_str (GsfXMLIn *xin, char const *str,
2266 GnmParsePos const *pp, GnmExprParseFlags flags,
2267 OOFormula type)
2269 OOParseState *state = (OOParseState *)xin->user_state;
2270 GnmExprTop const *texpr;
2271 GnmParseError perr;
2273 parse_error_init (&perr);
2275 texpr = oo_expr_parse_str_try (xin, str, pp, flags, type, &perr);
2276 if (texpr == NULL) {
2277 if (*str != '[') {
2278 /* There are faulty expressions in the wild that */
2279 /* are references w/o [] */
2280 char *test = g_strdup_printf ("[%s]", str);
2281 texpr = oo_expr_parse_str_try (xin, test, pp,
2282 flags, type, NULL);
2283 g_free (test);
2285 if (texpr == NULL)
2286 oo_warning (xin, _("Unable to parse '%s' ('%s')"),
2287 str, perr.err->message);
2289 parse_error_free (&perr);
2291 if (texpr)
2292 texpr = gnm_expr_sharer_share (state->sharer, texpr);
2294 return texpr;
2297 static GnmExprTop const *
2298 odf_parse_range_address_or_expr (GsfXMLIn *xin, char const *str)
2300 OOParseState *state = (OOParseState *)xin->user_state;
2301 GnmExprTop const *texpr = NULL;
2302 OOFormula f_type = odf_get_formula_type (xin, &str);
2304 if (str != NULL && strlen (str) > 0 && f_type != FORMULA_NOT_SUPPORTED) {
2305 GnmParsePos pp;
2306 GnmRangeRef ref;
2307 char const *ptr;
2308 gnm_cellref_init (&ref.a, invalid_sheet, 0, 0, TRUE);
2309 gnm_cellref_init (&ref.b, invalid_sheet, 0, 0, TRUE);
2310 ptr = oo_rangeref_parse
2311 (&ref, str,
2312 parse_pos_init_sheet (&pp, state->pos.sheet),
2313 NULL);
2314 if (ptr == str
2315 || ref.a.sheet == invalid_sheet)
2316 texpr = oo_expr_parse_str (xin, str,
2317 &state->pos,
2318 GNM_EXPR_PARSE_DEFAULT,
2319 f_type);
2320 else {
2321 GnmValue *v = value_new_cellrange (&ref.a, &ref.b, 0, 0);
2322 texpr = gnm_expr_top_new_constant (v);
2325 return texpr;
2329 /****************************************************************************/
2331 static void
2332 oo_date_convention (GsfXMLIn *xin, xmlChar const **attrs)
2334 /* <table:null-date table:date-value="1904-01-01"/> */
2335 OOParseState *state = (OOParseState *)xin->user_state;
2336 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
2337 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "date-value")) {
2338 if (!strncmp (CXML2C (attrs[1]), "1904", 4))
2339 workbook_set_1904 (state->pos.wb, TRUE);
2342 static void
2343 oo_iteration (GsfXMLIn *xin, xmlChar const **attrs)
2345 /* <table:iteration table:status="enable"/> */
2346 OOParseState *state = (OOParseState *)xin->user_state;
2347 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
2348 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "status"))
2349 workbook_iteration_enabled (state->pos.wb,
2350 strcmp (CXML2C (attrs[1]), "enable") == 0);
2353 static void
2354 odf_pi_parse_format_spec (GsfXMLIn *xin, char **fmt, char const *needle, char const *tag)
2356 OOParseState *state = (OOParseState *)xin->user_state;
2357 GString *str = g_string_new (*fmt);
2358 gint start = 0;
2359 gchar *found = NULL;
2361 while (NULL != (found = g_strstr_len (str->str + start, -1, needle))) {
2362 char *op_start = found + strlen (needle);
2363 gchar *p = op_start;
2365 while (*p && (*p != ']'))
2366 p++;
2367 if (*p == ']') {
2368 char *id = g_strndup (op_start, p - op_start);
2369 char const *formula = g_hash_table_lookup (state->strings, id);
2370 char const *orig_formula = formula;
2371 OOFormula f_type;
2372 GnmExprTop const *texpr = NULL;
2373 char *text, *subs;
2374 gint start_pos = found - str->str;
2376 g_free (id);
2377 g_string_erase (str, start_pos, p - found + 1);
2379 if (formula == NULL)
2380 goto stop_parse;
2382 f_type = odf_get_formula_type (xin, &formula);
2383 if (f_type == FORMULA_NOT_SUPPORTED) {
2384 oo_warning (xin, _("Unsupported formula type encountered: %s"),
2385 orig_formula);
2386 goto stop_parse;
2388 formula = gnm_expr_char_start_p (formula);
2389 if (formula == NULL) {
2390 oo_warning (xin, _("Expression '%s' does not start "
2391 "with a recognized character"), orig_formula);
2392 goto stop_parse;
2394 texpr = oo_expr_parse_str
2395 (xin, formula, &state->pos, GNM_EXPR_PARSE_DEFAULT, f_type);
2397 if (texpr != NULL) {
2398 text = gnm_expr_top_as_string (texpr, &state->pos,
2399 gnm_conventions_default);
2400 gnm_expr_top_unref (texpr);
2402 if (tag == NULL) {
2403 subs = text;
2404 } else {
2405 subs = g_strdup_printf ("&[%s:%s]", tag, text);
2406 g_free (text);
2408 g_string_insert (str, start_pos, subs);
2409 start = start_pos + strlen (subs);
2410 g_free (subs);
2412 } else
2413 break;
2416 stop_parse:
2417 g_free (*fmt);
2418 *fmt = g_string_free (str, FALSE);
2421 static void
2422 odf_pi_parse_format (GsfXMLIn *xin, char **fmt)
2424 if ((*fmt == NULL) ||
2425 (NULL == g_strstr_len (*fmt, -1, "&[cell")))
2426 return;
2428 odf_pi_parse_format_spec (xin, fmt, "&[cellt:", NULL);
2429 odf_pi_parse_format_spec (xin, fmt, "&[cell:", _("cell"));
2432 static void
2433 odf_pi_parse_hf (GsfXMLIn *xin, GnmPrintHF *hf)
2435 odf_pi_parse_format (xin, &hf->left_format);
2436 odf_pi_parse_format (xin, &hf->middle_format);
2437 odf_pi_parse_format (xin, &hf->right_format);
2440 static void
2441 odf_pi_parse_expressions (GsfXMLIn *xin, GnmPrintInformation *pi)
2443 odf_pi_parse_hf (xin, pi->header);
2444 odf_pi_parse_hf (xin, pi->footer);
2447 static void
2448 oo_table_start (GsfXMLIn *xin, xmlChar const **attrs)
2450 OOParseState *state = (OOParseState *)xin->user_state;
2451 gchar *style_name = NULL;
2452 gchar *print_range = NULL;
2453 gboolean do_not_print = FALSE, tmp_b;
2455 state->pos.eval.col = 0;
2456 state->pos.eval.row = 0;
2457 state->print.rep_rows_from = -1;
2458 state->print.rep_rows_to = -1;
2459 state->print.rep_cols_from = -1;
2460 state->print.rep_cols_to = -1;
2462 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
2463 /* We need not check for the table name since we did that during pre-parsing */
2464 /* if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "name")) { */
2465 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "style-name")) {
2466 style_name = g_strdup (CXML2C (attrs[1]));
2467 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "print-ranges")) {
2468 print_range = g_strdup (CXML2C (attrs[1]));
2469 } else if (oo_attr_bool (xin, attrs, OO_NS_TABLE, "print", &tmp_b))
2470 do_not_print = !tmp_b;
2472 ++state->table_n;
2473 state->pos.sheet = ((sheet_order_t *)g_slist_nth_data (state->sheet_order, state->table_n))->sheet;
2475 if (style_name != NULL) {
2476 OOSheetStyle const *style = g_hash_table_lookup (state->styles.sheet, style_name);
2477 if (style) {
2478 GnmPrintInformation *pi = NULL;
2479 if (style->master_page_name)
2480 pi = g_hash_table_lookup (state->styles.master_pages,
2481 style->master_page_name);
2482 if (pi != NULL) {
2483 gnm_print_info_free (state->pos.sheet->print_info);
2484 state->pos.sheet->print_info = gnm_print_info_dup (pi);
2485 odf_pi_parse_expressions (xin, state->pos.sheet->print_info);
2487 g_object_set (state->pos.sheet,
2488 "visibility", style->visibility,
2489 "text-is-rtl", style->is_rtl,
2490 "display-formulas", style->display_formulas,
2491 "display-column-header", !style->hide_col_header,
2492 "display-row-header", !style->hide_row_header,
2493 NULL);
2494 if (style->tab_color_set) {
2495 GnmColor *color
2496 = gnm_color_new_go (style->tab_color);
2497 g_object_set
2498 (state->pos.sheet,
2499 "tab-background",
2500 color,
2501 NULL);
2502 style_color_unref (color);
2504 if (style->tab_text_color_set){
2505 GnmColor *color
2506 = gnm_color_new_go
2507 (style->tab_text_color);
2508 g_object_set
2509 (state->pos.sheet,
2510 "tab-foreground",
2511 color,
2512 NULL);
2513 style_color_unref (color);
2517 g_free (style_name);
2520 state->pos.sheet->print_info->do_not_print = do_not_print;
2522 if (state->default_style.rows != NULL)
2523 sheet_row_set_default_size_pts (state->pos.sheet,
2524 state->default_style.rows->size_pts);
2525 if (state->default_style.columns != NULL)
2526 sheet_col_set_default_size_pts (state->pos.sheet,
2527 state->default_style.columns->size_pts);
2528 if (print_range != NULL) {
2529 GnmExprTop const *texpr = odf_parse_range_address_or_expr (xin, print_range);
2530 if (texpr != NULL) {
2531 GnmNamedExpr *nexpr = expr_name_lookup (&state->pos, "Print_Area");
2532 if (nexpr != NULL)
2533 expr_name_set_expr (nexpr, texpr);
2538 static void
2539 odf_shapes (GsfXMLIn *xin, xmlChar const **attrs)
2541 OOParseState *state = (OOParseState *)xin->user_state;
2542 state->pos.eval.col = -1; /* we use that to know that objects have absolute anchors */
2545 static void
2546 odf_shapes_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
2548 OOParseState *state = (OOParseState *)xin->user_state;
2549 state->pos.eval.col = 0;
2552 static void
2553 odf_init_pp (GnmParsePos *pp, GsfXMLIn *xin, gchar const *base)
2555 OOParseState *state = (OOParseState *)xin->user_state;
2557 *pp = state->pos;
2558 if (base != NULL && *base != 0) {
2559 GnmExprTop const *texpr = NULL;
2560 char *tmp = g_strconcat ("[", base, "]", NULL);
2561 GnmParsePos ppp;
2562 /* base-cell-addresses are always required to be absolute (and contain a sheet name) */
2563 parse_pos_init (&ppp, state->pos.wb, state->pos.sheet, 0, 0);
2564 texpr = oo_expr_parse_str
2565 (xin, tmp, &ppp,
2566 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES,
2567 FORMULA_OPENFORMULA);
2568 g_free (tmp);
2569 if (texpr != NULL) {
2570 if (GNM_EXPR_GET_OPER (texpr->expr) ==
2571 GNM_EXPR_OP_CELLREF) {
2572 GnmCellRef const *ref = &texpr->expr->cellref.ref;
2573 parse_pos_init (pp, state->pos.wb, ref->sheet,
2574 ref->col, ref->row);
2576 gnm_expr_top_unref (texpr);
2582 static GnmValidation *
2583 odf_validation_new_list (GsfXMLIn *xin, odf_validation_t *val, guint offset)
2585 OOParseState *state = (OOParseState *)xin->user_state;
2586 GnmValidation *validation = NULL;
2587 char *start = NULL, *end = NULL;
2588 GString *str;
2589 GnmExprTop const *texpr = NULL;
2590 GnmParsePos pp;
2592 start = strchr (val->condition + offset, '(');
2593 if (start != NULL)
2594 end = strrchr (start, ')');
2595 if (end == NULL)
2596 return NULL;
2598 odf_init_pp (&pp, xin, val->base_cell_address);
2600 if (*(start + 1) == '\"') {
2601 str = g_string_new ("{");
2602 g_string_append_len (str, start + 1, end - start - 1);
2603 g_string_append_c (str, '}');
2604 } else {
2605 str = g_string_new (NULL);
2606 g_string_append_len (str, start + 1, end - start - 1);
2609 texpr = oo_expr_parse_str (xin, str->str, &pp,
2610 GNM_EXPR_PARSE_DEFAULT,
2611 val->f_type);
2613 if (texpr != NULL)
2614 validation = gnm_validation_new (val->style,
2615 GNM_VALIDATION_TYPE_IN_LIST,
2616 GNM_VALIDATION_OP_NONE,
2617 state->pos.sheet,
2618 val->title,
2619 val->message ? val->message->str : NULL,
2620 texpr,
2621 NULL,
2622 val->allow_blank,
2623 val->use_dropdown);
2625 g_string_free (str, TRUE);
2627 return validation;
2630 static GnmValidation *
2631 odf_validation_new_single_expr (GsfXMLIn *xin, odf_validation_t *val,
2632 char const *start, ValidationType val_type,
2633 ValidationOp val_op)
2635 OOParseState *state = (OOParseState *)xin->user_state;
2636 GnmExprTop const *texpr = NULL;
2637 GnmParsePos pp;
2638 GnmExprParseFlags flag;
2640 odf_init_pp (&pp, xin, val->base_cell_address);
2641 flag = (pp.sheet == NULL || state->pos.sheet == pp.sheet)
2642 ? GNM_EXPR_PARSE_DEFAULT
2643 : GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES;
2645 texpr = oo_expr_parse_str (xin, start, &pp, flag, val->f_type);
2647 if (texpr != NULL)
2648 return gnm_validation_new (val->style,
2649 val_type,
2650 val_op,
2651 state->pos.sheet,
2652 val->title,
2653 val->message ? val->message->str : NULL,
2654 texpr,
2655 NULL,
2656 val->allow_blank,
2657 val->use_dropdown);
2658 return NULL;
2663 static GnmValidation *
2664 odf_validation_new_pair_expr (GsfXMLIn *xin, odf_validation_t *val,
2665 char const *start, ValidationType val_type,
2666 ValidationOp val_op)
2668 OOParseState *state = (OOParseState *)xin->user_state;
2669 GnmParsePos pp;
2670 GnmExprParseFlags flag;
2671 GnmExprTop const *texpr_a = NULL, *texpr_b = NULL;
2672 char *pair = NULL;
2673 guint len = strlen (start);
2675 if (*start != '(' || *(start + len - 1) != ')')
2676 return NULL;
2677 start++;
2678 len -= 2;
2679 pair = g_strndup (start, len);
2681 odf_init_pp (&pp, xin, val->base_cell_address);
2682 flag = (pp.sheet == NULL || state->pos.sheet == pp.sheet)
2683 ? GNM_EXPR_PARSE_DEFAULT
2684 : GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES;
2686 while (1) {
2687 gchar * try = g_strrstr_len (pair, len, ",");
2688 GnmExprTop const *texpr;
2690 if (try == NULL || try == pair)
2691 goto pair_error;
2693 texpr = oo_expr_parse_str (xin, try + 1, &pp, flag, val->f_type);
2694 if (texpr != NULL) {
2695 texpr_b = texpr;
2696 *try = '\0';
2697 break;
2699 len = try - pair - 1;
2701 texpr_a = oo_expr_parse_str (xin, pair, &pp, flag, val->f_type);
2703 if (texpr_b != NULL) {
2704 g_free (pair);
2705 return gnm_validation_new (val->style,
2706 val_type,
2707 val_op,
2708 state->pos.sheet,
2709 val->title,
2710 val->message ? val->message->str : NULL,
2711 texpr_a,
2712 texpr_b,
2713 val->allow_blank,
2714 val->use_dropdown);
2716 pair_error:
2717 g_free (pair);
2718 return NULL;
2721 static GnmValidation *
2722 odf_validation_new_between (GsfXMLIn *xin, odf_validation_t *val, guint offset, ValidationType vtype,
2723 gboolean no_not)
2725 char *start = val->condition + offset;
2727 while (*start == ' ')
2728 start++;
2730 return odf_validation_new_pair_expr
2731 (xin, val, start, vtype, no_not ? GNM_VALIDATION_OP_BETWEEN : GNM_VALIDATION_OP_NOT_BETWEEN);
2734 static GnmValidation *
2735 odf_validation_new_op (GsfXMLIn *xin, odf_validation_t *val, guint offset, ValidationType vtype)
2737 char *start = val->condition + offset;
2738 ValidationOp val_op = GNM_VALIDATION_OP_NONE;
2740 while (*start == ' ')
2741 start++;
2743 if (g_str_has_prefix (start, ">=")) {
2744 val_op = GNM_VALIDATION_OP_GTE;
2745 start += 2;
2746 } else if (g_str_has_prefix (start, "<=")) {
2747 val_op = GNM_VALIDATION_OP_LTE;
2748 start += 2;
2749 } else if (g_str_has_prefix (start, "!=")) {
2750 val_op = GNM_VALIDATION_OP_NOT_EQUAL;
2751 start += 2;
2752 } else if (g_str_has_prefix (start, "=")) {
2753 val_op = GNM_VALIDATION_OP_EQUAL;
2754 start += 1;
2755 } else if (g_str_has_prefix (start, ">")) {
2756 val_op = GNM_VALIDATION_OP_GT;
2757 start += 1;
2758 } else if (g_str_has_prefix (start, "<")) {
2759 val_op = GNM_VALIDATION_OP_LT;
2760 start += 1;
2763 if (val_op == GNM_VALIDATION_OP_NONE)
2764 return NULL;
2766 while (*start == ' ')
2767 start++;
2769 return odf_validation_new_single_expr
2770 (xin, val, start, vtype, val_op);
2773 static GnmValidation *
2774 odf_validations_analyze (GsfXMLIn *xin, odf_validation_t *val, guint offset,
2775 ValidationType vtype, OOFormula f_type)
2777 char const *str = val->condition + offset;
2779 while (*str == ' ')
2780 str++;
2782 if (g_str_has_prefix (str, "cell-content-is-in-list"))
2783 return odf_validation_new_list
2784 (xin, val, str - val->condition + strlen ("cell-content-is-in-list"));
2785 else if (g_str_has_prefix (str, "cell-content-text-length()"))
2786 return odf_validation_new_op
2787 (xin, val, str - val->condition + strlen ("cell-content-text-length()"),
2788 GNM_VALIDATION_TYPE_TEXT_LENGTH);
2789 else if (g_str_has_prefix (str, "cell-content-text-length-is-between"))
2790 return odf_validation_new_between
2791 (xin, val, str - val->condition + strlen ("cell-content-text-length-is-between"),
2792 GNM_VALIDATION_TYPE_TEXT_LENGTH, TRUE);
2793 else if (g_str_has_prefix (str, "cell-content-text-length-is-not-between"))
2794 return odf_validation_new_between
2795 (xin, val, str - val->condition + strlen ("cell-content-text-length-is-not-between"),
2796 GNM_VALIDATION_TYPE_TEXT_LENGTH, FALSE);
2797 else if (g_str_has_prefix (str, "cell-content-is-decimal-number() and"))
2798 return odf_validations_analyze
2799 (xin, val, str - val->condition + strlen ("cell-content-is-decimal-number() and"),
2800 GNM_VALIDATION_TYPE_AS_NUMBER, f_type);
2801 else if (g_str_has_prefix (str, "cell-content-is-whole-number() and"))
2802 return odf_validations_analyze
2803 (xin, val, str - val->condition + strlen ("cell-content-is-whole-number() and"),
2804 GNM_VALIDATION_TYPE_AS_INT, f_type);
2805 else if (g_str_has_prefix (str, "cell-content-is-date() and"))
2806 return odf_validations_analyze
2807 (xin, val, str - val->condition + strlen ("cell-content-is-date() and"),
2808 GNM_VALIDATION_TYPE_AS_DATE, f_type);
2809 else if (g_str_has_prefix (str, "cell-content-is-time() and"))
2810 return odf_validations_analyze
2811 (xin, val, str - val->condition + strlen ("cell-content-is-time() and"),
2812 GNM_VALIDATION_TYPE_AS_TIME, f_type);
2813 else if (g_str_has_prefix (str, "is-true-formula(") && g_str_has_suffix (str, ")")) {
2814 GString *gstr = g_string_new (str + strlen ("is-true-formula("));
2815 GnmValidation *validation;
2816 g_string_truncate (gstr, gstr->len - 1);
2817 if (vtype != GNM_VALIDATION_TYPE_ANY) {
2818 oo_warning
2819 (xin, _("Validation condition '%s' is not supported. "
2820 "It has been changed to '%s'."),
2821 val->condition, str);
2823 validation = odf_validation_new_single_expr
2824 (xin, val, gstr->str, GNM_VALIDATION_TYPE_CUSTOM,
2825 GNM_VALIDATION_OP_NONE);
2826 g_string_free (gstr, TRUE);
2827 return validation;
2828 } else if (g_str_has_prefix (str, "cell-content()"))
2829 return odf_validation_new_op
2830 (xin, val, str - val->condition + strlen ("cell-content()"),
2831 vtype);
2832 else if (g_str_has_prefix (str, "cell-content-is-between"))
2833 return odf_validation_new_between
2834 (xin, val, str - val->condition + strlen ("cell-content-is-between"),
2835 vtype, TRUE);
2836 else if (g_str_has_prefix (str, "cell-content-is-not-between"))
2837 return odf_validation_new_between
2838 (xin, val, str - val->condition + strlen ("cell-content-is-not-between"),
2839 vtype, FALSE);
2841 return NULL;
2844 static GnmInputMsg *
2845 odf_validation_get_input_message (GsfXMLIn *xin, char const *name)
2847 OOParseState *state = (OOParseState *)xin->user_state;
2848 odf_validation_t *val = g_hash_table_lookup (state->validations, name);
2850 if (val == NULL)
2851 return NULL;
2853 if ((val->help_message != NULL && val->help_message->len > 0) ||
2854 (val->help_title != NULL && strlen (val->help_title) > 0))
2855 return gnm_input_msg_new (val->help_message ? val->help_message->str : NULL, val->help_title);
2856 else
2857 return NULL;
2860 static GnmValidation *
2861 odf_validations_translate (GsfXMLIn *xin, char const *name)
2863 OOParseState *state = (OOParseState *)xin->user_state;
2864 odf_validation_t *val = g_hash_table_lookup (state->validations, name);
2866 if (val == NULL) {
2867 oo_warning
2868 (xin, _("Undefined validation style encountered: %s"),
2869 name);
2870 return NULL;
2873 if (val->condition != NULL && val->f_type != FORMULA_NOT_SUPPORTED) {
2874 const char *str = val->condition;
2875 GnmValidation *validation;
2876 OOFormula f_type = odf_get_formula_type (xin, &str);
2877 validation = odf_validations_analyze
2878 (xin, val, str - val->condition, GNM_VALIDATION_TYPE_ANY, f_type);
2879 if (validation != NULL) {
2880 GError *err;
2881 if (NULL == (err = gnm_validation_is_ok (validation)))
2882 return validation;
2883 else {
2884 oo_warning (xin,
2885 _("Ignoring invalid data "
2886 "validation because : %s"),
2887 _(err->message));
2888 gnm_validation_unref (validation);
2889 return NULL;
2893 if (val->condition != NULL)
2894 oo_warning (xin, _("Unsupported validation condition "
2895 "encountered: \"%s\" with base address: \"%s\""),
2896 val->condition, val->base_cell_address);
2898 return NULL;
2901 static void
2902 odf_validation_free (odf_validation_t *val)
2904 g_free (val->condition);
2905 g_free (val->base_cell_address);
2906 g_free (val->title);
2907 g_free (val->help_title);
2908 if (val->message)
2909 g_string_free (val->message, TRUE);
2910 if (val->help_message)
2911 g_string_free (val->help_message, TRUE);
2912 g_free (val);
2915 static odf_validation_t *
2916 odf_validation_new (void)
2918 odf_validation_t *val = g_new0 (odf_validation_t, 1);
2919 val->use_dropdown = TRUE;
2920 val->allow_blank = TRUE;
2921 val->f_type = FORMULA_NOT_SUPPORTED;
2922 val->style = GNM_VALIDATION_STYLE_WARNING;
2923 return val;
2926 static void
2927 odf_validation (GsfXMLIn *xin, xmlChar const **attrs)
2929 static OOEnum const dropdown_types [] = {
2930 { "none", 0 },
2931 { "sort-ascending", 1 },
2932 { "unsorted", 1 },
2933 { NULL, 0 },
2936 OOParseState *state = (OOParseState *)xin->user_state;
2937 char const *name = NULL;
2938 int tmp;
2939 odf_validation_t *validation = odf_validation_new ();
2941 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2){
2942 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
2943 OO_NS_TABLE, "name" )) {
2944 name = CXML2C (attrs[1]);
2945 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
2946 OO_NS_TABLE, "condition")) {
2947 char const *cond = CXML2C (attrs[1]);
2948 validation->f_type = odf_get_formula_type (xin, &cond);
2949 validation->condition = g_strdup (cond);
2950 } else if (oo_attr_bool (xin, attrs,
2951 OO_NS_TABLE, "allow-empty-cell",
2952 &validation->allow_blank)) {
2953 } else if (oo_attr_enum (xin, attrs, OO_NS_TABLE, "display-list", dropdown_types, &tmp)) {
2954 validation->use_dropdown = (tmp == 1);
2955 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
2956 OO_NS_TABLE, "base-cell-address")) {
2957 validation->base_cell_address = g_strdup (CXML2C (attrs[1]));
2960 if (name != NULL) {
2961 g_hash_table_insert (state->validations, g_strdup (name), validation);
2962 state->cur_validation = validation;
2963 } else {
2964 odf_validation_free (validation);
2965 state->cur_validation = NULL;
2970 static void
2971 odf_validation_error_message (GsfXMLIn *xin, xmlChar const **attrs)
2973 static OOEnum const message_styles [] = {
2974 { "information", GNM_VALIDATION_STYLE_INFO },
2975 { "stop", GNM_VALIDATION_STYLE_STOP },
2976 { "warning", GNM_VALIDATION_STYLE_WARNING },
2977 { NULL, 0 },
2980 OOParseState *state = (OOParseState *)xin->user_state;
2981 int tmp;
2983 if (state->cur_validation)
2984 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2){
2985 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
2986 OO_NS_TABLE, "title" )) {
2987 g_free (state->cur_validation->title);
2988 state->cur_validation->title = g_strdup (CXML2C (attrs[1]));
2989 } else if (oo_attr_enum (xin, attrs, OO_NS_TABLE, "message-type", message_styles, &tmp))
2990 state->cur_validation->style = tmp;
2991 /* ignoring TABLE "display" */
2994 odf_push_text_p (state, FALSE);
2997 static void
2998 odf_validation_error_message_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
3000 oo_text_p_t *ptr;
3001 OOParseState *state = (OOParseState *)xin->user_state;
3003 g_return_if_fail (state->text_p_stack != NULL);
3004 ptr = state->text_p_stack->data;
3005 g_return_if_fail (ptr != NULL);
3007 if (state->cur_validation) {
3008 state->cur_validation->message = ptr->gstr;
3009 ptr->gstr = NULL;
3011 odf_pop_text_p (state);
3014 static void
3015 odf_validation_help_message (GsfXMLIn *xin, xmlChar const **attrs)
3017 OOParseState *state = (OOParseState *)xin->user_state;
3019 if (state->cur_validation)
3020 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2){
3021 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
3022 OO_NS_TABLE, "title" )) {
3023 g_free (state->cur_validation->help_title);
3024 state->cur_validation->help_title = g_strdup (CXML2C (attrs[1]));
3026 /* ignoring TABLE "display" */
3029 odf_push_text_p (state, FALSE);
3032 static void
3033 odf_validation_help_message_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
3035 oo_text_p_t *ptr;
3036 OOParseState *state = (OOParseState *)xin->user_state;
3038 g_return_if_fail (state->text_p_stack != NULL);
3039 ptr = state->text_p_stack->data;
3040 g_return_if_fail (ptr != NULL);
3042 if (state->cur_validation) {
3043 state->cur_validation->help_message = ptr->gstr;
3044 ptr->gstr = NULL;
3046 odf_pop_text_p (state);
3049 static void
3050 odf_adjust_offsets_col (OOParseState *state, int *col, double *x, gboolean absolute)
3052 ColRowInfo const *cr = sheet_col_get_info (state->pos.sheet,
3053 *col);
3054 int last = gnm_sheet_get_last_col (state->pos.sheet);
3055 if (absolute && *col > 0)
3056 *x -= sheet_col_get_distance_pts (state->pos.sheet, 0, *col);
3057 while (cr->size_pts < *x && *col < last) {
3058 (*col)++;
3059 (*x) -= cr->size_pts;
3060 cr = sheet_col_get_info (state->pos.sheet, *col);
3062 while (*x < 0 && *col > 0) {
3063 (*col)--;
3064 cr = sheet_col_get_info (state->pos.sheet, *col);
3065 (*x) += cr->size_pts;
3067 *x /= cr->size_pts;
3070 static void
3071 odf_adjust_offsets_row (OOParseState *state, int *row, double *y, gboolean absolute)
3073 ColRowInfo const *cr = sheet_row_get_info (state->pos.sheet,
3074 *row);
3075 int last = gnm_sheet_get_last_row (state->pos.sheet);
3076 if (absolute && *row > 0)
3077 *y -= sheet_row_get_distance_pts (state->pos.sheet, 0, *row);
3078 while (cr->size_pts < *y && *row < last) {
3079 (*row)++;
3080 (*y) -= cr->size_pts;
3081 cr = sheet_row_get_info (state->pos.sheet, *row);
3083 while (*y < 0 && *row > 0) {
3084 (*row)--;
3085 cr = sheet_row_get_info (state->pos.sheet, *row);
3086 (*y) += cr->size_pts;
3088 *y /= cr->size_pts;
3091 static void
3092 odf_adjust_offsets (OOParseState *state, GnmCellPos *pos, double *x, double *y, gboolean absolute)
3094 odf_adjust_offsets_col (state, &pos->col, x, absolute);
3095 odf_adjust_offsets_row (state, &pos->row, y, absolute);
3098 static gint
3099 odf_z_idx_compare (gconstpointer a, gconstpointer b)
3101 object_offset_t const *za = a, *zb = b;
3103 /* We are sorting indices in increasing order! */
3104 return (za->z_index - zb->z_index);
3107 static void
3108 odf_destroy_object_offset (gpointer data)
3110 object_offset_t *ob_off = data;
3112 g_free (ob_off->control);
3113 g_object_unref (ob_off->so);
3114 g_free (ob_off);
3117 static void
3118 odf_complete_control_setup (OOParseState *state, object_offset_t const *ob_off)
3120 OOControl *oc = g_hash_table_lookup (state->controls, ob_off->control);
3121 GnmExprTop const *result_texpr = NULL;
3122 SheetObject *so = ob_off->so;
3124 if (oc == NULL)
3125 return;
3127 if ((oc->t == sheet_widget_checkbox_get_type () ||
3128 oc->t == sheet_widget_radio_button_get_type ()) && oc->current_state != NULL)
3129 g_object_set (G_OBJECT (so), "active",
3130 strcmp (oc->current_state, "checked") == 0 ||
3131 strcmp (oc->current_state, "true") == 0, NULL);
3132 if (oc->linked_cell) {
3133 GnmParsePos pp;
3134 GnmRangeRef ref;
3135 char const *ptr = oo_rangeref_parse
3136 (&ref, oc->linked_cell,
3137 parse_pos_init_sheet (&pp, state->pos.sheet),
3138 NULL);
3139 if (ptr != oc->linked_cell
3140 && ref.a.sheet != invalid_sheet) {
3141 GnmValue *v = value_new_cellrange
3142 (&ref.a, &ref.a, 0, 0);
3143 GnmExprTop const *texpr
3144 = gnm_expr_top_new_constant (v);
3145 if (texpr != NULL) {
3146 if (oc->t == sheet_widget_scrollbar_get_type () ||
3147 oc->t == sheet_widget_spinbutton_get_type () ||
3148 oc->t == sheet_widget_slider_get_type ())
3149 sheet_widget_adjustment_set_link
3150 (so, texpr);
3151 else if (oc->t == sheet_widget_checkbox_get_type ())
3152 sheet_widget_checkbox_set_link
3153 (so, texpr);
3154 else if (oc->t == sheet_widget_radio_button_get_type ())
3155 sheet_widget_radio_button_set_link
3156 (so, texpr);
3157 else if (oc->t == sheet_widget_button_get_type ())
3158 sheet_widget_button_set_link
3159 (so, texpr);
3160 else if (oc->t == sheet_widget_list_get_type () ||
3161 oc->t == sheet_widget_combo_get_type ()) {
3162 gnm_expr_top_ref ((result_texpr = texpr));
3163 sheet_widget_list_base_set_links (so, texpr, NULL);
3165 gnm_expr_top_unref (texpr);
3169 if (oc->t == sheet_widget_list_get_type () ||
3170 oc->t == sheet_widget_combo_get_type ()) {
3171 if (oc->source_cell_range) {
3172 GnmParsePos pp;
3173 GnmRangeRef ref;
3174 char const *ptr = oo_rangeref_parse
3175 (&ref, oc->source_cell_range,
3176 parse_pos_init_sheet (&pp, state->pos.sheet),
3177 NULL);
3178 if (ptr != oc->source_cell_range &&
3179 ref.a.sheet != invalid_sheet) {
3180 GnmValue *v = value_new_cellrange
3181 (&ref.a, &ref.b, 0, 0);
3182 GnmExprTop const *texpr
3183 = gnm_expr_top_new_constant (v);
3184 if (texpr != NULL) {
3185 sheet_widget_list_base_set_links
3186 (so,
3187 result_texpr, texpr);
3188 gnm_expr_top_unref (texpr);
3192 if (result_texpr != NULL)
3193 gnm_expr_top_unref (result_texpr);
3194 sheet_widget_list_base_set_result_type (so, oc->as_index);
3198 static void
3199 oo_table_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
3201 OOParseState *state = (OOParseState *)xin->user_state;
3202 int max_cols, max_rows;
3203 GSList *l;
3204 gint top_z = -1;
3206 maybe_update_progress (xin);
3208 if (NULL != state->print.page_breaks.h) {
3209 print_info_set_breaks (state->pos.sheet->print_info,
3210 state->print.page_breaks.h);
3211 state->print.page_breaks.h = NULL;
3213 if (NULL != state->print.page_breaks.v) {
3214 print_info_set_breaks (state->pos.sheet->print_info,
3215 state->print.page_breaks.v);
3216 state->print.page_breaks.v = NULL;
3219 max_cols = gnm_sheet_get_max_cols (state->pos.sheet);
3220 max_rows = gnm_sheet_get_max_rows (state->pos.sheet);
3222 if (state->print.rep_rows_from >= 0) {
3223 if (state->print.rep_rows_to < 0)
3224 state->print.rep_rows_to = max_rows - 1;
3225 g_free (state->pos.sheet->print_info->repeat_top);
3226 state->pos.sheet->print_info->repeat_top
3227 = g_strdup (rows_name (state->print.rep_rows_from,
3228 state->print.rep_rows_to));
3230 if (state->print.rep_cols_from >= 0) {
3231 if (state->print.rep_cols_to < 0)
3232 state->print.rep_cols_to = max_cols - 1;
3233 g_free (state->pos.sheet->print_info->repeat_left);
3234 state->pos.sheet->print_info->repeat_left
3235 = g_strdup (cols_name (state->print.rep_cols_from,
3236 state->print.rep_cols_to));
3239 /* We need to fix the anchors of all offsets, ensure that each object has an "odf-z-index", */
3240 /* and add the objects in the correct order. */
3241 state->chart_list = g_slist_reverse (state->chart_list);
3243 for (l = state->chart_list; l != NULL; l = l->next) {
3244 object_offset_t *ob_off = l->data;
3245 if (top_z < ob_off->z_index)
3246 top_z = ob_off->z_index;
3249 for (l = state->chart_list; l != NULL; l = l->next) {
3250 object_offset_t *ob_off = l->data;
3251 if (ob_off->z_index < 0) {
3252 top_z++;
3253 ob_off->z_index = top_z;
3254 if (state->debug)
3255 g_print ("Object Ordering: Object without z-index encountered.\n");
3259 state->chart_list = g_slist_sort (state->chart_list,
3260 odf_z_idx_compare);
3263 for (l = state->chart_list; l != NULL; l = l->next) {
3264 object_offset_t *ob_off = l->data;
3265 SheetObjectAnchor new;
3266 SheetObjectAnchor const *old = sheet_object_get_anchor (ob_off->so);
3267 GnmRange cell_base = *sheet_object_get_range (ob_off->so);
3269 if (old->mode != GNM_SO_ANCHOR_ABSOLUTE) {
3270 odf_adjust_offsets (state, &cell_base.start, &ob_off->frame_offset[0],
3271 &ob_off->frame_offset[1], ob_off->absolute_distance);
3272 if (old->mode == GNM_SO_ANCHOR_TWO_CELLS)
3273 odf_adjust_offsets (state, &cell_base.end, &ob_off->frame_offset[2],
3274 &ob_off->frame_offset[3], ob_off->absolute_distance);
3276 sheet_object_anchor_init (&new, &cell_base, ob_off->frame_offset,
3277 old->base.direction,
3278 old->mode);
3279 sheet_object_set_anchor (ob_off->so, &new);
3281 sheet_object_set_sheet (ob_off->so, state->pos.sheet);
3282 if (ob_off->control)
3283 odf_complete_control_setup (state, ob_off);
3284 odf_destroy_object_offset (ob_off);
3285 l->data = NULL;
3288 g_slist_free (state->chart_list);
3289 state->chart_list = NULL;
3290 state->pos.eval.col = state->pos.eval.row = 0;
3291 state->pos.sheet = NULL;
3294 static void
3295 oo_append_page_break (OOParseState *state, int pos, gboolean is_vert, gboolean is_manual)
3297 GnmPageBreaks *breaks;
3299 if (is_vert) {
3300 if (NULL == (breaks = state->print.page_breaks.v))
3301 breaks = state->print.page_breaks.v = gnm_page_breaks_new (TRUE);
3302 } else {
3303 if (NULL == (breaks = state->print.page_breaks.h))
3304 breaks = state->print.page_breaks.h = gnm_page_breaks_new (FALSE);
3307 gnm_page_breaks_append_break (breaks, pos,
3308 is_manual ? GNM_PAGE_BREAK_MANUAL : GNM_PAGE_BREAK_NONE);
3311 static void
3312 oo_set_page_break (OOParseState *state, int pos, gboolean is_vert, gboolean is_manual)
3314 GnmPageBreaks *breaks = (is_vert) ? state->print.page_breaks.v : state->print.page_breaks.h;
3316 switch (gnm_page_breaks_get_break (breaks, pos)) {
3317 case GNM_PAGE_BREAK_NONE:
3318 oo_append_page_break (state, pos, is_vert, is_manual);
3319 return;
3320 case GNM_PAGE_BREAK_MANUAL:
3321 return;
3322 case GNM_PAGE_BREAK_AUTO:
3323 default:
3324 if (is_manual)
3325 gnm_page_breaks_set_break (breaks, pos, GNM_PAGE_BREAK_MANUAL);
3326 break;
3330 static void
3331 oo_col_row_style_apply_breaks (OOParseState *state, OOColRowStyle *cr_style,
3332 int pos, gboolean is_vert)
3335 if (cr_style->break_before != OO_PAGE_BREAK_NONE)
3336 oo_set_page_break (state, pos, is_vert,
3337 cr_style->break_before == OO_PAGE_BREAK_MANUAL);
3338 if (cr_style->break_after != OO_PAGE_BREAK_NONE)
3339 oo_append_page_break (state, pos+1, is_vert,
3340 cr_style->break_after == OO_PAGE_BREAK_MANUAL);
3343 static void
3344 oo_update_data_extent (OOParseState *state, int cols, int rows)
3346 if (state->extent_data.col < (state->pos.eval.col + cols - 1))
3347 state->extent_data.col = state->pos.eval.col + cols - 1;
3348 if (state->extent_data.row < (state->pos.eval.row + rows - 1))
3349 state->extent_data.row = state->pos.eval.row + rows - 1;
3352 static OOCellStyle *
3353 odf_oo_cell_style_new (GnmStyle *style)
3355 OOCellStyle *oostyle = g_new0 (OOCellStyle, 1);
3356 oostyle->ref = 1;
3357 if (style)
3358 oostyle->style = gnm_style_dup (style);
3359 else
3360 oostyle->style = gnm_style_new ();
3361 return oostyle;
3364 static void
3365 odf_oo_cell_style_unref (OOCellStyle *oostyle)
3367 if (oostyle != NULL && (--(oostyle->ref)) == 0) {
3368 gnm_style_unref (oostyle->style);
3369 g_slist_free_full (oostyle->styles, (GDestroyNotify) odf_oo_cell_style_unref);
3370 g_slist_free_full (oostyle->conditions, g_free);
3371 g_slist_free_full (oostyle->bases, g_free);
3372 g_free (oostyle);
3376 static OOCellStyle *
3377 odf_oo_cell_style_ref (OOCellStyle *oostyle)
3379 oostyle->ref++;
3380 return oostyle;
3383 static OOCellStyle *
3384 odf_oo_cell_style_copy (OOCellStyle *oostyle)
3386 OOCellStyle *new = odf_oo_cell_style_new (oostyle->style);
3387 new->styles = go_slist_map (oostyle->styles, (GOMapFunc)odf_oo_cell_style_ref);
3388 new->conditions = go_slist_map (oostyle->conditions, (GOMapFunc)g_strdup);
3389 new->bases = go_slist_map (oostyle->bases, (GOMapFunc)g_strdup);
3390 return new;
3393 static void
3394 odf_oo_cell_style_attach_condition (OOCellStyle *oostyle, OOCellStyle *cstyle,
3395 gchar const *condition, gchar const *base)
3397 g_return_if_fail (oostyle != NULL);
3398 g_return_if_fail (cstyle != NULL);
3399 g_return_if_fail (condition != NULL);
3401 if (base == NULL)
3402 base = "";
3404 oostyle->styles = g_slist_append (oostyle->styles, odf_oo_cell_style_ref (cstyle));
3405 oostyle->conditions = g_slist_append (oostyle->conditions, g_strdup (condition));
3406 oostyle->bases = g_slist_append (oostyle->bases, g_strdup (base));
3409 static gboolean
3410 odf_style_load_two_values (GsfXMLIn *xin, char *condition, GnmStyleCond *cond, gchar const *base, OOFormula f_type)
3412 condition = g_strstrip (condition);
3413 if (*(condition++) == '(') {
3414 guint len = strlen (condition);
3415 char *end = condition + len - 1;
3416 if (*end == ')') {
3417 GnmParsePos pp;
3418 GnmExprTop const *texpr;
3420 odf_init_pp (&pp, xin, base);
3421 len -= 1;
3422 *end = '\0';
3423 while (1) {
3424 gchar * try = g_strrstr_len (condition, len, ",");
3425 GnmExprTop const *texpr;
3427 if (try == NULL || try == condition) return FALSE;
3429 texpr = oo_expr_parse_str
3430 (xin, try + 1, &pp,
3431 GNM_EXPR_PARSE_DEFAULT,
3432 f_type);
3433 if (texpr != NULL) {
3434 gnm_style_cond_set_expr (cond, texpr, 1);
3435 gnm_expr_top_unref (texpr);
3436 *try = '\0';
3437 break;
3439 len = try - condition - 1;
3441 texpr = oo_expr_parse_str
3442 (xin, condition, &pp,
3443 GNM_EXPR_PARSE_DEFAULT,
3444 f_type);
3445 gnm_style_cond_set_expr (cond, texpr, 0);
3446 if (texpr) gnm_expr_top_unref (texpr);
3448 return (gnm_style_cond_get_expr (cond, 0) &&
3449 gnm_style_cond_get_expr (cond, 1));
3452 return FALSE;
3455 static gboolean
3456 odf_style_load_one_value (GsfXMLIn *xin, char *condition, GnmStyleCond *cond, gchar const *base, OOFormula f_type)
3458 GnmParsePos pp;
3459 GnmExprTop const *texpr;
3461 odf_init_pp (&pp, xin, base);
3462 texpr = oo_expr_parse_str
3463 (xin, condition, &pp,
3464 GNM_EXPR_PARSE_DEFAULT,
3465 f_type);
3466 gnm_style_cond_set_expr (cond, texpr, 0);
3467 if (texpr) gnm_expr_top_unref (texpr);
3468 return (gnm_style_cond_get_expr (cond, 0) != NULL);
3471 static void
3472 odf_style_add_condition (GsfXMLIn *xin, GnmStyle *style, GnmStyle *cstyle,
3473 gchar const *condition, gchar const *base)
3475 OOParseState *state = (OOParseState *)xin->user_state;
3477 gchar const *full_condition = condition;
3478 GnmStyleCond *cond = NULL;
3479 GnmStyleConditions *sc;
3480 gboolean success = FALSE;
3481 OOFormula f_type;
3482 Sheet *sheet = state->pos.sheet;
3484 g_return_if_fail (style != NULL);
3485 g_return_if_fail (cstyle != NULL);
3486 g_return_if_fail (condition != NULL);
3487 g_return_if_fail (base != NULL);
3489 f_type = odf_get_formula_type (xin, &condition);
3491 if (g_str_has_prefix (condition, "cell-content()")) {
3492 condition += strlen ("cell-content()") - 1;
3493 while (*(++condition) == ' ');
3494 switch (*(condition++)) {
3495 case '<':
3496 if (*condition == '=') {
3497 condition++;
3498 cond = gnm_style_cond_new (GNM_STYLE_COND_LTE, sheet);
3499 } else
3500 cond = gnm_style_cond_new (GNM_STYLE_COND_LT, sheet);
3501 success = TRUE;
3502 break;
3503 case '>':
3504 if (*condition == '=') {
3505 condition++;
3506 cond = gnm_style_cond_new (GNM_STYLE_COND_GTE, sheet);
3507 } else
3508 cond = gnm_style_cond_new (GNM_STYLE_COND_GT, sheet);
3509 success = TRUE;
3510 break;
3511 break;
3512 case '=':
3513 cond = gnm_style_cond_new (GNM_STYLE_COND_EQUAL, sheet);
3514 success = TRUE;
3515 break;
3516 case '!':
3517 if (*condition == '=') {
3518 condition++;
3519 cond = gnm_style_cond_new (GNM_STYLE_COND_NOT_EQUAL, sheet);
3520 success = TRUE;
3522 break;
3523 default:
3524 break;
3526 if (success) {
3527 char *text = g_strdup (condition);
3528 success = odf_style_load_one_value (xin, text, cond, base, f_type);
3529 g_free (text);
3532 } else if (g_str_has_prefix (condition, "cell-content-is-between")) {
3533 char *text;
3534 cond = gnm_style_cond_new (GNM_STYLE_COND_BETWEEN, sheet);
3535 condition += strlen ("cell-content-is-between");
3536 text = g_strdup (condition);
3537 success = odf_style_load_two_values (xin, text, cond, base, f_type);
3538 g_free (text);
3539 } else if (g_str_has_prefix (condition, "cell-content-is-not-between")) {
3540 char *text;
3541 cond = gnm_style_cond_new (GNM_STYLE_COND_NOT_BETWEEN, sheet);
3542 condition += strlen ("cell-content-is-not-between");
3543 text = g_strdup (condition);
3544 success = odf_style_load_two_values (xin, text, cond, base, f_type);
3545 g_free (text);
3546 } else if (g_str_has_prefix (condition, "is-true-formula(") && g_str_has_suffix (condition, ")") ) {
3547 char *text;
3548 cond = gnm_style_cond_new (GNM_STYLE_COND_CUSTOM, sheet);
3549 condition += strlen ("is-true-formula(");
3550 text = g_strdup (condition);
3551 *(text + strlen (text) - 1) = '\0';
3552 success = odf_style_load_one_value (xin, text, cond, base, f_type);
3553 g_free (text);
3556 if (!success || !cond) {
3557 if (cond)
3558 gnm_style_cond_free (cond);
3559 oo_warning (xin,
3560 _("Unknown condition '%s' encountered, ignoring."),
3561 full_condition);
3562 return;
3565 gnm_style_cond_canonicalize (cond);
3566 gnm_style_cond_set_overlay (cond, cstyle);
3568 if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS) &&
3569 (sc = gnm_style_get_conditions (style)) != NULL)
3570 gnm_style_conditions_insert (sc, cond, -1);
3571 else {
3572 sc = gnm_style_conditions_new (sheet);
3573 gnm_style_conditions_insert (sc, cond, -1);
3574 gnm_style_set_conditions (style, sc);
3577 gnm_style_cond_free (cond);
3580 static GnmStyle *
3581 odf_style_from_oo_cell_style (GsfXMLIn *xin, OOCellStyle *oostyle)
3583 g_return_val_if_fail (oostyle != NULL, NULL);
3585 if (oostyle->conditions != NULL) {
3586 /* We need to incorporate the conditional styles */
3587 GnmStyle *new_style = gnm_style_dup (oostyle->style);
3588 GSList *styles = oostyle->styles, *conditions = oostyle->conditions, *bases = oostyle->bases;
3589 while (styles && conditions && bases) {
3590 GnmStyle *cstyle = odf_style_from_oo_cell_style (xin, styles->data);
3591 odf_style_add_condition (xin, new_style, cstyle,
3592 conditions->data, bases->data);
3593 gnm_style_unref (cstyle);
3594 styles = styles->next;
3595 conditions = conditions->next;
3596 bases = bases->next;
3598 gnm_style_unref (oostyle->style);
3599 oostyle->style = new_style;
3600 g_slist_free_full (oostyle->styles, (GDestroyNotify) odf_oo_cell_style_unref);
3601 g_slist_free_full (oostyle->conditions, g_free);
3602 g_slist_free_full (oostyle->bases, g_free);
3603 oostyle->styles = NULL;
3604 oostyle->conditions = NULL;
3605 oostyle->bases = NULL;
3607 gnm_style_ref (oostyle->style);
3608 return oostyle->style;
3611 static void
3612 oo_col_start (GsfXMLIn *xin, xmlChar const **attrs)
3614 OOParseState *state = (OOParseState *)xin->user_state;
3615 OOColRowStyle *col_info = NULL;
3616 OOCellStyle *oostyle = NULL;
3617 GnmStyle *style = NULL;
3618 int i, repeat_count = 1;
3619 gboolean hidden = FALSE;
3620 int max_cols = gnm_sheet_get_max_cols (state->pos.sheet);
3622 maybe_update_progress (xin);
3624 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
3625 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "default-cell-style-name")) {
3626 oostyle = g_hash_table_lookup (state->styles.cell, attrs[1]);
3627 if (oostyle)
3628 style = odf_style_from_oo_cell_style (xin, oostyle);
3629 else
3630 oo_warning (xin, "The cell style with name <%s> is missing", CXML2C (attrs[01]));
3631 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "style-name"))
3632 col_info = g_hash_table_lookup (state->styles.col, attrs[1]);
3633 else if (oo_attr_int_range (xin, attrs, OO_NS_TABLE, "number-columns-repeated",
3634 &repeat_count, 0, INT_MAX - state->pos.eval.col))
3636 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "visibility"))
3637 hidden = !attr_eq (attrs[1], "visible");
3639 if (state->pos.eval.col + repeat_count > max_cols) {
3640 max_cols = gnm_sheet_get_max_cols (state->pos.sheet);
3641 if (state->pos.eval.col + repeat_count > max_cols) {
3642 oo_warning (xin, _("Ignoring column information beyond"
3643 " column %i"), max_cols);
3644 repeat_count = max_cols - state->pos.eval.col - 1;
3648 if (hidden)
3649 colrow_set_visibility (state->pos.sheet, TRUE, FALSE, state->pos.eval.col,
3650 state->pos.eval.col + repeat_count - 1);
3652 if (NULL != style) {
3653 GnmRange r;
3654 r.start.col = state->pos.eval.col;
3655 r.end.col = state->pos.eval.col + repeat_count - 1;
3656 r.start.row = 0;
3657 r.end.row = ((sheet_order_t *)g_slist_nth_data (state->sheet_order, state->table_n))->rows - 1;
3658 sheet_style_apply_range (state->pos.sheet, &r, style);
3660 if (col_info != NULL) {
3661 if (state->default_style.columns == NULL && repeat_count > max_cols/2) {
3662 int const last = state->pos.eval.col + repeat_count;
3663 state->default_style.columns = g_memdup (col_info, sizeof (*col_info));
3664 state->default_style.columns->count = repeat_count;
3665 sheet_col_set_default_size_pts (state->pos.sheet,
3666 state->default_style.columns->size_pts);
3667 if (col_info->break_before != OO_PAGE_BREAK_NONE)
3668 for (i = state->pos.eval.row ; i < last; i++ )
3669 oo_set_page_break (state, i, TRUE,
3670 col_info->break_before
3671 == OO_PAGE_BREAK_MANUAL);
3672 if (col_info->break_after!= OO_PAGE_BREAK_NONE)
3673 for (i = state->pos.eval.col ; i < last; i++ )
3674 oo_append_page_break (state, i+1, FALSE,
3675 col_info->break_after
3676 == OO_PAGE_BREAK_MANUAL);
3677 } else {
3678 int last = state->pos.eval.col + repeat_count;
3679 for (i = state->pos.eval.col ; i < last; i++ ) {
3680 /* I can not find a listing for the default but will
3681 * assume it is TRUE to keep the files rational */
3682 if (col_info->size_pts > 0.)
3683 sheet_col_set_size_pts (state->pos.sheet, i,
3684 col_info->size_pts, col_info->manual);
3685 oo_col_row_style_apply_breaks (state, col_info, i, TRUE);
3687 col_info->count += repeat_count;
3691 state->pos.eval.col += repeat_count;
3694 static void
3695 odf_table_header_rows (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
3697 OOParseState *state = (OOParseState *)xin->user_state;
3699 if (state->print.rep_rows_from < 0)
3700 state->print.rep_rows_from = state->pos.eval.row;
3701 /* otherwise we are continuing an existing range */
3703 static void
3704 odf_table_header_rows_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
3706 OOParseState *state = (OOParseState *)xin->user_state;
3708 state->print.rep_rows_to = state->pos.eval.row - 1;
3711 static void
3712 odf_table_header_cols (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
3714 OOParseState *state = (OOParseState *)xin->user_state;
3716 if (state->print.rep_cols_from < 0)
3717 state->print.rep_cols_from = state->pos.eval.col;
3718 /* otherwise we are continuing an existing range */
3720 static void
3721 odf_table_header_cols_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
3723 OOParseState *state = (OOParseState *)xin->user_state;
3725 state->print.rep_cols_to = state->pos.eval.col - 1;
3728 static void
3729 oo_row_start (GsfXMLIn *xin, xmlChar const **attrs)
3731 OOParseState *state = (OOParseState *)xin->user_state;
3732 OOColRowStyle *row_info = NULL;
3733 OOCellStyle *oostyle = NULL;
3734 GnmStyle *style = NULL;
3735 int i, repeat_count = 1;
3736 gboolean hidden = FALSE;
3737 int max_rows = gnm_sheet_get_max_rows (state->pos.sheet);
3739 maybe_update_progress (xin);
3741 state->pos.eval.col = 0;
3743 if (state->pos.eval.row >= max_rows) {
3744 max_rows = gnm_sheet_get_max_rows (state->pos.sheet);
3745 if (state->pos.eval.row >= max_rows) {
3746 oo_warning (xin, _("Content past the maximum number of rows (%i) supported."), max_rows);
3747 state->row_inc = 0;
3748 return;
3752 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
3753 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "default-cell-style-name")) {
3754 oostyle = g_hash_table_lookup (state->styles.cell, attrs[1]);
3755 if (oostyle)
3756 style = odf_style_from_oo_cell_style (xin, oostyle);
3757 else
3758 oo_warning (xin, "The cell style with name <%s> is missing", CXML2C (attrs[01]));
3759 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "style-name"))
3760 row_info = g_hash_table_lookup (state->styles.row, attrs[1]);
3761 else if (oo_attr_int_range (xin, attrs, OO_NS_TABLE, "number-rows-repeated", &repeat_count, 0,
3762 INT_MAX - state->pos.eval.row))
3764 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "visibility"))
3765 hidden = !attr_eq (attrs[1], "visible");
3768 if (state->pos.eval.row + repeat_count > max_rows)
3769 /* There are probably lots of empty lines at the end. */
3770 repeat_count = max_rows - state->pos.eval.row - 1;
3772 if (hidden)
3773 colrow_set_visibility (state->pos.sheet, FALSE, FALSE, state->pos.eval.row,
3774 state->pos.eval.row+repeat_count - 1);
3776 if (NULL != style) {
3777 GnmRange r;
3778 r.start.row = state->pos.eval.row;
3779 r.end.row = state->pos.eval.row + repeat_count - 1;
3780 r.start.col = 0;
3781 r.end.col = ((sheet_order_t *)g_slist_nth_data (state->sheet_order, state->table_n))->cols - 1;;
3782 sheet_style_apply_range (state->pos.sheet, &r, style);
3785 if (row_info != NULL) {
3786 if (state->default_style.rows == NULL && repeat_count > max_rows/2) {
3787 int const last = state->pos.eval.row + repeat_count;
3788 state->default_style.rows = g_memdup (row_info, sizeof (*row_info));
3789 state->default_style.rows->count = repeat_count;
3790 sheet_row_set_default_size_pts (state->pos.sheet,
3791 state->default_style.rows->size_pts);
3792 if (row_info->break_before != OO_PAGE_BREAK_NONE)
3793 for (i = state->pos.eval.row ; i < last; i++ )
3794 oo_set_page_break (state, i, FALSE,
3795 row_info->break_before
3796 == OO_PAGE_BREAK_MANUAL);
3797 if (row_info->break_after!= OO_PAGE_BREAK_NONE)
3798 for (i = state->pos.eval.row ; i < last; i++ )
3799 oo_append_page_break (state, i+1, FALSE,
3800 row_info->break_after
3801 == OO_PAGE_BREAK_MANUAL);
3802 } else {
3803 int const last = state->pos.eval.row + repeat_count;
3804 for (i = state->pos.eval.row ; i < last; i++ ) {
3805 if (row_info->size_pts > 0.)
3806 sheet_row_set_size_pts (state->pos.sheet, i,
3807 row_info->size_pts, row_info->manual);
3808 oo_col_row_style_apply_breaks (state, row_info, i, FALSE);
3810 row_info->count += repeat_count;
3814 state->row_inc = repeat_count;
3817 static void
3818 oo_row_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
3820 OOParseState *state = (OOParseState *)xin->user_state;
3821 state->pos.eval.row += state->row_inc;
3824 static OOFormula
3825 odf_get_formula_type (GsfXMLIn *xin, char const **str)
3827 OOParseState *state = (OOParseState *)xin->user_state;
3828 OOFormula f_type = FORMULA_NOT_SUPPORTED;
3829 if (state->ver == OOO_VER_OPENDOC) {
3830 if (strncmp (*str, "msoxl:", 6) == 0) {
3831 *str += 6;
3832 f_type = FORMULA_MICROSOFT;
3833 } else if (strncmp (*str, "oooc:", 5) == 0) {
3834 *str += 5;
3835 f_type = FORMULA_OLD_OPENOFFICE;
3836 } else if (strncmp (*str, "of:", 3) == 0) {
3837 *str += 3;
3838 f_type = FORMULA_OPENFORMULA;
3839 } else {
3840 /* They really should include a namespace */
3841 /* We assume that it is an OpenFormula expression */
3842 *str += 0;
3843 f_type = FORMULA_OPENFORMULA;
3845 } else if (state->ver == OOO_VER_1)
3846 f_type = FORMULA_OLD_OPENOFFICE;
3848 return f_type;
3851 static void
3852 oo_cell_start (GsfXMLIn *xin, xmlChar const **attrs)
3854 OOParseState *state = (OOParseState *)xin->user_state;
3855 GnmExprTop const *texpr = NULL;
3856 GnmValue *val = NULL;
3857 gboolean has_date = FALSE, has_datetime = FALSE, has_time = FALSE;
3858 gboolean bool_val;
3859 gnm_float float_val = 0;
3860 int array_cols = -1, array_rows = -1;
3861 int merge_cols = 1, merge_rows = 1;
3862 OOCellStyle *oostyle = NULL;
3863 GnmStyle *style = NULL;
3864 char const *style_name = NULL;
3865 char const *validation_name = NULL;
3866 char const *expr_string;
3867 GnmRange tmp;
3868 int max_cols = gnm_sheet_get_max_cols (state->pos.sheet);
3869 int max_rows = gnm_sheet_get_max_rows (state->pos.sheet);
3870 GnmValidation *validation = NULL;
3871 gboolean possible_error_constant = FALSE;
3872 gboolean columns_spanned_fake = FALSE;
3874 maybe_update_progress (xin);
3876 state->col_inc = 1;
3877 state->content_is_error = FALSE;
3878 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
3879 if (oo_attr_int_range (xin, attrs, OO_NS_TABLE, "number-columns-repeated",
3880 &state->col_inc, 0, INT_MAX - state->pos.eval.col))
3882 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "error-value"))
3883 /* While the value of this attribute contains the true error value */
3884 /* it could have been retained by a consumer/producer who did change */
3885 /* the cell value, so we just remember that we saw this attribute. */
3886 possible_error_constant = TRUE;
3887 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "formula")) {
3888 OOFormula f_type;
3890 if (attrs[1] == NULL) {
3891 oo_warning (xin, _("Missing expression"));
3892 continue;
3895 expr_string = CXML2C (attrs[1]);
3896 f_type = odf_get_formula_type (xin, &expr_string);
3897 if (f_type == FORMULA_NOT_SUPPORTED) {
3898 oo_warning (xin,
3899 _("Unsupported formula type encountered: %s"),
3900 expr_string);
3901 continue;
3904 expr_string = gnm_expr_char_start_p (expr_string);
3905 if (expr_string == NULL)
3906 oo_warning (xin, _("Expression '%s' does not start "
3907 "with a recognized character"), attrs[1]);
3908 else if (*expr_string == '\0')
3909 /* Ick. They seem to store error cells as
3910 * having value date with expr : '=' and the
3911 * message in the content.
3913 state->content_is_error = TRUE;
3914 else {
3915 texpr = oo_expr_parse_str
3916 (xin, expr_string,
3917 &state->pos, GNM_EXPR_PARSE_DEFAULT, f_type);
3918 if (possible_error_constant && texpr != NULL &&
3919 GNM_EXPR_GET_OPER (texpr->expr) == GNM_EXPR_OP_CONSTANT) {
3920 GnmValue const *eval = gnm_expr_get_constant (texpr->expr);
3921 if (VALUE_IS_ERROR (eval)) {
3922 value_release (val);
3923 val = value_dup (eval);
3924 gnm_expr_top_unref (texpr);
3925 texpr = NULL;
3929 } else if (oo_attr_bool (xin, attrs,
3930 (state->ver == OOO_VER_OPENDOC) ?
3931 OO_NS_OFFICE : OO_NS_TABLE,
3932 "boolean-value", &bool_val)) {
3933 if (val == NULL)
3934 val = value_new_bool (bool_val);
3935 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
3936 (state->ver == OOO_VER_OPENDOC) ? OO_NS_OFFICE : OO_NS_TABLE,
3937 "date-value")) {
3938 if (val == NULL) {
3939 unsigned y, m, d, h, mi;
3940 gnm_float s;
3941 unsigned n = sscanf (CXML2C (attrs[1]), "%u-%u-%uT%u:%u:%" GNM_SCANF_g,
3942 &y, &m, &d, &h, &mi, &s);
3944 if (n >= 3) {
3945 GDate date;
3946 g_date_set_dmy (&date, d, m, y);
3947 if (g_date_valid (&date)) {
3948 unsigned d_serial = go_date_g_to_serial (&date,
3949 workbook_date_conv (state->pos.wb));
3950 if (n >= 6) {
3951 gnm_float time_frac
3952 = h + ((gnm_float)mi / 60) +
3953 ((gnm_float)s / 3600);
3954 val = value_new_float (d_serial + time_frac / 24);
3955 has_datetime = TRUE;
3956 } else {
3957 val = value_new_int (d_serial);
3958 has_date = TRUE;
3963 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
3964 (state->ver == OOO_VER_OPENDOC) ?
3965 OO_NS_OFFICE : OO_NS_TABLE,
3966 "time-value")) {
3967 if (val == NULL) {
3968 unsigned h, m, s;
3969 if (3 == sscanf (CXML2C (attrs[1]), "PT%uH%uM%uS", &h, &m, &s)) {
3970 unsigned secs = h * 3600 + m * 60 + s;
3971 val = value_new_float (secs / (gnm_float)86400);
3972 has_time = TRUE;
3975 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
3976 (state->ver == OOO_VER_OPENDOC) ?
3977 OO_NS_OFFICE : OO_NS_TABLE,
3978 "string-value")) {
3979 if (val == NULL)
3980 val = value_new_string (CXML2C (attrs[1]));
3981 } else if (oo_attr_float (xin, attrs,
3982 (state->ver == OOO_VER_OPENDOC) ? OO_NS_OFFICE : OO_NS_TABLE,
3983 "value", &float_val)) {
3984 if (val == NULL)
3985 val = value_new_float (float_val);
3986 } else if (oo_attr_int_range (xin, attrs, OO_NS_TABLE,
3987 "number-matrix-columns-spanned",
3988 &array_cols, 0, INT_MAX))
3990 else if (oo_attr_int_range (xin, attrs, OO_NS_TABLE,
3991 "number-matrix-rows-spanned",
3992 &array_rows, 0, INT_MAX))
3994 else if (oo_attr_int_range (xin, attrs, OO_NS_TABLE,
3995 "number-columns-spanned", &merge_cols, 0, INT_MAX))
3997 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT,
3998 "columns-spanned-fake",
3999 &columns_spanned_fake))
4001 else if (oo_attr_int_range (xin, attrs, OO_NS_TABLE,
4002 "number-rows-spanned", &merge_rows, 0, INT_MAX))
4004 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "style-name"))
4005 style_name = attrs[1];
4006 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE,
4007 "content-validation-name"))
4008 validation_name = attrs[1];
4011 if (columns_spanned_fake)
4012 merge_cols = 1;
4014 if (state->pos.eval.col >= max_cols ||
4015 state->pos.eval.row >= max_rows) {
4016 if (texpr)
4017 gnm_expr_top_unref (texpr);
4018 value_release (val);
4019 return;
4022 merge_cols = MIN (merge_cols, max_cols - state->pos.eval.col);
4023 merge_rows = MIN (merge_rows, max_rows - state->pos.eval.row);
4025 if (style_name != NULL) {
4026 if (has_datetime)
4027 oostyle = g_hash_table_lookup (state->styles.cell_datetime, style_name);
4028 else if (has_date)
4029 oostyle = g_hash_table_lookup (state->styles.cell_date, style_name);
4030 else if (has_time)
4031 oostyle = g_hash_table_lookup (state->styles.cell_time, style_name);
4033 if (oostyle == NULL) {
4034 oostyle = g_hash_table_lookup (state->styles.cell, style_name);
4035 if (((oostyle != NULL) || (state->ver == OOO_VER_1))
4036 && (has_datetime || has_date || has_time)) {
4037 if ((oostyle == NULL) ||
4038 ((!gnm_style_is_element_set (oostyle->style, MSTYLE_FORMAT))
4039 || go_format_is_general (gnm_style_get_format (oostyle->style)))) {
4040 GOFormat *format;
4041 oostyle = (oostyle == NULL) ? odf_oo_cell_style_new (NULL) :
4042 odf_oo_cell_style_copy (oostyle);
4043 if (has_datetime) {
4044 format = go_format_default_date_time ();
4045 g_hash_table_replace (state->styles.cell_datetime,
4046 g_strdup (style_name), oostyle);
4047 } else if (has_date) {
4048 format = go_format_default_date ();
4049 g_hash_table_replace (state->styles.cell_date,
4050 g_strdup (style_name), oostyle);
4051 } else {
4052 format = go_format_default_time ();
4053 g_hash_table_replace (state->styles.cell_time,
4054 g_strdup (style_name), oostyle);
4056 gnm_style_set_format (oostyle->style, format);
4060 if (oostyle != NULL)
4061 style = odf_style_from_oo_cell_style (xin, oostyle);
4064 if (validation_name != NULL) {
4065 GnmInputMsg *message;
4066 if (NULL != (validation = odf_validations_translate (xin, validation_name))) {
4067 if (style == NULL)
4068 style = gnm_style_new ();
4069 /* 1 reference for style*/
4070 gnm_style_set_validation (style, validation);
4072 if (NULL != (message = odf_validation_get_input_message (xin, validation_name))) {
4073 if (style == NULL)
4074 style = gnm_style_new ();
4075 /* 1 reference for style*/
4076 gnm_style_set_input_msg (style, message);
4080 if (style == NULL && (merge_cols > 1 || merge_rows > 1)) {
4081 /* We may not have a new style but the current cell may */
4082 /* have been assigned a style earlier */
4083 GnmStyle const *old_style
4084 = sheet_style_get (state->pos.sheet, state->pos.eval.col,
4085 state->pos.eval.row);
4086 if (old_style != NULL)
4087 style = gnm_style_dup (old_style);
4090 if (style != NULL) {
4091 if (state->col_inc > 1 || state->row_inc > 1) {
4092 range_init_cellpos_size (&tmp, &state->pos.eval,
4093 state->col_inc, state->row_inc);
4094 sheet_style_apply_range (state->pos.sheet, &tmp, style);
4095 } else if (merge_cols > 1 || merge_rows > 1) {
4096 range_init_cellpos_size (&tmp, &state->pos.eval,
4097 merge_cols, merge_rows);
4098 sheet_style_apply_range (state->pos.sheet, &tmp, style);
4099 } else {
4100 sheet_style_apply_pos (state->pos.sheet,
4101 state->pos.eval.col, state->pos.eval.row,
4102 style);
4106 state->text_p_for_cell.content_is_simple = FALSE;
4107 if (texpr != NULL) {
4108 GnmCell *cell = sheet_cell_fetch (state->pos.sheet,
4109 state->pos.eval.col,
4110 state->pos.eval.row);
4112 if (array_cols > 0 || array_rows > 0) {
4113 GnmRange r;
4114 Sheet *sheet = state->pos.sheet;
4116 if (array_cols <= 0) {
4117 array_cols = 1;
4118 oo_warning (xin, _("Invalid array expression does not specify number of columns."));
4119 } else if (array_rows <= 0) {
4120 array_rows = 1;
4121 oo_warning (xin, _("Invalid array expression does not specify number of rows."));
4124 r.start = state->pos.eval;
4125 r.end = r.start;
4126 r.end.col += array_cols - 1;
4127 r.end.row += array_rows - 1;
4129 if (r.end.col > gnm_sheet_get_last_col (sheet)) {
4130 oo_warning
4131 (xin,
4132 _("Content past the maximum number "
4133 "of columns (%i) supported."),
4134 gnm_sheet_get_max_cols (sheet));
4135 r.end.col = gnm_sheet_get_last_col (sheet);
4137 if (r.end.row > gnm_sheet_get_last_row (sheet)) {
4138 oo_warning
4139 (xin,
4140 _("Content past the maximum number "
4141 "of rows (%i) supported."),
4142 gnm_sheet_get_max_rows (sheet));
4143 r.end.row = gnm_sheet_get_last_row (sheet);
4146 gnm_cell_set_array (sheet, &r, texpr);
4147 gnm_expr_top_unref (texpr);
4148 if (val != NULL)
4149 gnm_cell_assign_value (cell, val);
4150 } else {
4151 if (val != NULL)
4152 gnm_cell_set_expr_and_value (cell, texpr, val,
4153 TRUE);
4154 else
4155 gnm_cell_set_expr (cell, texpr);
4156 gnm_expr_top_unref (texpr);
4158 } else if (val != NULL) {
4159 GnmCell *cell = sheet_cell_fetch (state->pos.sheet,
4160 state->pos.eval.col, state->pos.eval.row);
4162 /* has cell previously been initialized as part of an array */
4163 if (gnm_cell_is_nonsingleton_array (cell))
4164 gnm_cell_assign_value (cell, val);
4165 else
4166 gnm_cell_set_value (cell, val);
4167 } else if (!state->content_is_error)
4168 /* store the content as a string */
4169 state->text_p_for_cell.content_is_simple = TRUE;
4171 if (merge_cols > 1 || merge_rows > 1) {
4172 range_init_cellpos_size (&tmp, &state->pos.eval,
4173 merge_cols, merge_rows);
4174 gnm_sheet_merge_add (state->pos.sheet, &tmp, FALSE, NULL);
4178 static void
4179 oo_cell_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
4181 OOParseState *state = (OOParseState *)xin->user_state;
4183 if (state->col_inc > 1 || state->row_inc > 1) {
4184 GnmCell *cell = sheet_cell_get (state->pos.sheet,
4185 state->pos.eval.col, state->pos.eval.row);
4187 if (!gnm_cell_is_empty (cell)) {
4188 int i, j;
4189 GnmCell *next;
4190 for (j = 0; j < state->row_inc ; j++)
4191 for (i = 0; i < state->col_inc ; i++)
4192 if (j > 0 || i > 0) {
4193 next = sheet_cell_fetch (state->pos.sheet,
4194 state->pos.eval.col + i, state->pos.eval.row + j);
4195 if (gnm_cell_is_nonsingleton_array (next))
4196 gnm_cell_assign_value (next, value_dup (cell->value));
4197 else
4198 gnm_cell_set_value (next, value_dup (cell->value));
4202 state->pos.eval.col += state->col_inc;
4205 static void
4206 oo_add_text_to_cell (OOParseState *state, char const *str, PangoAttrList *attrs)
4208 GnmValue *v = NULL;
4209 PangoAttrList *old = NULL;
4210 int start = 0;
4211 GOFormat *fmt;
4213 if (state->curr_cell == NULL)
4214 return;
4216 if ((NULL != state->curr_cell->value) && VALUE_IS_STRING (state->curr_cell->value)) {
4217 GOFormat *fmt = state->curr_cell->value->v_str.fmt;
4218 start = strlen (state->curr_cell->value->v_str.val->str);
4219 if (fmt != NULL)
4220 go_format_ref (fmt);
4221 v = value_new_string_str
4222 (go_string_new_nocopy
4223 (g_strconcat (state->curr_cell->value->v_str.val->str,
4224 str, NULL)));
4225 if (fmt != NULL) {
4226 value_set_fmt (v, fmt);
4227 go_format_unref (fmt);
4229 } else
4230 v = value_new_string (str);
4231 if (v != NULL)
4232 gnm_cell_assign_value (state->curr_cell, v);
4234 if (attrs) {
4235 if (state->curr_cell->value->v_str.fmt != NULL) {
4236 old = pango_attr_list_copy
4237 ((PangoAttrList *)go_format_get_markup (state->curr_cell->value->v_str.fmt));
4238 } else
4239 old = pango_attr_list_new ();
4240 pango_attr_list_splice (old, attrs, start, strlen (str));
4241 fmt = go_format_new_markup (old, FALSE);
4242 value_set_fmt (state->curr_cell->value, fmt);
4243 go_format_unref (fmt);
4247 static void
4248 oo_cell_content_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
4250 OOParseState *state = (OOParseState *)xin->user_state;
4252 odf_push_text_p (state, TRUE);
4254 if (state->text_p_for_cell.content_is_simple) {
4255 int max_cols = gnm_sheet_get_max_cols (state->pos.sheet);
4256 int max_rows = gnm_sheet_get_max_rows (state->pos.sheet);
4258 if (state->pos.eval.col >= max_cols ||
4259 state->pos.eval.row >= max_rows)
4260 return;
4262 state->curr_cell = sheet_cell_fetch (state->pos.sheet,
4263 state->pos.eval.col,
4264 state->pos.eval.row);
4266 if (VALUE_IS_STRING (state->curr_cell->value)) {
4267 /* embedded newlines stored as a series of <p> */
4268 GnmValue *v;
4269 v = value_new_string_str
4270 (go_string_new_nocopy
4271 (g_strconcat (state->curr_cell->value->v_str.val->str, "\n", NULL)));
4272 gnm_cell_assign_value (state->curr_cell, v);
4279 static void
4280 oo_cell_content_end (GsfXMLIn *xin, GsfXMLBlob *blob)
4282 OOParseState *state = (OOParseState *)xin->user_state;
4284 if (state->content_is_error) {
4285 GnmValue *v;
4286 if (state->curr_cell == NULL) {
4287 int max_cols = gnm_sheet_get_max_cols (state->pos.sheet);
4288 int max_rows = gnm_sheet_get_max_rows (state->pos.sheet);
4290 if (state->pos.eval.col >= max_cols ||
4291 state->pos.eval.row >= max_rows)
4292 return;
4294 state->curr_cell = sheet_cell_fetch (state->pos.sheet,
4295 state->pos.eval.col,
4296 state->pos.eval.row);
4298 v = value_new_error (NULL, xin->content->str);
4299 gnm_cell_assign_value (state->curr_cell, v);
4300 } else if (state->text_p_for_cell.content_is_simple) {
4301 odf_text_content_end (xin, blob);
4302 if (state->text_p_for_cell.gstr)
4303 oo_add_text_to_cell (state, state->text_p_for_cell.gstr->str, state->text_p_for_cell.attrs);
4305 odf_pop_text_p (state);
4310 static void
4311 oo_cell_content_link (GsfXMLIn *xin, xmlChar const **attrs)
4313 OOParseState *state = (OOParseState *)xin->user_state;
4314 char const *href = NULL;
4315 char const *tip = NULL;
4316 GnmHLink *hlink = NULL;
4318 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4319 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_XLINK, "href"))
4320 href = CXML2C (attrs[1]);
4321 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_OFFICE, "title"))
4322 tip = CXML2C (attrs[1]);
4324 if (href != NULL) {
4325 GnmStyle *style;
4326 GType type;
4327 char *link_text = NULL;
4329 if (g_str_has_prefix (href, "http"))
4330 type = gnm_hlink_url_get_type ();
4331 else if (g_str_has_prefix (href, "mail"))
4332 type = gnm_hlink_email_get_type ();
4333 else if (g_str_has_prefix (href, "file"))
4334 type = gnm_hlink_external_get_type ();
4335 else {
4336 char *dot;
4337 type = gnm_hlink_cur_wb_get_type ();
4338 if (href[0] == '#')
4339 href++;
4340 link_text = g_strdup (href);
4342 // Switch to Sheet!A1 format quick'n'dirty.
4343 dot = strchr (link_text, '.');
4344 if (dot)
4345 *dot = '!';
4348 if (!link_text)
4349 link_text = g_strdup (href);
4351 hlink = gnm_hlink_new (type, state->pos.sheet);
4352 gnm_hlink_set_target (hlink, link_text);
4353 gnm_hlink_set_tip (hlink, tip);
4354 style = gnm_style_new ();
4355 gnm_style_set_hlink (style, hlink);
4356 gnm_style_set_font_uline (style, UNDERLINE_SINGLE);
4357 gnm_style_set_font_color (style, gnm_color_new_go (GO_COLOR_BLUE));
4358 sheet_style_apply_pos (state->pos.sheet,
4359 state->pos.eval.col, state->pos.eval.row,
4360 style);
4361 g_free (link_text);
4365 static void
4366 oo_covered_cell_start (GsfXMLIn *xin, xmlChar const **attrs)
4368 OOParseState *state = (OOParseState *)xin->user_state;
4370 state->col_inc = 1;
4371 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4372 if (oo_attr_int_range (xin, attrs, OO_NS_TABLE, "number-columns-repeated",
4373 &state->col_inc, 0, INT_MAX - state->pos.eval.col))
4375 #if 0
4376 /* why bother it is covered ? */
4377 else if (!strcmp (CXML2C (attrs[0]), OO_NS_TABLE, "style-name"))
4378 style = g_hash_table_lookup (state->styles.cell, attrs[1]);
4380 if (style != NULL) {
4381 gnm_style_ref (style);
4382 sheet_style_apply_pos (state->pos.sheet,
4383 state->pos.eval.col, state->pos.eval.row,
4384 style);
4386 #endif
4389 static void
4390 oo_covered_cell_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
4392 OOParseState *state = (OOParseState *)xin->user_state;
4393 state->pos.eval.col += state->col_inc;
4398 static void
4399 oo_dash (GsfXMLIn *xin, xmlChar const **attrs)
4401 OOParseState *state = (OOParseState *)xin->user_state;
4402 GOLineDashType t = GO_LINE_DOT;
4403 char const *name = NULL;
4404 gnm_float distance = 0., len_dot1 = 0., len_dot2 = 0.;
4405 int n_dots1 = 0, n_dots2 = 2;
4406 gboolean found_percent;
4408 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4409 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "name"))
4410 name = CXML2C (attrs[1]);
4411 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "style"))
4412 /* rect or round, ignored */;
4413 else if (oo_attr_percent_or_distance (xin, attrs, OO_NS_DRAW, "distance",
4414 &distance, &found_percent));
4415 else if (oo_attr_percent_or_distance (xin, attrs, OO_NS_DRAW, "dots1-length",
4416 &len_dot1, &found_percent));
4417 else if (oo_attr_percent_or_distance (xin, attrs, OO_NS_DRAW, "dots2-length",
4418 &len_dot2, &found_percent));
4419 else if (oo_attr_int_range (xin, attrs, OO_NS_DRAW,
4420 "dots1", &n_dots1, 0, 10));
4421 else if (oo_attr_int_range (xin, attrs, OO_NS_DRAW,
4422 "dots2", &n_dots2, 0, 10));
4424 /* We need to figure out the best matching dot style */
4426 if (n_dots2 == 0) {
4427 /* only one type of dots */
4428 if (len_dot1 < 1.5)
4429 t = GO_LINE_S_DOT;
4430 else if (len_dot1 < 4.5)
4431 t = GO_LINE_DOT;
4432 else if (len_dot1 < 9)
4433 t = GO_LINE_S_DASH;
4434 else if (len_dot1 < 15)
4435 t = GO_LINE_DASH;
4436 else
4437 t = GO_LINE_LONG_DASH;
4438 } else if (n_dots2 > 1 && n_dots1 > 1 )
4439 t = GO_LINE_DASH_DOT_DOT_DOT; /* no matching dashing available */
4440 else if ( n_dots2 == 1 && n_dots1 == 1) {
4441 gnm_float max = (len_dot1 < len_dot2) ? len_dot2 : len_dot1;
4442 if (max > 7.5)
4443 t = GO_LINE_DASH_DOT;
4444 else
4445 t = GO_LINE_S_DASH_DOT;
4446 } else {
4447 gnm_float max = (len_dot1 < len_dot2) ? len_dot2 : len_dot1;
4448 int max_dots = (n_dots1 < n_dots2) ? n_dots2 : n_dots1;
4450 if (max_dots > 2)
4451 t = GO_LINE_DASH_DOT_DOT_DOT;
4452 else if (max > 7.5)
4453 t = GO_LINE_DASH_DOT_DOT;
4454 else
4455 t = GO_LINE_S_DASH_DOT_DOT;
4458 if (name != NULL)
4459 g_hash_table_replace (state->chart.dash_styles,
4460 g_strdup (name), GUINT_TO_POINTER (t));
4461 else
4462 oo_warning (xin, _("Unnamed dash style encountered."));
4466 static void
4467 oo_fill_image (GsfXMLIn *xin, xmlChar const **attrs)
4469 OOParseState *state = (OOParseState *)xin->user_state;
4470 char const *name = NULL;
4471 char const *href = NULL;
4473 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4474 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "name"))
4475 name = CXML2C (attrs[1]);
4476 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
4477 OO_NS_XLINK, "href"))
4478 href = CXML2C (attrs[1]);
4479 if (name == NULL)
4480 oo_warning (xin, _("Unnamed image fill style encountered."));
4481 else if (href == NULL)
4482 oo_warning (xin, _("Image fill style \'%s\' has no attached image."),
4483 name);
4484 else {
4485 g_hash_table_replace (state->chart.fill_image_styles,
4486 g_strdup (name), g_strdup (href));
4490 static void
4491 oo_gradient (GsfXMLIn *xin, xmlChar const **attrs)
4493 OOParseState *state = (OOParseState *)xin->user_state;
4494 gradient_info_t *info = g_new0 (gradient_info_t, 1);
4495 char const *name = NULL;
4496 int angle = 0;
4497 char const *style = NULL;
4498 unsigned int axial_types[] =
4499 {GO_GRADIENT_S_TO_N_MIRRORED, GO_GRADIENT_SE_TO_NW_MIRRORED,
4500 GO_GRADIENT_E_TO_W_MIRRORED, GO_GRADIENT_NE_TO_SW_MIRRORED,
4501 GO_GRADIENT_N_TO_S_MIRRORED, GO_GRADIENT_NW_TO_SE_MIRRORED,
4502 GO_GRADIENT_W_TO_E_MIRRORED, GO_GRADIENT_SW_TO_NE_MIRRORED};
4503 unsigned int linear_types[] =
4504 {GO_GRADIENT_S_TO_N, GO_GRADIENT_SE_TO_NW,
4505 GO_GRADIENT_E_TO_W, GO_GRADIENT_NE_TO_SW,
4506 GO_GRADIENT_N_TO_S, GO_GRADIENT_NW_TO_SE,
4507 GO_GRADIENT_W_TO_E, GO_GRADIENT_SW_TO_NE};
4509 info->brightness = -1.;
4511 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4512 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "name"))
4513 name = CXML2C (attrs[1]);
4514 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "start-color")) {
4515 GdkRGBA rgba;
4516 if (gdk_rgba_parse (&rgba, CXML2C (attrs[1])))
4517 go_color_from_gdk_rgba (&rgba, &info->from);
4518 else
4519 oo_warning (xin, _("Unable to parse gradient color: %s"), CXML2C (attrs[1]));
4520 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "end-color")) {
4521 GdkRGBA rgba;
4522 if (gdk_rgba_parse (&rgba, CXML2C (attrs[1])))
4523 go_color_from_gdk_rgba (&rgba, &info->to);
4524 else
4525 oo_warning (xin, _("Unable to parse gradient color: %s"), CXML2C (attrs[1]));
4526 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "style"))
4527 style = CXML2C (attrs[1]);
4528 else if (oo_attr_float (xin, attrs, OO_GNUM_NS_EXT,
4529 "brightness", &info->brightness));
4530 else if (NULL != oo_attr_angle (xin, attrs, OO_NS_DRAW, "angle", &angle));
4532 if (name != NULL) {
4533 if (angle < 0)
4534 angle += 360;
4535 angle = ((angle + 22)/45) % 8; /* angle is now 0,1,2,...,7*/
4537 if (style != NULL && 0 == strcmp (style, "axial"))
4538 info->dir = axial_types[angle];
4539 else /* linear */
4540 info->dir = linear_types[angle];
4542 g_hash_table_replace (state->chart.gradient_styles,
4543 g_strdup (name), info);
4544 } else {
4545 oo_warning (xin, _("Unnamed gradient style encountered."));
4546 g_free (info);
4550 static void
4551 oo_hatch (GsfXMLIn *xin, xmlChar const **attrs)
4553 OOParseState *state = (OOParseState *)xin->user_state;
4554 GOPattern *hatch = g_new (GOPattern, 1);
4555 char const *hatch_name = NULL;
4556 gnm_float distance = -1.0;
4557 int angle = 0;
4558 char const *style = NULL;
4560 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4561 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "color")) {
4562 GdkRGBA rgba;
4563 if (gdk_rgba_parse (&rgba, CXML2C (attrs[1])))
4564 go_color_from_gdk_rgba (&rgba, &hatch->fore);
4565 else
4566 oo_warning (xin, _("Unable to parse hatch color: %s"), CXML2C (attrs[1]));
4567 } else if (NULL != oo_attr_distance (xin, attrs, OO_NS_DRAW, "distance", &distance))
4569 else if (NULL != oo_attr_angle (xin, attrs, OO_NS_DRAW, "rotation", &angle))
4571 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "name"))
4572 hatch_name = CXML2C (attrs[1]);
4573 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "style"))
4574 style = CXML2C (attrs[1]);
4576 if (style == NULL)
4577 hatch->pattern = GO_PATTERN_THATCH;
4578 else if (0 == strcmp (style, "single")) {
4579 while (angle < 0)
4580 angle = angle + 180;
4581 angle = (angle + 22) / 45;
4582 switch (angle) {
4583 case 0:
4584 hatch->pattern = (distance < 2.5) ? GO_PATTERN_HORIZ : GO_PATTERN_THIN_HORIZ;
4585 break;
4586 case 1:
4587 hatch->pattern = (distance < 2.5) ? GO_PATTERN_DIAG : GO_PATTERN_THIN_DIAG;
4588 break;
4589 case 2:
4590 hatch->pattern = (distance < 2.5) ? GO_PATTERN_VERT : GO_PATTERN_THIN_VERT;
4591 break;
4592 default:
4593 hatch->pattern = (distance < 2.5) ? GO_PATTERN_REV_DIAG : GO_PATTERN_THIN_REV_DIAG;
4594 break;
4596 } else if (0 == strcmp (style, "double")) {
4597 if (angle < 0)
4598 angle = - angle;
4599 angle = (angle + 22) / 45;
4600 angle = angle & 2;
4601 switch ((int)(distance + 0.5)) {
4602 case 0:
4603 case 1:
4604 hatch->pattern = (angle == 0) ? GO_PATTERN_GREY75 : GO_PATTERN_THICK_DIAG_CROSS;
4605 break;
4606 case 2:
4607 hatch->pattern = (angle == 0) ? GO_PATTERN_GREY50 : GO_PATTERN_DIAG_CROSS;
4608 break;
4609 case 3:
4610 hatch->pattern = (angle == 0) ? GO_PATTERN_THIN_HORIZ_CROSS : GO_PATTERN_THIN_DIAG_CROSS;
4611 break;
4612 case 4:
4613 hatch->pattern = GO_PATTERN_GREY125;
4614 break;
4615 default:
4616 hatch->pattern = GO_PATTERN_GREY625;
4617 break;
4619 hatch->pattern = GO_PATTERN_THATCH;
4620 } else if (0 == strcmp (style, "triple")) {
4621 while (angle < 0)
4622 angle += 180;
4623 angle = angle % 180;
4624 angle = (angle + 22)/45;
4625 switch (angle) {
4626 case 0:
4627 hatch->pattern = (distance < 2.5) ? GO_PATTERN_SMALL_CIRCLES : GO_PATTERN_LARGE_CIRCLES;
4628 break;
4629 case 1:
4630 hatch->pattern = (distance < 2.5) ? GO_PATTERN_SEMI_CIRCLES : GO_PATTERN_BRICKS;
4631 break;
4632 default:
4633 hatch->pattern = GO_PATTERN_THATCH;
4634 break;
4638 if (hatch_name == NULL) {
4639 g_free (hatch);
4640 oo_warning (xin, _("Unnamed hatch encountered!"));
4641 } else
4642 g_hash_table_replace (state->chart.hatches,
4643 g_strdup (hatch_name), hatch);
4647 static void odf_style_set_align_h (GnmStyle *style, gint h_align_is_valid, gboolean repeat_content,
4648 int text_align, int gnm_halign);
4650 static void
4651 odf_free_cur_style (OOParseState *state)
4653 switch (state->cur_style.type) {
4654 case OO_STYLE_CELL :
4655 if (state->cur_style.cells != NULL) {
4656 odf_style_set_align_h (state->cur_style.cells->style,
4657 state->h_align_is_valid,
4658 state->repeat_content,
4659 state->text_align, state->gnm_halign);
4660 odf_oo_cell_style_unref (state->cur_style.cells);
4661 state->cur_style.cells = NULL;
4663 break;
4664 case OO_STYLE_COL :
4665 case OO_STYLE_ROW :
4666 if (state->cur_style.requires_disposal)
4667 g_free (state->cur_style.col_rows);
4668 state->cur_style.col_rows = NULL;
4669 break;
4670 case OO_STYLE_SHEET :
4671 if (state->cur_style.requires_disposal)
4672 oo_sheet_style_free (state->cur_style.sheets);
4673 state->cur_style.sheets = NULL;
4674 break;
4675 case OO_STYLE_CHART :
4676 case OO_STYLE_GRAPHICS :
4677 if (state->cur_style.requires_disposal)
4678 oo_chart_style_free (state->chart.cur_graph_style);
4679 state->chart.cur_graph_style = NULL;
4680 break;
4681 case OO_STYLE_TEXT:
4682 pango_attr_list_unref (state->cur_style.text);
4683 state->cur_style.text = NULL;
4684 break;
4685 default :
4686 break;
4688 state->cur_style.type = OO_STYLE_UNKNOWN;
4689 state->cur_style.requires_disposal = FALSE;
4692 static void
4693 oo_style (GsfXMLIn *xin, xmlChar const **attrs)
4695 static OOEnum const style_types [] = {
4696 { "table-cell", OO_STYLE_CELL },
4697 { "table-row", OO_STYLE_ROW },
4698 { "table-column", OO_STYLE_COL },
4699 { "table", OO_STYLE_SHEET },
4700 { "graphics", OO_STYLE_GRAPHICS },
4701 { "paragraph", OO_STYLE_PARAGRAPH },
4702 { "text", OO_STYLE_TEXT },
4703 { "chart", OO_STYLE_CHART },
4704 { "graphic", OO_STYLE_GRAPHICS },
4705 { NULL, 0 },
4708 OOParseState *state = (OOParseState *)xin->user_state;
4709 char const *name = NULL;
4710 char const *mp_name = NULL;
4711 char const *parent_name = NULL;
4712 OOCellStyle *oostyle;
4713 GOFormat *fmt = NULL;
4714 int tmp;
4715 OOChartStyle *cur_style;
4717 if (state->cur_style.type != OO_STYLE_UNKNOWN)
4718 odf_free_cur_style (state);
4720 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4721 if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "family", style_types, &tmp))
4722 state->cur_style.type = tmp;
4723 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "name"))
4724 name = CXML2C (attrs[1]);
4725 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "master-page-name"))
4726 mp_name = CXML2C (attrs[1]);
4727 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "parent-style-name"))
4728 parent_name = CXML2C (attrs[1]);
4729 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "data-style-name")) {
4730 GOFormat *tmp = g_hash_table_lookup (state->formats, attrs[1]);
4731 if (tmp != NULL)
4732 fmt = tmp;
4735 switch (state->cur_style.type) {
4736 case OO_STYLE_TEXT:
4737 state->cur_style.text = pango_attr_list_new ();
4738 if (name != NULL)
4739 g_hash_table_replace (state->styles.text,
4740 g_strdup (name), pango_attr_list_ref (state->cur_style.text));
4742 break;
4743 case OO_STYLE_CELL:
4744 oostyle = (parent_name != NULL)
4745 ? g_hash_table_lookup (state->styles.cell, parent_name)
4746 : NULL;
4747 if (oostyle)
4748 state->cur_style.cells = odf_oo_cell_style_copy (oostyle);
4749 else
4750 state->cur_style.cells = odf_oo_cell_style_new (NULL);
4752 state->h_align_is_valid = 0;
4753 state->repeat_content = FALSE;
4754 state->text_align = -2;
4755 state->gnm_halign = -2;
4757 if (fmt != NULL)
4758 gnm_style_set_format (state->cur_style.cells->style, fmt);
4760 if (name != NULL) {
4761 odf_oo_cell_style_ref (state->cur_style.cells);
4762 g_hash_table_replace (state->styles.cell,
4763 g_strdup (name), state->cur_style.cells);
4764 } else if (0 == strcmp (xin->node->id, "DEFAULT_STYLE")) {
4765 if (state->default_style.cells)
4766 odf_oo_cell_style_unref (state->default_style.cells);
4767 state->default_style.cells = state->cur_style.cells;
4768 odf_oo_cell_style_ref (state->cur_style.cells);
4771 break;
4773 case OO_STYLE_COL:
4774 state->cur_style.col_rows = g_new0 (OOColRowStyle, 1);
4775 state->cur_style.col_rows->size_pts = -1.;
4776 if (name)
4777 g_hash_table_replace (state->styles.col,
4778 g_strdup (name), state->cur_style.col_rows);
4779 else if (0 == strcmp (xin->node->id, "DEFAULT_STYLE")) {
4780 if (state->default_style.columns) {
4781 oo_warning (xin, _("Duplicate default column style encountered."));
4782 g_free (state->default_style.columns);
4784 state->default_style.columns = state->cur_style.col_rows;
4785 } else
4786 state->cur_style.requires_disposal = TRUE;
4787 break;
4789 case OO_STYLE_ROW:
4790 state->cur_style.col_rows = g_new0 (OOColRowStyle, 1);
4791 state->cur_style.col_rows->size_pts = -1.;
4792 if (name)
4793 g_hash_table_replace (state->styles.row,
4794 g_strdup (name), state->cur_style.col_rows);
4795 else if (0 == strcmp (xin->node->id, "DEFAULT_STYLE")) {
4796 if (state->default_style.rows) {
4797 oo_warning (xin, _("Duplicate default row style encountered."));
4798 g_free (state->default_style.rows);
4800 state->default_style.rows = state->cur_style.col_rows;
4801 } else
4802 state->cur_style.requires_disposal = TRUE;
4803 break;
4805 case OO_STYLE_SHEET:
4806 state->cur_style.sheets = g_new0 (OOSheetStyle, 1);
4807 state->cur_style.sheets->master_page_name = g_strdup (mp_name);
4808 if (name)
4809 g_hash_table_replace (state->styles.sheet,
4810 g_strdup (name), state->cur_style.sheets);
4811 else
4812 state->cur_style.requires_disposal = TRUE;
4813 break;
4815 case OO_STYLE_CHART:
4816 case OO_STYLE_GRAPHICS:
4817 state->chart.plot_type = OO_PLOT_UNKNOWN;
4818 cur_style = g_new0(OOChartStyle, 1);
4819 cur_style->axis_props = NULL;
4820 cur_style->plot_props = NULL;
4821 cur_style->style_props = NULL;
4822 cur_style->other_props = NULL;
4823 if (fmt != NULL)
4824 cur_style->fmt = go_format_ref (fmt);
4825 state->chart.cur_graph_style = cur_style;
4826 if (name != NULL)
4827 g_hash_table_replace (state->chart.graph_styles,
4828 g_strdup (name),
4829 state->chart.cur_graph_style);
4830 else if (0 == strcmp (xin->node->id, "DEFAULT_STYLE")) {
4831 if (state->default_style.graphics) {
4832 oo_warning (xin, _("Duplicate default chart/graphics style encountered."));
4833 g_free (state->default_style.graphics);
4835 state->default_style.graphics = state->chart.cur_graph_style;
4836 } else
4837 state->cur_style.requires_disposal = TRUE;
4838 break;
4839 default:
4840 break;
4844 static void
4845 oo_style_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
4847 OOParseState *state = (OOParseState *)xin->user_state;
4848 odf_free_cur_style (state);
4851 static GOFormat *
4852 oo_canonical_format (const char *s)
4855 * Quoting certain characters is options and has no functions effect
4856 * for the meaning of the format. However, some formats are recognized
4857 * as built-in and others are not. We therefore apply a simple mapping
4858 * to whatever form we prefer.
4860 if (g_str_equal (s, "_(* -??_)"))
4861 s = "_(* \"-\"??_)";
4863 return go_format_new_from_XL (s);
4867 static void
4868 oo_date_day (GsfXMLIn *xin, xmlChar const **attrs)
4870 OOParseState *state = (OOParseState *)xin->user_state;
4871 gboolean is_short = TRUE;
4873 if (state->cur_format.accum == NULL)
4874 return;
4876 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4877 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_NUMBER, "style"))
4878 is_short = (attr_eq (attrs[1], "short"));
4880 g_string_append (state->cur_format.accum, is_short ? "d" : "dd");
4883 static void
4884 oo_date_month (GsfXMLIn *xin, xmlChar const **attrs)
4886 OOParseState *state = (OOParseState *)xin->user_state;
4887 gboolean as_text = FALSE;
4888 gboolean is_short = TRUE;
4890 if (state->cur_format.accum == NULL)
4891 return;
4893 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4894 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_NUMBER, "style"))
4895 is_short = attr_eq (attrs[1], "short");
4896 else if (oo_attr_bool (xin, attrs, OO_NS_NUMBER, "textual", &as_text))
4898 g_string_append (state->cur_format.accum, as_text
4899 ? (is_short ? "mmm" : "mmmm")
4900 : (is_short ? "m" : "mm"));
4902 static void
4903 oo_date_year (GsfXMLIn *xin, xmlChar const **attrs)
4905 OOParseState *state = (OOParseState *)xin->user_state;
4906 gboolean is_short = TRUE;
4908 if (state->cur_format.accum == NULL)
4909 return;
4911 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4912 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_NUMBER, "style"))
4913 is_short = attr_eq (attrs[1], "short");
4914 g_string_append (state->cur_format.accum, is_short ? "yy" : "yyyy");
4916 static void
4917 oo_date_era (G_GNUC_UNUSED GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
4920 static void
4921 oo_date_day_of_week (GsfXMLIn *xin, xmlChar const **attrs)
4923 OOParseState *state = (OOParseState *)xin->user_state;
4924 gboolean is_short = TRUE;
4926 if (state->cur_format.accum == NULL)
4927 return;
4929 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4930 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_NUMBER, "style"))
4931 is_short = attr_eq (attrs[1], "short");
4932 g_string_append (state->cur_format.accum, is_short ? "ddd" : "dddd");
4934 static void
4935 oo_date_week_of_year (G_GNUC_UNUSED GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
4938 static void
4939 oo_date_quarter (G_GNUC_UNUSED GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
4942 static void
4943 oo_date_hours (GsfXMLIn *xin, xmlChar const **attrs)
4945 OOParseState *state = (OOParseState *)xin->user_state;
4946 gboolean is_short = TRUE;
4947 gboolean truncate_hour_on_overflow = TRUE;
4948 gboolean truncate_hour_on_overflow_set = FALSE;
4950 if (state->cur_format.accum == NULL)
4951 return;
4953 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4954 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_NUMBER, "style"))
4955 is_short = attr_eq (attrs[1], "short");
4956 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT,
4957 "truncate-on-overflow",
4958 &truncate_hour_on_overflow))
4959 truncate_hour_on_overflow_set = TRUE;
4961 if (truncate_hour_on_overflow_set) {
4962 if (truncate_hour_on_overflow)
4963 g_string_append (state->cur_format.accum, is_short ? "h" : "hh");
4964 else {
4965 g_string_append (state->cur_format.accum, is_short ? "[h]" : "[hh]");
4966 state->cur_format.elapsed_set |= ODF_ELAPSED_SET_HOURS;
4968 } else {
4969 if (state->cur_format.truncate_hour_on_overflow)
4970 g_string_append (state->cur_format.accum, is_short ? "h" : "hh");
4971 else {
4972 g_string_append (state->cur_format.accum, is_short ? "[h]" : "[hh]");
4973 state->cur_format.elapsed_set |= ODF_ELAPSED_SET_HOURS;
4978 static void
4979 oo_date_minutes (GsfXMLIn *xin, xmlChar const **attrs)
4981 OOParseState *state = (OOParseState *)xin->user_state;
4982 gboolean is_short = TRUE;
4983 gboolean truncate_hour_on_overflow = TRUE;
4984 gboolean truncate_hour_on_overflow_set = FALSE;
4986 if (state->cur_format.accum == NULL)
4987 return;
4989 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
4990 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_NUMBER, "style"))
4991 is_short = attr_eq (attrs[1], "short");
4992 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT,
4993 "truncate-on-overflow",
4994 &truncate_hour_on_overflow))
4995 truncate_hour_on_overflow_set = TRUE;
4996 state->cur_format.pos_minutes = state->cur_format.accum->len;
4998 if (truncate_hour_on_overflow_set) {
4999 if (truncate_hour_on_overflow)
5000 g_string_append (state->cur_format.accum, is_short ? "m" : "mm");
5001 else {
5002 g_string_append (state->cur_format.accum, is_short ? "[m]" : "[mm]");
5003 state->cur_format.elapsed_set |= ODF_ELAPSED_SET_MINUTES;
5005 } else {
5006 if (state->cur_format.truncate_hour_on_overflow ||
5007 0 != (state->cur_format.elapsed_set & ODF_ELAPSED_SET_HOURS))
5008 g_string_append (state->cur_format.accum, is_short ? "m" : "mm");
5009 else {
5010 g_string_append (state->cur_format.accum, is_short ? "[m]" : "[mm]");
5011 state->cur_format.elapsed_set |= ODF_ELAPSED_SET_MINUTES;
5016 #define OO_DATE_SECONDS_PRINT_SECONDS { \
5017 g_string_append (state->cur_format.accum, \
5018 is_short ? "s" : "ss"); \
5019 if (digits > 0) { \
5020 g_string_append_c (state->cur_format.accum, \
5021 '.'); \
5022 odf_go_string_append_c_n \
5023 (state->cur_format.accum, '0', digits); \
5028 static void
5029 oo_date_seconds (GsfXMLIn *xin, xmlChar const **attrs)
5031 OOParseState *state = (OOParseState *)xin->user_state;
5032 gboolean is_short = TRUE;
5033 int digits = 0;
5034 gboolean truncate_hour_on_overflow = TRUE;
5035 gboolean truncate_hour_on_overflow_set = FALSE;
5037 if (state->cur_format.accum == NULL)
5038 return;
5040 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5041 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_NUMBER, "style"))
5042 is_short = attr_eq (attrs[1], "short");
5043 else if (oo_attr_int_range (xin, attrs, OO_NS_NUMBER,
5044 "decimal-places", &digits, 0, 9))
5046 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT,
5047 "truncate-on-overflow",
5048 &truncate_hour_on_overflow))
5049 truncate_hour_on_overflow_set = TRUE;
5051 state->cur_format.pos_seconds = state->cur_format.accum->len;
5053 if (truncate_hour_on_overflow_set) {
5054 if (truncate_hour_on_overflow) {
5055 OO_DATE_SECONDS_PRINT_SECONDS;
5056 } else {
5057 g_string_append_c (state->cur_format.accum, '[');
5058 OO_DATE_SECONDS_PRINT_SECONDS;
5059 g_string_append_c (state->cur_format.accum, ']');
5060 state->cur_format.elapsed_set |= ODF_ELAPSED_SET_SECONDS;
5062 } else {
5063 if (state->cur_format.truncate_hour_on_overflow ||
5064 0 != (state->cur_format.elapsed_set &
5065 (ODF_ELAPSED_SET_HOURS | ODF_ELAPSED_SET_MINUTES))) {
5066 OO_DATE_SECONDS_PRINT_SECONDS;
5067 } else {
5068 g_string_append_c (state->cur_format.accum, '[');
5069 OO_DATE_SECONDS_PRINT_SECONDS;
5070 g_string_append_c (state->cur_format.accum, ']');
5071 state->cur_format.elapsed_set |= ODF_ELAPSED_SET_SECONDS;
5076 #undef OO_DATE_SECONDS_PRINT_SECONDS
5078 static void
5079 oo_date_am_pm (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
5081 OOParseState *state = (OOParseState *)xin->user_state;
5082 gchar const *am_suffix = "AM";
5083 gchar const *pm_suffix = "PM";
5085 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5086 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "am-suffix"))
5087 am_suffix = CXML2C (attrs[1]);
5088 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "pm-suffix"))
5089 pm_suffix = CXML2C (attrs[1]);
5091 if (strlen (am_suffix) > 2 || (*am_suffix != 'a' && *am_suffix != 'A') ||
5092 (*(am_suffix + 1) != 'm' && *(am_suffix + 1) != 'M' && *(am_suffix + 1) != 0))
5093 am_suffix = "AM";
5094 if (strlen (pm_suffix) > 2 || (*pm_suffix != 'p' && *pm_suffix != 'P') ||
5095 (*(pm_suffix + 1) != 'm' && *(pm_suffix + 1) != 'M' && *(pm_suffix + 1) != 0))
5096 pm_suffix = "PM";
5097 if (strlen (am_suffix) != strlen (pm_suffix))
5098 pm_suffix = am_suffix = "AM";
5100 if (state->cur_format.accum != NULL) {
5101 g_string_append (state->cur_format.accum, am_suffix);
5102 g_string_append_c (state->cur_format.accum, '/');
5103 g_string_append (state->cur_format.accum, pm_suffix);
5107 static void
5108 odf_embedded_text_start (GsfXMLIn *xin, xmlChar const **attrs)
5110 OOParseState *state = (OOParseState *)xin->user_state;
5112 state->cur_format.offset = 0;
5114 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5115 if (oo_attr_int (xin, attrs, OO_NS_NUMBER,
5116 "position", &(state->cur_format.offset)))
5120 static void
5121 odf_insert_in_integer (OOParseState *state, const char *str)
5123 gboolean needs_quoting = FALSE;
5124 const char *p;
5125 GString *accum = state->cur_format.accum;
5126 int pos = state->cur_format.offset;
5128 g_return_if_fail (pos >= 0 && pos < (int)accum->len);
5131 * We want to insert str in front of the state->cur_format.offset's
5132 * integer digit. For the moment we assume that we have just an
5133 * integer and str does not contain any quotation marks
5136 for (p = str; *p; p++) {
5137 switch (*p) {
5138 case '-':
5139 case ' ':
5140 case '(':
5141 case ')':
5142 break;
5143 default:
5144 needs_quoting = TRUE;
5145 break;
5149 if (needs_quoting) {
5150 g_string_insert (accum, accum->len - pos, "\"\"");
5151 g_string_insert (accum, accum->len - pos - 1, str);
5152 } else {
5153 g_string_insert (accum, accum->len - pos, str);
5157 static void
5158 odf_embedded_text_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
5160 OOParseState *state = (OOParseState *)xin->user_state;
5162 if (state->cur_format.accum == NULL)
5163 return;
5165 odf_insert_in_integer (state, xin->content->str);
5167 state->cur_format.offset = 0;
5170 static void
5171 odf_date_text_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
5173 OOParseState *state = (OOParseState *)xin->user_state;
5174 state->cur_format.offset = 0;
5175 state->cur_format.string_opened = FALSE;
5178 static void
5179 oo_date_text_append_quoted (OOParseState *state, char const *cnt, int cnt_len)
5181 if (!state->cur_format.string_opened)
5182 g_string_append_c (state->cur_format.accum, '"');
5183 state->cur_format.string_opened = TRUE;
5184 g_string_append_len (state->cur_format.accum, cnt, cnt_len);
5187 static void
5188 oo_date_text_append_unquoted (OOParseState *state, char cnt)
5190 if (state->cur_format.string_opened)
5191 g_string_append_c (state->cur_format.accum, '"');
5192 state->cur_format.string_opened = FALSE;
5193 g_string_append_c (state->cur_format.accum, cnt);
5196 static void
5197 oo_date_text_append (OOParseState *state, char const *cnt, int cnt_len)
5199 if (cnt_len > 0) {
5200 if (NULL != strchr (" /-(),:",*cnt)) {
5201 oo_date_text_append_unquoted (state, *cnt);
5202 oo_date_text_append (state, cnt + 1, cnt_len - 1);
5203 return;
5204 } else if (state->cur_format.percentage && *cnt == '%') {
5205 oo_date_text_append_unquoted (state, '%');
5206 state->cur_format.percent_sign_seen = TRUE;
5207 oo_date_text_append (state, cnt + 1, cnt_len - 1);
5208 return;
5209 } else if (*cnt == '"') {
5210 oo_date_text_append_unquoted (state, '\\');
5211 oo_date_text_append_unquoted (state, '"');
5212 oo_date_text_append (state, cnt + 1, cnt_len - 1);
5213 return;
5214 } else {
5215 oo_date_text_append_quoted (state, cnt, 1);
5216 oo_date_text_append (state, cnt + 1, cnt_len - 1);
5221 /* date_text_end is also used for non-date formats */
5222 static void
5223 oo_date_text_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
5225 OOParseState *state = (OOParseState *)xin->user_state;
5227 if (state->cur_format.accum == NULL)
5228 return;
5230 if (xin->content->len > state->cur_format.offset)
5231 oo_date_text_append (state, xin->content->str + state->cur_format.offset,
5232 xin->content->len - state->cur_format.offset);
5234 if (state->cur_format.string_opened) {
5235 g_string_append_c (state->cur_format.accum, '"');
5236 state->cur_format.string_opened = FALSE;
5238 state->cur_format.offset = 0;
5241 static void
5242 oo_date_style (GsfXMLIn *xin, xmlChar const **attrs)
5244 OOParseState *state = (OOParseState *)xin->user_state;
5245 char const *name = NULL;
5246 int magic = GO_FORMAT_MAGIC_NONE;
5247 gboolean format_source_is_language = FALSE;
5248 gboolean truncate_hour_on_overflow = TRUE;
5250 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5251 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "name"))
5252 name = CXML2C (attrs[1]);
5253 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "family") &&
5254 !attr_eq (attrs[1], "data-style"))
5255 return;
5256 else if (oo_attr_int (xin, attrs, OO_GNUM_NS_EXT,
5257 "format-magic", &magic))
5259 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_NUMBER, "format-source"))
5260 format_source_is_language = attr_eq (attrs[1], "language");
5261 else if (oo_attr_bool (xin, attrs, OO_NS_NUMBER,
5262 "truncate-on-overflow", &truncate_hour_on_overflow));
5264 g_return_if_fail (state->cur_format.accum == NULL);
5266 /* We always save a magic number with source language, so if that is gone somebody may have changed formats */
5267 state->cur_format.magic = format_source_is_language ? magic : GO_FORMAT_MAGIC_NONE;
5268 state->cur_format.accum = (state->cur_format.magic == GO_FORMAT_MAGIC_NONE) ? g_string_new (NULL) : NULL;
5269 state->cur_format.name = g_strdup (name);
5270 state->cur_format.percentage = FALSE;
5271 state->cur_format.truncate_hour_on_overflow = truncate_hour_on_overflow;
5272 state->cur_format.elapsed_set = 0;
5273 state->cur_format.pos_seconds = 0;
5274 state->cur_format.pos_minutes = 0;
5277 static void
5278 oo_date_style_end_rm_elapsed (GString *str, guint pos)
5280 guint end;
5281 g_return_if_fail (str->len > pos && str->str[pos] == '[');
5283 g_string_erase (str, pos, 1);
5284 end = strcspn (str->str + pos, "]");
5285 g_string_erase (str, pos + end, 1);
5288 static void
5289 oo_date_style_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
5291 OOParseState *state = (OOParseState *)xin->user_state;
5292 int elapsed = state->cur_format.elapsed_set;
5294 if (state->cur_format.name == NULL) {
5295 if (state->cur_format.accum) {
5296 g_string_free (state->cur_format.accum, TRUE);
5297 state->cur_format.accum = NULL;
5299 oo_warning (xin, _("Unnamed date style ignored."));
5300 } else {
5301 if (state->cur_format.magic != GO_FORMAT_MAGIC_NONE)
5302 g_hash_table_insert (state->formats, state->cur_format.name,
5303 go_format_new_magic (state->cur_format.magic));
5304 else {
5305 g_return_if_fail (state->cur_format.accum != NULL);
5307 while (elapsed != 0 && elapsed != ODF_ELAPSED_SET_SECONDS
5308 && elapsed != ODF_ELAPSED_SET_MINUTES
5309 && elapsed != ODF_ELAPSED_SET_HOURS) {
5310 /*We need to fix the format string since several times are set as "elapsed". */
5311 if (0 != (elapsed & ODF_ELAPSED_SET_SECONDS)) {
5312 oo_date_style_end_rm_elapsed (state->cur_format.accum,
5313 state->cur_format.pos_seconds);
5314 if (state->cur_format.pos_seconds < state->cur_format.pos_minutes)
5315 state->cur_format.pos_minutes -= 2;
5316 elapsed -= ODF_ELAPSED_SET_SECONDS;
5317 } else {
5318 oo_date_style_end_rm_elapsed (state->cur_format.accum,
5319 state->cur_format.pos_minutes);
5320 elapsed -= ODF_ELAPSED_SET_MINUTES;
5321 break;
5325 g_hash_table_insert (state->formats, state->cur_format.name,
5326 oo_canonical_format (state->cur_format.accum->str));
5327 g_string_free (state->cur_format.accum, TRUE);
5330 state->cur_format.accum = NULL;
5331 state->cur_format.name = NULL;
5334 /*****************************************************************************************************/
5336 static void
5337 odf_fraction (GsfXMLIn *xin, xmlChar const **attrs)
5339 OOParseState *state = (OOParseState *)xin->user_state;
5340 gboolean grouping = FALSE;
5341 gboolean no_int_part = FALSE;
5342 gboolean denominator_fixed = FALSE;
5343 gboolean pi_scale = FALSE;
5344 int denominator = 0;
5345 int min_d_digits = 0;
5346 int max_d_digits = 3;
5347 int min_i_digits = -1;
5348 int min_n_digits = 0;
5350 if (state->cur_format.accum == NULL)
5351 return;
5353 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5354 if (oo_attr_bool (xin, attrs, OO_NS_NUMBER, "grouping", &grouping)) {}
5355 else if (oo_attr_int (xin, attrs, OO_NS_NUMBER, "denominator-value", &denominator))
5356 denominator_fixed = TRUE;
5357 else if (oo_attr_int_range (xin, attrs, OO_NS_NUMBER,
5358 "min-denominator-digits", &min_d_digits, 0, 30))
5360 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT,
5361 "max-denominator-digits", &max_d_digits, 0, 30))
5363 else if (oo_attr_int_range (xin, attrs, OO_NS_NUMBER,
5364 "min-integer-digits", &min_i_digits, 0, 30))
5366 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "no-integer-part", &no_int_part)) {}
5367 else if (oo_attr_int_range (xin, attrs, OO_NS_NUMBER,
5368 "min-numerator-digits", &min_n_digits, 0, 30)) {}
5369 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "display-factor") &&
5370 attr_eq (attrs[1], "pi"))
5371 pi_scale = TRUE;
5373 if (!no_int_part && (state->ver_odf < 1.2 || min_i_digits >= 0)) {
5374 g_string_append_c (state->cur_format.accum, '#');
5375 odf_go_string_append_c_n (state->cur_format.accum, '0',
5376 min_i_digits > 0 ? min_i_digits : 0);
5377 g_string_append_c (state->cur_format.accum, ' ');
5379 odf_go_string_append_c_n (state->cur_format.accum, '?', max_d_digits - min_n_digits);
5380 odf_go_string_append_c_n (state->cur_format.accum, '0', min_n_digits);
5381 if (pi_scale)
5382 g_string_append (state->cur_format.accum, " pi");
5383 g_string_append_c (state->cur_format.accum, '/');
5384 if (denominator_fixed) {
5385 int denom = denominator;
5386 int count = 0;
5387 while (denom > 0) {
5388 denom /= 10;
5389 count ++;
5391 min_d_digits -= count;
5392 odf_go_string_append_c_n (state->cur_format.accum, '0',
5393 min_d_digits);
5394 g_string_append_printf (state->cur_format.accum, "%i", denominator);
5395 } else {
5396 max_d_digits -= min_d_digits;
5397 odf_go_string_append_c_n (state->cur_format.accum, '?',
5398 max_d_digits);
5399 odf_go_string_append_c_n (state->cur_format.accum, '0',
5400 min_d_digits);
5404 static void
5405 odf_text_content (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
5407 OOParseState *state = (OOParseState *)xin->user_state;
5408 g_string_append_c (state->cur_format.accum, '@');
5411 static void
5412 odf_number (GsfXMLIn *xin, xmlChar const **attrs)
5414 OOParseState *state = (OOParseState *)xin->user_state;
5415 gboolean grouping = FALSE;
5416 int decimal_places = 0;
5417 gboolean decimals_specified = FALSE;
5418 /* gnm_float display_factor = 1.; */
5419 int min_i_digits = 1;
5420 int min_i_chars = 1;
5422 if (state->cur_format.accum == NULL)
5423 return;
5425 /* We are ignoring number:decimal-replacement */
5427 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5428 if (oo_attr_bool (xin, attrs, OO_NS_NUMBER, "grouping", &grouping))
5430 else if (oo_attr_int_range (xin, attrs, OO_NS_NUMBER, "decimal-places", &decimal_places, 0, 30)) {
5431 decimals_specified = TRUE;
5432 } /* else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_NUMBER, */
5433 /* "display-factor")) */
5434 /* display_factor = gnm_strto (CXML2C (attrs[1]), NULL); */
5435 else if (oo_attr_int_range (xin, attrs, OO_NS_NUMBER,
5436 "min-integer-digits", &min_i_digits, 0, 30))
5438 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT,
5439 "min-integer-chars", &min_i_chars, 0, 30))
5442 if (decimals_specified || (min_i_digits != 1) || grouping || (min_i_chars > min_i_digits)) {
5443 if (min_i_chars > min_i_digits) {
5444 go_format_generate_number_str (state->cur_format.accum, min_i_chars, decimal_places,
5445 grouping, FALSE, FALSE, NULL, NULL);
5446 while (min_i_chars > min_i_digits) {
5447 /* substitute the left most 0 by ? */
5448 char *zero = strchr (state->cur_format.accum->str, '0');
5449 if (zero)
5450 *zero = '?';
5451 min_i_chars--;
5453 } else
5454 go_format_generate_number_str (state->cur_format.accum, min_i_digits, decimal_places,
5455 grouping, FALSE, FALSE, NULL, NULL);
5457 } else
5458 g_string_append (state->cur_format.accum, go_format_as_XL (go_format_general ()));
5461 static void
5462 odf_scientific (GsfXMLIn *xin, xmlChar const **attrs)
5464 OOParseState *state = (OOParseState *)xin->user_state;
5465 GOFormatDetails *details;
5466 gboolean engineering = FALSE;
5467 gboolean use_literal_E = FALSE;
5469 if (state->cur_format.accum == NULL)
5470 return;
5472 details = go_format_details_new (GO_FORMAT_SCIENTIFIC);
5474 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5475 if (oo_attr_bool (xin, attrs, OO_NS_NUMBER, "grouping", &details->thousands_sep)) {}
5476 else if (oo_attr_int_range (xin, attrs, OO_NS_NUMBER, "decimal-places",
5477 &details->num_decimals, 0, 30))
5479 else if (oo_attr_int_range (xin, attrs, OO_NS_NUMBER, "min-integer-digits",
5480 &details->min_digits, 0, 30))
5482 else if (oo_attr_int_range (xin, attrs, OO_NS_NUMBER, "min-exponent-digits",
5483 &details->exponent_digits, 0, 30))
5485 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "forced-exponent-sign",
5486 &(details->exponent_sign_forced)))
5488 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "engineering",
5489 &engineering))
5491 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "literal-E",
5492 &use_literal_E));
5493 if (engineering)
5494 details->exponent_step = 3;
5495 details->use_markup = !use_literal_E;
5496 details->simplify_mantissa = (details->min_digits == 0) && !use_literal_E;
5497 go_format_generate_str (state->cur_format.accum, details);
5499 go_format_details_free (details);
5502 static void
5503 odf_currency_symbol_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
5505 OOParseState *state = (OOParseState *)xin->user_state;
5507 if (state->cur_format.accum == NULL)
5508 return;
5509 if (0 == strcmp (xin->content->str, "$")) {
5510 g_string_append_c (state->cur_format.accum, '$');
5511 return;
5513 g_string_append (state->cur_format.accum, "[$");
5514 go_string_append_gstring (state->cur_format.accum, xin->content);
5515 g_string_append_c (state->cur_format.accum, ']');
5519 static void
5520 odf_map (GsfXMLIn *xin, xmlChar const **attrs)
5522 OOParseState *state = (OOParseState *)xin->user_state;
5523 char const *condition = NULL;
5524 char const *style_name = NULL;
5526 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5527 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "condition"))
5528 condition = attrs[1];
5529 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "apply-style-name"))
5530 style_name = attrs[1];
5532 if (condition != NULL && style_name != NULL && g_str_has_prefix (condition, "value()")) {
5533 condition += 7;
5534 while (*condition == ' ') condition++;
5535 state->conditions = g_slist_prepend (state->conditions, g_strdup (condition));
5536 state->cond_formats = g_slist_prepend (state->cond_formats,
5537 g_strdup (style_name));
5541 static void
5542 odf_format_invisible_text (GsfXMLIn *xin, xmlChar const **attrs)
5544 /* This can only be called inside a fixed text string */
5545 OOParseState *state = (OOParseState *)xin->user_state;
5546 char const *cnt = xin->content->str + state->cur_format.offset;
5547 int cnt_len = xin->content->len - state->cur_format.offset;
5548 char const *text = NULL;
5550 if (cnt_len == 1) {
5551 state->cur_format.offset += 1;
5553 } else if (cnt_len > 1) {
5554 oo_date_text_append (state, cnt, cnt_len - 1);
5555 state->cur_format.offset += cnt_len;
5558 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5559 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "char"))
5560 text = CXML2C (attrs[1]);
5562 if (text != NULL) {
5563 if (state->cur_format.string_opened) {
5564 g_string_append_c (state->cur_format.accum, '"');
5565 state->cur_format.string_opened = FALSE;
5567 g_string_append_c (state->cur_format.accum, '_');
5568 g_string_append (state->cur_format.accum, text);
5572 static void
5573 odf_format_repeated_text_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
5575 OOParseState *state = (OOParseState *)xin->user_state;
5577 /* This really only works for a single character. */
5578 g_string_append_c (state->cur_format.accum, '*');
5579 g_string_append (state->cur_format.accum, xin->content->str);
5582 static void
5583 odf_number_color (GsfXMLIn *xin, xmlChar const **attrs)
5585 OOParseState *state = (OOParseState *)xin->user_state;
5587 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
5588 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "color")){
5589 int r, b, g;
5590 if (3 == sscanf (CXML2C (attrs[1]), "#%2x%2x%2x", &r, &g, &b)) {
5591 GOColor col = GO_COLOR_FROM_RGB (r, g, b);
5592 int i = go_format_palette_index_from_color (col);
5593 char *color = go_format_palette_name_of_index (i);
5594 g_string_append_c (state->cur_format.accum, '[');
5595 g_string_append (state->cur_format.accum, color);
5596 g_string_append_c (state->cur_format.accum, ']');
5597 g_free (color);
5603 static void
5604 odf_number_style (GsfXMLIn *xin, xmlChar const **attrs)
5606 OOParseState *state = (OOParseState *)xin->user_state;
5607 char const *name = NULL;
5609 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5610 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "name"))
5611 name = CXML2C (attrs[1]);
5613 g_return_if_fail (state->cur_format.accum == NULL);
5615 state->cur_format.accum = g_string_new (NULL);
5616 state->cur_format.name = g_strdup (name);
5617 state->cur_format.percentage = FALSE;
5618 state->cur_format.percent_sign_seen = FALSE;
5619 state->conditions = NULL;
5620 state->cond_formats = NULL;
5624 static void
5625 odf_number_percentage_style (GsfXMLIn *xin, xmlChar const **attrs)
5627 OOParseState *state = (OOParseState *)xin->user_state;
5629 odf_number_style (xin, attrs);
5630 state->cur_format.percentage = TRUE;
5633 static void
5634 odf_cond_to_xl (GsfXMLIn *xin, GString *dst, const char *cond, int part, int parts)
5636 double val;
5637 const char *oper; /* xl-syntax */
5638 char *end;
5639 const char *cond0 = cond;
5641 while (g_ascii_isspace (*cond))
5642 cond++;
5644 if (cond[0] == '>' && cond[1] == '=')
5645 oper = ">=", cond += 2;
5646 else if (cond[0] == '>')
5647 oper = ">", cond++;
5648 else if (cond[0] == '<' && cond[1] == '=')
5649 oper = "<=", cond += 2;
5650 else if (cond[0] == '<' && cond[1] == '>')
5651 oper = "<>", cond += 2; /* Not standard, see bug 727297 */
5652 else if (cond[0] == '<')
5653 oper = "<", cond++;
5654 else if (cond[0] == '!' && cond[1] == '=')
5655 oper = "<>", cond += 2; /* surprise! */
5656 else if (cond[0] == '=')
5657 oper = "=", cond++;
5658 else
5659 goto bad;
5661 while (g_ascii_isspace (*cond))
5662 cond++;
5663 val = go_strtod (cond, &end);
5664 if (*end != 0 || !go_finite (val))
5665 goto bad;
5668 * Don't add the default condition. Note, that on save we cannot store
5669 * whether the condition was implicit or not, so just assume it was.
5671 if (part <= 2 && val == 0.0) {
5672 static const char *defaults[3] = { ">", "<", "=" };
5673 const char *def = (parts == 2 && part == 0)
5674 ? ">="
5675 : defaults[part];
5676 if (g_str_equal (oper, def))
5677 return;
5680 g_string_append_c (dst, '[');
5681 g_string_append (dst, oper);
5682 g_string_append (dst, cond); /* Copy value in string form */
5683 g_string_append_c (dst, ']');
5684 return;
5686 bad:
5687 oo_warning (xin, _("Corrupted file: invalid number format condition [%s]."), cond0);
5691 static void
5692 odf_number_style_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
5694 OOParseState *state = (OOParseState *)xin->user_state;
5696 g_return_if_fail (state->cur_format.accum != NULL);
5698 if (state->cur_format.percentage && !state->cur_format.percent_sign_seen)
5699 g_string_append_c (state->cur_format.accum, '%');
5700 state->cur_format.percentage = FALSE;
5702 if (state->cur_format.name == NULL) {
5703 g_string_free (state->cur_format.accum, TRUE);
5704 state->cur_format.accum = NULL;
5705 oo_warning (xin, _("Corrupted file: unnamed number style ignored."));
5706 return;
5709 if (state->conditions != NULL) {
5710 /* We have conditional formats */
5711 int part = 0, parts = g_slist_length (state->conditions) + 1;
5712 GSList *lc, *lf;
5713 GString *accum = g_string_new (NULL);
5715 /* We added things in opposite order, so reverse now. */
5716 lc = state->conditions = g_slist_reverse (state->conditions);
5717 lf = state->cond_formats = g_slist_reverse (state->cond_formats);
5719 while (lc && lf) {
5720 const char *cond = lc->data;
5721 const char *fmtname = lf->data;
5722 GOFormat const *fmt = g_hash_table_lookup (state->formats, fmtname);
5724 odf_cond_to_xl (xin, accum, cond, part, parts);
5726 if (!fmt) {
5727 oo_warning (xin, _("This file appears corrupted, required "
5728 "formats are missing."));
5729 fmt = go_format_general ();
5732 g_string_append (accum, go_format_as_XL (fmt));
5733 g_string_append_c (accum, ';');
5734 part++;
5735 lc = lc->next;
5736 lf = lf->next;
5739 if (state->cur_format.accum->len == 0)
5740 g_string_append (accum, "General");
5741 else
5742 g_string_append (accum, state->cur_format.accum->str);
5744 g_string_free (state->cur_format.accum, TRUE);
5745 state->cur_format.accum = accum;
5748 g_hash_table_insert (state->formats, state->cur_format.name,
5749 oo_canonical_format (state->cur_format.accum->str));
5750 g_string_free (state->cur_format.accum, TRUE);
5751 state->cur_format.accum = NULL;
5752 state->cur_format.name = NULL;
5753 g_slist_free_full (state->conditions, g_free);
5754 state->conditions = NULL;
5755 g_slist_free_full (state->cond_formats, g_free);
5756 state->cond_formats = NULL;
5759 /*****************************************************************************************************/
5761 static GtkPaperSize *
5762 odf_get_paper_size (gnm_float width, gnm_float height, gint orient)
5764 GtkPaperSize *size = NULL;
5765 char *name, *display_name;
5767 GList *plist = gtk_paper_size_get_paper_sizes (TRUE), *l;
5769 for (l = plist; l != NULL; l = l->next) {
5770 GtkPaperSize *n_size = l->data;
5771 double n_width = gtk_paper_size_get_width (n_size, GTK_UNIT_POINTS);
5772 double n_height = gtk_paper_size_get_height (n_size, GTK_UNIT_POINTS);
5773 double w_diff;
5774 double h_diff;
5776 if (orient == GTK_PAGE_ORIENTATION_PORTRAIT ||
5777 orient == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT) {
5778 w_diff = n_width - width;
5779 h_diff = n_height - height;
5780 } else {
5781 w_diff = n_height - width;
5782 h_diff = n_width - height;
5785 if (w_diff > -2. && w_diff < 2. && h_diff > -2 && h_diff < 2) {
5786 size = gtk_paper_size_copy (n_size);
5787 break;
5790 g_list_free_full (plist, (GDestroyNotify)gtk_paper_size_free);
5792 if (size != NULL)
5793 return size;
5795 name = g_strdup_printf ("odf_%ix%i", (int)width, (int)height);
5796 display_name = g_strdup_printf (_("Paper from ODF file: %ipt\xE2\xA8\x89%ipt"), (int)width, (int)height);
5797 size = gtk_paper_size_new_custom (name, display_name, width, height, GTK_UNIT_POINTS);
5798 g_free (name);
5799 g_free (display_name);
5800 return size;
5803 static void
5804 odf_header_properties (GsfXMLIn *xin, xmlChar const **attrs)
5806 OOParseState *state = (OOParseState *)xin->user_state;
5807 gboolean height_set = FALSE;
5808 gnm_float pts;
5809 double page_margin;
5810 GtkPageSetup *gps;
5812 if (state->print.cur_pi == NULL)
5813 return;
5814 gps = gnm_print_info_get_page_setup (state->print.cur_pi);
5815 page_margin = gtk_page_setup_get_top_margin (gps, GTK_UNIT_POINTS);
5817 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5818 if (oo_attr_distance (xin, attrs, OO_NS_SVG, "height", &pts)) {
5819 print_info_set_edge_to_below_header (state->print.cur_pi, pts + page_margin);
5820 height_set = TRUE;
5821 } else if (oo_attr_distance (xin, attrs, OO_NS_FO, "min-height", &pts))
5822 if (!height_set)
5823 print_info_set_edge_to_below_header
5824 (state->print.cur_pi, pts + page_margin);
5827 static void
5828 odf_footer_properties (GsfXMLIn *xin, xmlChar const **attrs)
5830 OOParseState *state = (OOParseState *)xin->user_state;
5831 gboolean height_set = FALSE;
5832 gnm_float pts;
5833 double page_margin;
5834 GtkPageSetup *gps;
5836 if (state->print.cur_pi == NULL)
5837 return;
5838 gps = gnm_print_info_get_page_setup (state->print.cur_pi);
5839 page_margin = gtk_page_setup_get_bottom_margin (gps, GTK_UNIT_POINTS);
5841 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5842 if (oo_attr_distance (xin, attrs, OO_NS_SVG, "height", &pts)) {
5843 print_info_set_edge_to_above_footer (state->print.cur_pi, pts + page_margin);
5844 height_set = TRUE;
5845 } else if (oo_attr_distance (xin, attrs, OO_NS_FO, "min-height", &pts))
5846 if (!height_set)
5847 print_info_set_edge_to_above_footer
5848 (state->print.cur_pi, pts + page_margin);
5852 static void
5853 odf_page_layout_properties (GsfXMLIn *xin, xmlChar const **attrs)
5855 static OOEnum const centre_type [] = {
5856 {"none" , 0},
5857 {"horizontal" , 1},
5858 {"vertical" , 2},
5859 {"both" , 1|2},
5860 {NULL , 0},
5862 static OOEnum const print_order_type [] = {
5863 {"ltr" , 0},
5864 {"ttb" , 1},
5865 {NULL , 0},
5867 static OOEnum const print_orientation_type [] = {
5868 {"portrait" , GTK_PAGE_ORIENTATION_PORTRAIT},
5869 {"landscape" , GTK_PAGE_ORIENTATION_LANDSCAPE},
5870 {NULL , 0},
5873 OOParseState *state = (OOParseState *)xin->user_state;
5874 gnm_float pts, height, width;
5875 gboolean h_set = FALSE, w_set = FALSE;
5876 GtkPageSetup *gps;
5877 gint tmp;
5878 gint orient = GTK_PAGE_ORIENTATION_PORTRAIT;
5879 gboolean gnm_style_print = FALSE;
5880 gboolean annotations_at_end = FALSE;
5881 gnm_float scale_to = 1.;
5882 gint scale_to_x = 0;
5883 gint scale_to_y = 0;
5884 GnmPrintInformation *pi = state->print.cur_pi;
5886 if (pi == NULL)
5887 return;
5888 gps = gnm_print_info_get_page_setup (state->print.cur_pi);
5889 gtk_page_setup_set_orientation (gps, GTK_PAGE_ORIENTATION_PORTRAIT);
5891 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
5892 if (oo_attr_distance (xin, attrs, OO_NS_FO, "margin-left", &pts))
5893 gtk_page_setup_set_left_margin (gps, pts, GTK_UNIT_POINTS);
5894 else if (oo_attr_distance (xin, attrs, OO_NS_FO, "margin-right", &pts))
5895 gtk_page_setup_set_right_margin (gps, pts, GTK_UNIT_POINTS);
5896 else if (oo_attr_distance (xin, attrs, OO_NS_FO, "margin-top", &pts))
5897 gtk_page_setup_set_top_margin (gps, pts, GTK_UNIT_POINTS);
5898 else if (oo_attr_distance (xin, attrs, OO_NS_FO, "margin-bottom", &pts))
5899 gtk_page_setup_set_bottom_margin (gps, pts, GTK_UNIT_POINTS);
5900 else if (oo_attr_distance (xin, attrs, OO_NS_FO, "page-height", &height))
5901 h_set = TRUE;
5902 else if (oo_attr_distance (xin, attrs, OO_NS_FO, "page-width", &width))
5903 w_set = TRUE;
5904 else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "table-centering",
5905 centre_type, &tmp)) {
5906 pi->center_horizontally = ((1 & tmp) != 0);
5907 pi->center_vertically = ((2 & tmp) != 0);
5908 } else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "print-page-order",
5909 print_order_type, &tmp)) {
5910 pi->print_across_then_down = (tmp == 0);
5911 } else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "print-orientation",
5912 print_orientation_type, &orient)) {
5913 gtk_page_setup_set_orientation (gps, orient);
5914 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
5915 OO_NS_STYLE, "print")) {
5916 gchar **items = g_strsplit (CXML2C (attrs[1]), " ", 0);
5917 gchar **items_c = items;
5918 pi->print_grid_lines = 0;
5919 pi->print_titles = 0;
5920 pi->comment_placement = GNM_PRINT_COMMENTS_NONE;
5921 for (;items != NULL && *items; items++)
5922 if (0 == strcmp (*items, "grid"))
5923 pi->print_grid_lines = 1;
5924 else if (0 == strcmp (*items, "headers"))
5925 pi->print_titles = 1;
5926 else if (0 == strcmp (*items, "annotations"))
5927 /* ODF does not distinguish AT_END and IN_PLACE */
5928 pi->comment_placement = GNM_PRINT_COMMENTS_AT_END;
5929 g_strfreev (items_c);
5930 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
5931 OO_GNUM_NS_EXT, "style-print")) {
5932 gchar **items = g_strsplit (CXML2C (attrs[1]), " ", 0);
5933 gchar **items_c = items;
5934 gnm_style_print = TRUE;
5935 pi->print_black_and_white = 0;
5936 pi->print_as_draft = 0;
5937 pi->print_even_if_only_styles = 0;
5938 pi->error_display = GNM_PRINT_ERRORS_AS_DISPLAYED;
5939 for (;items != NULL && *items; items++)
5940 if (0 == strcmp (*items, "annotations_at_end"))
5941 annotations_at_end = TRUE;
5942 else if (0 == strcmp (*items, "black_n_white"))
5943 pi->print_black_and_white = 1;
5944 else if (0 == strcmp (*items, "draft"))
5945 pi->print_as_draft = 1;
5946 else if (0 == strcmp (*items, "errors_as_blank"))
5947 pi->error_display = GNM_PRINT_ERRORS_AS_BLANK;
5948 else if (0 == strcmp (*items, "errors_as_dashes"))
5949 pi->error_display = GNM_PRINT_ERRORS_AS_DASHES;
5950 else if (0 == strcmp (*items, "errors_as_na"))
5951 pi->error_display = GNM_PRINT_ERRORS_AS_NA;
5952 else if (0 == strcmp (*items, "print_even_if_only_styles"))
5953 pi->print_even_if_only_styles = 1;
5954 g_strfreev (items_c);
5955 } else if (oo_attr_int_range (xin, attrs, OO_NS_STYLE, "scale-to-pages",
5956 &scale_to_x, 1, INT_MAX)) {
5957 scale_to_y = scale_to_x;
5958 scale_to = -1.;
5959 } else if (oo_attr_int_range (xin, attrs, OO_NS_STYLE, "scale-to-X",
5960 &scale_to_x, 1, INT_MAX)) {
5961 scale_to = -1.;
5962 } else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT, "scale-to-X",
5963 &scale_to_x, 1, INT_MAX)) {
5964 scale_to = -1.;
5965 } else if (oo_attr_int_range (xin, attrs, OO_NS_STYLE, "scale-to-Y",
5966 &scale_to_y, 1, INT_MAX)) {
5967 scale_to = -1.;
5968 } else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT, "scale-to-Y",
5969 &scale_to_y, 1, INT_MAX)) {
5970 scale_to = -1.;
5971 } else if (oo_attr_percent (xin, attrs, OO_NS_STYLE, "scale-to", &scale_to))
5973 if (scale_to < 0) {
5974 pi->scaling.dim.cols = scale_to_x;
5975 pi->scaling.dim.rows = scale_to_y;
5976 pi->scaling.type = PRINT_SCALE_FIT_PAGES;
5977 } else {
5978 pi->scaling.type = PRINT_SCALE_PERCENTAGE;
5979 pi->scaling.percentage.x = pi->scaling.percentage.y = scale_to * 100;
5982 if (gnm_style_print && pi->comment_placement != GNM_PRINT_COMMENTS_NONE)
5983 pi->comment_placement = annotations_at_end ? GNM_PRINT_COMMENTS_AT_END :
5984 GNM_PRINT_COMMENTS_IN_PLACE;
5986 /* STYLE "writing-mode" is being ignored since we can't store it anywhere atm */
5988 if (h_set && w_set) {
5989 GtkPaperSize *size;
5990 size = odf_get_paper_size (width, height, orient);
5991 gtk_page_setup_set_paper_size (gps, size);
5992 gtk_paper_size_free (size);
5997 static void
5998 odf_page_layout (GsfXMLIn *xin, xmlChar const **attrs)
6000 OOParseState *state = (OOParseState *)xin->user_state;
6001 char const *name = NULL;
6003 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6004 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "name"))
6005 name = CXML2C (attrs[1]);
6007 if (name == NULL) {
6008 oo_warning (xin, _("Missing page layout identifier"));
6009 name = "Missing page layout identifier";
6011 state->print.cur_pi = gnm_print_information_new (TRUE);
6012 g_hash_table_insert (state->styles.page_layouts, g_strdup (name),
6013 state->print.cur_pi);
6016 static void
6017 odf_page_layout_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
6019 OOParseState *state = (OOParseState *)xin->user_state;
6021 state->print.cur_pi = NULL;
6024 static void
6025 odf_header_footer_left (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
6027 OOParseState *state = (OOParseState *)xin->user_state;
6028 gboolean display = TRUE;
6030 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6031 if (oo_attr_bool (xin, attrs, OO_NS_STYLE, "display",
6032 &display)) ;
6034 if (display && !state->hd_ft_left_warned) {
6035 oo_warning (xin, _("Gnumeric does not support having a different "
6036 "style for left pages. This style is ignored."));
6037 state->hd_ft_left_warned = TRUE;
6041 static void
6042 odf_master_page (GsfXMLIn *xin, xmlChar const **attrs)
6044 OOParseState *state = (OOParseState *)xin->user_state;
6045 char const *name = NULL;
6046 char const *pl_name = NULL;
6047 GnmPrintInformation *pi = NULL;
6049 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6050 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "name"))
6051 name = CXML2C (attrs[1]);
6052 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
6053 OO_NS_STYLE, "page-layout-name"))
6054 pl_name = CXML2C (attrs[1]);
6056 if (pl_name != NULL)
6057 pi = g_hash_table_lookup (state->styles.page_layouts, pl_name);
6058 if (pi == NULL) {
6059 if (state->ver != OOO_VER_1) /* For OOO_VER_1 this may be acceptable */
6060 oo_warning (xin, _("Master page style without page layout encountered!"));
6061 state->print.cur_pi = gnm_print_information_new (TRUE);
6062 } else
6063 state->print.cur_pi = gnm_print_info_dup (pi);
6065 if (name == NULL) {
6066 oo_warning (xin, _("Master page style without name encountered!"));
6067 name = "Master page style without name encountered!";
6070 gnm_print_hf_free (state->print.cur_pi->header);
6071 gnm_print_hf_free (state->print.cur_pi->footer);
6072 state->print.cur_pi->header = gnm_print_hf_new (NULL, NULL, NULL);
6073 state->print.cur_pi->footer = gnm_print_hf_new (NULL, NULL, NULL);
6075 g_hash_table_insert (state->styles.master_pages, g_strdup (name), state->print.cur_pi);
6078 static void
6079 odf_master_page_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
6081 OOParseState *state = (OOParseState *)xin->user_state;
6083 state->print.cur_pi = NULL;
6084 state->print.cur_hf = NULL;
6085 state->print.cur_hf_format = NULL;
6088 static void
6089 odf_header_footer_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
6091 OOParseState *state = (OOParseState *)xin->user_state;
6093 if (state->text_p_stack) {
6094 oo_text_p_t *ptr = state->text_p_stack->data;
6095 if (ptr->gstr) {
6096 g_free (*(state->print.cur_hf_format));
6097 *(state->print.cur_hf_format) = g_string_free (ptr->gstr, FALSE);
6098 ptr->gstr = NULL;
6102 odf_pop_text_p (state);
6105 static void
6106 odf_header_footer (GsfXMLIn *xin, xmlChar const **attrs)
6108 OOParseState *state = (OOParseState *)xin->user_state;
6109 gboolean display = TRUE;
6110 gdouble margin;
6111 GtkPageSetup *gps;
6113 if (state->print.cur_pi == NULL)
6114 return;
6115 gps = gnm_print_info_get_page_setup (state->print.cur_pi);
6117 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6118 if (oo_attr_bool (xin, attrs, OO_NS_STYLE, "display",
6119 &display)) ;
6120 if (xin->node->user_data.v_int == 0) {
6121 state->print.cur_hf = state->print.cur_pi->header;
6122 margin = gtk_page_setup_get_top_margin (gps, GTK_UNIT_POINTS);
6123 if (display) {
6124 if (margin >= state->print.cur_pi->edge_to_below_header)
6125 print_info_set_edge_to_below_header (state->print.cur_pi, margin + 1);
6126 } else
6127 print_info_set_edge_to_below_header (state->print.cur_pi, margin);
6128 } else {
6129 state->print.cur_hf = state->print.cur_pi->footer;
6130 margin = gtk_page_setup_get_bottom_margin (gps, GTK_UNIT_POINTS);
6131 if (display) {
6132 if (margin >= state->print.cur_pi->edge_to_above_footer)
6133 print_info_set_edge_to_above_footer (state->print.cur_pi, margin + 1);
6134 } else
6135 print_info_set_edge_to_above_footer (state->print.cur_pi, margin);
6137 state->print.cur_hf_format = &state->print.cur_hf->middle_format;
6139 odf_push_text_p (state, FALSE);
6142 static void
6143 odf_hf_region_end (GsfXMLIn *xin, GsfXMLBlob *blob)
6145 OOParseState *state = (OOParseState *)xin->user_state;
6147 odf_header_footer_end (xin, blob);
6148 state->print.cur_hf_format = &state->print.cur_hf->middle_format;
6151 static void
6152 odf_hf_region (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
6154 OOParseState *state = (OOParseState *)xin->user_state;
6156 if (state->print.cur_hf != NULL)
6157 switch (xin->node->user_data.v_int) {
6158 case 0:
6159 state->print.cur_hf_format = &state->print.cur_hf->left_format;
6160 break;
6161 case 1:
6162 state->print.cur_hf_format = &state->print.cur_hf->middle_format;
6163 break;
6164 case 2:
6165 state->print.cur_hf_format = &state->print.cur_hf->right_format;
6166 break;
6168 odf_push_text_p (state, FALSE);
6171 static void
6172 odf_hf_item_start (GsfXMLIn *xin)
6174 OOParseState *state = (OOParseState *)xin->user_state;
6176 if (xin->content->str != NULL && *xin->content->str != 0) {
6177 oo_text_p_t *ptr = state->text_p_stack->data;
6178 odf_text_p_add_text (state, xin->content->str + ptr->offset);
6179 ptr->offset = strlen (xin->content->str);
6183 static void
6184 odf_hf_item (GsfXMLIn *xin, char const *item)
6186 OOParseState *state = (OOParseState *)xin->user_state;
6188 odf_text_p_add_text (state, "&[");
6189 odf_text_p_add_text (state, item);
6190 odf_text_p_add_text (state, "]");
6193 static void
6194 odf_hf_sheet_name (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
6196 odf_hf_item_start (xin);
6197 odf_hf_item (xin, _("TAB"));
6200 static void
6201 odf_hf_item_w_data_style (GsfXMLIn *xin, xmlChar const **attrs, char const *item)
6203 OOParseState *state = (OOParseState *)xin->user_state;
6204 char const *data_style_name = NULL;
6206 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6207 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "data-style-name"))
6208 data_style_name = CXML2C (attrs[1]);
6210 odf_hf_item_start (xin);
6211 if (data_style_name == NULL)
6212 odf_hf_item (xin, item);
6213 else {
6214 GOFormat const *fmt =
6215 g_hash_table_lookup (state->formats, data_style_name);
6216 if (fmt != NULL) {
6217 char const *fmt_str = go_format_as_XL (fmt);
6218 char *str = g_strconcat (item, ":", fmt_str, NULL);
6219 odf_hf_item (xin, str);
6220 g_free (str);
6225 static void
6226 odf_hf_date (GsfXMLIn *xin, xmlChar const **attrs)
6228 odf_hf_item_start (xin);
6229 odf_hf_item_w_data_style (xin, attrs, _("DATE"));
6232 static void
6233 odf_hf_time (GsfXMLIn *xin, xmlChar const **attrs)
6235 odf_hf_item_start (xin);
6236 odf_hf_item_w_data_style (xin, attrs, _("TIME"));
6239 static void
6240 odf_hf_page_number (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
6242 odf_hf_item_start (xin);
6243 odf_hf_item (xin, _("PAGE"));
6246 static void
6247 odf_hf_page_count (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
6249 odf_hf_item_start (xin);
6250 odf_hf_item (xin, _("PAGES"));
6253 static void
6254 odf_hf_file (GsfXMLIn *xin, xmlChar const **attrs)
6256 static OOEnum const display_types [] = {
6257 { "full", 0 },
6258 { "path", 1 },
6259 { "name", 2 },
6260 { "name-and-extension", 2 },
6261 { NULL, 0 },
6263 OOParseState *state = (OOParseState *)xin->user_state;
6264 int tmp = 2;
6266 if (state->print.cur_hf_format == NULL)
6267 return;
6269 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6270 if (oo_attr_enum (xin, attrs, OO_NS_TEXT, "display", display_types, &tmp)) ;
6272 odf_hf_item_start (xin);
6273 switch (tmp) {
6274 case 0:
6275 odf_hf_item (xin, _("PATH"));
6276 odf_text_p_add_text (state, "/");
6277 odf_hf_item (xin, _("FILE"));
6278 break;
6279 case 1:
6280 odf_hf_item (xin, _("PATH"));
6281 break;
6282 default:
6283 case 2:
6284 odf_hf_item (xin, _("FILE"));
6285 break;
6289 static void
6290 odf_hf_expression (GsfXMLIn *xin, xmlChar const **attrs)
6292 static OOEnum const display_types [] = {
6293 { "none", 0 },
6294 { "formula", 1 },
6295 { "value", 2 },
6296 { NULL, 0 },
6298 OOParseState *state = (OOParseState *)xin->user_state;
6299 char const *formula = NULL;
6300 gint tmp = 2;
6302 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6303 if (oo_attr_enum (xin, attrs, OO_NS_TEXT, "display", display_types, &tmp)) ;
6304 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TEXT, "formula"))
6305 formula = CXML2C (attrs[1]);
6307 if (tmp == 0)
6308 return;
6310 if (formula == NULL || *formula == '\0') {
6311 oo_warning (xin, _("Missing expression"));
6312 return;
6313 } else {
6314 /* Since we have no sheets we postpone parsing the expression */
6315 gchar const *str = odf_string_id (state, formula);
6316 char *new;
6317 new = g_strconcat ((tmp == 1) ? "cellt" : "cell", ":", str, NULL);
6318 odf_hf_item_start (xin);
6319 odf_hf_item (xin, new);
6320 g_free (new);
6324 static void
6325 odf_hf_title (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
6327 odf_hf_item_start (xin);
6328 odf_hf_item (xin, _("TITLE"));
6331 /*****************************************************************************************************/
6335 static void
6336 oo_set_gnm_border (G_GNUC_UNUSED GsfXMLIn *xin, GnmStyle *style,
6337 xmlChar const *str, GnmStyleElement location)
6339 GnmStyleBorderType border_style;
6340 GnmBorder *old_border, *new_border;
6341 GnmStyleBorderLocation const loc =
6342 GNM_STYLE_BORDER_TOP + (int)(location - MSTYLE_BORDER_TOP);
6344 if (!strcmp ((char const *)str, "hair"))
6345 border_style = GNM_STYLE_BORDER_HAIR;
6346 else if (!strcmp ((char const *)str, "medium-dash"))
6347 border_style = GNM_STYLE_BORDER_MEDIUM_DASH;
6348 else if (!strcmp ((char const *)str, "dash-dot"))
6349 border_style = GNM_STYLE_BORDER_DASH_DOT;
6350 else if (!strcmp ((char const *)str, "medium-dash-dot"))
6351 border_style = GNM_STYLE_BORDER_MEDIUM_DASH_DOT;
6352 else if (!strcmp ((char const *)str, "dash-dot-dot"))
6353 border_style = GNM_STYLE_BORDER_DASH_DOT_DOT;
6354 else if (!strcmp ((char const *)str, "medium-dash-dot-dot"))
6355 border_style = GNM_STYLE_BORDER_MEDIUM_DASH_DOT_DOT;
6356 else if (!strcmp ((char const *)str, "slanted-dash-dot"))
6357 border_style = GNM_STYLE_BORDER_SLANTED_DASH_DOT;
6358 else {
6359 oo_warning (xin, _("Unknown Gnumeric border style \'%s\' "
6360 "encountered."), (char const *)str);
6361 return;
6364 old_border = gnm_style_get_border (style, location);
6365 new_border = gnm_style_border_fetch (border_style,
6366 old_border ?
6367 style_color_ref(old_border->color)
6368 : style_color_black (),
6369 gnm_style_border_get_orientation (loc));
6370 gnm_style_set_border (style, location, new_border);
6373 static void
6374 oo_parse_border (GsfXMLIn *xin, GnmStyle *style,
6375 xmlChar const *str, GnmStyleElement location)
6377 double pts;
6378 char const *end = oo_parse_distance (xin, str, "border", &pts);
6379 GnmBorder *border = NULL;
6380 GnmColor *color = NULL;
6381 const char *border_color = NULL;
6382 GnmStyleBorderType border_style;
6383 GnmStyleBorderLocation const loc =
6384 GNM_STYLE_BORDER_TOP + (int)(location - MSTYLE_BORDER_TOP);
6386 if (end == NULL || end == CXML2C (str))
6387 return;
6388 while (*end == ' ')
6389 end++;
6390 /* "0.035cm solid #000000" */
6391 border_color = strchr (end, '#');
6392 if (border_color) {
6393 char *border_type = g_strndup (end, border_color - end);
6394 color = oo_parse_color (xin, CC2XML (border_color), "color");
6396 if (color) {
6397 if (g_str_has_prefix (border_type, "none")||
6398 g_str_has_prefix (border_type, "hidden"))
6399 border_style = GNM_STYLE_BORDER_NONE;
6400 else if (g_str_has_prefix (border_type, "solid") ||
6401 g_str_has_prefix (border_type, "groove") ||
6402 g_str_has_prefix (border_type, "ridge") ||
6403 g_str_has_prefix (border_type, "inset") ||
6404 g_str_has_prefix (border_type, "outset")) {
6405 if (pts <= OD_BORDER_THIN)
6406 border_style = GNM_STYLE_BORDER_THIN;
6407 else if (pts <= OD_BORDER_MEDIUM)
6408 border_style = GNM_STYLE_BORDER_MEDIUM;
6409 else
6410 border_style = GNM_STYLE_BORDER_THICK;
6411 } else if (g_str_has_prefix (border_type, "dashed"))
6412 border_style = GNM_STYLE_BORDER_DASHED;
6413 else if (g_str_has_prefix (border_type, "dotted"))
6414 border_style = GNM_STYLE_BORDER_DOTTED;
6415 else
6416 border_style = GNM_STYLE_BORDER_DOUBLE;
6418 border = gnm_style_border_fetch (border_style, color,
6419 gnm_style_border_get_orientation (loc));
6420 border->width = pts;
6421 gnm_style_set_border (style, location, border);
6423 g_free (border_type);
6427 static void
6428 odf_style_set_align_h (GnmStyle *style, gint h_align_is_valid, gboolean repeat_content,
6429 int text_align, int gnm_halign)
6431 if (repeat_content)
6432 gnm_style_set_align_h (style, GNM_HALIGN_FILL);
6433 else switch (h_align_is_valid) {
6434 case 1:
6435 if (gnm_halign > -1)
6436 gnm_style_set_align_h (style, gnm_halign);
6437 else
6438 gnm_style_set_align_h (style, (text_align < 0) ? GNM_HALIGN_LEFT : text_align);
6439 break;
6440 case 2:
6441 gnm_style_set_align_h (style, GNM_HALIGN_GENERAL);
6442 break;
6443 default:
6444 break;
6448 static void
6449 oo_style_prop_cell (GsfXMLIn *xin, xmlChar const **attrs)
6451 static OOEnum const underline_styles [] = {
6452 { "none", 1 },
6453 { "dash", 2 },
6454 { "dot-dash", 2 },
6455 { "dot-dot-dash", 2 },
6456 { "dotted", 2 },
6457 { "long-dash", 2 },
6458 { "solid", 3 },
6459 { "wave", 4 },
6460 { NULL, 0 },
6462 static OOEnum const underline_types [] = {
6463 { "none", 0 },
6464 { "single", 1 },
6465 { "double", 2 },
6466 { NULL, 0 },
6468 static OOEnum const text_line_through_styles [] = {
6469 { "none", 0 },
6470 { "dash", 1 },
6471 { "dot-dash", 1 },
6472 { "dot-dot-dash", 1 },
6473 { "dotted", 1 },
6474 { "long-dash", 1 },
6475 { "solid", 1 },
6476 { "wave", 1 },
6477 { NULL, 0 },
6479 static OOEnum const text_line_through_types [] = {
6480 { "none", 0 },
6481 { "single", 1 },
6482 { "double", 1 },
6483 { NULL, 0 },
6485 static OOEnum const h_alignments [] = {
6486 { "start", -1 }, /* see below, we may have a gnm:GnmHAlign attribute */
6487 { "left", GNM_HALIGN_LEFT },
6488 { "center", GNM_HALIGN_CENTER },
6489 { "end", GNM_HALIGN_RIGHT }, /* This really depends on the text direction */
6490 { "right", GNM_HALIGN_RIGHT },
6491 { "justify", GNM_HALIGN_JUSTIFY },
6492 { "automatic", GNM_HALIGN_GENERAL },
6493 { NULL, 0 },
6495 static OOEnum const v_alignments [] = {
6496 { "bottom", GNM_VALIGN_BOTTOM },
6497 { "top", GNM_VALIGN_TOP },
6498 { "middle", GNM_VALIGN_CENTER },
6499 { "automatic", -1 }, /* see below, we may have a gnm:GnmVAlign attribute */
6500 { NULL, 0 },
6502 static OOEnum const protections [] = {
6503 { "none", 0 },
6504 { "hidden-and-protected", 1 | 2 },
6505 { "protected", 2 },
6506 { "formula-hidden", 1 },
6507 { "protected formula-hidden", 1 | 2 },
6508 { "formula-hidden protected", 1 | 2 },
6509 { NULL, 0 },
6511 OOParseState *state = (OOParseState *)xin->user_state;
6512 GnmColor *color, *gnm_b_color = NULL, *gnm_p_color = NULL;
6513 int gnm_pattern = 0;
6514 GnmStyle *style = state->cur_style.cells->style;
6515 gboolean btmp;
6516 int tmp;
6517 gnm_float tmp_f;
6518 gboolean v_alignment_is_fixed = FALSE;
6519 int strike_through_type = -1, strike_through_style = -1;
6520 int underline_type = 0;
6521 int underline_style = 0;
6522 gboolean underline_bold = FALSE;
6523 gboolean underline_low = FALSE;
6525 g_return_if_fail (style != NULL);
6527 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6528 if ((color = oo_attr_color (xin, attrs, OO_NS_FO, "background-color"))) {
6529 gnm_style_set_back_color (style, color);
6530 if (color == magic_transparent)
6531 gnm_style_set_pattern (style, 0);
6532 else
6533 gnm_style_set_pattern (style, 1);
6534 } else if ((color = oo_attr_color (xin, attrs, OO_GNUM_NS_EXT, "background-colour"))) {
6535 gnm_b_color = color;
6536 } else if ((color = oo_attr_color (xin, attrs, OO_GNUM_NS_EXT, "pattern-colour"))) {
6537 gnm_p_color = color;
6538 } else if (oo_attr_int (xin, attrs, OO_GNUM_NS_EXT, "pattern", &gnm_pattern)) {
6539 } else if ((color = oo_attr_color (xin, attrs, OO_NS_FO, "color")))
6540 gnm_style_set_font_color (style, color);
6541 else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "cell-protect", protections, &tmp)) {
6542 gnm_style_set_contents_locked (style, (tmp & 2) != 0);
6543 gnm_style_set_contents_hidden (style, (tmp & 1) != 0);
6544 } else if (oo_attr_enum (xin, attrs,
6545 (state->ver >= OOO_VER_OPENDOC) ? OO_NS_FO : OO_NS_STYLE,
6546 "text-align", h_alignments, &(state->text_align))) {
6547 /* Note that style:text-align-source, style:text_align, style:repeat-content */
6548 /* and gnm:GnmHAlign interact but can appear in any order and arrive from different */
6549 /* elements, so we can't use local variables */
6550 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "text-align-source")) {
6551 state->h_align_is_valid = attr_eq (attrs[1], "fix") ? 1 : 2;
6552 } else if (oo_attr_bool (xin, attrs, OO_NS_STYLE, "repeat-content", &(state->repeat_content))) {
6553 } else if (oo_attr_int (xin,attrs, OO_GNUM_NS_EXT, "GnmHAlign", &(state->gnm_halign))) {
6554 }else if (oo_attr_enum (xin, attrs,
6555 (state->ver >= OOO_VER_OPENDOC) ? OO_NS_STYLE : OO_NS_FO,
6556 "vertical-align", v_alignments, &tmp)) {
6557 if (tmp != -1) {
6558 gnm_style_set_align_v (style, tmp);
6559 v_alignment_is_fixed = TRUE;
6560 } else if (!v_alignment_is_fixed)
6561 /* This should depend on the rotation */
6562 gnm_style_set_align_v (style, GNM_VALIGN_BOTTOM);
6563 } else if (oo_attr_int (xin,attrs, OO_GNUM_NS_EXT, "GnmVAlign", &tmp)) {
6564 if (!v_alignment_is_fixed) {
6565 gnm_style_set_align_v (style, tmp);
6566 v_alignment_is_fixed = TRUE;
6568 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "wrap-option"))
6569 gnm_style_set_wrap_text (style, attr_eq (attrs[1], "wrap"));
6570 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "border-bottom"))
6571 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_BOTTOM);
6572 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "border-left"))
6573 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_LEFT);
6574 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "border-right"))
6575 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_RIGHT);
6576 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "border-top"))
6577 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_TOP);
6578 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "border")) {
6579 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_BOTTOM);
6580 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_LEFT);
6581 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_RIGHT);
6582 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_TOP);
6583 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "diagonal-bl-tr"))
6584 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_DIAGONAL);
6585 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "diagonal-tl-br"))
6586 oo_parse_border (xin, style, attrs[1], MSTYLE_BORDER_REV_DIAGONAL);
6587 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "border-line-style-bottom"))
6588 oo_set_gnm_border (xin, style, attrs[1], MSTYLE_BORDER_BOTTOM);
6589 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "border-line-style-top"))
6590 oo_set_gnm_border (xin, style, attrs[1], MSTYLE_BORDER_TOP);
6591 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "border-line-style-left"))
6592 oo_set_gnm_border (xin, style, attrs[1], MSTYLE_BORDER_LEFT);
6593 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "border-line-style-right"))
6594 oo_set_gnm_border (xin, style, attrs[1], MSTYLE_BORDER_RIGHT);
6595 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "diagonal-bl-tr-line-style"))
6596 oo_set_gnm_border (xin, style, attrs[1], MSTYLE_BORDER_DIAGONAL);
6597 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "diagonal-tl-br-line-style"))
6598 oo_set_gnm_border (xin, style, attrs[1], MSTYLE_BORDER_REV_DIAGONAL);
6599 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "font-name"))
6600 /* According to the ODF standards, this name is just a reference to a */
6601 /* <style:font-face> element. So this may not be an acceptable font name! */
6602 gnm_style_set_font_name (style, CXML2C (attrs[1]));
6603 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "font-family"))
6604 gnm_style_set_font_name (style, CXML2C (attrs[1]));
6605 else if (oo_attr_distance (xin, attrs, OO_NS_FO, "font-size", &tmp_f))
6606 gnm_style_set_font_size (style, tmp_f);
6607 else if (oo_attr_bool (xin, attrs, OO_NS_STYLE, "shrink-to-fit", &btmp))
6608 gnm_style_set_shrink_to_fit (style, btmp);
6609 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "direction"))
6610 gnm_style_set_text_dir (style, attr_eq (attrs[1], "rtl") ? GNM_TEXT_DIR_RTL : GNM_TEXT_DIR_LTR);
6611 else if (oo_attr_int (xin, attrs, OO_NS_STYLE, "rotation-angle", &tmp)) {
6612 tmp = tmp % 360;
6613 gnm_style_set_rotation (style, tmp);
6614 } else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "text-underline-style",
6615 underline_styles, &underline_style)) {
6616 } else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "text-underline-type",
6617 underline_types, &underline_type)) {
6618 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
6619 OO_NS_STYLE, "text-underline-width")) {
6620 underline_bold = attr_eq (attrs[1], "bold");
6621 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
6622 OO_GNUM_NS_EXT, "text-underline-placement")) {
6623 underline_low = attr_eq (attrs[1], "low");
6624 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "font-style"))
6625 gnm_style_set_font_italic (style, attr_eq (attrs[1], "italic"));
6626 else if (oo_attr_font_weight (xin, attrs, &tmp))
6627 gnm_style_set_font_bold (style, tmp >= PANGO_WEIGHT_SEMIBOLD);
6628 else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "text-line-through-style",
6629 text_line_through_styles, &strike_through_style));
6630 else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "text-line-through-type",
6631 text_line_through_types, &strike_through_type));
6632 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
6633 OO_NS_STYLE, "text-position")) {
6634 if (g_str_has_prefix (attrs[1],"super"))
6635 gnm_style_set_font_script (style, GO_FONT_SCRIPT_SUPER);
6636 else if (g_str_has_prefix (attrs[1], "sub"))
6637 gnm_style_set_font_script (style, GO_FONT_SCRIPT_SUB);
6638 else
6639 gnm_style_set_font_script (style, GO_FONT_SCRIPT_STANDARD);
6640 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "margin-left")) {
6641 tmp_f = 0.;
6642 oo_parse_distance (xin, attrs[1], "margin-left", &tmp_f);
6643 gnm_style_set_indent (style, tmp_f);
6646 if (strike_through_style != -1 || strike_through_type != -1)
6647 gnm_style_set_font_strike (style, strike_through_style > 0 ||
6648 (strike_through_type > 0 && strike_through_style == -1));
6651 if (underline_style > 0) {
6652 GnmUnderline underline = UNDERLINE_NONE;
6653 if (underline_style > 1) {
6654 switch (underline_type) {
6655 case 0:
6656 underline = UNDERLINE_NONE;
6657 break;
6658 case 2:
6659 if (underline_low) {
6660 underline = UNDERLINE_DOUBLE_LOW;
6661 } else {
6662 underline = UNDERLINE_DOUBLE;
6664 break;
6665 case 1:
6666 default:
6667 if (underline_low) {
6668 underline = underline_bold ? UNDERLINE_DOUBLE_LOW : UNDERLINE_SINGLE_LOW;
6669 } else {
6670 underline = underline_bold ? UNDERLINE_DOUBLE : UNDERLINE_SINGLE;
6672 break;
6675 gnm_style_set_font_uline (style, underline);
6678 if (gnm_pattern > 0)
6679 gnm_style_set_pattern (style, gnm_pattern);
6680 if (gnm_b_color)
6681 gnm_style_set_back_color (style, gnm_b_color);
6682 if (gnm_p_color)
6683 gnm_style_set_pattern_color (style, gnm_p_color);
6686 static OOPageBreakType
6687 oo_page_break_type (GsfXMLIn *xin, xmlChar const *attr)
6689 /* Note that truly automatic of soft page breaks are stored */
6690 /* via text:soft-page-break tags */
6691 if (!strcmp (attr, "page"))
6692 return OO_PAGE_BREAK_MANUAL;
6693 if (!strcmp (attr, "column"))
6694 return OO_PAGE_BREAK_MANUAL;
6695 if (!strcmp (attr, "auto"))
6696 return OO_PAGE_BREAK_NONE;
6697 oo_warning (xin,
6698 _("Unknown break type '%s' defaulting to NONE"), attr);
6699 return OO_PAGE_BREAK_NONE;
6702 static void
6703 oo_style_prop_col_row (GsfXMLIn *xin, xmlChar const **attrs)
6705 OOParseState *state = (OOParseState *)xin->user_state;
6706 char const * const size_tag = (state->cur_style.type == OO_STYLE_COL)
6707 ? "column-width" : "row-height";
6708 char const * const use_optimal = (state->cur_style.type == OO_STYLE_COL)
6709 ? "use-optimal-column-width" : "use-optimal-row-height";
6710 gnm_float pts;
6711 gboolean auto_size;
6713 g_return_if_fail (state->cur_style.col_rows != NULL);
6715 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6716 if (NULL != oo_attr_distance (xin, attrs, OO_NS_STYLE, size_tag, &pts))
6717 state->cur_style.col_rows->size_pts = pts;
6718 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "break-before"))
6719 state->cur_style.col_rows->break_before =
6720 oo_page_break_type (xin, attrs[1]);
6721 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "break-after"))
6722 state->cur_style.col_rows->break_after =
6723 oo_page_break_type (xin, attrs[1]);
6724 else if (oo_attr_bool (xin, attrs, OO_NS_STYLE, use_optimal, &auto_size))
6725 state->cur_style.col_rows->manual = !auto_size;
6728 static void
6729 oo_style_prop_table (GsfXMLIn *xin, xmlChar const **attrs)
6731 static OOEnum const modes [] = {
6732 { "lr-tb", 0 },
6733 { "rl-tb", 1 },
6734 { "tb-rl", 1 },
6735 { "tb-lr", 0 },
6736 { "lr", 0 },
6737 { "rl", 1 },
6738 { "tb", 0 }, /* what do tb and page imply in this context ? */
6739 { "page", 0 },
6740 { NULL, 0 },
6742 OOParseState *state = (OOParseState *)xin->user_state;
6743 OOSheetStyle *style = state->cur_style.sheets;
6744 int tmp_i;
6745 gboolean tmp_b;
6747 g_return_if_fail (style != NULL);
6749 style->visibility = GNM_SHEET_VISIBILITY_VISIBLE;
6750 style->is_rtl = FALSE;
6752 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6753 if (oo_attr_bool (xin, attrs, OO_NS_TABLE, "display", &tmp_b)) {
6754 if (!tmp_b)
6755 style->visibility = GNM_SHEET_VISIBILITY_HIDDEN;
6756 } else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "display-formulas",
6757 &style->display_formulas)) {
6758 } else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "display-col-header",
6759 &tmp_b)) {
6760 style->hide_col_header = !tmp_b;
6761 } else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "display-row-header",
6762 &tmp_b)) {
6763 style->hide_row_header = !tmp_b;
6764 } else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "writing-mode", modes, &tmp_i))
6765 style->is_rtl = tmp_i;
6766 else if ((!style->tab_color_set &&
6767 /* Gnumeric's version */
6768 gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
6769 OO_GNUM_NS_EXT, "tab-color")) ||
6770 (!style->tab_color_set &&
6771 /* Used by LO 3.3.3 and later */
6772 gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
6773 OO_NS_TABLE_OOO, "tab-color")) ||
6774 /* For ODF 1.3 etc. */
6775 (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
6776 OO_NS_TABLE, "tab-color"))) {
6777 /* For ODF 1.3 etc. */
6778 GdkRGBA rgba;
6779 if (gdk_rgba_parse (&rgba, CXML2C (attrs[1]))) {
6780 go_color_from_gdk_rgba (&rgba, &style->tab_color);
6781 style->tab_color_set = TRUE;
6782 } else
6783 oo_warning (xin, _("Unable to parse "
6784 "tab color \'%s\'"),
6785 CXML2C (attrs[1]));
6786 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
6787 OO_GNUM_NS_EXT,
6788 "tab-text-color")) {
6789 GdkRGBA rgba;
6790 if (gdk_rgba_parse (&rgba, CXML2C (attrs[1]))) {
6791 go_color_from_gdk_rgba (&rgba, &style->tab_text_color);
6792 style->tab_text_color_set = TRUE;
6793 } else
6794 oo_warning (xin, _("Unable to parse tab "
6795 "text color \'%s\'"),
6796 CXML2C (attrs[1]));
6802 static void
6803 oo_style_map (GsfXMLIn *xin, xmlChar const **attrs)
6805 OOParseState *state = (OOParseState *)xin->user_state;
6806 char const *style_name = NULL, *base = NULL;
6807 char const *condition = NULL;
6808 OOCellStyle *style = NULL;
6810 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
6811 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "condition"))
6812 condition = attrs[1];
6813 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "apply-style-name"))
6814 style_name = attrs[1];
6815 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_STYLE, "base-cell-address"))
6816 base = attrs[1];
6817 if (style_name == NULL || condition == NULL)
6818 return;
6820 style = g_hash_table_lookup (state->styles.cell, style_name);
6821 odf_oo_cell_style_attach_condition(state->cur_style.cells, style, condition, base);
6824 static OOProp *
6825 oo_prop_new_double (char const *name, gnm_float val)
6827 OOProp *res = g_new0 (OOProp, 1);
6828 res->name = name;
6829 g_value_init (&res->value, G_TYPE_DOUBLE);
6830 g_value_set_double (&res->value, val);
6831 return res;
6833 static OOProp *
6834 oo_prop_new_bool (char const *name, gboolean val)
6836 OOProp *res = g_new0 (OOProp, 1);
6837 res->name = name;
6838 g_value_init (&res->value, G_TYPE_BOOLEAN);
6839 g_value_set_boolean (&res->value, val);
6840 return res;
6842 static OOProp *
6843 oo_prop_new_int (char const *name, int val)
6845 OOProp *res = g_new0 (OOProp, 1);
6846 res->name = name;
6847 g_value_init (&res->value, G_TYPE_INT);
6848 g_value_set_int (&res->value, val);
6849 return res;
6851 static OOProp *
6852 oo_prop_new_string (char const *name, char const *val)
6854 OOProp *res = g_new0 (OOProp, 1);
6855 res->name = name;
6856 g_value_init (&res->value, G_TYPE_STRING);
6857 g_value_set_string (&res->value, val);
6858 return res;
6860 static void
6861 oo_prop_free (OOProp *prop)
6863 g_value_unset (&prop->value);
6864 g_free (prop);
6867 static void
6868 oo_prop_list_free (GSList *props)
6870 g_slist_free_full (props, (GDestroyNotify)oo_prop_free);
6873 static void
6874 oo_prop_list_apply (GSList *props, GObject *obj)
6876 GSList *ptr;
6877 OOProp *prop;
6878 GObjectClass *klass;
6880 if (NULL == obj)
6881 return;
6882 klass = G_OBJECT_GET_CLASS (obj);
6884 for (ptr = props; ptr; ptr = ptr->next) {
6885 prop = ptr->data;
6886 if (NULL != g_object_class_find_property (klass, prop->name))
6887 g_object_set_property (obj, prop->name, &prop->value);
6891 static void
6892 odf_apply_expression (GsfXMLIn *xin, gint dim, GObject *obj, gchar const *expression)
6894 OOParseState *state = (OOParseState *)xin->user_state;
6895 GnmParsePos pp;
6896 GOData *data;
6897 GnmExprTop const *expr;
6898 parse_pos_init (&pp, state->pos.wb, state->pos.sheet, 0, 0);
6899 expr = oo_expr_parse_str
6900 (xin, expression, &pp,
6901 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES,
6902 FORMULA_OPENFORMULA);
6903 if (expr != NULL) {
6904 data = gnm_go_data_scalar_new_expr (state->pos.sheet, expr);
6905 gog_dataset_set_dim (GOG_DATASET (obj), dim, data, NULL);
6909 static void
6910 oo_prop_list_apply_to_axisline (GsfXMLIn *xin, GSList *props, GObject *obj)
6912 GSList *ptr;
6913 OOProp *prop;
6914 gchar const *pos_str_expression = NULL;
6915 gchar const *pos_str_val = NULL;
6917 oo_prop_list_apply (props, obj);
6919 for (ptr = props; ptr; ptr = ptr->next) {
6920 prop = ptr->data;
6921 if (0 == strcmp ("pos-str-expr", prop->name))
6922 pos_str_expression = g_value_get_string (&prop->value);
6923 else if (0 == strcmp ("pos-str-val", prop->name))
6924 pos_str_val = g_value_get_string (&prop->value);
6927 if (pos_str_expression)
6928 odf_apply_expression (xin, 4, obj, pos_str_expression);
6929 else if (pos_str_val)
6930 odf_apply_expression (xin, 4, obj, pos_str_val);
6934 static void
6935 oo_prop_list_apply_to_axis (GsfXMLIn *xin, GSList *props, GObject *obj)
6937 OOParseState *state = (OOParseState *)xin->user_state;
6939 GSList *ptr;
6940 OOProp *prop;
6941 GOData *data;
6943 double minimum = go_ninf, maximum = go_pinf;
6944 double interval_major = 0.;
6945 double interval_minor_divisor = 0.;
6946 gchar const *minimum_expression = NULL;
6947 gchar const *maximum_expression = NULL;
6948 gboolean logarithmic = FALSE;
6951 oo_prop_list_apply_to_axisline (xin, props, obj);
6953 for (ptr = props; ptr; ptr = ptr->next) {
6954 prop = ptr->data;
6955 if (0 == strcmp ("minimum", prop->name))
6956 minimum = g_value_get_double (&prop->value);
6957 else if (0 == strcmp ("maximum", prop->name))
6958 maximum = g_value_get_double (&prop->value);
6959 else if (0 == strcmp ("interval-major", prop->name))
6960 interval_major = g_value_get_double (&prop->value);
6961 else if (0 == strcmp ("interval-minor-divisor", prop->name))
6962 interval_minor_divisor
6963 = g_value_get_double (&prop->value);
6964 else if (0 == strcmp ("minimum-expression", prop->name))
6965 minimum_expression = g_value_get_string (&prop->value);
6966 else if (0 == strcmp ("maximum-expression", prop->name))
6967 maximum_expression = g_value_get_string (&prop->value);
6968 else if (0 == strcmp ("map-name", prop->name))
6969 logarithmic = (0 == strcmp (g_value_get_string (&prop->value), "Log"));
6972 gog_axis_set_bounds (GOG_AXIS (obj), minimum, maximum);
6973 if (minimum_expression)
6974 odf_apply_expression (xin, 0, obj, minimum_expression);
6975 if (maximum_expression)
6976 odf_apply_expression (xin, 1, obj, maximum_expression);
6978 if (interval_major > 0) {
6979 data = gnm_go_data_scalar_new_expr
6980 (state->chart.src_sheet, gnm_expr_top_new_constant
6981 (value_new_float(interval_major)));
6982 gog_dataset_set_dim (GOG_DATASET (obj), 2, data, NULL);
6983 if (interval_minor_divisor > 1) {
6984 if (logarithmic)
6985 data = gnm_go_data_scalar_new_expr
6986 (state->chart.src_sheet,
6987 gnm_expr_top_new_constant
6988 (value_new_float (interval_minor_divisor - 1)));
6989 else
6990 data = gnm_go_data_scalar_new_expr
6991 (state->chart.src_sheet,
6992 gnm_expr_top_new_constant
6993 (value_new_float (interval_major/interval_minor_divisor)));
6994 gog_dataset_set_dim (GOG_DATASET (obj), 3, data, NULL);
6999 static void
7000 oo_chart_style_to_series (GsfXMLIn *xin, OOChartStyle *oostyle, GObject *obj)
7002 GOStyle *style = NULL;
7004 if (oostyle == NULL)
7005 return;
7007 oo_prop_list_apply (oostyle->plot_props, obj);
7009 style = go_styled_object_get_style (GO_STYLED_OBJECT (obj));
7010 if (style != NULL) {
7011 style = go_style_dup (style);
7012 odf_apply_style_props (xin, oostyle->style_props, style, TRUE);
7013 go_styled_object_set_style (GO_STYLED_OBJECT (obj), style);
7014 g_object_unref (style);
7018 static void
7019 oo_prop_list_has (GSList *props, gboolean *threed, char const *tag)
7021 GSList *ptr;
7022 gboolean res;
7023 for (ptr = props; ptr; ptr = ptr->next) {
7024 OOProp *prop = ptr->data;
7025 if (0 == strcmp (prop->name, tag) &&
7026 ((res = g_value_get_boolean (&prop->value))))
7027 *threed = res;
7031 static gboolean
7032 oo_style_has_property (OOChartStyle **style, char const *prop, gboolean def)
7034 int i;
7035 gboolean has_prop = def;
7036 for (i = 0; i < OO_CHART_STYLE_INHERITANCE; i++)
7037 if (style[i] != NULL)
7038 oo_prop_list_has (style[i]->other_props,
7039 &has_prop, prop);
7040 return has_prop;
7043 static gboolean
7044 oo_style_has_plot_property (OOChartStyle **style, char const *prop, gboolean def)
7046 int i;
7047 gboolean has_prop = def;
7048 for (i = 0; i < OO_CHART_STYLE_INHERITANCE; i++)
7049 if (style[i] != NULL)
7050 oo_prop_list_has (style[i]->plot_props,
7051 &has_prop, prop);
7052 return has_prop;
7055 static int
7056 odf_scale_initial_angle (int angle)
7058 angle = 90 - angle;
7059 while (angle < 0)
7060 angle += 360;
7062 return (angle % 360);
7065 static void
7066 od_style_prop_chart (GsfXMLIn *xin, xmlChar const **attrs)
7068 static OOEnum const symbol_type [] = {
7069 {"automatic" , OO_SYMBOL_TYPE_AUTO},
7070 {"none" , OO_SYMBOL_TYPE_NONE},
7071 {"named-symbol", OO_SYMBOL_TYPE_NAMED},
7072 {NULL , 0},
7074 static OOEnum const named_symbols [] = {
7075 { "square", GO_MARKER_SQUARE},
7076 { "diamond", GO_MARKER_DIAMOND},
7077 { "arrow-down", GO_MARKER_TRIANGLE_DOWN},
7078 { "arrow-up", GO_MARKER_TRIANGLE_UP},
7079 { "arrow-right", GO_MARKER_TRIANGLE_RIGHT},
7080 { "arrow-left", GO_MARKER_TRIANGLE_LEFT},
7081 { "circle", GO_MARKER_CIRCLE},
7082 { "x", GO_MARKER_X},
7083 { "plus", GO_MARKER_CROSS},
7084 { "asterisk", GO_MARKER_ASTERISK},
7085 { "horizontal-bar", GO_MARKER_BAR},
7086 { "bow-tie", GO_MARKER_BUTTERFLY},
7087 { "hourglass", GO_MARKER_HOURGLASS},
7088 { "star", GO_MARKER_LEFT_HALF_BAR},
7089 { "vertical-bar", GO_MARKER_HALF_BAR},
7090 { NULL, 0},
7093 static OOEnum const font_variants [] = {
7094 {"normal", PANGO_VARIANT_NORMAL},
7095 {"small-caps", PANGO_VARIANT_SMALL_CAPS},
7096 { NULL, 0},
7099 static OOEnum const font_styles [] = {
7100 { "normal", PANGO_STYLE_NORMAL},
7101 { "oblique", PANGO_STYLE_OBLIQUE},
7102 { "italic", PANGO_STYLE_ITALIC},
7103 { NULL, 0},
7106 static OOEnum const image_fill_types [] = {
7107 {"stretch", GO_IMAGE_STRETCHED },
7108 {"repeat", GO_IMAGE_WALLPAPER },
7109 {"no-repeat", GO_IMAGE_CENTERED },
7110 { NULL, 0 },
7113 OOParseState *state = (OOParseState *)xin->user_state;
7114 OOChartStyle *style = state->chart.cur_graph_style;
7115 gboolean btmp;
7116 int tmp;
7117 gnm_float ftmp;
7118 gboolean default_style_has_lines_set = FALSE;
7119 gboolean draw_stroke_set = FALSE;
7120 gboolean draw_stroke = FALSE; /* to avoid a warning only */
7121 gboolean stacked_set = FALSE;
7122 gboolean stacked_unset = FALSE;
7123 gboolean overlap_set = FALSE;
7124 gboolean percentage_set = FALSE;
7125 gboolean regression_force_intercept_set = FALSE;
7126 gboolean regression_force_intercept = FALSE;
7127 gnm_float regression_force_intercept_value = 0.;
7128 char const *interpolation = NULL;
7129 gboolean local_style = FALSE;
7132 g_return_if_fail (style != NULL ||
7133 state->default_style.cells != NULL);
7135 if (style == NULL && state->default_style.cells != NULL) {
7136 local_style = TRUE;
7137 style = g_new0 (OOChartStyle, 1);
7141 style->grid = FALSE;
7142 style->src_in_rows = FALSE;
7143 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
7144 if (oo_attr_bool (xin, attrs, OO_NS_CHART, "logarithmic", &btmp)) {
7145 if (btmp)
7146 style->axis_props = g_slist_prepend (style->axis_props,
7147 oo_prop_new_string ("map-name", "Log"));
7148 } else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "link-data-style-to-source", &btmp)) {
7149 if (btmp)
7150 style->other_props = g_slist_prepend
7151 (style->other_props,
7152 oo_prop_new_bool ("ignore-axis-data-style", btmp));
7153 } else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "vertical", &btmp)) {
7154 /* This is backwards from my intuition */
7155 style->plot_props = g_slist_prepend (style->plot_props,
7156 oo_prop_new_bool ("horizontal", btmp));
7157 /* This is for BoxPlots */
7158 style->plot_props = g_slist_prepend (style->plot_props,
7159 oo_prop_new_bool ("vertical", btmp));
7160 } else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "outliers", &btmp))
7161 style->plot_props = g_slist_prepend (style->plot_props,
7162 oo_prop_new_bool ("outliers", btmp));
7163 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "reverse-direction", &btmp))
7164 style->axis_props = g_slist_prepend (style->axis_props,
7165 oo_prop_new_bool ("invert-axis", btmp));
7166 else if (oo_attr_bool (xin, attrs, OO_NS_CHART,
7167 "reverse-direction", &btmp))
7168 style->axis_props = g_slist_prepend (style->axis_props,
7169 oo_prop_new_bool ("invert-axis", btmp));
7170 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT,
7171 "vary-style-by-element", &btmp))
7172 style->plot_props = g_slist_prepend (style->plot_props,
7173 oo_prop_new_bool ("vary-style-by-element",
7174 btmp));
7175 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT,
7176 "show-negatives", &btmp))
7177 style->plot_props = g_slist_prepend (style->plot_props,
7178 oo_prop_new_bool ("show-negatives", btmp));
7179 else if (oo_attr_float (xin, attrs, OO_NS_CHART,
7180 "minimum", &ftmp))
7181 style->axis_props = g_slist_prepend (style->axis_props,
7182 oo_prop_new_double ("minimum", ftmp));
7183 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT,
7184 "chart-minimum-expression"))
7185 style->axis_props = g_slist_prepend
7186 (style->axis_props,
7187 oo_prop_new_string ("minimum-expression",
7188 CXML2C(attrs[1])));
7189 else if (oo_attr_float (xin, attrs, OO_NS_CHART,
7190 "maximum", &ftmp))
7191 style->axis_props = g_slist_prepend (style->axis_props,
7192 oo_prop_new_double ("maximum", ftmp));
7193 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT,
7194 "chart-maximum-expression"))
7195 style->axis_props = g_slist_prepend
7196 (style->axis_props,
7197 oo_prop_new_string ("maximum-expression",
7198 CXML2C(attrs[1])));
7199 else if (oo_attr_float (xin, attrs, OO_NS_CHART,
7200 "interval-major", &ftmp))
7201 style->axis_props = g_slist_prepend (style->axis_props,
7202 oo_prop_new_double ("interval-major", ftmp));
7203 else if (oo_attr_float (xin, attrs, OO_NS_CHART,
7204 "interval-minor-divisor", &ftmp))
7205 style->axis_props = g_slist_prepend
7206 (style->axis_props,
7207 oo_prop_new_double ("interval-minor-divisor",
7208 ftmp));
7209 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART,
7210 "axis-position")) {
7211 if (0 == strcmp (CXML2C(attrs[1]), "start"))
7212 style->axis_props = g_slist_prepend
7213 (style->axis_props,
7214 oo_prop_new_string ("pos-str",
7215 "low"));
7216 else if (0 == strcmp (CXML2C(attrs[1]), "end"))
7217 style->axis_props = g_slist_prepend
7218 (style->axis_props,
7219 oo_prop_new_string ("pos-str",
7220 "high"));
7221 else {
7222 style->axis_props = g_slist_prepend
7223 (style->axis_props,
7224 oo_prop_new_string ("pos-str", "cross"));
7225 style->axis_props = g_slist_prepend
7226 (style->axis_props, oo_prop_new_string ("pos-str-val",
7227 CXML2C(attrs[1])));
7229 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT,
7230 "axis-position-expression"))
7231 style->axis_props = g_slist_prepend (style->axis_props,
7232 oo_prop_new_string ("pos-str-expr",
7233 CXML2C(attrs[1])));
7234 else if (oo_attr_float (xin, attrs, OO_GNUM_NS_EXT,
7235 "radius-ratio", &ftmp))
7236 style->plot_props = g_slist_prepend (style->plot_props,
7237 oo_prop_new_double ("radius-ratio", ftmp));
7238 else if (oo_attr_percent (xin, attrs, OO_GNUM_NS_EXT,
7239 "default-separation", &ftmp))
7240 style->plot_props = g_slist_prepend (style->plot_props,
7241 oo_prop_new_double ("default-separation", ftmp));
7242 else if (oo_attr_int_range (xin, attrs, OO_NS_CHART,
7243 "pie-offset", &tmp, 0, 500)) {
7244 style->plot_props = g_slist_prepend (style->plot_props,
7245 oo_prop_new_double ("default-separation",
7246 tmp/100.));
7247 style->plot_props = g_slist_prepend (style->plot_props,
7248 oo_prop_new_double ("separation",
7249 tmp/100.));
7250 } else if (oo_attr_percent (xin, attrs, OO_NS_CHART,
7251 "hole-size", &ftmp))
7252 style->plot_props = g_slist_prepend (style->plot_props,
7253 oo_prop_new_double ("center-size", ftmp));
7254 else if (oo_attr_angle (xin, attrs, OO_NS_CHART,
7255 "angle-offset", &tmp))
7256 style->plot_props = g_slist_prepend
7257 (style->plot_props, oo_prop_new_double ("plot-initial-angle",
7258 (double) odf_scale_initial_angle (tmp)));
7259 else if (oo_attr_bool (xin, attrs, OO_NS_CHART,
7260 "reverse-direction", &btmp))
7261 style->axis_props = g_slist_prepend (style->axis_props,
7262 oo_prop_new_bool ("invert-axis", btmp));
7263 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "stacked",
7264 &btmp)) {
7265 if (btmp) {
7266 style->plot_props = g_slist_prepend
7267 (style->plot_props,
7268 oo_prop_new_string ("type",
7269 "stacked"));
7270 stacked_set = TRUE;
7271 } else
7272 stacked_unset = TRUE;
7273 } else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "percentage",
7274 &btmp)) {
7275 if (btmp) {
7276 style->plot_props = g_slist_prepend
7277 (style->plot_props,
7278 oo_prop_new_string ("type",
7279 "as_percentage"));
7280 percentage_set = TRUE;
7282 } else if (oo_attr_int_range (xin, attrs, OO_NS_CHART,
7283 "overlap", &tmp, -150, 150)) {
7284 style->plot_props = g_slist_prepend (style->plot_props,
7285 oo_prop_new_int ("overlap-percentage", tmp));
7286 overlap_set = TRUE;
7287 } else if (oo_attr_int_range (xin, attrs, OO_NS_CHART,
7288 "gap-width", &tmp, 0, 500))
7289 style->plot_props = g_slist_prepend (style->plot_props,
7290 oo_prop_new_int ("gap-percentage", tmp));
7291 else if (oo_attr_enum (xin, attrs, OO_NS_CHART, "symbol-type",
7292 symbol_type, &tmp)) {
7293 style->plot_props = g_slist_prepend
7294 (style->plot_props,
7295 oo_prop_new_bool ("default-style-has-markers",
7296 tmp != OO_SYMBOL_TYPE_NONE));
7297 style->style_props = g_slist_prepend
7298 (style->style_props,
7299 oo_prop_new_int ("symbol-type", tmp));
7300 } else if (oo_attr_enum (xin, attrs, OO_NS_CHART,
7301 "symbol-name",
7302 named_symbols, &tmp)) {
7303 style->style_props = g_slist_prepend
7304 (style->style_props,
7305 oo_prop_new_int ("symbol-name", tmp));
7306 } else if (oo_attr_distance (xin, attrs, OO_NS_CHART, "symbol-width",
7307 &ftmp)) {
7308 style->style_props = g_slist_prepend
7309 (style->style_props,
7310 oo_prop_new_double ("symbol-width", ftmp));
7311 } else if (oo_attr_distance (xin, attrs, OO_NS_CHART, "symbol-height",
7312 &ftmp)) {
7313 style->style_props = g_slist_prepend
7314 (style->style_props,
7315 oo_prop_new_double ("symbol-height", ftmp));
7316 } else if ((interpolation == NULL) &&
7317 (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7318 OO_NS_CHART, "interpolation") ||
7319 gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7320 OO_GNUM_NS_EXT, "interpolation"))) {
7321 if (attr_eq (attrs[1], "none"))
7322 interpolation = "linear";
7323 else if (attr_eq (attrs[1], "b-spline")) {
7324 interpolation = "spline";
7325 oo_warning
7326 (xin, _("Unknown interpolation type "
7327 "encountered: \'%s\', using "
7328 "Bezier cubic spline instead."),
7329 CXML2C(attrs[1]));
7330 } else if (attr_eq (attrs[1], "cubic-spline"))
7331 interpolation = "odf-spline";
7332 else if (g_str_has_prefix (CXML2C(attrs[1]), "gnm:"))
7333 interpolation = CXML2C(attrs[1]) + 4;
7334 else oo_warning
7335 (xin, _("Unknown interpolation type "
7336 "encountered: %s"),
7337 CXML2C(attrs[1]));
7338 if (interpolation != NULL)
7339 style->plot_props = g_slist_prepend
7340 (style->plot_props,
7341 oo_prop_new_string
7342 ("interpolation", interpolation));
7343 } else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "interpolation-skip-invalid", &btmp))
7344 style->plot_props = g_slist_prepend
7345 (style->plot_props,
7346 oo_prop_new_bool ("interpolation-skip-invalid", btmp));
7347 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7348 OO_GNUM_NS_EXT, "fill-type"))
7349 style->plot_props = g_slist_prepend
7350 (style->plot_props,
7351 oo_prop_new_string ("fill-type",
7352 CXML2C(attrs[1])));
7353 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7354 OO_NS_DRAW, "stroke")) {
7355 draw_stroke = !attr_eq (attrs[1], "none");
7356 draw_stroke_set = TRUE;
7357 style->style_props = g_slist_prepend
7358 (style->style_props,
7359 oo_prop_new_string ("stroke",
7360 CXML2C(attrs[1])));
7361 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7362 OO_NS_DRAW, "stroke-dash")) {
7363 style->style_props = g_slist_prepend
7364 (style->style_props,
7365 oo_prop_new_string ("stroke-dash",
7366 CXML2C(attrs[1])));
7367 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7368 OO_NS_SVG, "stroke-color")) {
7369 style->style_props = g_slist_prepend
7370 (style->style_props,
7371 oo_prop_new_string ("stroke-color",
7372 CXML2C(attrs[1])));
7373 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7374 OO_GNUM_NS_EXT, "marker-outline-colour")) {
7375 style->style_props = g_slist_prepend
7376 (style->style_props,
7377 oo_prop_new_string ("marker-outline-colour",
7378 CXML2C(attrs[1])));
7379 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7380 OO_GNUM_NS_EXT, "marker-fill-colour")) {
7381 style->style_props = g_slist_prepend
7382 (style->style_props,
7383 oo_prop_new_string ("marker-fill-colour",
7384 CXML2C(attrs[1])));
7385 } else if (NULL != oo_attr_distance (xin, attrs, OO_NS_SVG,
7386 "stroke-width", &ftmp))
7387 style->style_props = g_slist_prepend
7388 (style->style_props,
7389 oo_prop_new_double ("stroke-width",
7390 ftmp));
7391 else if (NULL != oo_attr_distance (xin, attrs, OO_GNUM_NS_EXT,
7392 "stroke-width", &ftmp))
7393 style->style_props = g_slist_prepend
7394 (style->style_props,
7395 oo_prop_new_double ("gnm-stroke-width",
7396 ftmp));
7397 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "lines", &btmp)) {
7398 style->style_props = g_slist_prepend
7399 (style->style_props,
7400 oo_prop_new_bool ("lines", btmp));
7401 style->plot_props = g_slist_prepend
7402 (style->plot_props,
7403 oo_prop_new_bool ("default-style-has-lines", btmp));
7404 default_style_has_lines_set = TRUE;
7405 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "series-source"))
7406 style->src_in_rows = attr_eq (attrs[1], "rows");
7407 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "three-dimensional", &btmp))
7408 style->other_props = g_slist_prepend (style->other_props,
7409 oo_prop_new_bool ("three-dimensional", btmp));
7410 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "fill"))
7411 style->style_props = g_slist_prepend
7412 (style->style_props,
7413 oo_prop_new_string ("fill",
7414 CXML2C(attrs[1])));
7415 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "foreground-solid", &btmp))
7416 style->style_props = g_slist_prepend
7417 (style->style_props,
7418 oo_prop_new_bool ("gnm-foreground-solid", btmp));
7419 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "auto-type", &btmp))
7420 style->style_props = g_slist_prepend
7421 (style->style_props,
7422 oo_prop_new_bool ("gnm-auto-type", btmp));
7423 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "fill-color"))
7424 style->style_props = g_slist_prepend
7425 (style->style_props,
7426 oo_prop_new_string ("fill-color",
7427 CXML2C(attrs[1])));
7428 else if (oo_attr_percent (xin, attrs, OO_NS_DRAW, "opacity", &ftmp))
7429 style->style_props = g_slist_prepend (style->style_props,
7430 oo_prop_new_double ("opacity", ftmp));
7431 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "fill-hatch-name"))
7432 style->style_props = g_slist_prepend
7433 (style->style_props,
7434 oo_prop_new_string ("fill-hatch-name",
7435 CXML2C(attrs[1])));
7436 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "fill-image-name"))
7437 style->style_props = g_slist_prepend
7438 (style->style_props,
7439 oo_prop_new_string ("fill-image-name",
7440 CXML2C(attrs[1])));
7441 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "fill-gradient-name"))
7442 style->style_props = g_slist_prepend
7443 (style->style_props,
7444 oo_prop_new_string ("fill-gradient-name",
7445 CXML2C(attrs[1])));
7446 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "fill-hatch-solid", &btmp))
7447 style->other_props = g_slist_prepend (style->other_props,
7448 oo_prop_new_bool ("fill-hatch-solid", btmp));
7449 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT,
7450 "pattern", &tmp,
7451 GO_PATTERN_GREY75, GO_PATTERN_MAX - 1))
7452 style->style_props = g_slist_prepend
7453 (style->style_props,
7454 oo_prop_new_int ("gnm-pattern", tmp));
7455 else if (oo_attr_angle (xin, attrs, OO_NS_STYLE,
7456 "text-rotation-angle", &tmp)) {
7457 style->style_props = g_slist_prepend
7458 (style->style_props,
7459 oo_prop_new_int ("text-rotation-angle", tmp));
7460 } else if (oo_attr_angle (xin, attrs, OO_NS_STYLE,
7461 "rotation-angle", &tmp)) {
7462 style->style_props = g_slist_prepend
7463 (style->style_props,
7464 oo_prop_new_int ("text-rotation-angle", tmp));
7465 style->plot_props = g_slist_prepend
7466 (style->plot_props,
7467 oo_prop_new_int ("rotation-angle", tmp));
7468 } else if (NULL != oo_attr_distance (xin, attrs, OO_NS_FO, "font-size", &ftmp))
7469 style->style_props = g_slist_prepend
7470 (style->style_props,
7471 oo_prop_new_double ("font-size", ftmp));
7472 else if (oo_attr_font_weight (xin, attrs, &tmp))
7473 style->style_props = g_slist_prepend
7474 (style->style_props,
7475 oo_prop_new_int ("font-weight", tmp));
7476 else if (oo_attr_enum (xin, attrs, OO_NS_FO, "font-variant",
7477 font_variants, &tmp))
7478 style->style_props = g_slist_prepend
7479 (style->style_props,
7480 oo_prop_new_int ("font-variant", tmp));
7481 else if (oo_attr_enum (xin, attrs, OO_NS_FO, "font-style",
7482 font_styles, &tmp))
7483 style->style_props = g_slist_prepend
7484 (style->style_props,
7485 oo_prop_new_int ("font-style", tmp));
7486 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "font-family"))
7487 style->style_props = g_slist_prepend
7488 (style->style_props,
7489 oo_prop_new_string ("font-family",
7490 CXML2C(attrs[1])));
7491 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "auto-font",
7492 &btmp))
7493 style->style_props = g_slist_prepend (style->style_props,
7494 oo_prop_new_bool ("gnm-auto-font", btmp));
7495 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT,
7496 "font-stretch-pango", &tmp,
7497 0, PANGO_STRETCH_ULTRA_EXPANDED))
7498 style->style_props = g_slist_prepend
7499 (style->style_props,
7500 oo_prop_new_int ("font-stretch-pango", tmp));
7501 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT,
7502 "font-gravity-pango", &tmp,
7503 0, PANGO_GRAVITY_WEST))
7504 style->style_props = g_slist_prepend
7505 (style->style_props,
7506 oo_prop_new_int ("font-gravity-pango", tmp));
7507 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_FO, "color"))
7508 style->style_props = g_slist_prepend
7509 (style->style_props,
7510 oo_prop_new_string ("color",
7511 CXML2C(attrs[1])));
7512 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART,
7513 "regression-type"))
7514 style->other_props = g_slist_prepend
7515 (style->other_props,
7516 oo_prop_new_string ("regression-type",
7517 CXML2C(attrs[1])));
7518 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT,
7519 "regression-polynomial-dims", &tmp,
7520 1, 100))
7521 style->other_props = g_slist_prepend
7522 (style->other_props,
7523 oo_prop_new_int ("dims", tmp));
7524 #if HAVE_OO_NS_LOCALC_EXT
7525 else if (oo_attr_int_range (xin, attrs, OO_NS_LOCALC_EXT,
7526 "regression-max-degree", &tmp,
7527 1, 100))
7528 style->other_props = g_slist_prepend
7529 (style->other_props,
7530 oo_prop_new_int ("lo-dims", tmp));
7531 else if (oo_attr_bool (xin, attrs, OO_NS_LOCALC_EXT, "regression-force-intercept",
7532 &regression_force_intercept))
7534 regression_force_intercept_set = TRUE;
7536 else if (oo_attr_float (xin, attrs, OO_NS_LOCALC_EXT,
7537 "regression-intercept-value", &ftmp))
7538 regression_force_intercept_value = ftmp;
7539 #endif
7540 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "regression-affine",
7541 &btmp))
7542 style->other_props = g_slist_prepend (style->other_props,
7543 oo_prop_new_bool ("affine", btmp));
7544 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT,
7545 "regression-name"))
7546 style->other_props = g_slist_prepend
7547 (style->other_props,
7548 oo_prop_new_string ("regression-name-expression",
7549 CXML2C(attrs[1])));
7550 #if HAVE_OO_NS_LOCALC_EXT
7551 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_LOCALC_EXT,
7552 "regression-name"))
7553 style->other_props = g_slist_prepend
7554 (style->other_props,
7555 oo_prop_new_string ("regression-name-constant",
7556 CXML2C(attrs[1])));
7557 #endif
7558 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT,
7559 "is-position-manual",
7560 &btmp))
7561 style->plot_props = g_slist_prepend
7562 (style->plot_props,
7563 oo_prop_new_bool
7564 ("is-position-manual", btmp));
7565 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7566 OO_GNUM_NS_EXT,
7567 "position"))
7568 style->plot_props = g_slist_prepend
7569 (style->plot_props,
7570 oo_prop_new_string
7571 ("position", CXML2C(attrs[1])));
7572 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7573 OO_GNUM_NS_EXT,
7574 "anchor"))
7575 style->plot_props = g_slist_prepend
7576 (style->plot_props,
7577 oo_prop_new_string
7578 ("anchor", CXML2C(attrs[1])));
7579 else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "repeat",
7580 image_fill_types, &tmp))
7581 style->style_props = g_slist_prepend
7582 (style->style_props,
7583 oo_prop_new_int ("repeat", tmp));
7584 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7585 OO_NS_DRAW,
7586 "marker-start"))
7587 style->other_props = g_slist_prepend
7588 (style->other_props,
7589 oo_prop_new_string
7590 ("marker-start", CXML2C(attrs[1])));
7591 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7592 OO_NS_DRAW,
7593 "marker-end"))
7594 style->other_props = g_slist_prepend
7595 (style->other_props,
7596 oo_prop_new_string
7597 ("marker-end", CXML2C(attrs[1])));
7598 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7599 OO_NS_FO,
7600 "border"))
7601 style->other_props = g_slist_prepend
7602 (style->other_props,
7603 oo_prop_new_string
7604 ("border", CXML2C(attrs[1])));
7605 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "auto-marker-outline-colour", &btmp))
7606 style->style_props = g_slist_prepend (style->style_props,
7607 oo_prop_new_bool ("gnm-auto-marker-outline-colour", btmp));
7608 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "auto-color", &btmp))
7609 style->style_props = g_slist_prepend (style->style_props,
7610 oo_prop_new_bool ("gnm-auto-color", btmp));
7611 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "auto-marker-fill-colour", &btmp))
7612 style->style_props = g_slist_prepend (style->style_props,
7613 oo_prop_new_bool ("gnm-auto-marker-fill-colour", btmp));
7614 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "auto-dash", &btmp))
7615 style->style_props = g_slist_prepend (style->style_props,
7616 oo_prop_new_bool ("gnm-auto-dash", btmp));
7617 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "auto-width", &btmp))
7618 style->style_props = g_slist_prepend (style->style_props,
7619 oo_prop_new_bool ("gnm-auto-width", btmp));
7620 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "tick-marks-major-inner", &btmp))
7621 style->axis_props = g_slist_prepend (style->axis_props,
7622 oo_prop_new_bool ("major-tick-in", btmp));
7623 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "tick-marks-major-outer", &btmp))
7624 style->axis_props = g_slist_prepend (style->axis_props,
7625 oo_prop_new_bool ("major-tick-out", btmp));
7626 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "tick-marks-minor-inner", &btmp))
7627 style->axis_props = g_slist_prepend (style->axis_props,
7628 oo_prop_new_bool ("minor-tick-in", btmp));
7629 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "tick-marks-minor-outer", &btmp))
7630 style->axis_props = g_slist_prepend (style->axis_props,
7631 oo_prop_new_bool ("minor-tick-out", btmp));
7632 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "display-label", &btmp))
7633 style->axis_props = g_slist_prepend (style->axis_props,
7634 oo_prop_new_bool ("major-tick-labeled", btmp));
7637 if (regression_force_intercept_set) {
7638 btmp = regression_force_intercept && (regression_force_intercept_value == 0);
7639 style->other_props = g_slist_prepend (style->other_props,
7640 oo_prop_new_bool ("affine", btmp));
7643 if ((stacked_set && !overlap_set) ||
7644 (percentage_set && !stacked_unset && !overlap_set))
7645 style->plot_props = g_slist_prepend (style->plot_props,
7646 oo_prop_new_int ("overlap-percentage", 100));
7648 if (!default_style_has_lines_set)
7649 style->plot_props = g_slist_prepend
7650 (style->plot_props,
7651 oo_prop_new_bool ("default-style-has-lines", draw_stroke_set && draw_stroke));
7653 if (local_style) {
7654 /* odf_apply_style_props (xin, style->style_props, state->default_style.cells, TRUE);*/
7655 /* We should apply the styles to this GnmStyle */
7656 oo_chart_style_free (style);
7660 static void
7661 od_style_prop_text (GsfXMLIn *xin, xmlChar const **attrs)
7663 static OOEnum const style_types [] = {
7664 { "normal", PANGO_STYLE_NORMAL},
7665 { "italic", PANGO_STYLE_ITALIC},
7666 { "oblique", PANGO_STYLE_OBLIQUE},
7667 { NULL, 0 },
7669 static OOEnum const underline_styles [] = {
7670 { "none", 1 },
7671 { "dash", 2 },
7672 { "dot-dash", 2 },
7673 { "dot-dot-dash", 2 },
7674 { "dotted", 2 },
7675 { "long-dash", 2 },
7676 { "solid", 3 },
7677 { "wave", 4 },
7678 { NULL, 0 },
7680 static OOEnum const underline_types [] = {
7681 { "none", 0 },
7682 { "single", 1 },
7683 { "double", 2 },
7684 { NULL, 0 },
7686 static OOEnum const line_through_styles [] = {
7687 { "none", 0},
7688 { "solid", 1},
7689 { "dotted", 2},
7690 { "dash", 3},
7691 { "long-dash", 4},
7692 { "dot-dash", 5},
7693 { "dot-dot-dash",6},
7694 { "wave", 7},
7695 { NULL, 0 },
7698 OOParseState *state = (OOParseState *)xin->user_state;
7699 PangoAttribute *attr;
7700 int tmp;
7701 gnm_float size = -1.0;
7702 int underline_type = 0;
7703 int underline_style = 0;
7704 gboolean underline_bold = FALSE;
7705 GnmColor *color;
7707 g_return_if_fail (state->cur_style.text != NULL);
7708 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
7709 if (NULL != oo_attr_distance (xin, attrs, OO_NS_FO, "font-size", &size) && size >= 0.) {
7710 attr = pango_attr_size_new ((int) gnm_floor (size * PANGO_SCALE + 0.5));
7711 attr->start_index = 0;
7712 attr->end_index = 0;
7713 pango_attr_list_insert (state->cur_style.text, attr); ;
7714 } else if (oo_attr_font_weight (xin, attrs, &tmp)) {
7715 attr = pango_attr_weight_new (tmp);
7716 attr->start_index = 0;
7717 attr->end_index = 0;
7718 pango_attr_list_insert (state->cur_style.text, attr);
7719 } else if (oo_attr_enum (xin, attrs, OO_NS_FO, "font-style", style_types, &tmp)) {
7720 attr = pango_attr_style_new (tmp);
7721 attr->start_index = 0;
7722 attr->end_index = 0;
7723 pango_attr_list_insert (state->cur_style.text, attr);
7724 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7725 OO_NS_STYLE, "text-position")) {
7726 attr = NULL;
7727 if (g_str_has_prefix (attrs[1],"super"))
7728 attr = go_pango_attr_superscript_new (1);
7729 else if (g_str_has_prefix (attrs[1], "sub"))
7730 attr = go_pango_attr_subscript_new (1);
7731 else if (g_str_has_prefix (attrs[1], "0")) {
7732 attr = go_pango_attr_superscript_new (0);
7733 attr->start_index = 0;
7734 attr->end_index = 0;
7735 pango_attr_list_insert (state->cur_style.text, attr);
7736 attr = go_pango_attr_subscript_new (0);
7738 if (attr != NULL) {
7739 attr->start_index = 0;
7740 attr->end_index = 0;
7741 pango_attr_list_insert (state->cur_style.text, attr);
7743 } else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "text-underline-style",
7744 underline_styles, &underline_style)) {
7745 } else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "text-underline-type",
7746 underline_types, &underline_type)) {
7747 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
7748 OO_NS_STYLE, "text-underline-width"))
7749 underline_bold = attr_eq (attrs[1], "bold");
7750 else if (oo_attr_enum (xin, attrs, OO_NS_STYLE, "text-line-through-style",
7751 line_through_styles, &tmp)) {
7752 attr = pango_attr_strikethrough_new (tmp > 0);
7753 attr->start_index = 0;
7754 attr->end_index = 0;
7755 pango_attr_list_insert (state->cur_style.text, attr);
7756 } else if ((color = oo_attr_color (xin, attrs, OO_NS_FO, "color"))) {
7757 attr = go_color_to_pango (color->go_color, TRUE);
7758 style_color_unref (color);
7759 attr->start_index = 0;
7760 attr->end_index = 0;
7761 pango_attr_list_insert (state->cur_style.text, attr);
7762 } else if ((color = oo_attr_color (xin, attrs, OO_NS_FO, "background-color"))) {
7763 attr = go_color_to_pango (color->go_color, FALSE);
7764 style_color_unref (color);
7765 attr->start_index = 0;
7766 attr->end_index = 0;
7767 pango_attr_list_insert (state->cur_style.text, attr);
7770 if (underline_style > 0) {
7771 PangoUnderline underline;
7772 if (underline_style == 1)
7773 underline = PANGO_UNDERLINE_NONE;
7774 else if (underline_style == 4)
7775 underline = PANGO_UNDERLINE_ERROR;
7776 else if (underline_bold)
7777 underline = PANGO_UNDERLINE_LOW;
7778 else if (underline_type == 2)
7779 underline = PANGO_UNDERLINE_DOUBLE;
7780 else
7781 underline = PANGO_UNDERLINE_SINGLE;
7783 attr = pango_attr_underline_new (underline);
7784 attr->start_index = 0;
7785 attr->end_index = 0;
7786 pango_attr_list_insert (state->cur_style.text, attr);
7790 static void
7791 oo_style_prop (GsfXMLIn *xin, xmlChar const **attrs)
7793 OOParseState *state = (OOParseState *)xin->user_state;
7794 switch (state->cur_style.type) {
7795 case OO_STYLE_CELL : oo_style_prop_cell (xin, attrs); break;
7796 case OO_STYLE_COL :
7797 case OO_STYLE_ROW : oo_style_prop_col_row (xin, attrs); break;
7798 case OO_STYLE_SHEET : oo_style_prop_table (xin, attrs); break;
7799 case OO_STYLE_TEXT : od_style_prop_text (xin, attrs); break;
7800 case OO_STYLE_CHART :
7801 case OO_STYLE_GRAPHICS :
7802 od_style_prop_chart (xin, attrs); break;
7804 default :
7805 break;
7809 static void
7810 oo_named_expr_common (GsfXMLIn *xin, xmlChar const **attrs, gboolean preparse)
7812 OOParseState *state = (OOParseState *)xin->user_state;
7813 char const *name = NULL;
7814 char const *base_str = NULL;
7815 char const *expr_str = NULL;
7816 char const *scope = NULL;
7817 char *range_str = NULL;
7819 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
7820 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "name"))
7821 name = CXML2C (attrs[1]);
7822 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "base-cell-address"))
7823 base_str = CXML2C (attrs[1]);
7824 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "expression"))
7825 expr_str = CXML2C (attrs[1]);
7826 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "cell-range-address"))
7827 expr_str = range_str = g_strconcat ("[", CXML2C (attrs[1]), "]", NULL);
7828 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "scope"))
7829 scope = CXML2C (attrs[1]);
7831 #if 0
7832 g_printerr ("%s: %s [sheet=%s] [%s]\n",
7833 (preparse ? "preparse" : "parse"),
7834 name,
7835 state->pos.sheet ? state->pos.sheet->name_unquoted : "-",
7836 expr_str);
7837 #endif
7839 if (preparse) {
7840 expr_str = "of:=#REF!";
7841 base_str = NULL;
7844 if (name && expr_str &&
7845 g_str_equal (name, "Print_Area") &&
7846 g_str_equal (expr_str, "of:=[.#REF!]")) {
7847 // Deal with XL nonsense
7848 expr_str = NULL;
7851 if (name != NULL && expr_str != NULL) {
7852 GnmParsePos pp;
7853 GnmExprTop const *texpr;
7854 OOFormula f_type;
7856 parse_pos_init (&pp, state->pos.wb, NULL, 0, 0);
7858 /* Note that base_str is not required */
7859 if (base_str != NULL) {
7860 char *tmp = g_strconcat ("[", base_str, "]", NULL);
7862 texpr = oo_expr_parse_str
7863 (xin, tmp, &pp,
7864 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES,
7865 FORMULA_OPENFORMULA);
7866 g_free (tmp);
7868 if (texpr == NULL ||
7869 !gnm_expr_top_get_cellref (texpr)) {
7870 oo_warning (xin, _("expression '%s' @ '%s' "
7871 "is not a cellref"),
7872 name, base_str);
7873 } else {
7874 GnmCellRef const *ref =
7875 &texpr->expr->cellref.ref;
7876 parse_pos_init (&pp, state->pos.wb, ref->sheet,
7877 ref->col, ref->row);
7879 if (texpr != NULL)
7880 gnm_expr_top_unref (texpr);
7883 f_type = odf_get_formula_type (xin, &expr_str);
7884 if (f_type == FORMULA_NOT_SUPPORTED) {
7885 oo_warning
7886 (xin, _("Expression '%s' has "
7887 "unknown namespace"),
7888 expr_str);
7889 } else {
7891 /* Note that an = sign is only required if a */
7892 /* name space is given. */
7893 if (*expr_str == '=')
7894 expr_str++;
7896 if (*expr_str == 0)
7897 texpr = gnm_expr_top_new_constant (value_new_error_REF (NULL));
7898 else
7899 texpr = oo_expr_parse_str (xin, expr_str,
7900 &pp, GNM_EXPR_PARSE_DEFAULT,
7901 f_type);
7902 if (texpr != NULL) {
7903 pp.sheet = state->pos.sheet;
7904 if (pp.sheet == NULL && scope != NULL)
7905 pp.sheet = workbook_sheet_by_name (pp.wb, scope);
7907 if (preparse) {
7908 gnm_expr_top_unref (texpr);
7909 texpr = NULL;
7912 expr_name_add (&pp, name, texpr, NULL,
7913 TRUE, NULL);
7918 g_free (range_str);
7921 static void
7922 oo_named_expr (GsfXMLIn *xin, xmlChar const **attrs)
7924 oo_named_expr_common (xin, attrs, FALSE);
7927 static void
7928 oo_db_range_start (GsfXMLIn *xin, xmlChar const **attrs)
7930 OOParseState *state = (OOParseState *)xin->user_state;
7931 gboolean buttons = FALSE;
7932 char const *name = NULL;
7933 GnmExpr const *expr = NULL;
7934 gchar const *target = NULL;
7936 g_return_if_fail (state->filter == NULL);
7938 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
7939 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "target-range-address"))
7940 target = CXML2C (attrs[1]);
7941 else if (oo_attr_bool (xin, attrs, OO_NS_TABLE, "display-filter-buttons", &buttons))
7942 /* ignore this */;
7943 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "name"))
7944 name = CXML2C (attrs[1]);
7946 if (target) {
7947 GnmRangeRef ref;
7948 GnmRange r;
7949 char const *ptr = oo_cellref_parse
7950 (&ref.a, target, &state->pos, NULL);
7951 if (ref.a.sheet != invalid_sheet &&
7952 ':' == *ptr &&
7953 '\0' == *oo_cellref_parse (&ref.b, ptr+1, &state->pos, NULL) &&
7954 ref.b.sheet != invalid_sheet) {
7955 range_init_rangeref (&r, &ref);
7956 if (buttons)
7957 state->filter = gnm_filter_new (ref.a.sheet, &r);
7958 expr = gnm_expr_new_constant
7959 (value_new_cellrange_r (ref.a.sheet, &r));
7960 } else
7961 oo_warning (xin, _("Invalid DB range '%s'"), target);
7964 /* It appears that OOo likes to use the names it assigned to filters as named-ranges */
7965 /* This really violates ODF/OpenFormula. So we make sure that there isn't already a named */
7966 /* expression or range with that name. */
7967 if (expr != NULL) {
7968 GnmNamedExpr *nexpr = NULL;
7969 GnmParsePos pp;
7970 if (name != NULL &&
7971 (NULL == (nexpr = expr_name_lookup
7972 (parse_pos_init (&pp, state->pos.wb, NULL, 0, 0), name)) ||
7973 expr_name_is_placeholder (nexpr))) {
7974 GnmExprTop const *texpr = gnm_expr_top_new (expr);
7975 expr_name_add (&pp, name, texpr, NULL, TRUE, NULL);
7976 } else
7977 gnm_expr_free (expr);
7981 static void
7982 oo_db_range_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
7984 OOParseState *state = (OOParseState *)xin->user_state;
7986 if (state->filter != NULL) {
7987 gnm_filter_reapply (state->filter);
7988 state->filter = NULL;
7992 static void
7993 odf_filter_or (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
7995 oo_warning (xin, _("Gnumeric does not support 'or'-ed autofilter conditions."));
7998 static void
7999 oo_filter_cond (GsfXMLIn *xin, xmlChar const **attrs)
8001 static OOEnum const datatypes [] = {
8002 { "text", VALUE_STRING },
8003 { "number", VALUE_FLOAT },
8004 { NULL, 0 },
8006 static OOEnum const operators [] = {
8007 { "=", GNM_FILTER_OP_EQUAL },
8008 { "!=", GNM_FILTER_OP_NOT_EQUAL },
8009 { "<", GNM_FILTER_OP_LT },
8010 { "<=", GNM_FILTER_OP_LTE },
8011 { ">", GNM_FILTER_OP_GT },
8012 { ">=", GNM_FILTER_OP_GTE },
8014 { "match", GNM_FILTER_OP_MATCH },
8015 { "!match", GNM_FILTER_OP_NO_MATCH },
8016 { "empty", GNM_FILTER_OP_BLANKS },
8017 { "!empty", GNM_FILTER_OP_NON_BLANKS },
8018 { "bottom percent", GNM_FILTER_OP_BOTTOM_N_PERCENT_N },
8019 { "bottom values", GNM_FILTER_OP_BOTTOM_N },
8020 { "top percent", GNM_FILTER_OP_TOP_N_PERCENT_N },
8021 { "top values", GNM_FILTER_OP_TOP_N },
8023 { NULL, 0 },
8025 OOParseState *state = (OOParseState *)xin->user_state;
8026 int field_num = 0, type = -1, op = -1;
8027 char const *val_str = NULL;
8029 if (NULL == state->filter)
8030 return;
8032 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
8033 if (oo_attr_int_range (xin, attrs, OO_NS_TABLE, "field-number", &field_num, 0, INT_MAX)) ;
8034 else if (oo_attr_enum (xin, attrs, OO_NS_TABLE, "data-type", datatypes, &type)) ;
8035 else if (oo_attr_enum (xin, attrs, OO_NS_TABLE, "operator", operators, &op)) ;
8036 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "value"))
8037 val_str = CXML2C (attrs[1]);
8039 if (field_num >= 0 && op >= 0) {
8040 GnmFilterCondition *cond = NULL;
8041 GnmValue *v = NULL;
8043 if (type >= 0 && val_str != NULL)
8044 v = value_new_from_string (type, val_str, NULL, FALSE);
8046 switch (op) {
8047 case GNM_FILTER_OP_EQUAL:
8048 case GNM_FILTER_OP_NOT_EQUAL:
8049 case GNM_FILTER_OP_LT:
8050 case GNM_FILTER_OP_LTE:
8051 case GNM_FILTER_OP_GT:
8052 case GNM_FILTER_OP_GTE:
8053 case GNM_FILTER_OP_MATCH:
8054 case GNM_FILTER_OP_NO_MATCH:
8055 if (NULL != v) {
8056 cond = gnm_filter_condition_new_single (op, v);
8057 v = NULL;
8059 break;
8061 case GNM_FILTER_OP_BLANKS:
8062 cond = gnm_filter_condition_new_single (
8063 GNM_FILTER_OP_BLANKS, NULL);
8064 break;
8065 case GNM_FILTER_OP_NON_BLANKS:
8066 cond = gnm_filter_condition_new_single (
8067 GNM_FILTER_OP_NON_BLANKS, NULL);
8068 break;
8070 case GNM_FILTER_OP_BOTTOM_N_PERCENT:
8071 case GNM_FILTER_OP_BOTTOM_N:
8072 case GNM_FILTER_OP_TOP_N_PERCENT:
8073 case GNM_FILTER_OP_TOP_N:
8074 if (v && VALUE_IS_NUMBER(v))
8075 cond = gnm_filter_condition_new_bucket (
8076 0 == (op & GNM_FILTER_OP_BOTTOM_MASK),
8077 0 == (op & GNM_FILTER_OP_PERCENT_MASK),
8078 0 == (op & GNM_FILTER_OP_REL_N_MASK),
8079 value_get_as_float (v));
8080 break;
8082 value_release (v);
8083 if (NULL != cond)
8084 gnm_filter_set_condition (state->filter, field_num, cond, FALSE);
8088 static void
8089 odf_draw_frame_store_location (OOParseState *state, double *frame_offset, gdouble height, gdouble width)
8091 state->chart.width = width;
8092 state->chart.height = height;
8094 state->chart.plot_area_x = 0;
8095 state->chart.plot_area_y = 0;
8096 state->chart.plot_area_width = width;
8097 state->chart.plot_area_height =height;
8099 /* Column width and row heights are not correct */
8100 /* yet so we need to save this */
8101 /* info and adjust later. */
8102 state->chart.frame_offset[0] = frame_offset[0];
8103 state->chart.frame_offset[1] = frame_offset[1];
8104 state->chart.frame_offset[2] = frame_offset[2];
8105 state->chart.frame_offset[3] = frame_offset[3];
8108 static void
8109 od_draw_frame_start (GsfXMLIn *xin, xmlChar const **attrs)
8111 /* Note that in ODF spreadsheet files svg:height and svg:width are */
8112 /* ignored. We only consider */
8113 /* table:end-x and table:end-y together with table:end-cell-address */
8115 OOParseState *state = (OOParseState *)xin->user_state;
8116 GnmRange cell_base;
8117 double frame_offset[4];
8118 gdouble height = 0., width = 0., x = 0., y = 0., end_x = 0., end_y = 0.;
8119 GnmExprTop const *texpr = NULL;
8120 int z = -1;
8121 GnmSOAnchorMode mode;
8122 int last_row = gnm_sheet_get_last_row (state->pos.sheet);
8123 int last_col = gnm_sheet_get_last_col (state->pos.sheet);
8125 state->chart.name = NULL;
8127 height = width = x = y = 0.;
8128 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2){
8129 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "width"))
8130 oo_parse_distance (xin, attrs[1], "width", &width);
8131 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "height"))
8132 oo_parse_distance (xin, attrs[1], "height", &height);
8133 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "x"))
8134 oo_parse_distance (xin, attrs[1], "x", &x);
8135 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "y"))
8136 oo_parse_distance (xin, attrs[1], "y", &y);
8137 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "end-x"))
8138 oo_parse_distance (xin, attrs[1], "end-x", &end_x);
8139 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "end-y"))
8140 oo_parse_distance (xin, attrs[1], "end-y", &end_y);
8141 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "end-cell-address")) {
8142 GnmParsePos pp;
8143 char *end_str = g_strconcat ("[", CXML2C (attrs[1]), "]", NULL);
8144 parse_pos_init (&pp, state->pos.wb, NULL, 0, 0);
8145 texpr = oo_expr_parse_str (xin, end_str, &pp,
8146 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES,
8147 FORMULA_OPENFORMULA);
8148 g_free (end_str);
8149 } else if (oo_attr_int_range (xin,attrs, OO_NS_DRAW, "z-index",
8150 &z, 0, G_MAXINT))
8152 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "name"))
8153 state->chart.name = g_strdup (CXML2C (attrs[1]));
8156 frame_offset[0] = x;
8157 frame_offset[1] = y;
8158 if (state->pos.eval.col >= 0) {
8159 cell_base.start.col = cell_base.end.col = state->pos.eval.col;
8160 cell_base.start.row = cell_base.end.row = state->pos.eval.row;
8162 if (texpr == NULL || (GNM_EXPR_GET_OPER (texpr->expr) != GNM_EXPR_OP_CELLREF)) {
8163 cell_base.end.col = cell_base.start.col;
8164 cell_base.end.row = cell_base.start.row;
8165 frame_offset[2] = width;
8166 frame_offset[3] = height;
8167 mode = GNM_SO_ANCHOR_ONE_CELL;
8169 } else {
8170 GnmCellRef const *ref = &texpr->expr->cellref.ref;
8171 cell_base.end.col = ref->col;
8172 cell_base.end.row = ref->row;
8173 frame_offset[2] = end_x;
8174 frame_offset[3] = end_y ;
8175 mode = GNM_SO_ANCHOR_TWO_CELLS;
8177 if (texpr)
8178 gnm_expr_top_unref (texpr);
8179 } else {
8180 cell_base.end.col = cell_base.start.col =
8181 cell_base.end.row = cell_base.start.row = 0; /* actually not needed */
8182 frame_offset[2] = width;
8183 frame_offset[3] = height;
8184 mode = GNM_SO_ANCHOR_ABSOLUTE;
8187 odf_draw_frame_store_location (state, frame_offset,
8188 (height > 0) ? height : go_nan,
8189 (width > 0) ? width : go_nan);
8191 if (cell_base.start.col > last_col || cell_base.start.row > last_row) {
8192 oo_warning (xin, _("Moving sheet object from column %i and row %i"),
8193 cell_base.start.col, cell_base.start.row);
8194 cell_base.start.col = cell_base.start.row = 0;
8195 range_ensure_sanity (&cell_base, state->pos.sheet);
8198 sheet_object_anchor_init (&state->chart.anchor, &cell_base, frame_offset,
8199 GOD_ANCHOR_DIR_DOWN_RIGHT, mode);
8200 state->chart.so = NULL;
8201 state->chart.z_index = z;
8204 static void
8205 od_draw_frame_end_full (GsfXMLIn *xin, gboolean absolute_distance, char const *control_name)
8207 OOParseState *state = (OOParseState *)xin->user_state;
8209 if (state->chart.so != NULL) {
8210 /* Column width and row heights are not correct yet, z-index meaningless, */
8211 /* so we need to save this info and adjust later. */
8212 object_offset_t *ob_off = g_new (object_offset_t, 1);
8214 sheet_object_set_anchor (state->chart.so, &state->chart.anchor);
8215 ob_off->so = state->chart.so;
8216 ob_off->absolute_distance = absolute_distance;
8217 ob_off->z_index = state->chart.z_index;
8218 ob_off->control = g_strdup (control_name);
8219 ob_off->frame_offset[0] = state->chart.frame_offset[0];
8220 ob_off->frame_offset[1] = state->chart.frame_offset[1];
8221 ob_off->frame_offset[2] = state->chart.frame_offset[2];
8222 ob_off->frame_offset[3] = state->chart.frame_offset[3];
8223 state->chart_list = g_slist_prepend ( state->chart_list, ob_off);
8225 state->chart.so = NULL;
8227 g_free (state->chart.name);
8228 state->chart.name = NULL;
8231 static void
8232 od_draw_frame_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
8234 od_draw_frame_end_full (xin, FALSE, NULL);
8236 static void
8237 od_draw_text_frame_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
8239 OOParseState *state = (OOParseState *)xin->user_state;
8240 oo_text_p_t *ptr;
8242 if (state->text_p_stack != NULL && (NULL != (ptr = state->text_p_stack->data))
8243 && ptr->gstr != NULL)
8244 g_object_set (state->chart.so, "text", ptr->gstr->str, "markup", ptr->attrs, NULL);
8245 od_draw_frame_end_full (xin, FALSE, NULL);
8246 odf_pop_text_p (state);
8249 static void
8250 odf_line_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
8252 OOParseState *state = (OOParseState *)xin->user_state;
8253 oo_text_p_t *ptr;
8255 if (state->text_p_stack != NULL && (NULL != (ptr = state->text_p_stack->data))
8256 && ptr->gstr != NULL)
8257 oo_warning (xin, _("Gnumeric's sheet object lines do not support attached text. "
8258 "The text \"%s\" has been dropped."), ptr->gstr->str);
8259 od_draw_frame_end_full (xin, TRUE, NULL);
8260 odf_pop_text_p (state);
8263 static void
8264 od_draw_control_start (GsfXMLIn *xin, xmlChar const **attrs)
8266 OOParseState *state = (OOParseState *)xin->user_state;
8267 char const *name = NULL;
8269 od_draw_frame_start (xin, attrs);
8271 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
8272 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_DRAW, "control"))
8273 name = CXML2C (attrs[1]);
8275 if (name != NULL) {
8276 OOControl *oc = g_hash_table_lookup (state->controls, name);
8277 if (oc != NULL) {
8278 SheetObject *so = NULL;
8279 if (oc->t == sheet_widget_scrollbar_get_type () ||
8280 oc->t == sheet_widget_spinbutton_get_type () ||
8281 oc->t == sheet_widget_slider_get_type ()) {
8282 GtkAdjustment *adj;
8283 int min_real = (oc->min < oc->max) ? oc->min : oc->max;
8284 int max_real = (oc->min < oc->max) ? oc->max : oc->min;
8285 gnm_float value_real;
8287 if (oc->value != NULL) {
8288 char *end;
8289 value_real = gnm_strto (oc->value, &end);
8290 if (*end) {
8291 oo_warning (xin, _("Invalid attribute 'form:value', "
8292 "expected number, received '%s'"), oc->value);
8293 value_real = 0.;
8295 if (oc->value_type != NULL && 0 != strcmp (oc->value_type, "float"))
8296 oo_warning (xin, _("Invalid value-type '%s' advertised for "
8297 "'form:value' attribute in 'form:value-range' "
8298 "element."),
8299 oc->value_type);
8300 } else value_real = 0.;
8302 if (value_real < (gnm_float)min_real)
8303 value_real = min_real;
8304 if (value_real > (gnm_float)max_real)
8305 value_real = max_real;
8307 so = state->chart.so = g_object_new
8308 (oc->t, "horizontal", oc->horizontal, NULL);
8309 adj = sheet_widget_adjustment_get_adjustment (so);
8311 gtk_adjustment_configure (adj,
8312 value_real,
8313 min_real,
8314 max_real,
8315 oc->step,
8316 oc->page_step,
8318 } else if (oc->t == sheet_widget_radio_button_get_type ()) {
8319 so = state->chart.so = g_object_new
8320 (oc->t, "text", oc->label, NULL);
8321 if (oc->value != NULL) {
8322 GnmValue *val = NULL;
8323 if (oc->value_type == NULL ||
8324 0 == strcmp (oc->value_type, "string"))
8325 val = value_new_string (oc->value);
8326 else if (0 == strcmp (oc->value_type, "float")) {
8327 char *end;
8328 gnm_float value_real = gnm_strto (oc->value, &end);
8329 if (*end) {
8330 oo_warning (xin, _("Invalid attribute 'form:value', "
8331 "expected number, received '%s'"), oc->value);
8332 val = value_new_string (oc->value);
8333 } else
8334 val = value_new_float (value_real);
8335 } else if (0 == strcmp (oc->value_type, "boolean")) {
8336 gboolean b = (g_ascii_strcasecmp (oc->value, "false") &&
8337 strcmp (oc->value, "0"));
8338 val = value_new_bool (b);
8339 } else
8340 val = value_new_string (oc->value);
8341 sheet_widget_radio_button_set_value (so, val);
8342 value_release (val);
8344 } else if (oc->t == sheet_widget_checkbox_get_type ()) {
8345 state->chart.so = g_object_new
8346 (oc->t, "text", oc->label, NULL);
8347 } else if (oc->t == sheet_widget_list_get_type () ||
8348 oc->t == sheet_widget_combo_get_type ()) {
8349 state->chart.so = g_object_new
8350 (oc->t, NULL);
8351 } else if (oc->t == sheet_widget_button_get_type ()) {
8352 state->chart.so = g_object_new
8353 (oc->t, "text", oc->label, NULL);
8354 } else if (oc->t == sheet_widget_frame_get_type ()) {
8355 state->chart.so = g_object_new
8356 (oc->t, "text", oc->label, NULL);
8358 } else
8359 oo_warning (xin, "Undefined control '%s' encountered!", name);
8361 od_draw_frame_end_full (xin, FALSE, name);
8364 static void
8365 pop_hash (GSList **list, GHashTable **hash)
8367 g_hash_table_destroy (*hash);
8368 if (*list == NULL)
8369 *hash = NULL;
8370 else {
8371 *hash = (*list)->data;
8372 *list = g_slist_delete_link (*list, *list);
8376 static void
8377 odf_clear_conventions (OOParseState *state)
8379 gint i;
8380 for (i = 0; i < NUM_FORMULAE_SUPPORTED; i++)
8381 if (state->convs[i] != NULL) {
8382 gnm_conventions_unref (state->convs[i]);
8383 state->convs[i] = NULL;
8387 static void
8388 od_draw_object (GsfXMLIn *xin, xmlChar const **attrs)
8390 OOParseState *state = (OOParseState *)xin->user_state;
8391 gchar const *name_start = NULL;
8392 gchar * name;
8393 gint name_len;
8394 GsfInput *content = NULL;
8395 int i;
8397 if (state->chart.so != NULL) {
8398 if (GNM_IS_SO_GRAPH (state->chart.so))
8399 /* Only one object per frame! */
8400 return;
8401 /* We prefer objects over images etc. */
8402 /* We probably should figure out though whether */
8403 /* we in fact understand this object. */
8404 g_object_unref (state->chart.so);
8405 state->chart.so = NULL;
8408 state->chart.so = sheet_object_graph_new (NULL);
8409 state->chart.graph = sheet_object_graph_get_gog (state->chart.so);
8411 state->chart.saved_graph_styles
8412 = g_slist_prepend (state->chart.saved_graph_styles,
8413 state->chart.graph_styles);
8414 state->chart.saved_hatches
8415 = g_slist_prepend (state->chart.saved_hatches,
8416 state->chart.hatches);
8417 state->chart.saved_dash_styles
8418 = g_slist_prepend (state->chart.saved_dash_styles,
8419 state->chart.dash_styles);
8420 state->chart.saved_fill_image_styles
8421 = g_slist_prepend (state->chart.saved_fill_image_styles,
8422 state->chart.fill_image_styles);
8423 state->chart.saved_gradient_styles
8424 = g_slist_prepend (state->chart.saved_gradient_styles,
8425 state->chart.gradient_styles);
8427 for (i = 0; i < OO_CHART_STYLE_INHERITANCE; i++)
8428 state->chart.i_plot_styles[i] = NULL;
8430 state->chart.graph_styles = g_hash_table_new_full
8431 (g_str_hash, g_str_equal,
8432 (GDestroyNotify) g_free,
8433 (GDestroyNotify) oo_chart_style_free);
8434 state->chart.hatches = g_hash_table_new_full
8435 (g_str_hash, g_str_equal,
8436 (GDestroyNotify) g_free,
8437 (GDestroyNotify) g_free);
8438 state->chart.dash_styles = g_hash_table_new_full
8439 (g_str_hash, g_str_equal,
8440 (GDestroyNotify) g_free,
8441 NULL);
8442 state->chart.fill_image_styles = g_hash_table_new_full
8443 (g_str_hash, g_str_equal,
8444 (GDestroyNotify) g_free,
8445 (GDestroyNotify) g_free);
8446 state->chart.gradient_styles = g_hash_table_new_full
8447 (g_str_hash, g_str_equal,
8448 (GDestroyNotify) g_free,
8449 (GDestroyNotify) g_free);
8451 odf_free_cur_style (state);
8453 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
8454 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_XLINK, "href")) {
8455 name_start = CXML2C (attrs[1]);
8456 if (strncmp (CXML2C (attrs[1]), "./", 2) == 0)
8457 name_start += 2;
8458 if (strncmp (CXML2C (attrs[1]), "/", 1) == 0)
8459 name_start = NULL;
8460 break;
8463 if (!name_start)
8464 return;
8465 name_len = strlen (name_start);
8466 if (*(name_start + name_len - 1) == '/') /* OOo does not append a / */
8467 name_len--;
8468 name = g_strndup (name_start, name_len);
8469 state->object_name = name;
8471 /* We should be saving/protecting some info to avoid it being overwritten. */
8473 if (state->debug)
8474 g_print ("START %s\n", name);
8476 content = gsf_infile_child_by_vname (state->zip, name, "styles.xml", NULL);
8477 if (content != NULL) {
8478 GsfXMLInDoc *doc =
8479 gsf_xml_in_doc_new (get_styles_dtd (),
8480 gsf_odf_get_ns ());
8481 gsf_xml_in_doc_parse (doc, content, state);
8482 gsf_xml_in_doc_free (doc);
8483 odf_clear_conventions (state); /* contain references to xin */
8484 g_object_unref (content);
8487 content = gsf_infile_child_by_vname (state->zip, name, "content.xml", NULL);
8488 if (content != NULL) {
8489 GsfXMLInDoc *doc =
8490 gsf_xml_in_doc_new (get_dtd (), gsf_odf_get_ns ());
8491 gsf_xml_in_doc_parse (doc, content, state);
8492 gsf_xml_in_doc_free (doc);
8493 odf_clear_conventions (state); /* contain references to xin */
8494 g_object_unref (content);
8496 if (state->debug)
8497 g_print ("END %s\n", name);
8498 state->object_name = NULL;
8499 g_free (name);
8501 odf_free_cur_style (state);
8503 for (i = 0; i < OO_CHART_STYLE_INHERITANCE; i++)
8504 state->chart.i_plot_styles[i] = NULL;
8506 if (state->chart.width != go_nan)
8507 g_object_set (state->chart.graph, "width-pts", state->chart.width, NULL);
8508 if (state->chart.height != go_nan)
8509 g_object_set (state->chart.graph, "height-pts", state->chart.height, NULL);
8511 pop_hash (&state->chart.saved_graph_styles, &state->chart.graph_styles);
8512 pop_hash (&state->chart.saved_hatches, &state->chart.hatches);
8513 pop_hash (&state->chart.saved_dash_styles, &state->chart.dash_styles);
8514 pop_hash (&state->chart.saved_fill_image_styles,
8515 &state->chart.fill_image_styles);
8516 pop_hash (&state->chart.saved_gradient_styles,
8517 &state->chart.gradient_styles);
8520 static void
8521 od_draw_image (GsfXMLIn *xin, xmlChar const **attrs)
8523 GsfInput *input;
8525 OOParseState *state = (OOParseState *)xin->user_state;
8526 gchar const *file = NULL;
8527 char **path;
8529 if (state->chart.so != NULL)
8530 /* We only use images if there is no object available. */
8531 return;
8533 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
8534 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
8535 OO_NS_XLINK, "href")) {
8536 file = CXML2C (attrs[1]);
8537 break;
8540 if (!file)
8541 return;
8543 path = g_strsplit (file, "/", -1);
8544 input = gsf_infile_child_by_aname (state->zip, (const char **) path);
8545 g_strfreev (path);
8547 if (input != NULL) {
8548 SheetObjectImage *soi;
8549 gsf_off_t len = gsf_input_size (input);
8550 guint8 const *data = gsf_input_read (input, len, NULL);
8551 soi = g_object_new (GNM_SO_IMAGE_TYPE, NULL);
8552 state->chart.so = GNM_SO (soi);
8553 sheet_object_image_set_image (soi, "", data, len);
8554 g_object_unref (input);
8555 if (state->chart.name != NULL) {
8556 GOImage *image = NULL;
8557 g_object_get (G_OBJECT (soi),
8558 "image", &image,
8559 NULL);
8560 go_image_set_name (image, state->chart.name);
8561 g_object_unref (image);
8563 } else
8564 oo_warning (xin, _("Unable to load "
8565 "the file \'%s\'."),
8566 file);
8570 static void
8571 od_draw_text_box (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
8573 OOParseState *state = (OOParseState *)xin->user_state;
8574 GOStyle *style;
8576 if (state->chart.so != NULL)
8577 /* We have already created frame content */
8578 return;
8580 style = go_style_new ();
8582 style->line.width = 0;
8583 style->line.dash_type = GO_LINE_NONE;
8584 style->line.auto_dash = FALSE;
8585 style->fill.type = GO_STYLE_FILL_NONE;
8586 style->fill.auto_type = FALSE;
8588 state->chart.so = g_object_new (GNM_SO_FILLED_TYPE, "is-oval", FALSE, "style", style, NULL);
8589 g_object_unref (style);
8591 odf_push_text_p (state, FALSE);
8594 /* oo_chart_title is used both for chart titles and legend titles */
8595 /* 0: title, 1: subtitle, 2:footer, 3: axis */
8596 static void
8597 oo_chart_title (GsfXMLIn *xin, xmlChar const **attrs)
8599 OOParseState *state = (OOParseState *)xin->user_state;
8601 state->chart.title_expr = NULL;
8602 state->chart.title_style = NULL;
8603 state->chart.title_position = NULL;
8604 state->chart.title_anchor = NULL;
8605 state->chart.title_manual_pos = TRUE;
8606 state->chart.title_x = go_nan;
8607 state->chart.title_y = go_nan;
8609 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2){
8610 if ((gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
8611 OO_NS_TABLE, "cell-address" ) ||
8612 gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
8613 OO_NS_TABLE, "cell-range" ))
8614 && state->chart.title_expr == NULL) {
8615 GnmParsePos pp;
8616 char *end_str = g_strconcat ("[", CXML2C (attrs[1]), "]", NULL);
8618 parse_pos_init (&pp, state->pos.wb, NULL, 0, 0);
8619 state->chart.title_expr
8620 = oo_expr_parse_str
8621 (xin, end_str, &pp,
8622 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES,
8623 FORMULA_OPENFORMULA);
8624 g_free (end_str);
8625 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
8626 OO_GNUM_NS_EXT, "expression")) {
8627 GnmParsePos pp;
8629 if (state->chart.title_expr != NULL)
8630 gnm_expr_top_unref (state->chart.title_expr);
8632 parse_pos_init (&pp, state->pos.wb, NULL, 0, 0);
8633 state->chart.title_expr
8634 = oo_expr_parse_str
8635 (xin, CXML2C (attrs[1]), &pp,
8636 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES,
8637 FORMULA_OPENFORMULA);
8638 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
8639 OO_NS_CHART, "style-name")) {
8640 state->chart.title_style = g_strdup (CXML2C (attrs[1]));
8641 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
8642 OO_GNUM_NS_EXT, "compass"))
8643 state->chart.title_position = g_strdup (CXML2C (attrs[1]));
8644 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
8645 OO_GNUM_NS_EXT, "anchor"))
8646 state->chart.title_anchor = g_strdup (CXML2C (attrs[1]));
8647 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "is-position-manual",
8648 &state->chart.title_manual_pos))
8650 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "x"))
8651 oo_parse_distance (xin, attrs[1], "x", &state->chart.title_x);
8652 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "y"))
8653 oo_parse_distance (xin, attrs[1], "y", &state->chart.title_y);
8656 if (!(go_finite (state->chart.title_x) && go_finite (state->chart.title_y)))
8657 state->chart.title_manual_pos = FALSE;
8658 if (state->chart.title_position == NULL)
8659 state->chart.title_position = g_strdup ((xin->node->user_data.v_int == 2) ? "bottom" : "top");
8661 odf_push_text_p (state, FALSE);
8664 static void
8665 oo_chart_title_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
8667 OOParseState *state = (OOParseState *)xin->user_state;
8668 oo_text_p_t *ptr;
8669 gboolean use_markup = FALSE;
8671 g_return_if_fail (state->text_p_stack != NULL);
8672 ptr = state->text_p_stack->data;
8673 g_return_if_fail (ptr != NULL);
8675 if (state->chart.title_expr == NULL && ptr->gstr) {
8676 state->chart.title_expr = gnm_expr_top_new_constant
8677 (value_new_string_nocopy
8678 (go_pango_attrs_to_markup (ptr->attrs, ptr->gstr->str)));
8679 use_markup = (ptr->attrs != NULL &&
8680 !go_pango_attr_list_is_empty (ptr->attrs));
8683 if (state->chart.title_expr) {
8684 GOData *data = gnm_go_data_scalar_new_expr
8685 (state->chart.src_sheet, state->chart.title_expr);
8686 GogObject *label;
8687 GogObject *obj;
8688 gchar const *tag;
8690 if (state->chart.axis != NULL && xin->node->user_data.v_int == 3) {
8691 obj = (GogObject *)state->chart.axis;
8692 tag = "Label";
8693 } else if (state->chart.legend != NULL) {
8694 obj = (GogObject *)state->chart.legend;
8695 tag = "Title";
8696 } else if (xin->node->user_data.v_int == 0) {
8697 obj = (GogObject *)state->chart.graph;
8698 tag = "Title";
8699 } else {
8700 obj = (GogObject *)state->chart.chart;
8701 tag = "Title";
8704 label = gog_object_add_by_name (obj, tag, NULL);
8705 gog_dataset_set_dim (GOG_DATASET (label), 0, data, NULL);
8706 state->chart.title_expr = NULL;
8707 if (state->chart.title_style != NULL) {
8708 OOChartStyle *oostyle = g_hash_table_lookup
8709 (state->chart.graph_styles, state->chart.title_style);
8710 GOStyle *style =
8711 go_styled_object_get_style (GO_STYLED_OBJECT (label));
8712 if (oostyle && style) {
8713 style = go_style_dup (style);
8714 odf_apply_style_props (xin, oostyle->style_props, style, TRUE);
8715 go_styled_object_set_style (GO_STYLED_OBJECT (label), style);
8716 g_object_unref (style);
8718 g_free (state->chart.title_style);
8719 state->chart.title_style = NULL;
8721 if (use_markup)
8722 g_object_set (label, "allow-markup", TRUE, NULL);
8723 if (xin->node->user_data.v_int != 3) {
8724 if (state->chart.title_anchor)
8725 g_object_set (label, "anchor", state->chart.title_anchor, NULL);
8726 g_object_set (label,
8727 "compass", state->chart.title_position,
8728 "is-position-manual", state->chart.title_manual_pos,
8729 NULL);
8730 } else
8731 g_object_set (label,
8732 "is-position-manual", state->chart.title_manual_pos,
8733 NULL);
8735 if (state->chart.title_manual_pos) {
8736 if (go_finite (state->chart.width) && go_finite (state->chart.height)) {
8737 GogViewAllocation alloc;
8738 alloc.x = state->chart.title_x / state->chart.width;
8739 alloc.w = 0;
8740 alloc.y = state->chart.title_y / state->chart.height;
8741 alloc.h = 0;
8743 gog_object_set_position_flags (label, GOG_POSITION_MANUAL, GOG_POSITION_ANY_MANUAL);
8744 gog_object_set_manual_position (label, &alloc);
8745 } else {
8746 g_object_set (label,
8747 "is-position-manual", FALSE,
8748 NULL);
8749 oo_warning (xin, _("Unable to determine manual position for a chart component!"));
8754 g_free (state->chart.title_position);
8755 state->chart.title_position = NULL;
8756 g_free (state->chart.title_anchor);
8757 state->chart.title_anchor = NULL;
8758 odf_pop_text_p (state);
8761 static void
8762 od_chart_axis_categories (GsfXMLIn *xin, xmlChar const **attrs)
8764 OOParseState *state = (OOParseState *)xin->user_state;
8766 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
8767 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
8768 OO_NS_TABLE, "cell-range-address")) {
8769 if (state->chart.cat_expr == NULL)
8770 state->chart.cat_expr
8771 = g_strdup (CXML2C (attrs[1]));
8776 static void
8777 oo_chart_axis (GsfXMLIn *xin, xmlChar const **attrs)
8779 static OOEnum const types[] = {
8780 { "x", GOG_AXIS_X },
8781 { "y", GOG_AXIS_Y },
8782 { "z", GOG_AXIS_Z },
8783 { NULL, 0 },
8785 static OOEnum const types_bar[] = {
8786 { "x", GOG_AXIS_Y },
8787 { "y", GOG_AXIS_X },
8788 { "z", GOG_AXIS_Z },
8789 { NULL, 0 },
8791 static OOEnum const types_contour[] = {
8792 { "x", GOG_AXIS_X },
8793 { "y", GOG_AXIS_Y },
8794 { "z", GOG_AXIS_PSEUDO_3D },
8795 { NULL, 0 },
8797 static OOEnum const types_radar[] = {
8798 { "x", GOG_AXIS_CIRCULAR },
8799 { "y", GOG_AXIS_RADIAL },
8800 { NULL, 0 },
8802 GSList *axes, *l;
8804 OOParseState *state = (OOParseState *)xin->user_state;
8805 OOChartStyle *style = NULL;
8806 gchar const *style_name = NULL;
8807 gchar const *chart_name = NULL;
8808 gchar const *color_map_name = NULL;
8809 GogAxisType axis_type;
8810 int tmp;
8811 int gnm_id = 0;
8812 OOEnum const *axes_types;
8814 switch (state->chart.plot_type) {
8815 case OO_PLOT_RADAR:
8816 case OO_PLOT_RADARAREA:
8817 case OO_PLOT_POLAR:
8818 axes_types = types_radar;
8819 break;
8820 case OO_PLOT_BAR:
8821 if (oo_style_has_plot_property (state->chart.i_plot_styles, "horizontal", FALSE))
8822 axes_types = types_bar;
8823 else
8824 axes_types = types;
8825 break;
8826 case OO_PLOT_CIRCLE:
8827 case OO_PLOT_RING:
8828 return;
8829 case OO_PLOT_XL_CONTOUR:
8830 case OO_PLOT_CONTOUR:
8831 if (oo_style_has_property (state->chart.i_plot_styles, "three-dimensional", FALSE))
8832 axes_types = types;
8833 else axes_types = types_contour;
8834 break;
8835 default:
8836 axes_types = types;
8837 break;
8840 axis_type = GOG_AXIS_UNKNOWN;
8841 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
8842 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name"))
8843 style_name = CXML2C (attrs[1]);
8844 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "name"))
8845 chart_name = CXML2C (attrs[1]);
8846 else if (oo_attr_enum (xin, attrs, OO_NS_CHART, "dimension", axes_types, &tmp))
8847 axis_type = tmp;
8848 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT, "id", &gnm_id, 1, INT_MAX))
8850 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "color-map-name"))
8851 color_map_name = CXML2C (attrs[1]);
8853 if (gnm_id == 0) {
8854 switch (axis_type) {
8855 case GOG_AXIS_X:
8856 gnm_id = ++(state->chart.x_axis_count);
8857 break;
8858 case GOG_AXIS_Y:
8859 gnm_id = ++(state->chart.y_axis_count);
8860 break;
8861 case GOG_AXIS_Z:
8862 gnm_id = ++(state->chart.z_axis_count);
8863 break;
8864 case GOG_AXIS_CIRCULAR:
8865 case GOG_AXIS_RADIAL:
8866 case GOG_AXIS_UNKNOWN:
8867 default:
8868 gnm_id = 1;
8869 break;
8873 axes = gog_chart_get_axes (state->chart.chart, axis_type);
8874 for (l = axes; NULL != l; l = l->next) {
8875 if (((unsigned)gnm_id) == gog_object_get_id (GOG_OBJECT (l->data))) {
8876 state->chart.axis = l->data;
8877 break;
8880 g_slist_free (axes);
8881 if (NULL == state->chart.axis && (axis_type == GOG_AXIS_X || axis_type == GOG_AXIS_Y
8882 || axis_type == GOG_AXIS_Z)) {
8883 GogObject *axis = GOG_OBJECT (g_object_new (GOG_TYPE_AXIS, "type", axis_type, NULL));
8884 gog_object_add_by_name (GOG_OBJECT (state->chart.chart),
8885 axis_type == GOG_AXIS_X ? "X-Axis" :
8886 (axis_type == GOG_AXIS_Y ? "Y-Axis" : "Z-Axis"), axis);
8887 axes = gog_chart_get_axes (state->chart.chart, axis_type);
8888 for (l = axes; NULL != l; l = l->next) {
8889 if (((unsigned)gnm_id) == gog_object_get_id (GOG_OBJECT (l->data))) {
8890 state->chart.axis = l->data;
8891 break;
8894 g_slist_free (axes);
8896 if (NULL == state->chart.axis)
8897 g_print ("Did not find axis with type %i and id %i.\n", axis_type, gnm_id);
8899 if (NULL != style_name &&
8900 NULL != (style = g_hash_table_lookup (state->chart.graph_styles, style_name))) {
8901 if (NULL != state->chart.axis) {
8902 GOStyle *gostyle;
8903 g_object_get (G_OBJECT (state->chart.axis), "style", &gostyle, NULL);
8905 oo_prop_list_apply_to_axis (xin, style->axis_props,
8906 G_OBJECT (state->chart.axis));
8907 odf_apply_style_props (xin, style->style_props, gostyle, TRUE);
8908 g_object_unref (gostyle);
8910 if (style->fmt) {
8911 gboolean has_prop = FALSE;
8912 oo_prop_list_has (style->other_props, &has_prop, "ignore-axis-data-style");
8913 if (!has_prop)
8914 gog_axis_set_format (GOG_AXIS (state->chart.axis),
8915 go_format_ref (style->fmt));
8919 if (NULL != state->chart.plot && (state->ver == OOO_VER_1))
8920 oo_prop_list_apply (style->plot_props, G_OBJECT (state->chart.plot));
8922 if (NULL != chart_name && NULL != state->chart.axis)
8923 g_hash_table_replace (state->chart.named_axes,
8924 g_strdup (chart_name),
8925 state->chart.axis);
8926 if (NULL != color_map_name && NULL != state->chart.axis)
8927 g_object_set (G_OBJECT(state->chart.axis), "color-map-name", color_map_name, NULL);
8930 static void
8931 oo_chart_axis_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
8933 OOParseState *state = (OOParseState *)xin->user_state;
8934 state->chart.axis = NULL;
8937 static int
8938 gog_series_map_dim (GogSeries const *series, GogMSDimType ms_type)
8940 GogSeriesDesc const *desc = &series->plot->desc.series;
8941 unsigned i = desc->num_dim;
8943 if (ms_type == GOG_MS_DIM_LABELS)
8944 return -1;
8945 while (i-- > 0)
8946 if (desc->dim[i].ms_type == ms_type)
8947 return i;
8948 return -2;
8951 static int
8952 gog_series_map_dim_by_name (GogSeries const *series, char const *dim_name)
8954 GogSeriesDesc const *desc = &series->plot->desc.series;
8955 unsigned i = desc->num_dim;
8957 while (i-- > 0)
8958 if (desc->dim[i].name != NULL && strcmp (desc->dim[i].name, dim_name) == 0)
8959 return i;
8960 return -2;
8963 /* If range == %NULL use an implicit range */
8964 /* If general_expr, then range contains an expression not necessarily a range. */
8965 static void
8966 oo_plot_assign_dim (GsfXMLIn *xin, xmlChar const *range, int dim_type, char const *dim_name,
8967 gboolean general_expr)
8969 OOParseState *state = (OOParseState *)xin->user_state;
8971 /* force relative to A1, not the containing cell */
8972 GnmExprTop const *texpr;
8973 GnmValue *v;
8974 int dim;
8975 gboolean set_default_labels = FALSE;
8976 gboolean set_default_series_name = FALSE;
8978 if (NULL == state->chart.series)
8979 return;
8980 if (dim_type < 0)
8981 dim = - (1 + dim_type);
8982 else if (dim_name == NULL)
8983 dim = gog_series_map_dim (state->chart.series, dim_type);
8984 else
8985 dim = gog_series_map_dim_by_name (state->chart.series, dim_name);
8986 if (dim < -1)
8987 return;
8989 if (NULL != range) {
8990 if (general_expr) {
8991 texpr = odf_parse_range_address_or_expr (xin, CXML2C (range));
8992 if (state->debug)
8993 g_print ("%d = rangeref (%s) -- general expression\n", dim, range);
8994 } else {
8995 char const *range_list = CXML2C (range);
8996 GnmParsePos pp;
8997 GnmExprList *args = NULL;
8998 GnmExpr const *expr;
9000 parse_pos_init_sheet (&pp, state->pos.sheet);
9001 while (*range_list != 0) {
9002 GnmRangeRef ref;
9003 char const *ptr = oo_rangeref_parse
9004 (&ref, range_list, &pp, NULL);
9005 if (ptr == range_list || ref.a.sheet == invalid_sheet) {
9006 return;
9008 v = value_new_cellrange (&ref.a, &ref.b, 0, 0);
9009 expr = gnm_expr_new_constant (v);
9010 args = gnm_expr_list_append (args, expr);
9011 range_list = ptr;
9012 while (*range_list == ' ')
9013 range_list++;
9015 if (1 == gnm_expr_list_length (args)) {
9016 expr = args->data;
9017 gnm_expr_list_free (args);
9018 } else
9019 expr = gnm_expr_new_set (args);
9020 texpr = gnm_expr_top_new (expr);
9021 if (state->debug)
9022 g_print ("%d = rangeref (%s)\n", dim, range);
9024 } else if (NULL != gog_dataset_get_dim (GOG_DATASET (state->chart.series), dim))
9025 return; /* implicit does not overwrite existing */
9026 else if (state->chart.src_n_vectors <= 0) {
9027 oo_warning (xin,
9028 _("Not enough data in the supplied range (%s) for all the requests"), CXML2C (range));
9029 return;
9030 } else {
9031 v = value_new_cellrange_r (
9032 state->chart.src_sheet,
9033 &state->chart.src_range);
9035 if (state->debug)
9036 g_print ("%d = implicit (%s)\n", dim,
9037 range_as_string (&state->chart.src_range));
9039 state->chart.src_n_vectors--;
9040 if (state->chart.src_in_rows)
9041 state->chart.src_range.end.row = ++state->chart.src_range.start.row;
9042 else
9043 state->chart.src_range.end.col = ++state->chart.src_range.start.col;
9045 set_default_labels = state->chart.src_abscissa_set;
9046 set_default_series_name = state->chart.src_label_set;
9047 texpr = gnm_expr_top_new_constant (v);
9050 if (NULL != texpr)
9051 gog_series_set_dim (state->chart.series, dim,
9052 (dim_type != GOG_MS_DIM_LABELS)
9053 ? gnm_go_data_vector_new_expr (state->pos.sheet, texpr)
9054 : gnm_go_data_scalar_new_expr (state->pos.sheet, texpr),
9055 NULL);
9056 if (set_default_labels) {
9057 v = value_new_cellrange_r (state->chart.src_sheet,
9058 &state->chart.src_abscissa);
9059 texpr = gnm_expr_top_new_constant (v);
9060 if (NULL != texpr)
9061 gog_series_set_dim (state->chart.series, GOG_DIM_LABEL,
9062 gnm_go_data_vector_new_expr
9063 (state->pos.sheet, texpr),
9064 NULL);
9066 if (set_default_series_name) {
9067 v = value_new_cellrange_r (state->chart.src_sheet,
9068 &state->chart.src_label);
9069 texpr = gnm_expr_top_new_constant (v);
9070 if (NULL != texpr)
9071 gog_series_set_name (state->chart.series,
9072 GO_DATA_SCALAR (gnm_go_data_scalar_new_expr
9073 (state->pos.sheet, texpr)),
9074 NULL);
9075 if (state->chart.src_in_rows)
9076 state->chart.src_label.end.row = ++state->chart.src_label.start.row;
9077 else
9078 state->chart.src_label.end.col = ++state->chart.src_label.start.col;
9082 static void
9083 odf_gog_check_position (GsfXMLIn *xin, xmlChar const **attrs, GSList **list)
9085 gboolean b;
9087 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
9088 if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "is-position-manual", &b))
9089 *list = g_slist_prepend (*list, oo_prop_new_bool("is-position-manual",
9090 b));
9091 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "position"))
9092 *list = g_slist_prepend (*list, oo_prop_new_string ("position",
9093 CXML2C(attrs[1])));
9094 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "anchor"))
9095 *list = g_slist_prepend (*list, oo_prop_new_string ("anchor",
9096 CXML2C(attrs[1])));
9099 static void
9100 odf_gog_plot_area_check_position (GsfXMLIn *xin, xmlChar const **attrs, GSList **list)
9102 gboolean b;
9104 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
9105 if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "is-position-manual", &b))
9106 *list = g_slist_prepend (*list, oo_prop_new_bool("is-plot-area-manual",
9107 b));
9108 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "position"))
9109 *list = g_slist_prepend (*list, oo_prop_new_string ("plot-area",
9110 CXML2C(attrs[1])));
9114 static void
9115 oo_legend_set_position (OOParseState *state)
9117 GogObject *legend = state->chart.legend;
9119 if (legend == NULL)
9120 return;
9122 if (go_finite (state->chart.legend_x) && go_finite (state->chart.legend_y) &&
9123 go_finite (state->chart.width) && go_finite (state->chart.height)) {
9124 GogViewAllocation alloc;
9125 alloc.x = (state->chart.legend_x - state->chart.plot_area_x) / state->chart.plot_area_width;
9126 alloc.w = 0;
9127 alloc.y = (state->chart.legend_y - state->chart.plot_area_y) / state->chart.plot_area_height;
9128 alloc.h = 0;
9130 gog_object_set_position_flags (legend, GOG_POSITION_MANUAL, GOG_POSITION_ANY_MANUAL);
9131 gog_object_set_manual_position (legend, &alloc);
9132 } else
9133 gog_object_set_position_flags (legend, state->chart.legend_flag,
9134 GOG_POSITION_COMPASS | GOG_POSITION_ALIGNMENT);
9137 static gchar const
9138 *odf_find_plot_type (OOParseState *state, OOPlotType *oo_type)
9140 switch (*oo_type) {
9141 case OO_PLOT_AREA: return "GogAreaPlot"; break;
9142 case OO_PLOT_BAR: return "GogBarColPlot"; break;
9143 case OO_PLOT_CIRCLE: return "GogPiePlot"; break;
9144 case OO_PLOT_LINE: return "GogLinePlot"; break;
9145 case OO_PLOT_RADAR: return "GogRadarPlot"; break;
9146 case OO_PLOT_RADARAREA: return "GogRadarAreaPlot";break;
9147 case OO_PLOT_RING: return "GogRingPlot"; break;
9148 case OO_PLOT_SCATTER: return "GogXYPlot"; break;
9149 case OO_PLOT_STOCK: return "GogMinMaxPlot"; break; /* This is not quite right! */
9150 case OO_PLOT_CONTOUR:
9151 if (oo_style_has_property (state->chart.i_plot_styles,
9152 "three-dimensional", FALSE)) {
9153 *oo_type = OO_PLOT_SURFACE;
9154 return "GogSurfacePlot";
9155 } else
9156 return "GogContourPlot";
9157 break;
9158 case OO_PLOT_BUBBLE: return "GogBubblePlot"; break;
9159 case OO_PLOT_GANTT: return "GogDropBarPlot"; break;
9160 case OO_PLOT_POLAR: return "GogPolarPlot"; break;
9161 case OO_PLOT_XYZ_SURFACE:
9162 if (oo_style_has_property (state->chart.i_plot_styles,
9163 "three-dimensional", FALSE))
9164 return "GogXYZSurfacePlot";
9165 else
9166 return "GogXYZContourPlot";
9167 break;
9168 case OO_PLOT_SURFACE: return "GogSurfacePlot"; break;
9169 case OO_PLOT_SCATTER_COLOUR: return "GogXYColorPlot"; break;
9170 case OO_PLOT_XL_SURFACE: return "XLSurfacePlot"; break;
9171 case OO_PLOT_XL_CONTOUR: return "XLContourPlot"; break;
9172 case OO_PLOT_BOX: return "GogBoxPlot"; break;
9173 case OO_PLOT_UNKNOWN:
9174 default:
9175 return "GogLinePlot";
9176 /* It is simpler to create a plot than to check that we don't have one */
9177 break;
9181 static gboolean
9182 oo_prop_list_has_double (GSList *props, double *d, char const *tag)
9184 GSList *ptr;
9185 for (ptr = props; ptr; ptr = ptr->next) {
9186 OOProp *prop = ptr->data;
9187 if (0 == strcmp (prop->name, tag)) {
9188 *d = g_value_get_double (&prop->value);
9189 return TRUE;
9192 return FALSE;
9196 static GogPlot *odf_create_plot (OOParseState *state, OOPlotType *oo_type)
9198 GogPlot *plot;
9199 gchar const *type;
9201 type = odf_find_plot_type (state, oo_type);
9202 plot = gog_plot_new_by_name (type);
9204 gog_object_add_by_name (GOG_OBJECT (state->chart.chart),
9205 "Plot", GOG_OBJECT (plot));
9207 if (state->chart.i_plot_styles[OO_CHART_STYLE_PLOTAREA] != NULL)
9208 oo_prop_list_apply (state->chart.i_plot_styles[OO_CHART_STYLE_PLOTAREA]->
9209 plot_props, G_OBJECT (plot));
9211 if (0 == strcmp (type, "GogPiePlot") || 0 == strcmp (type, "GogRingPlot")) {
9212 /* Note we cannot use the oo_prop_list_apply method since series also have a */
9213 /* initial-angle property */
9214 double angle = 0.;
9215 if (!((state->chart.i_plot_styles[OO_CHART_STYLE_PLOTAREA] != NULL) &&
9216 oo_prop_list_has_double (state->chart.i_plot_styles[OO_CHART_STYLE_PLOTAREA]->
9217 plot_props, &angle, "plot-initial-angle")))
9218 angle = odf_scale_initial_angle (90);
9219 g_object_set (plot, "initial-angle", angle, NULL);
9222 return plot;
9225 static void
9226 oo_plot_area (GsfXMLIn *xin, xmlChar const **attrs)
9228 static OOEnum const labels[] = {
9229 { "both", 2 | 1 },
9230 { "column", 2 },
9231 { "row", 1 },
9232 { "none", 0 },
9233 { NULL, 0 },
9236 OOParseState *state = (OOParseState *)xin->user_state;
9237 xmlChar const *source_range_str = NULL;
9238 int label_flags = 0;
9239 GSList *prop_list = NULL;
9240 double x = go_nan, y = go_nan, width = go_nan, height = go_nan;
9242 odf_gog_plot_area_check_position (xin, attrs, &prop_list);
9244 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
9245 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
9246 OO_NS_CHART, "style-name"))
9247 state->chart.i_plot_styles[OO_CHART_STYLE_PLOTAREA] = g_hash_table_lookup
9248 (state->chart.graph_styles, CXML2C (attrs[1]));
9249 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "cell-range-address"))
9250 source_range_str = attrs[1];
9251 else if (oo_attr_enum (xin, attrs, OO_NS_CHART, "data-source-has-labels", labels, &label_flags))
9253 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "x"))
9254 oo_parse_distance (xin, attrs[1], "x", &x);
9255 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "y"))
9256 oo_parse_distance (xin, attrs[1], "y", &y);
9257 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "width"))
9258 oo_parse_distance (xin, attrs[1], "width", &width);
9259 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "height"))
9260 oo_parse_distance (xin, attrs[1], "height", &height);
9262 state->chart.src_n_vectors = -1;
9263 state->chart.src_in_rows = TRUE;
9264 state->chart.src_abscissa_set = FALSE;
9265 state->chart.src_label_set = FALSE;
9266 state->chart.series = NULL;
9267 state->chart.series_count = 0;
9268 state->chart.x_axis_count = 0;
9269 state->chart.y_axis_count = 0;
9270 state->chart.z_axis_count = 0;
9271 state->chart.list = NULL;
9272 state->chart.named_axes = g_hash_table_new_full
9273 (g_str_hash, g_str_equal,
9274 (GDestroyNotify) g_free,
9275 NULL);
9276 if (NULL != source_range_str) {
9277 GnmParsePos pp;
9278 GnmEvalPos ep;
9279 GnmRangeRef ref;
9280 Sheet *dummy;
9281 char const *ptr = oo_rangeref_parse
9282 (&ref, CXML2C (source_range_str),
9283 parse_pos_init_sheet (&pp, state->pos.sheet), NULL);
9284 if (ptr != CXML2C (source_range_str)
9285 && ref.a.sheet != invalid_sheet) {
9286 gnm_rangeref_normalize (&ref,
9287 eval_pos_init_sheet (&ep, state->pos.sheet),
9288 &state->chart.src_sheet, &dummy,
9289 &state->chart.src_range);
9291 if (label_flags & 1)
9292 state->chart.src_range.start.row++;
9293 if (label_flags & 2)
9294 state->chart.src_range.start.col++;
9296 if (state->chart.i_plot_styles[OO_CHART_STYLE_PLOTAREA] != NULL)
9297 state->chart.src_in_rows = state->chart.i_plot_styles
9298 [OO_CHART_STYLE_PLOTAREA]->src_in_rows;
9300 if (state->chart.src_in_rows) {
9301 state->chart.src_n_vectors = range_height (&state->chart.src_range);
9302 state->chart.src_range.end.row = state->chart.src_range.start.row;
9303 if (label_flags & 1) {
9304 state->chart.src_abscissa = state->chart.src_range;
9305 state->chart.src_abscissa.end.row = --state->chart.src_abscissa.start.row;
9306 state->chart.src_abscissa_set = TRUE;
9308 if (label_flags & 2) {
9309 state->chart.src_label = state->chart.src_range;
9310 state->chart.src_label.end.col = --state->chart.src_label.start.col;
9311 state->chart.src_label.end.row = state->chart.src_label.start.row;
9312 state->chart.src_label_set = TRUE;
9314 } else {
9315 state->chart.src_n_vectors = range_width (&state->chart.src_range);
9316 state->chart.src_range.end.col = state->chart.src_range.start.col;
9317 if (label_flags & 2) {
9318 state->chart.src_abscissa = state->chart.src_range;
9319 state->chart.src_abscissa.end.col = --state->chart.src_abscissa.start.col;
9320 state->chart.src_abscissa_set = TRUE;
9322 if (label_flags & 1) {
9323 state->chart.src_label = state->chart.src_range;
9324 state->chart.src_label.end.row = --state->chart.src_label.start.row;
9325 state->chart.src_label.end.col = state->chart.src_label.start.col;
9326 state->chart.src_label_set = TRUE;
9332 state->chart.plot = odf_create_plot (state, &state->chart.plot_type);
9334 if (go_finite (x) && go_finite (y) &&
9335 go_finite (width) && go_finite (height) &&
9336 go_finite (state->chart.width) && go_finite (state->chart.height)) {
9337 GogViewAllocation alloc;
9338 alloc.x = x / state->chart.width;
9339 alloc.w = width / state->chart.width;
9340 alloc.y = y / state->chart.height;
9341 alloc.h = height / state->chart.height;
9343 gog_object_set_position_flags (GOG_OBJECT (state->chart.chart),
9344 GOG_POSITION_MANUAL, GOG_POSITION_ANY_MANUAL);
9345 gog_object_set_manual_position (GOG_OBJECT (state->chart.chart), &alloc);
9346 g_object_set (G_OBJECT (state->chart.chart), "manual-size", "size", NULL);
9348 state->chart.plot_area_x = x;
9349 state->chart.plot_area_y = y;
9350 state->chart.plot_area_width = width;
9351 state->chart.plot_area_height = height;
9353 /* Since the plot area has changed we need to fix the legend position */
9354 oo_legend_set_position (state);
9357 oo_prop_list_apply (prop_list, G_OBJECT (state->chart.chart));
9358 oo_prop_list_free (prop_list);
9360 if (state->chart.plot_type == OO_PLOT_GANTT) {
9361 GogObject *yaxis = gog_object_get_child_by_name (GOG_OBJECT (state->chart.chart),
9362 "Y-Axis");
9363 if (yaxis != NULL) {
9364 GValue *val = g_value_init (g_new0 (GValue, 1), G_TYPE_BOOLEAN);
9365 g_value_set_boolean (val, TRUE);
9366 g_object_set_property (G_OBJECT (yaxis), "invert-axis", val);
9367 g_value_unset (val);
9368 g_free (val);
9373 static void
9374 odf_create_stock_plot (GsfXMLIn *xin)
9376 OOParseState *state = (OOParseState *)xin->user_state;
9377 GSList *series_addresses = state->chart.list;
9378 int len = g_slist_length (series_addresses);
9380 if (len > 3) {
9381 series_addresses = series_addresses->next;
9382 len--;
9385 if (len-- > 0) {
9386 state->chart.series = gog_plot_new_series (state->chart.plot);
9387 oo_plot_assign_dim (xin, series_addresses->data, GOG_MS_DIM_LOW, NULL, FALSE);
9389 if (len-- > 0) {
9390 series_addresses = series_addresses->next;
9391 oo_plot_assign_dim (xin, series_addresses->data, GOG_MS_DIM_HIGH, NULL, FALSE);
9395 static void
9396 oo_plot_area_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
9398 OOParseState *state = (OOParseState *)xin->user_state;
9399 if (state->chart.plot_type == OO_PLOT_STOCK) {
9400 odf_create_stock_plot (xin);
9401 g_slist_free_full (state->chart.list, g_free);
9402 state->chart.list = NULL;
9403 } else {
9404 if (state->chart.series_count == 0 && state->chart.series == NULL)
9405 state->chart.series = gog_plot_new_series (state->chart.plot);
9406 if (state->chart.series != NULL) {
9407 oo_plot_assign_dim (xin, NULL, GOG_MS_DIM_VALUES, NULL, FALSE);
9408 state->chart.series = NULL;
9411 state->chart.plot = NULL;
9412 state->chart.i_plot_styles[OO_CHART_STYLE_PLOTAREA] = NULL;
9413 g_hash_table_destroy (state->chart.named_axes);
9414 state->chart.named_axes = NULL;
9418 static void
9419 oo_plot_series (GsfXMLIn *xin, xmlChar const **attrs)
9421 OOParseState *state = (OOParseState *)xin->user_state;
9422 xmlChar const *label = NULL;
9423 OOPlotType plot_type = state->chart.plot_type;
9424 gboolean ignore_type_change = (state->chart.plot_type == OO_PLOT_SURFACE ||
9425 state->chart.plot_type == OO_PLOT_CONTOUR ||
9426 state->chart.plot_type == OO_PLOT_XL_SURFACE ||
9427 state->chart.plot_type == OO_PLOT_XL_CONTOUR);
9428 gboolean plot_type_set = FALSE;
9429 int tmp;
9430 GogPlot *plot;
9431 gchar const *cell_range_address = NULL;
9432 gchar const *cell_range_expression = NULL;
9433 GogObject *attached_axis = NULL;
9434 gboolean general_expression;
9436 if (state->debug)
9437 g_print ("<<<<< Start\n");
9439 state->chart.plot_type_default = state->chart.plot_type;
9440 state->chart.series_count++;
9441 state->chart.domain_count = 0;
9442 state->chart.data_pt_count = 0;
9444 /* Now check the attributes */
9445 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
9446 if (oo_attr_enum (xin, attrs, OO_NS_CHART, "class", odf_chart_classes, &tmp)) {
9447 if (!ignore_type_change) {
9448 state->chart.plot_type = plot_type = tmp;
9449 plot_type_set = TRUE;
9451 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "values-cell-range-address"))
9452 cell_range_address = CXML2C (attrs[1]);
9453 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "values-cell-range-expression"))
9454 cell_range_expression = CXML2C (attrs[1]);
9455 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "label-cell-address")) {
9456 if (label == NULL)
9457 label = attrs[1];
9458 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "label-cell-expression"))
9459 label = attrs[1];
9460 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
9461 OO_NS_CHART, "style-name"))
9462 state->chart.i_plot_styles[OO_CHART_STYLE_SERIES] = g_hash_table_lookup
9463 (state->chart.graph_styles, CXML2C (attrs[1]));
9464 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
9465 OO_NS_CHART, "attached-axis"))
9466 attached_axis = g_hash_table_lookup (state->chart.named_axes, CXML2C (attrs[1]));
9469 if (plot_type_set)
9470 plot = odf_create_plot (state, &plot_type);
9471 else
9472 plot = state->chart.plot;
9474 general_expression = (NULL != cell_range_expression);
9476 if (ignore_type_change && !general_expression && state->chart.series_count == 1 && NULL != cell_range_address) {
9477 /* We need to check whether this is type 2 of the contour/surface plots. */
9478 GnmExprTop const *texpr;
9479 texpr = odf_parse_range_address_or_expr (xin, cell_range_address);
9480 if (NULL != texpr) {
9481 GnmValue *val = gnm_expr_top_get_range (texpr);
9482 if (val != NULL) {
9483 GnmSheetRange r;
9484 gnm_sheet_range_from_value (&r, val);
9485 value_release (val);
9486 if ((range_width (&r.range) == 1) || (range_height (&r.range) == 1)) {
9487 if (state->chart.plot_type == OO_PLOT_SURFACE)
9488 plot_type = state->chart.plot_type_default = state->chart.plot_type
9489 = OO_PLOT_XL_SURFACE;
9490 else
9491 plot_type = state->chart.plot_type_default = state->chart.plot_type
9492 = OO_PLOT_XL_CONTOUR;
9493 /* We need to get rid of the original state->chart.plot */
9494 plot = state->chart.plot;
9495 state->chart.plot= odf_create_plot (state, &state->chart.plot_type);
9496 gog_object_clear_parent (GOG_OBJECT (plot));
9497 g_object_unref (G_OBJECT (plot));
9498 plot = state->chart.plot;
9499 plot_type_set = TRUE;
9502 gnm_expr_top_unref (texpr);
9506 /* Create the series */
9507 switch (plot_type) {
9508 case OO_PLOT_STOCK: /* We need to construct the series later. */
9509 break;
9510 case OO_PLOT_SURFACE:
9511 case OO_PLOT_CONTOUR:
9512 if (state->chart.series == NULL)
9513 state->chart.series = gog_plot_new_series (plot);
9514 break;
9515 case OO_PLOT_XL_SURFACE:
9516 case OO_PLOT_XL_CONTOUR:
9517 /* if (state->chart.series == NULL) */
9518 state->chart.series = gog_plot_new_series (plot);
9519 break;
9520 default:
9521 if (state->chart.series == NULL) {
9522 state->chart.series = gog_plot_new_series (plot);
9523 /* In ODF by default we skip invalid data for interpolation */
9524 g_object_set (state->chart.series, "interpolation-skip-invalid", TRUE, NULL);
9525 if (state->chart.cat_expr != NULL) {
9526 oo_plot_assign_dim
9527 (xin, state->chart.cat_expr,
9528 GOG_MS_DIM_CATEGORIES, NULL, FALSE);
9533 if (NULL != attached_axis && NULL != plot)
9534 gog_plot_set_axis (plot, GOG_AXIS (attached_axis));
9536 if (general_expression)
9537 cell_range_address = cell_range_expression;
9539 if (NULL != cell_range_address) {
9540 switch (plot_type) {
9541 case OO_PLOT_STOCK:
9542 state->chart.list = g_slist_append (state->chart.list,
9543 g_strdup (cell_range_address));
9544 break;
9545 case OO_PLOT_SURFACE:
9546 case OO_PLOT_CONTOUR:
9548 GnmExprTop const *texpr;
9549 texpr = odf_parse_range_address_or_expr (xin, cell_range_address);
9550 if (NULL != texpr)
9551 gog_series_set_dim (state->chart.series, 2,
9552 gnm_go_data_matrix_new_expr
9553 (state->pos.sheet, texpr), NULL);
9555 break;
9556 case OO_PLOT_XL_SURFACE:
9557 case OO_PLOT_XL_CONTOUR:
9559 GnmExprTop const *texpr;
9560 texpr = odf_parse_range_address_or_expr (xin, cell_range_address);
9561 if (NULL != texpr)
9562 gog_series_set_XL_dim (state->chart.series,
9563 GOG_MS_DIM_VALUES,
9564 gnm_go_data_vector_new_expr (state->pos.sheet, texpr),
9565 NULL);
9567 break;
9568 case OO_PLOT_GANTT:
9569 oo_plot_assign_dim (xin, cell_range_address,
9570 (state->chart.series_count % 2 == 1) ? GOG_MS_DIM_START : GOG_MS_DIM_END,
9571 NULL, general_expression);
9572 break;
9573 case OO_PLOT_BUBBLE:
9574 oo_plot_assign_dim (xin, cell_range_address, GOG_MS_DIM_BUBBLES, NULL, general_expression);
9575 break;
9576 case OO_PLOT_SCATTER_COLOUR:
9577 oo_plot_assign_dim (xin, cell_range_address, GOG_MS_DIM_EXTRA1, NULL, general_expression);
9578 break;
9579 default:
9580 oo_plot_assign_dim (xin, cell_range_address, GOG_MS_DIM_VALUES, NULL, general_expression);
9581 break;
9586 if (label != NULL) {
9587 GnmExprTop const *texpr = odf_parse_range_address_or_expr (xin, label);
9588 if (texpr != NULL)
9589 gog_series_set_name (state->chart.series,
9590 GO_DATA_SCALAR (gnm_go_data_scalar_new_expr
9591 (state->pos.sheet, texpr)),
9592 NULL);
9594 if (plot_type_set && state->chart.i_plot_styles[OO_CHART_STYLE_SERIES] != NULL)
9595 oo_prop_list_apply (state->chart.i_plot_styles[OO_CHART_STYLE_SERIES]->
9596 plot_props, G_OBJECT (plot));
9597 oo_chart_style_to_series (xin, state->chart.i_plot_styles[OO_CHART_STYLE_PLOTAREA],
9598 G_OBJECT (state->chart.series));
9599 oo_chart_style_to_series (xin, state->chart.i_plot_styles[OO_CHART_STYLE_SERIES],
9600 G_OBJECT (state->chart.series));
9603 static void
9604 oo_plot_series_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
9606 OOParseState *state = (OOParseState *)xin->user_state;
9608 switch (state->chart.plot_type) {
9609 case OO_PLOT_STOCK:
9610 case OO_PLOT_CONTOUR:
9611 break;
9612 case OO_PLOT_GANTT:
9613 if ((state->chart.series_count % 2) != 0)
9614 break;
9615 /* else no break */
9616 default:
9617 oo_plot_assign_dim (xin, NULL, GOG_MS_DIM_VALUES, NULL, FALSE);
9618 state->chart.series = NULL;
9619 break;
9621 state->chart.plot_type = state->chart.plot_type_default;
9622 state->chart.i_plot_styles[OO_CHART_STYLE_SERIES] = NULL;
9623 if (state->debug)
9624 g_print (">>>>> end\n");
9627 static void
9628 oo_series_domain (GsfXMLIn *xin, xmlChar const **attrs)
9630 OOParseState *state = (OOParseState *)xin->user_state;
9631 xmlChar const *src = NULL;
9632 xmlChar const *cell_range_expression = NULL;
9633 int dim = GOG_MS_DIM_VALUES;
9634 char const *name = NULL;
9636 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
9637 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "cell-range-address"))
9638 src = attrs[1];
9639 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "cell-range-expression"))
9640 cell_range_expression = attrs[1];
9642 switch (state->chart.plot_type) {
9643 case OO_PLOT_BUBBLE:
9644 case OO_PLOT_SCATTER_COLOUR:
9645 dim = (state->chart.domain_count == 0) ? GOG_MS_DIM_VALUES : GOG_MS_DIM_CATEGORIES;
9646 break;
9647 case OO_PLOT_XYZ_SURFACE:
9648 case OO_PLOT_SURFACE:
9649 name = (state->chart.domain_count == 0) ? "Y" : "X";
9650 break;
9651 case OO_PLOT_CONTOUR:
9652 /* y-values first, then x-values */
9653 dim = (state->chart.domain_count == 0) ? GOG_MS_DIM_CATEGORIES : -1;
9654 break;
9655 default:
9656 dim = GOG_MS_DIM_CATEGORIES;
9657 break;
9659 oo_plot_assign_dim (xin, (cell_range_expression != NULL) ? cell_range_expression : src, dim, name,
9660 cell_range_expression != NULL);
9661 state->chart.domain_count++;
9664 static void
9665 oo_series_pt (GsfXMLIn *xin, xmlChar const **attrs)
9667 OOParseState *state = (OOParseState *)xin->user_state;
9668 char const *style_name = NULL;
9669 guint repeat_count = 1;
9670 OOChartStyle *style = NULL;
9672 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
9673 if (oo_attr_int_range (xin, attrs, OO_NS_CHART, "repeated", &repeat_count, 0, INT_MAX))
9675 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name")) {
9676 style_name = attrs[1];
9678 if (repeat_count == 0)
9679 return; /* Why does ODF allow repeat counts of 0 ??*/
9681 if (style_name != NULL &&
9682 NULL != (style = g_hash_table_lookup (state->chart.graph_styles, style_name))) {
9683 guint index = state->chart.data_pt_count;
9684 state->chart.data_pt_count += repeat_count;
9685 for (; index < state->chart.data_pt_count; index++) {
9686 GogObject *element = gog_object_add_by_name (GOG_OBJECT (state->chart.series), "Point", NULL);
9687 if (element != NULL) {
9688 GOStyle *gostyle;
9689 g_object_set (G_OBJECT (element), "index", index, NULL);
9690 oo_prop_list_apply (style->plot_props, G_OBJECT (element));
9691 g_object_get (G_OBJECT (element), "style", &gostyle, NULL);
9692 if (gostyle != NULL) {
9693 GOStyle *nstyle = go_style_dup (gostyle);
9694 OOChartStyle *astyle = state->chart.i_plot_styles[OO_CHART_STYLE_PLOTAREA];
9695 if (astyle != NULL)
9696 odf_apply_style_props
9697 (xin, astyle->style_props, nstyle, TRUE);
9698 astyle = state->chart.i_plot_styles[OO_CHART_STYLE_SERIES];
9699 if (astyle != NULL)
9700 odf_apply_style_props
9701 (xin, astyle->style_props, nstyle, TRUE);
9702 odf_apply_style_props (xin, style->style_props, nstyle, TRUE);
9703 g_object_set (element, "style", nstyle, NULL);
9704 g_object_unref (gostyle);
9705 g_object_unref (nstyle);
9709 } else
9710 state->chart.data_pt_count += repeat_count;
9713 static void
9714 od_series_reg_equation (GsfXMLIn *xin, xmlChar const **attrs)
9716 OOParseState *state = (OOParseState *)xin->user_state;
9717 char const *style_name = NULL;
9718 GogObject *equation;
9719 gboolean automatic_content = TRUE;
9720 gboolean dispay_equation = TRUE;
9721 gboolean display_r_square = TRUE;
9722 GSList *prop_list = NULL;
9724 g_return_if_fail (state->chart.regression != NULL);
9726 odf_gog_check_position (xin, attrs, &prop_list);
9728 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
9729 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name"))
9730 style_name = CXML2C (attrs[1]);
9731 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "automatic-content",
9732 &automatic_content));
9733 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "automatic-content",
9734 &automatic_content));
9735 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "display-equation",
9736 &dispay_equation));
9737 else if (oo_attr_bool (xin, attrs, OO_GNUM_NS_EXT, "display-equation",
9738 &dispay_equation));
9739 else if (oo_attr_bool (xin, attrs, OO_NS_CHART, "display-r-square",
9740 &display_r_square));
9743 equation = gog_object_add_by_name (GOG_OBJECT (state->chart.regression),
9744 "Equation", NULL);
9746 g_object_set (G_OBJECT (equation),
9747 "show-eq", dispay_equation,
9748 "show-r2", display_r_square,
9749 NULL);
9751 oo_prop_list_apply (prop_list, G_OBJECT (equation));
9752 oo_prop_list_free (prop_list);
9754 if (!automatic_content)
9755 oo_warning (xin, _("Gnumeric does not support non-automatic"
9756 " regression equations. Using automatic"
9757 " equation instead."));
9759 if (style_name != NULL) {
9760 OOChartStyle *chart_style = g_hash_table_lookup
9761 (state->chart.graph_styles, style_name);
9762 if (chart_style) {
9763 GOStyle *style =
9764 go_styled_object_get_style (GO_STYLED_OBJECT (equation));
9765 if (style != NULL) {
9766 style = go_style_dup (style);
9767 odf_apply_style_props (xin, chart_style->style_props, style, TRUE);
9768 go_styled_object_set_style (GO_STYLED_OBJECT (equation), style);
9769 g_object_unref (style);
9771 /* In the moment we don't need this. */
9772 /* oo_prop_list_apply (chart_style->plot_props, G_OBJECT (equation)); */
9773 } else
9774 oo_warning (xin, _("The chart style \"%s\" is not defined!"), style_name);
9778 static void
9779 odf_store_data (OOParseState *state, gchar const *str, GogObject *obj, int dim)
9781 if (str != NULL) {
9782 GnmParsePos pp;
9783 GnmRangeRef ref;
9784 char const *ptr = oo_rangeref_parse
9785 (&ref, CXML2C (str),
9786 parse_pos_init (&pp, state->pos.wb, NULL, 0, 0),
9787 NULL);
9788 if (ptr != CXML2C (str) && ref.a.sheet != invalid_sheet) {
9789 GnmValue *v = value_new_cellrange (&ref.a, &ref.b, 0, 0);
9790 GnmExprTop const *texpr = gnm_expr_top_new_constant (v);
9791 if (NULL != texpr) {
9792 gog_dataset_set_dim (GOG_DATASET (obj), dim,
9793 gnm_go_data_scalar_new_expr
9794 (state->pos.sheet, texpr),
9795 NULL);
9801 static void
9802 od_series_regression (GsfXMLIn *xin, xmlChar const **attrs)
9804 OOParseState *state = (OOParseState *)xin->user_state;
9805 char const *style_name = NULL;
9806 gchar const *lower_bd = NULL;
9807 gchar const *upper_bd = NULL;
9809 state->chart.regression = NULL;
9811 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
9812 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name"))
9813 style_name = CXML2C (attrs[1]);
9814 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "lower-bound"))
9815 lower_bd = CXML2C (attrs[1]);
9816 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_GNUM_NS_EXT, "upper-bound"))
9817 upper_bd = CXML2C (attrs[1]);
9819 if (style_name != NULL) {
9820 OOChartStyle *chart_style = g_hash_table_lookup
9821 (state->chart.graph_styles, style_name);
9823 if (chart_style) {
9824 GSList *l;
9825 GOStyle *style = NULL;
9826 GogObject *regression;
9827 gchar const *type_name = "GogLinRegCurve";
9828 gchar const *regression_name = NULL;
9829 gchar const *regression_name_c = NULL;
9830 gboolean write_lo_dims = FALSE;
9831 GValue *lo_dim = NULL;
9832 for (l = chart_style->other_props; l != NULL; l = l->next) {
9833 OOProp *prop = l->data;
9834 if (0 == strcmp ("regression-type", prop->name)) {
9835 char const *reg_type = g_value_get_string (&prop->value);
9836 if (0 == strcmp (reg_type, "linear"))
9837 type_name = "GogLinRegCurve";
9838 else if (0 == strcmp (reg_type, "power"))
9839 type_name = "GogPowerRegCurve";
9840 else if (0 == strcmp (reg_type, "exponential"))
9841 type_name = "GogExpRegCurve";
9842 else if (0 == strcmp (reg_type, "logarithmic"))
9843 type_name = "GogLogRegCurve";
9844 else if (0 == strcmp
9845 (reg_type, "gnm:exponential-smoothed"))
9846 type_name = "GogExpSmooth";
9847 else if (0 == strcmp
9848 (reg_type, "gnm:logfit"))
9849 type_name = "GogLogFitCurve";
9850 else if (0 == strcmp
9851 (reg_type, "gnm:polynomial")) {
9852 type_name = "GogPolynomRegCurve";
9853 write_lo_dims = TRUE;
9854 } else if (0 == strcmp
9855 (reg_type, "gnm:moving-average"))
9856 type_name = "GogMovingAvg";
9857 } else if (0 == strcmp ("regression-name-expression", prop->name)) {
9858 regression_name = g_value_get_string (&prop->value);
9859 } else if (0 == strcmp ("regression-name-constant", prop->name)) {
9860 regression_name_c = g_value_get_string (&prop->value);
9861 } else if (0 == strcmp ("lo-dims", prop->name)) {
9862 lo_dim = &prop->value;
9866 state->chart.regression = regression =
9867 GOG_OBJECT (gog_trend_line_new_by_name (type_name));
9868 regression = gog_object_add_by_name (GOG_OBJECT (state->chart.series),
9869 "Trend line", regression);
9870 if (write_lo_dims && lo_dim != NULL)
9871 g_object_set_property ( G_OBJECT (regression), "dims", lo_dim);
9872 oo_prop_list_apply (chart_style->other_props, G_OBJECT (regression));
9874 style = go_styled_object_get_style (GO_STYLED_OBJECT (regression));
9875 if (style != NULL) {
9876 style = go_style_dup (style);
9877 odf_apply_style_props (xin, chart_style->style_props, style, TRUE);
9878 go_styled_object_set_style (GO_STYLED_OBJECT (regression), style);
9879 g_object_unref (style);
9882 if (regression_name != NULL) {
9883 GnmParsePos pp;
9884 GOData *data;
9885 GnmExprTop const *expr;
9886 parse_pos_init (&pp, state->pos.wb, state->pos.sheet, 0, 0);
9887 expr = oo_expr_parse_str
9888 (xin, regression_name, &pp,
9889 GNM_EXPR_PARSE_FORCE_EXPLICIT_SHEET_REFERENCES,
9890 FORMULA_OPENFORMULA);
9891 if (expr != NULL) {
9892 data = gnm_go_data_scalar_new_expr (state->pos.sheet, expr);
9893 gog_dataset_set_dim (GOG_DATASET (regression), -1, data, NULL);
9895 } else if (regression_name_c != NULL) {
9896 GOData *data;
9897 data = gnm_go_data_scalar_new_expr
9898 (state->pos.sheet, gnm_expr_top_new_constant
9899 (value_new_string (regression_name_c)));
9900 gog_dataset_set_dim (GOG_DATASET (regression), -1, data, NULL);
9903 odf_store_data (state, lower_bd, regression, 0);
9904 odf_store_data (state, upper_bd, regression, 1);
9910 static void
9911 oo_series_droplines (GsfXMLIn *xin, xmlChar const **attrs)
9913 OOParseState *state = (OOParseState *)xin->user_state;
9914 char const *style_name = NULL;
9915 gboolean vertical = TRUE;
9916 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
9917 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name"))
9918 style_name = CXML2C (attrs[1]);
9919 if (style_name != NULL) {
9920 OOChartStyle *chart_style = g_hash_table_lookup
9921 (state->chart.graph_styles, style_name);
9922 if (chart_style) {
9923 GOStyle *style = NULL;
9924 GSList *l;
9925 char const *role_name = NULL;
9926 GogObject const *lines;
9928 for (l = chart_style->plot_props; l != NULL; l = l->next) {
9929 OOProp *prop = l->data;
9930 if (0 == strcmp ("vertical", prop->name))
9931 vertical = g_value_get_boolean (&prop->value);
9934 switch (state->chart.plot_type) {
9935 case OO_PLOT_LINE:
9936 role_name = "Drop lines";
9937 break;
9938 case OO_PLOT_SCATTER:
9939 role_name = vertical ? "Vertical drop lines" : "Horizontal drop lines";
9940 break;
9941 default:
9942 oo_warning (xin , _("Encountered drop lines in a plot not supporting them."));
9943 return;
9946 lines = gog_object_add_by_name (GOG_OBJECT (state->chart.series), role_name, NULL);
9948 style = go_styled_object_get_style (GO_STYLED_OBJECT (lines));
9949 if (style != NULL) {
9950 style = go_style_dup (style);
9951 odf_apply_style_props (xin, chart_style->style_props, style, TRUE);
9952 go_styled_object_set_style (GO_STYLED_OBJECT (lines), style);
9953 g_object_unref (style);
9959 static void
9960 oo_series_serieslines (GsfXMLIn *xin, xmlChar const **attrs)
9962 OOParseState *state = (OOParseState *)xin->user_state;
9963 char const *style_name = NULL;
9964 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
9965 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name"))
9966 style_name = CXML2C (attrs[1]);
9967 if (style_name != NULL) {
9968 OOChartStyle *chart_style = g_hash_table_lookup (state->chart.graph_styles, style_name);
9969 GOStyle *style;
9970 GogObject const *lines;
9972 lines = gog_object_add_by_name (GOG_OBJECT (state->chart.series), "Series lines", NULL);
9973 style = go_styled_object_get_style (GO_STYLED_OBJECT (lines));
9974 if (chart_style && style) {
9975 style = go_style_dup (style);
9976 odf_apply_style_props (xin, chart_style->style_props, style, TRUE);
9977 go_styled_object_set_style (GO_STYLED_OBJECT (lines), style);
9978 g_object_unref (style);
9983 static void
9984 oo_chart_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
9986 OOParseState *state = (OOParseState *)xin->user_state;
9988 g_free (state->chart.cat_expr);
9989 state->chart.cat_expr = NULL;
9991 state->chart.legend = NULL;
9994 static void
9995 odf_chart_set_default_style (GogObject *chart)
9997 GOStyle *style;
9999 style = go_styled_object_get_style (GO_STYLED_OBJECT (chart));
10001 style->line.width = -1.0;
10002 style->line.dash_type = GO_LINE_NONE;
10004 go_styled_object_style_changed (GO_STYLED_OBJECT (chart));
10007 static void
10008 oo_chart (GsfXMLIn *xin, xmlChar const **attrs)
10010 OOParseState *state = (OOParseState *)xin->user_state;
10011 int tmp;
10012 OOPlotType type = OO_PLOT_UNKNOWN;
10013 OOChartStyle *style = NULL;
10015 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
10016 if (oo_attr_enum (xin, attrs, OO_NS_CHART, "class", odf_chart_classes, &tmp))
10017 type = tmp;
10018 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10019 OO_NS_CHART, "style-name"))
10020 style = g_hash_table_lookup
10021 (state->chart.graph_styles, CXML2C (attrs[1]));
10022 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10023 OO_GNUM_NS_EXT, "theme-name")) {
10024 GValue *val = g_value_init (g_new0 (GValue, 1), G_TYPE_STRING);
10025 g_value_set_string (val, CXML2C (attrs[0]));
10026 g_object_set_property (G_OBJECT (state->chart.graph), "theme-name", val);
10027 g_value_unset (val);
10028 g_free (val);
10031 state->chart.plot_type = type;
10032 state->chart.chart = GOG_CHART (gog_object_add_by_name (
10033 GOG_OBJECT (state->chart.graph), "Chart", NULL));
10034 odf_chart_set_default_style (GOG_OBJECT (state->chart.chart));
10035 state->chart.plot = NULL;
10036 state->chart.series = NULL;
10037 state->chart.axis = NULL;
10038 state->chart.legend = NULL;
10039 state->chart.cat_expr = NULL;
10040 if (NULL != style) {
10041 GSList *ptr;
10042 state->chart.src_in_rows = style->src_in_rows;
10043 for (ptr = style->other_props; ptr; ptr = ptr->next) {
10044 OOProp *prop = ptr->data;
10045 if (0 == strcmp (prop->name, "border")) {
10046 gnm_float pts = 0.;
10047 const char *border = g_value_get_string (&prop->value);
10048 const char *end;
10050 while (*border == ' ')
10051 border++;
10052 end = oo_parse_spec_distance (border, &pts);
10054 if (end == GINT_TO_POINTER(1) || end == NULL) {
10055 if (0 == strncmp (border, "thin", 4)) {
10056 pts = 0.;
10057 end = border + 4;
10058 } else if (0 == strncmp (border, "medium", 6)) {
10059 pts = 1.5;
10060 end = border + 6;
10061 } else if (0 == strncmp (border, "thick", 5)) {
10062 pts = 3.;
10063 end = border + 5;
10067 if (end != GINT_TO_POINTER(1) && end != NULL && end > border) {
10068 /* pts should be valid */
10069 GOStyle *go_style = go_styled_object_get_style (GO_STYLED_OBJECT (state->chart.chart));
10070 go_style->line.width = pts;
10071 go_style->line.dash_type = GO_LINE_SOLID;
10072 go_styled_object_style_changed (GO_STYLED_OBJECT (state->chart.chart));
10078 if (type == OO_PLOT_UNKNOWN)
10079 oo_warning (xin , _("Encountered an unknown chart type, "
10080 "trying to create a line plot."));
10083 static void
10084 oo_color_scale (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
10086 OOParseState *state = (OOParseState *)xin->user_state;
10087 gog_object_add_by_name ((GogObject *)state->chart.chart, "Color-Scale", NULL);
10090 static void
10091 oo_legend (GsfXMLIn *xin, xmlChar const **attrs)
10093 static OOEnum const positions [] = {
10094 { "top", GOG_POSITION_N },
10095 { "bottom", GOG_POSITION_S },
10096 { "start", GOG_POSITION_W },
10097 { "end", GOG_POSITION_E },
10098 { "top-start", GOG_POSITION_N | GOG_POSITION_W },
10099 { "bottom-start", GOG_POSITION_S | GOG_POSITION_W },
10100 { "top-end", GOG_POSITION_N | GOG_POSITION_E },
10101 { "bottom-end", GOG_POSITION_S | GOG_POSITION_E },
10102 { NULL, 0 },
10104 static OOEnum const alignments [] = {
10105 { "start", GOG_POSITION_ALIGN_START },
10106 { "center", GOG_POSITION_ALIGN_CENTER },
10107 { "end", GOG_POSITION_ALIGN_END },
10108 { NULL, 0 },
10110 OOParseState *state = (OOParseState *)xin->user_state;
10111 GogObjectPosition pos = GOG_POSITION_W | GOG_POSITION_ALIGN_CENTER;
10112 GogObjectPosition align = GOG_POSITION_ALIGN_CENTER;
10113 GogObject *legend;
10114 int tmp;
10115 char const *style_name = NULL;
10116 double x = go_nan, y = go_nan;
10118 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
10119 if (oo_attr_enum (xin, attrs, OO_NS_CHART, "legend-position", positions, &tmp))
10120 pos = tmp;
10121 else if (oo_attr_enum (xin, attrs, OO_NS_CHART, "legend-align", alignments, &tmp))
10122 align = tmp;
10123 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name"))
10124 style_name = CXML2C (attrs[1]);
10125 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "x"))
10126 oo_parse_distance (xin, attrs[1], "x", &x);
10127 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_SVG, "y"))
10128 oo_parse_distance (xin, attrs[1], "y", &y);
10130 legend = gog_object_add_by_name ((GogObject *)state->chart.chart, "Legend", NULL);
10131 state->chart.legend = legend;
10132 if (legend != NULL) {
10133 GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (legend));
10134 if (style_name && style) {
10135 OOChartStyle *chart_style = g_hash_table_lookup
10136 (state->chart.graph_styles, style_name);
10137 style = go_style_dup (style);
10138 if (chart_style)
10139 odf_apply_style_props (xin, chart_style->style_props, style, TRUE);
10140 else
10141 oo_warning (xin, _("Chart style with name '%s' is missing."),
10142 style_name);
10143 go_styled_object_set_style (GO_STYLED_OBJECT (legend), style);
10144 g_object_unref (style);
10146 state->chart.legend_x = x;
10147 state->chart.legend_y = y;
10148 state->chart.legend_flag = pos | align;
10150 /* We will need to redo this if we encounter a plot-area specification later */
10151 oo_legend_set_position (state);
10155 static void
10156 oo_chart_grid (GsfXMLIn *xin, xmlChar const **attrs)
10158 OOParseState *state = (OOParseState *)xin->user_state;
10159 gchar const *style_name = NULL;
10160 GogObject *grid = NULL;
10162 if (state->chart.axis == NULL)
10163 return;
10164 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
10165 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "class")) {
10166 if (attr_eq (attrs[1], "major"))
10167 grid = gog_object_add_by_name (state->chart.axis, "MajorGrid", NULL);
10168 else if (attr_eq (attrs[1], "minor"))
10169 grid = gog_object_add_by_name (state->chart.axis, "MinorGrid", NULL);
10170 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name"))
10171 style_name = CXML2C (attrs[1]);
10173 if (grid != NULL && style_name != NULL) {
10174 GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (grid));
10175 if (style) {
10176 OOChartStyle *chart_style = g_hash_table_lookup
10177 (state->chart.graph_styles, style_name);
10178 style = go_style_dup (style);
10179 if (chart_style)
10180 odf_apply_style_props (xin, chart_style->style_props, style, TRUE);
10181 else
10182 oo_warning (xin, _("Chart style with name '%s' is missing."),
10183 style_name);
10184 go_styled_object_set_style (GO_STYLED_OBJECT (grid), style);
10185 g_object_unref (style);
10190 static void
10191 oo_chart_wall (GsfXMLIn *xin, xmlChar const **attrs)
10193 OOParseState *state = (OOParseState *)xin->user_state;
10194 GogObject *backplane;
10195 gchar const *style_name = NULL;
10197 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
10198 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name"))
10199 style_name = CXML2C (attrs[1]);
10201 backplane = gog_object_add_by_name (GOG_OBJECT (state->chart.chart), "Backplane", NULL);
10203 if (style_name != NULL && backplane != NULL) {
10204 GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (backplane));
10205 if (style != NULL) {
10206 OOChartStyle *chart_style = g_hash_table_lookup
10207 (state->chart.graph_styles, style_name);
10208 style = go_style_dup (style);
10209 if (chart_style)
10210 odf_apply_style_props (xin, chart_style->style_props, style, TRUE);
10211 else
10212 oo_warning (xin, _("Chart style with name '%s' is missing."),
10213 style_name);
10214 go_styled_object_set_style (GO_STYLED_OBJECT (backplane), style);
10215 g_object_unref (style);
10220 static void
10221 oo_chart_axisline (GsfXMLIn *xin, xmlChar const **attrs)
10223 OOParseState *state = (OOParseState *)xin->user_state;
10224 gchar const *style_name = NULL;
10225 GogObject *axisline;
10227 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
10228 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CHART, "style-name"))
10229 style_name = CXML2C (attrs[1]);
10231 axisline = gog_object_add_by_name (GOG_OBJECT (state->chart.axis), "AxisLine", NULL);
10233 if (style_name != NULL && axisline != NULL) {
10234 GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (axisline));
10235 if (style != NULL) {
10236 OOChartStyle *chart_style = g_hash_table_lookup
10237 (state->chart.graph_styles, style_name);
10238 style = go_style_dup (style);
10239 if (chart_style) {
10240 oo_prop_list_apply_to_axisline (xin, chart_style->axis_props,
10241 G_OBJECT (axisline));
10242 odf_apply_style_props (xin, chart_style->style_props, style, TRUE);
10243 } else
10244 oo_warning (xin, _("Chart style with name '%s' is missing."),
10245 style_name);
10246 go_styled_object_set_style (GO_STYLED_OBJECT (axisline), style);
10247 g_object_unref (style);
10252 static void
10253 oo_chart_style_free (OOChartStyle *cstyle)
10255 if (cstyle == NULL)
10256 return;
10257 oo_prop_list_free (cstyle->axis_props);
10258 oo_prop_list_free (cstyle->style_props);
10259 oo_prop_list_free (cstyle->plot_props);
10260 oo_prop_list_free (cstyle->other_props);
10261 go_format_unref (cstyle->fmt);
10262 g_free (cstyle);
10265 static void
10266 oo_control_free (OOControl *ctrl)
10268 g_free (ctrl->value);
10269 g_free (ctrl->value_type);
10270 g_free (ctrl->label);
10271 g_free (ctrl->current_state);
10272 g_free (ctrl->linked_cell);
10273 g_free (ctrl->implementation);
10274 g_free (ctrl->source_cell_range);
10275 g_free (ctrl);
10278 static void
10279 oo_marker_free (OOMarker *m)
10281 g_free (m->view_box);
10282 g_free (m->d);
10283 g_free (m->arrow);
10284 g_free (m);
10287 static void
10288 odf_annotation_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
10290 OOParseState *state = (OOParseState *)xin->user_state;
10292 state->cell_comment = cell_set_comment (state->pos.sheet, &state->pos.eval,
10293 NULL, NULL, NULL);
10294 odf_push_text_p (state, FALSE);
10297 static void
10298 odf_annotation_author_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
10300 OOParseState *state = (OOParseState *)xin->user_state;
10302 cell_comment_author_set (state->cell_comment, xin->content->str);
10305 static void
10306 odf_annotation_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
10308 OOParseState *state = (OOParseState *)xin->user_state;
10309 oo_text_p_t *ptr;
10311 if (state->text_p_stack != NULL && (NULL != (ptr = state->text_p_stack->data)))
10312 g_object_set (G_OBJECT (state->cell_comment),
10313 "text", ptr->gstr ? ptr->gstr->str : "",
10314 "markup", ptr->attrs, NULL);
10315 state->cell_comment = NULL;
10316 odf_pop_text_p (state);
10319 /****************************************************************************/
10320 /******************************** graphic sheet objects *********************/
10322 static void
10323 odf_so_filled (GsfXMLIn *xin, xmlChar const **attrs, gboolean is_oval)
10325 OOParseState *state = (OOParseState *)xin->user_state;
10326 char const *style_name = NULL;
10327 GOStyle *style0;
10329 od_draw_frame_start (xin, attrs);
10330 state->chart.so = g_object_new (GNM_SO_FILLED_TYPE,
10331 "is-oval", is_oval, NULL);
10332 g_object_get (state->chart.so, "style", &style0, NULL);
10333 if (style0 != NULL) {
10334 GOStyle *style = go_style_dup (style0);
10335 if (state->default_style.graphics)
10336 odf_apply_style_props
10337 (xin, state->default_style.graphics->style_props,
10338 style, FALSE);
10340 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
10341 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10342 OO_NS_DRAW, "style-name"))
10343 style_name = CXML2C (attrs[1]);
10344 if (style_name != NULL) {
10345 OOChartStyle *oostyle = g_hash_table_lookup
10346 (state->chart.graph_styles, style_name);
10347 if (oostyle != NULL)
10348 odf_apply_style_props (xin, oostyle->style_props,
10349 style, FALSE);
10351 g_object_set (state->chart.so, "style", style, NULL);
10352 g_object_unref (style);
10353 g_object_unref (style0);
10357 static void
10358 odf_caption (GsfXMLIn *xin, xmlChar const **attrs)
10360 OOParseState *state = (OOParseState *)xin->user_state;
10362 oo_warning (xin, _("An unsupported caption was encountered and "
10363 "converted to a text rectangle."));
10364 odf_so_filled (xin, attrs, FALSE);
10365 odf_push_text_p (state, FALSE);
10368 static void
10369 odf_rect (GsfXMLIn *xin, xmlChar const **attrs)
10371 OOParseState *state = (OOParseState *)xin->user_state;
10373 odf_so_filled (xin, attrs, FALSE);
10374 odf_push_text_p (state, FALSE);
10377 static void
10378 odf_ellipse (GsfXMLIn *xin, xmlChar const **attrs)
10380 OOParseState *state = (OOParseState *)xin->user_state;
10382 odf_so_filled (xin, attrs, TRUE);
10383 odf_push_text_p (state, FALSE);
10386 static void
10387 odf_custom_shape_replace_object (OOParseState *state, SheetObject *so)
10389 GObjectClass *klass = G_OBJECT_GET_CLASS (G_OBJECT (so));
10391 if (NULL != g_object_class_find_property (klass, "text")) {
10392 char *text = NULL;
10393 g_object_get (state->chart.so, "text", &text, NULL);
10394 g_object_set (so, "text", text, NULL);
10395 g_free (text);
10397 if (NULL != g_object_class_find_property (klass, "style")) {
10398 GOStyle *style = NULL;
10399 g_object_get (state->chart.so, "style", &style, NULL);
10400 g_object_set (so, "style", style, NULL);
10401 g_object_unref (style);
10403 if (NULL != g_object_class_find_property (klass, "markup")) {
10404 PangoAttrList *attrs = NULL;
10405 g_object_get (state->chart.so, "markup", &attrs, NULL);
10406 g_object_set (so, "markup", attrs, NULL);
10407 pango_attr_list_unref (attrs);
10409 g_object_unref (state->chart.so);
10410 state->chart.so = so;
10413 static double
10414 odf_get_cs_formula_value (GsfXMLIn *xin, char const *key, GHashTable *vals, gint level)
10416 double *x = g_hash_table_lookup (vals, key);
10417 OOParseState *state = (OOParseState *)xin->user_state;
10418 gchar *formula, *o_formula;
10419 GString *gstr;
10420 GnmConventions const *convs = gnm_conventions_default;
10421 GnmExprTop const *texpr;
10422 GnmLocale *oldlocale = NULL;
10423 double x_ret = level;
10424 double viewbox_left = 0.;
10425 double viewbox_top = 0.;
10426 double viewbox_width = 0.;
10427 double viewbox_height = 0.;
10429 if (x)
10430 return *x;
10432 o_formula = formula = g_hash_table_lookup (state->chart.cs_variables, key);
10434 if (level < 0) {
10435 oo_warning (xin, _("Infinite loop encountered while parsing formula '%s' "
10436 "of name '%s'"),
10437 o_formula, key);
10438 return 0;
10441 g_return_val_if_fail (formula != NULL, level);
10443 if (state->chart.cs_viewbox) {
10445 Note:
10446 In ODF 1.2 part 1 19.570 svg:viewBox we have:
10447 "The syntax for using this attribute is the same as the [SVG] syntax.
10448 The value of the attribute are four numbers separated by white spaces,
10449 which define the left, top, right, and bottom dimensions of the user
10450 coordinate system."
10451 but [SVG] specifies:
10452 "The value of the viewBox attribute is a list of four numbers <min-x>,
10453 <min-y>, <width> and <height>"
10454 Since so far we have only seen cases with left == top == 0, We don't know which
10455 version is really used. We are implementing the [SVG] version.
10457 char *end = NULL;
10458 viewbox_left = go_strtod (state->chart.cs_viewbox, &end);
10459 viewbox_top = go_strtod (end, &end);
10460 viewbox_width = go_strtod (end, &end);
10461 viewbox_height = go_strtod (end, &end);
10464 gstr = g_string_new ("");
10466 while (*formula != 0) {
10467 gchar *here;
10468 gchar *name;
10469 double *val, fval;
10471 switch (*formula) {
10472 case ' ':
10473 case '\t':
10474 formula++;
10475 break;
10476 case '?':
10477 here = formula + 1;
10478 /* ODF 1.2 is quite clear that:
10479 --------------------------
10480 function_reference::= "?" name
10481 name::= [^#x20#x9]+
10482 --------------------------
10483 so we should grab all non-space, non-tab characters
10484 as a function_reference name.
10486 The problem is that LO creates files in which these
10487 function reference are not terminated by space or tab!
10489 So if we want to read them correctly we should only use
10490 alphanumerics...
10493 /* while (*here != ' ' && *here != '\t') */
10494 /* here++; */
10496 while (g_ascii_isalnum (*here))
10497 here++;
10499 name = g_strndup (formula, here - formula);
10500 formula = here;
10501 fval = odf_get_cs_formula_value (xin, name, vals, level - 1);
10502 g_string_append_printf (gstr, "%.12" GNM_FORMAT_g , fval);
10503 g_free (name);
10504 break;
10505 case '$':
10506 here = formula + 1;
10507 while (g_ascii_isdigit (*here))
10508 here++;
10509 name = g_strndup (formula, here - formula);
10510 formula = here;
10511 val = g_hash_table_lookup (vals, name);
10512 g_free (name);
10513 if (val == NULL)
10514 g_string_append_c (gstr, '0');
10515 else
10516 g_string_append_printf (gstr, "%.12" GNM_FORMAT_g, *val);
10517 break;
10518 case 'p':
10519 if (g_str_has_prefix (formula, "pi")) {
10520 formula += 2;
10521 g_string_append (gstr, "pi()");
10522 } else {
10523 g_string_append_c (gstr, *formula);
10524 formula++;
10526 break;
10527 case 't':
10528 if (g_str_has_prefix (formula, "top")) {
10529 formula += 3;
10530 g_string_append_printf (gstr, "%.12" GNM_FORMAT_g, viewbox_top);
10531 } else {
10532 g_string_append_c (gstr, *formula);
10533 formula++;
10535 break;
10536 case 'b':
10537 if (g_str_has_prefix (formula, "bottom")) {
10538 formula += 6;
10539 g_string_append_printf (gstr, "%.12" GNM_FORMAT_g,
10540 viewbox_top + viewbox_height);
10541 } else {
10542 g_string_append_c (gstr, *formula);
10543 formula++;
10545 break;
10546 case 'l':
10547 if (g_str_has_prefix (formula, "left")) {
10548 formula += 4;
10549 g_string_append_printf (gstr, "%.12" GNM_FORMAT_g, viewbox_left);
10550 } else {
10551 g_string_append_c (gstr, *formula);
10552 formula++;
10554 break;
10555 case 'r':
10556 if (g_str_has_prefix (formula, "right")) {
10557 formula += 5;
10558 g_string_append_printf (gstr, "%.12" GNM_FORMAT_g,
10559 viewbox_left + viewbox_width);
10560 } else {
10561 g_string_append_c (gstr, *formula);
10562 formula++;
10564 break;
10565 case 'h':
10566 if (g_str_has_prefix (formula, "height")) {
10567 formula += 6;
10568 g_string_append_printf (gstr, "%.12" GNM_FORMAT_g, viewbox_height);
10569 } else {
10570 g_string_append_c (gstr, *formula);
10571 formula++;
10573 break;
10574 case 'w':
10575 if (g_str_has_prefix (formula, "width")) {
10576 formula += 5;
10577 g_string_append_printf (gstr, "%.12" GNM_FORMAT_g, viewbox_width);
10578 } else {
10579 g_string_append_c (gstr, *formula);
10580 formula++;
10582 break;
10585 /* The ODF specs says (in ODF 1.2 part 1 item 19.171):
10586 "sin(n) returns the trigonometric sine of n, where n is an angle
10587 specified in degrees"
10588 but LibreOffice clearly uses sin(n) with n in radians
10590 /* case 'c': */
10591 /* if (g_str_has_prefix (formula, "cos(")) { */
10592 /* formula += 4; */
10593 /* /\* FIXME: this does not work in general, eg. if the argument *\/ */
10594 /* /\* to cos is a sum *\/ */
10595 /* g_string_append (gstr, "cos(pi()/180*"); */
10596 /* } else { */
10597 /* g_string_append_c (gstr, *formula); */
10598 /* formula++; */
10599 /* } */
10600 /* break; */
10601 /* case 's': */
10602 /* if (g_str_has_prefix (formula, "sin(")) { */
10603 /* formula += 4; */
10604 /* /\* FIXME: this does not work in general, eg. if the argument *\/ */
10605 /* /\* to sin is a sum *\/ */
10606 /* g_string_append (gstr, "sin(pi()/180*"); */
10607 /* } else { */
10608 /* g_string_append_c (gstr, *formula); */
10609 /* formula++; */
10610 /* } */
10611 /* break; */
10612 default:
10613 g_string_append_c (gstr, *formula);
10614 formula++;
10615 break;
10619 oldlocale = gnm_push_C_locale ();
10620 texpr = gnm_expr_parse_str (gstr->str, &state->pos,
10621 GNM_EXPR_PARSE_DEFAULT,
10622 convs,
10623 NULL);
10624 gnm_pop_C_locale (oldlocale);
10626 if (texpr) {
10627 GnmEvalPos ep;
10628 GnmValue *val;
10629 eval_pos_init_sheet (&ep, state->pos.sheet);
10630 val = gnm_expr_top_eval
10631 (texpr, &ep, GNM_EXPR_EVAL_PERMIT_NON_SCALAR);
10632 if (VALUE_IS_NUMBER (val)) {
10633 x_ret = value_get_as_float (val);
10634 x = g_new (double, 1);
10635 *x = x_ret;
10636 g_hash_table_insert (vals, g_strdup (key), x);
10637 } else
10638 oo_warning (xin, _("Unable to evaluate formula '%s' ('%s') of name '%s'"),
10639 o_formula, gstr->str, key);
10640 value_release (val);
10641 gnm_expr_top_unref (texpr);
10642 } else
10643 oo_warning (xin, _("Unable to parse formula '%s' ('%s') of name '%s'"),
10644 o_formula, gstr->str, key);
10645 g_string_free (gstr, TRUE);
10646 return x_ret;
10649 static void
10650 odf_custom_shape_end (GsfXMLIn *xin, GsfXMLBlob *blob)
10652 OOParseState *state = (OOParseState *)xin->user_state;
10653 GHashTable *vals = NULL;
10654 char **strs, **cur;
10655 GPtrArray *paths;
10657 if (state->chart.cs_variables || state->chart.cs_modifiers) {
10658 vals = g_hash_table_new_full
10659 (g_str_hash, g_str_equal,
10660 (GDestroyNotify) g_free, (GDestroyNotify) g_free);
10661 if (state->chart.cs_modifiers) {
10662 int i = 0;
10663 char *next = state->chart.cs_modifiers;
10665 while (*next != 0) {
10666 char *end = next;
10667 gnm_float x = gnm_strto (next, &end);
10668 if (end > next) {
10669 double *xp = g_new (double, 1);
10670 char *name = g_strdup_printf ("$%i", i);
10671 *xp = x;
10672 g_hash_table_insert (vals, name, xp);
10673 i++;
10674 while (*end == ' ')
10675 end++;
10676 next = end;
10677 } else break;
10680 if (state->chart.cs_variables) {
10681 GList *keys = g_hash_table_get_keys (state->chart.cs_variables);
10682 GList *l;
10683 gint level = g_hash_table_size (state->chart.cs_variables);
10684 for (l = keys; l != NULL; l = l->next)
10685 odf_get_cs_formula_value (xin, l->data, vals, level);
10686 g_list_free (keys);
10690 paths = g_ptr_array_new_with_free_func ((GDestroyNotify) go_path_free);
10692 if (state->chart.cs_enhanced_path != NULL) {
10693 strs = g_strsplit (state->chart.cs_enhanced_path, " N", 0);
10694 for (cur = strs; *cur != NULL; cur++) {
10695 GOPath *path;
10696 path = go_path_new_from_odf_enhanced_path (*cur, vals);
10697 if (path)
10698 g_ptr_array_add (paths, path);
10700 g_strfreev (strs);
10703 if (vals)
10704 g_hash_table_unref (vals);
10706 /* Note that we have already created a rectangle */
10708 if (paths->len == 1) {
10709 odf_custom_shape_replace_object
10710 (state, g_object_new (GNM_SO_PATH_TYPE,
10711 "path", g_ptr_array_index (paths, 0), NULL));
10712 } else if (paths->len > 1) {
10713 odf_custom_shape_replace_object
10714 (state, g_object_new (GNM_SO_PATH_TYPE,
10715 "paths", paths, NULL));
10716 } else if (state->chart.cs_type) {
10717 /* ignoring "ellipse" and "rectangle" since they will be handled by the GOPath */
10718 if (0 == g_ascii_strcasecmp (state->chart.cs_type, "frame") &&
10719 g_str_has_prefix (state->chart.cs_enhanced_path, "M ")) {
10720 odf_custom_shape_replace_object
10721 (state, g_object_new (GNM_SOW_FRAME_TYPE, NULL));
10722 } else if (0 == g_ascii_strcasecmp (state->chart.cs_type, "round-rectangle") ||
10723 0 == g_ascii_strcasecmp (state->chart.cs_type, "paper") ||
10724 0 == g_ascii_strcasecmp (state->chart.cs_type, "parallelogram") ||
10725 0 == g_ascii_strcasecmp (state->chart.cs_type, "trapezoid")) {
10726 /* We have already created the rectangle */
10727 oo_warning (xin , _("An unsupported custom shape of type '%s' was encountered and "
10728 "converted to a rectangle."), state->chart.cs_type);
10729 } else
10730 oo_warning (xin , _("An unsupported custom shape of type '%s' was encountered and "
10731 "converted to a rectangle."), state->chart.cs_type);
10732 } else
10733 oo_warning (xin , _("An unsupported custom shape was encountered and "
10734 "converted to a rectangle."));
10735 g_ptr_array_unref (paths);
10737 od_draw_text_frame_end (xin, blob);
10739 g_free (state->chart.cs_enhanced_path);
10740 g_free (state->chart.cs_modifiers);
10741 g_free (state->chart.cs_viewbox);
10742 g_free (state->chart.cs_type);
10743 state->chart.cs_enhanced_path = NULL;
10744 state->chart.cs_modifiers = NULL;
10745 state->chart.cs_viewbox = NULL;
10746 state->chart.cs_type = NULL;
10747 if (state->chart.cs_variables)
10748 g_hash_table_remove_all (state->chart.cs_variables);
10751 static void
10752 odf_custom_shape_equation (GsfXMLIn *xin, xmlChar const **attrs)
10754 OOParseState *state = (OOParseState *)xin->user_state;
10755 gchar const *name = NULL, *meaning = NULL;
10756 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
10757 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10758 OO_NS_DRAW, "name"))
10759 name = CXML2C (attrs[1]);
10760 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10761 OO_NS_DRAW, "formula"))
10762 meaning = CXML2C (attrs[1]);
10763 if (name && meaning) {
10764 if (state->chart.cs_variables == NULL)
10765 state->chart.cs_variables = g_hash_table_new_full
10766 (g_str_hash, g_str_equal,
10767 (GDestroyNotify) g_free, (GDestroyNotify) g_free);
10768 g_hash_table_insert (state->chart.cs_variables,
10769 g_strdup_printf ("?%s", name), g_strdup (meaning));
10773 static void
10774 odf_custom_shape (GsfXMLIn *xin, xmlChar const **attrs)
10776 OOParseState *state = (OOParseState *)xin->user_state;
10778 /* to avoid spill over */
10779 g_free (state->chart.cs_enhanced_path);
10780 g_free (state->chart.cs_type);
10781 state->chart.cs_enhanced_path = NULL;
10782 state->chart.cs_type = NULL;
10784 odf_so_filled (xin, attrs, FALSE);
10785 odf_push_text_p (state, FALSE);
10788 static void
10789 odf_custom_shape_enhanced_geometry (GsfXMLIn *xin, xmlChar const **attrs)
10791 OOParseState *state = (OOParseState *)xin->user_state;
10793 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
10794 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10795 OO_NS_DRAW, "type"))
10796 state->chart.cs_type = g_strdup (CXML2C (attrs[1]));
10797 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10798 OO_NS_DRAW, "enhanced-path"))
10799 state->chart.cs_enhanced_path = g_strdup (CXML2C (attrs[1]));
10800 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10801 OO_NS_DRAW, "modifiers"))
10802 state->chart.cs_modifiers = g_strdup (CXML2C (attrs[1]));
10803 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10804 OO_NS_SVG, "viewBox"))
10805 state->chart.cs_viewbox = g_strdup (CXML2C (attrs[1]));
10808 static GOArrow *
10809 odf_get_arrow_marker (OOParseState *state, char const *name)
10811 OOMarker *m = g_hash_table_lookup (state->chart.arrow_markers, name);
10813 if (m != NULL) {
10814 if (m->arrow == NULL) {
10815 m->arrow = g_new0 (GOArrow, 1);
10816 go_arrow_init_kite (m->arrow, 8, 10, 3);
10818 return go_arrow_dup (m->arrow);
10819 } else {
10820 GOArrow *arrow = g_new0 (GOArrow, 1);
10821 go_arrow_init_kite (arrow, 8, 10, 3);
10822 return arrow;
10826 static void
10827 odf_line (GsfXMLIn *xin, xmlChar const **attrs)
10829 OOParseState *state = (OOParseState *)xin->user_state;
10830 gnm_float x1 = 0., x2 = 0., y1 = 0., y2 = 0.;
10831 GODrawingAnchorDir direction;
10832 GnmRange cell_base;
10833 double frame_offset[4];
10834 char const *style_name = NULL;
10835 gdouble height, width;
10836 int z = -1;
10837 GnmSOAnchorMode mode;
10838 cell_base.start.col = state->pos.eval.col;
10839 cell_base.start.row = state->pos.eval.row;
10840 cell_base.end.col = cell_base.end.row = -1;
10842 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
10843 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10844 OO_NS_DRAW, "style-name"))
10845 style_name = CXML2C (attrs[1]);
10846 else if (NULL != oo_attr_distance (xin, attrs,
10847 OO_NS_SVG, "x1",
10848 &x1));
10849 else if (NULL != oo_attr_distance (xin, attrs,
10850 OO_NS_SVG, "x2",
10851 &x2));
10852 else if (NULL != oo_attr_distance (xin, attrs,
10853 OO_NS_SVG, "y1",
10854 &y1));
10855 else if (NULL != oo_attr_distance (xin, attrs,
10856 OO_NS_SVG, "y2",
10857 &y2));
10858 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
10859 OO_NS_TABLE, "end-cell-address")) {
10860 GnmParsePos pp;
10861 GnmRangeRef ref;
10862 char const *ptr = oo_rangeref_parse
10863 (&ref, CXML2C (attrs[1]),
10864 parse_pos_init_sheet (&pp, state->pos.sheet),
10865 NULL);
10866 if (ptr != CXML2C (attrs[1])
10867 && ref.a.sheet != invalid_sheet) {
10868 cell_base.end.col = ref.a.col;
10869 cell_base.end.row = ref.a.row;
10871 } else if (oo_attr_int_range (xin,attrs, OO_NS_DRAW, "z-index",
10872 &z, 0, G_MAXINT))
10875 if (x1 < x2) {
10876 if (y1 < y2)
10877 direction = GOD_ANCHOR_DIR_DOWN_RIGHT;
10878 else
10879 direction = GOD_ANCHOR_DIR_UP_RIGHT;
10880 frame_offset[0] = x1;
10881 frame_offset[2] = x2;
10882 width = x2 - x1;
10883 } else {
10884 if (y1 < y2)
10885 direction = GOD_ANCHOR_DIR_DOWN_LEFT;
10886 else
10887 direction = GOD_ANCHOR_DIR_UP_LEFT;
10888 frame_offset[0] = x2;
10889 frame_offset[2] = x1;
10890 width = x1 - x2;
10892 if (y1 < y2) {
10893 frame_offset[1] = y1;
10894 frame_offset[3] = y2;
10895 height = y2 - y1;
10896 } else {
10897 frame_offset[1] = y2;
10898 frame_offset[3] = y1;
10899 height = y1 - y2;
10902 if (state->pos.eval.col >= 0) {
10903 if (cell_base.end.col >= 0) {
10904 mode = GNM_SO_ANCHOR_TWO_CELLS;
10905 } else {
10906 cell_base.end.col = cell_base.start.col;
10907 cell_base.end.row = cell_base.start.row;
10908 frame_offset[2] = width;
10909 frame_offset[3] = height;
10910 mode = GNM_SO_ANCHOR_ONE_CELL;
10912 } else {
10913 cell_base.end.col = cell_base.start.col =
10914 cell_base.end.row = cell_base.start.row = 0; /* actually not needed */
10915 frame_offset[2] = width;
10916 frame_offset[3] = height;
10917 mode = GNM_SO_ANCHOR_ABSOLUTE;
10920 odf_draw_frame_store_location (state, frame_offset,
10921 height, width);
10923 sheet_object_anchor_init (&state->chart.anchor, &cell_base,
10924 frame_offset,
10925 direction,
10926 mode);
10927 state->chart.so = g_object_new (GNM_SO_LINE_TYPE, NULL);
10929 if (style_name != NULL) {
10930 OOChartStyle *oostyle = g_hash_table_lookup
10931 (state->chart.graph_styles, style_name);
10932 if (oostyle != NULL) {
10933 GOStyle *style0;
10934 char const *start_marker = NULL;
10935 char const *end_marker = NULL;
10936 GSList *l;
10938 g_object_get (state->chart.so, "style", &style0, NULL);
10939 if (style0 != NULL) {
10940 GOStyle *style = go_style_dup (style0);
10941 odf_apply_style_props (xin, oostyle->style_props,
10942 style, FALSE);
10943 g_object_set (state->chart.so, "style", style, NULL);
10944 g_object_unref (style);
10945 g_object_unref (style0);
10948 for (l = oostyle->other_props; l != NULL; l = l->next) {
10949 OOProp *prop = l->data;
10950 if (0 == strcmp ("marker-start", prop->name))
10951 start_marker = g_value_get_string (&prop->value);
10952 else if (0 == strcmp ("marker-end", prop->name))
10953 end_marker = g_value_get_string (&prop->value);
10956 if (start_marker != NULL) {
10957 GOArrow *arrow = odf_get_arrow_marker (state, start_marker);
10959 if (arrow != NULL) {
10960 g_object_set (G_OBJECT (state->chart.so),
10961 "start-arrow", arrow, NULL);
10962 g_free (arrow);
10965 if (end_marker != NULL) {
10966 GOArrow *arrow = odf_get_arrow_marker (state, end_marker);
10968 if (arrow != NULL) {
10969 g_object_set (G_OBJECT (state->chart.so),
10970 "end-arrow", arrow, NULL);
10971 g_free (arrow);
10976 odf_push_text_p (state, FALSE);
10977 state->chart.z_index = z;
10980 /****************************************************************************/
10981 /******************************** controls ******************************/
10983 static void
10984 odf_form_control (GsfXMLIn *xin, xmlChar const **attrs, GType t)
10986 OOControl *oc = g_new0 (OOControl, 1);
10987 OOParseState *state = (OOParseState *)xin->user_state;
10988 char *name = NULL;
10989 static OOEnum const orientations [] = {
10990 { "vertical", 0},
10991 { "horizontal", 1},
10992 { NULL, 0 },
10994 static OOEnum const list_linkages [] = {
10995 { "selection", 0},
10996 { "selection-indexes", 1},
10997 { "selection-indices", 1},
10998 { NULL, 0 },
11000 int tmp;
11002 state->cur_control = NULL;
11003 oc->step = oc->page_step = 1;
11004 oc->as_index = TRUE;
11006 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11007 /* ODF does not declare an xml: namespace but uses this attribute */
11008 if (0 == strcmp (CXML2C (attrs[0]), "xml:id")) {
11009 g_free (name);
11010 name = g_strdup (CXML2C (attrs[1]));
11011 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11012 OO_NS_FORM, "id")) {
11013 if (name == NULL)
11014 name = g_strdup (CXML2C (attrs[1]));
11015 } else if (oo_attr_enum (xin, attrs, OO_NS_FORM, "orientation", orientations,
11016 &tmp))
11017 oc->horizontal = (tmp != 0);
11018 else if (oo_attr_int (xin, attrs, OO_NS_FORM, "min-value",
11019 &(oc->min)));
11020 else if (oo_attr_int (xin, attrs, OO_NS_FORM, "max-value",
11021 &(oc->max)));
11022 else if (oo_attr_int_range (xin, attrs, OO_NS_FORM, "step-size",
11023 &(oc->step), 0, INT_MAX));
11024 else if (oo_attr_int_range (xin, attrs, OO_NS_FORM, "page-step-size",
11025 &(oc->page_step), 0, INT_MAX));
11026 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11027 OO_NS_FORM, "value")) {
11028 g_free (oc->value);
11029 oc->value = g_strdup (CXML2C (attrs[1]));
11030 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11031 OO_GNUM_NS_EXT, "value-type")) {
11032 g_free (oc->value_type);
11033 oc->value_type = g_strdup (CXML2C (attrs[1]));
11034 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11035 OO_NS_FORM, "linked-cell")) {
11036 g_free (oc->linked_cell);
11037 oc->linked_cell = g_strdup (CXML2C (attrs[1]));
11038 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11039 OO_GNUM_NS_EXT, "linked-cell")) {
11040 g_free (oc->linked_cell);
11041 oc->linked_cell = g_strdup (CXML2C (attrs[1]));
11042 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11043 OO_NS_FORM, "current-state")) {
11044 g_free (oc->current_state);
11045 oc->current_state = g_strdup (CXML2C (attrs[1]));
11046 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11047 OO_NS_FORM, "current-selected")) {
11048 g_free (oc->current_state);
11049 oc->current_state = g_strdup (CXML2C (attrs[1]));
11050 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11051 OO_NS_FORM, "label")) {
11052 g_free (oc->label);
11053 oc->label = g_strdup (CXML2C (attrs[1]));
11054 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11055 OO_NS_FORM, "control-implementation")) {
11056 g_free (oc->implementation);
11057 oc->implementation = g_strdup (CXML2C (attrs[1]));
11058 } else if (oo_attr_enum (xin, attrs, OO_NS_FORM, "list-linkage-type", list_linkages,
11059 &tmp))
11060 oc->as_index = (tmp != 0);
11061 else if (oo_attr_enum (xin, attrs, OO_GNUM_NS_EXT, "list-linkage-type", list_linkages,
11062 &tmp))
11063 oc->as_index = (tmp != 0);
11064 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11065 OO_NS_FORM, "source-cell-range")) {
11066 g_free (oc->source_cell_range);
11067 oc->source_cell_range = g_strdup (CXML2C (attrs[1]));
11068 } else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11069 OO_GNUM_NS_EXT, "source-cell-range")) {
11070 if (oc->source_cell_range == NULL)
11071 oc->source_cell_range = g_strdup (CXML2C (attrs[1]));
11072 } else if (oo_attr_int (xin, attrs, OO_NS_FORM, "bound-column",
11073 &tmp)) {
11074 if (tmp != 1)
11075 oo_warning (xin, _("Attribute '%s' has "
11076 "the unsupported value '%s'."),
11077 "form:bound-column", CXML2C (attrs[1]));
11080 if (name != NULL) {
11081 if (oc->implementation != NULL &&
11082 t == sheet_widget_slider_get_type ()) {
11083 if (0 == strcmp (oc->implementation, "gnm:scrollbar"))
11084 oc->t = sheet_widget_scrollbar_get_type ();
11085 else if (0 == strcmp (oc->implementation,
11086 "gnm:spinbutton"))
11087 oc->t = sheet_widget_spinbutton_get_type ();
11088 else if (0 == strcmp (oc->implementation,
11089 "gnm:slider"))
11090 oc->t = sheet_widget_slider_get_type ();
11091 else if (0 == strcmp (oc->implementation,
11092 "ooo:com.sun.star.form."
11093 "component.ScrollBar"))
11094 oc->t = sheet_widget_scrollbar_get_type ();
11095 } else if (t == sheet_widget_frame_get_type ()) {
11096 if (oc->implementation == NULL ||
11097 0 != strcmp (oc->implementation, "gnm:frame")) {
11098 oo_control_free (oc);
11099 return;
11100 } else
11101 oc->t = t;
11102 } else
11103 oc->t = t;
11104 g_hash_table_replace (state->controls, name, oc);
11105 } else {
11106 oo_control_free (oc);
11107 return;
11110 if (t == sheet_widget_button_get_type () ||
11111 t == sheet_widget_frame_get_type ())
11112 state->cur_control = oc;
11116 static void
11117 odf_form_value_range (GsfXMLIn *xin, xmlChar const **attrs)
11120 odf_form_control (xin, attrs, sheet_widget_slider_get_type ());
11123 static void
11124 odf_form_checkbox (GsfXMLIn *xin, xmlChar const **attrs)
11126 odf_form_control (xin, attrs, sheet_widget_checkbox_get_type ());
11129 static void
11130 odf_form_radio (GsfXMLIn *xin, xmlChar const **attrs)
11132 odf_form_control (xin, attrs, sheet_widget_radio_button_get_type ());
11135 static void
11136 odf_form_generic (GsfXMLIn *xin, xmlChar const **attrs)
11138 odf_form_control (xin, attrs, sheet_widget_frame_get_type ());
11141 static void
11142 odf_form_listbox (GsfXMLIn *xin, xmlChar const **attrs)
11144 odf_form_control (xin, attrs, sheet_widget_list_get_type ());
11147 static void
11148 odf_form_combobox (GsfXMLIn *xin, xmlChar const **attrs)
11150 odf_form_control (xin, attrs, sheet_widget_combo_get_type ());
11153 static void
11154 odf_form_button (GsfXMLIn *xin, xmlChar const **attrs)
11156 odf_form_control (xin, attrs, sheet_widget_button_get_type ());
11159 static void
11160 odf_form_control_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
11162 OOParseState *state = (OOParseState *)xin->user_state;
11163 state->cur_control = NULL;
11166 static void
11167 odf_button_event_listener (GsfXMLIn *xin, xmlChar const **attrs)
11169 OOParseState *state = (OOParseState *)xin->user_state;
11170 char const *event_name = NULL;
11171 char const *language = NULL;
11172 char const *macro_name = NULL;
11174 if (state->cur_control == NULL)
11175 return;
11177 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11178 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11179 OO_NS_SCRIPT, "event-name"))
11180 event_name = CXML2C (attrs[1]);
11181 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11182 OO_NS_SCRIPT, "language"))
11183 language = CXML2C (attrs[1]);
11184 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11185 OO_NS_SCRIPT, "macro-name"))
11186 macro_name = CXML2C (attrs[1]);
11188 if (event_name && (0 == strcmp (event_name, "dom:mousedown")) &&
11189 language && (0 == strcmp (language, "gnm:short-macro")) &&
11190 g_str_has_prefix (macro_name, "set-to-TRUE:"))
11191 state->cur_control->linked_cell = g_strdup (macro_name + 12);
11194 static void
11195 odf_control_property (GsfXMLIn *xin, xmlChar const **attrs)
11197 OOParseState *state = (OOParseState *)xin->user_state;
11198 char const *property_name = NULL;
11199 char const *value = NULL;
11201 if (state->cur_control == NULL)
11202 return;
11204 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11205 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11206 OO_NS_FORM, "property-name"))
11207 property_name = CXML2C (attrs[1]);
11208 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11209 OO_NS_OFFICE, "string-value"))
11210 value = CXML2C (attrs[1]);
11212 if ((property_name != NULL) &&
11213 (0 == strcmp (property_name, "gnm:label")) &&
11214 (NULL != value))
11215 state->cur_control->label = g_strdup (value);
11218 static void
11219 odf_selection_range (GsfXMLIn *xin, xmlChar const **attrs)
11221 OOParseState *state = (OOParseState *)xin->user_state;
11222 GnmRange r;
11223 if (odf_attr_range (xin, attrs, state->pos.sheet, &r))
11224 sv_selection_add_range (sheet_get_view (state->pos.sheet, state->wb_view), &r);
11227 static void
11228 odf_selection (GsfXMLIn *xin, xmlChar const **attrs)
11230 OOParseState *state = (OOParseState *)xin->user_state;
11231 Sheet *sheet = state->pos.sheet;
11232 int col = -1, row = -1;
11234 sv_selection_reset (sheet_get_view (sheet, state->wb_view));
11236 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11237 if (oo_attr_int_range
11238 (xin, attrs, OO_GNUM_NS_EXT, "cursor-col", &col,
11239 0, gnm_sheet_get_last_col(sheet))) {
11240 } else if (oo_attr_int_range
11241 (xin, attrs, OO_GNUM_NS_EXT, "cursor-row", &row,
11242 0, gnm_sheet_get_last_row(sheet))) {};
11244 state->pos.eval.col = col;
11245 state->pos.eval.row = row;
11248 static void
11249 odf_selection_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
11251 OOParseState *state = (OOParseState *)xin->user_state;
11253 gnm_sheet_view_set_edit_pos (sheet_get_view (state->pos.sheet, state->wb_view), &state->pos.eval);
11258 /****************************************************************************/
11259 /******************************** settings.xml ******************************/
11261 static void
11262 destroy_gvalue (gpointer data)
11264 g_value_unset (data);
11265 g_free (data);
11268 static void
11269 odf_config_item_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
11271 OOParseState *state = (OOParseState *)xin->user_state;
11272 GHashTable *parent_hash;
11274 if (state->settings.stack == NULL)
11275 parent_hash = state->settings.settings;
11276 else
11277 parent_hash = state->settings.stack->data;
11279 if (parent_hash != NULL && state->settings.config_item_name != NULL) {
11280 GValue *val = NULL;
11281 switch (state->settings.type) {
11282 case G_TYPE_BOOLEAN: {
11283 gboolean b = (g_ascii_strcasecmp (xin->content->str, "false") &&
11284 strcmp (xin->content->str, "0"));
11285 val = g_value_init (g_new0 (GValue, 1), G_TYPE_BOOLEAN);
11286 g_value_set_boolean (val, b);
11287 break;
11289 case G_TYPE_INT: {
11290 long n;
11291 char *end;
11293 errno = 0; /* strtol sets errno, but does not clear it. */
11294 n = strtol (xin->content->str, &end, 10);
11295 if (!(*end || errno != 0 || n < INT_MIN || n > INT_MAX)) {
11296 val = g_value_init (g_new0 (GValue, 1), G_TYPE_INT);
11297 g_value_set_int (val, (int)n);
11299 break;
11301 case G_TYPE_LONG: {
11302 long n;
11303 char *end;
11305 errno = 0; /* strtol sets errno, but does not clear it. */
11306 n = strtol (xin->content->str, &end, 10);
11307 if (!(*end || errno != 0)) {
11308 val = g_value_init (g_new0 (GValue, 1), G_TYPE_LONG);
11309 g_value_set_long (val, n);
11311 break;
11313 case G_TYPE_STRING:
11314 val = g_value_init (g_new0 (GValue, 1), G_TYPE_STRING);
11315 g_value_set_string (val, (xin->content->str));
11316 break;
11317 default:
11318 break;
11320 if (val != NULL)
11321 g_hash_table_replace
11322 (parent_hash, g_strdup (state->settings.config_item_name),
11323 val);
11326 g_free (state->settings.config_item_name);
11327 state->settings.config_item_name = NULL;
11331 static void
11332 odf_config_item (GsfXMLIn *xin, xmlChar const **attrs)
11334 static OOEnum const config_types [] = {
11335 {"base64Binary", G_TYPE_INVALID},
11336 {"boolean", G_TYPE_BOOLEAN},
11337 {"datetime", G_TYPE_INVALID},
11338 {"double", G_TYPE_INVALID},
11339 {"int", G_TYPE_INT},
11340 {"long", G_TYPE_LONG},
11341 {"short", G_TYPE_INT},
11342 {"string", G_TYPE_STRING},
11343 { NULL, 0},
11345 OOParseState *state = (OOParseState *)xin->user_state;
11347 state->settings.config_item_name = NULL;
11348 state->settings.type = G_TYPE_INVALID;
11350 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
11351 int i;
11353 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CONFIG, "name"))
11354 state->settings.config_item_name = g_strdup (CXML2C (attrs[1]));
11355 else if (oo_attr_enum (xin, attrs, OO_NS_CONFIG, "type", config_types, &i))
11356 state->settings.type = i;
11360 static void
11361 odf_config_stack_pop (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
11363 OOParseState *state = (OOParseState *)xin->user_state;
11365 g_return_if_fail (state->settings.stack != NULL);
11367 g_hash_table_unref (state->settings.stack->data);
11368 state->settings.stack = g_slist_delete_link
11369 (state->settings.stack, state->settings.stack);
11372 static void
11373 odf_config_item_set (GsfXMLIn *xin, xmlChar const **attrs)
11375 OOParseState *state = (OOParseState *)xin->user_state;
11376 GHashTable *set = g_hash_table_new_full (g_str_hash, g_str_equal,
11377 (GDestroyNotify) g_free,
11378 (GDestroyNotify) destroy_gvalue);
11379 GHashTable *parent_hash;
11380 gchar *name = NULL;
11381 GValue *val;
11383 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11384 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_CONFIG, "name"))
11385 name = g_strdup (CXML2C (attrs[1]));
11387 if (state->settings.stack == NULL)
11388 parent_hash = state->settings.settings;
11389 else
11390 parent_hash = state->settings.stack->data;
11392 if (name == NULL) {
11393 int i = 0;
11394 do {
11395 g_free (name);
11396 name = g_strdup_printf ("Unnamed_Config_Set-%i", i++);
11397 } while (NULL != g_hash_table_lookup (parent_hash, name));
11400 state->settings.stack = g_slist_prepend (state->settings.stack, set);
11402 val = g_value_init (g_new0 (GValue, 1), G_TYPE_HASH_TABLE);
11403 g_value_set_boxed (val, set);
11405 g_hash_table_replace (parent_hash, name, val);
11408 static void
11409 dump_settings_hash (char const *key, GValue *val, char const *prefix)
11411 gchar *content = g_strdup_value_contents (val);
11412 g_print ("%s Settings \'%s\' has \'%s\'\n", prefix, key, content);
11413 g_free (content);
11415 if (G_VALUE_HOLDS(val,G_TYPE_HASH_TABLE)) {
11416 char *pre = g_strconcat (prefix, ">>", NULL);
11417 GHashTable *hash = g_value_get_boxed (val);
11418 g_hash_table_foreach (hash, (GHFunc)dump_settings_hash, pre);
11419 g_free (pre);
11423 static gboolean
11424 odf_created_by_gnumeric (OOParseState *state)
11426 GsfDocMetaData *meta_data = go_doc_get_meta_data (GO_DOC (state->pos.wb));
11427 GsfDocProp *prop = gsf_doc_meta_data_lookup (meta_data,
11428 "meta:generator");
11429 char const *str;
11431 return (prop != NULL &&
11432 (NULL != (str = g_value_get_string
11433 (gsf_doc_prop_get_val (prop)))) &&
11434 g_str_has_prefix (str, "gnumeric"));
11437 static gboolean
11438 odf_has_gnm_foreign (OOParseState *state)
11440 GValue *val;
11441 if ((state->settings.settings != NULL) &&
11442 NULL != (val = g_hash_table_lookup (state->settings.settings, "gnm:settings")) &&
11443 G_VALUE_HOLDS(val,G_TYPE_HASH_TABLE)) {
11444 GHashTable *hash = g_value_get_boxed (val);
11445 val = g_hash_table_lookup (hash, "gnm:has_foreign");
11446 if (val != NULL && G_VALUE_HOLDS(val, G_TYPE_BOOLEAN))
11447 return g_value_get_boolean (val);
11449 return FALSE;
11452 static void
11453 odf_apply_gnm_config (OOParseState *state)
11455 GValue *val;
11456 if ((state->settings.settings != NULL) &&
11457 NULL != (val = g_hash_table_lookup (state->settings.settings, "gnm:settings")) &&
11458 G_VALUE_HOLDS(val,G_TYPE_HASH_TABLE)) {
11459 int width = 0, height = 0;
11460 GHashTable *hash = g_value_get_boxed (val);
11461 val = g_hash_table_lookup (hash, "gnm:active-sheet");
11462 if (val != NULL && G_VALUE_HOLDS(val, G_TYPE_STRING)) {
11463 const gchar *name = g_value_get_string (val);
11464 Sheet *sheet = workbook_sheet_by_name (state->pos.wb, name);
11465 if (sheet != NULL)
11466 wb_view_sheet_focus (state->wb_view, sheet);
11468 val = g_hash_table_lookup (hash, "gnm:geometry-width");
11469 if (val != NULL && G_VALUE_HOLDS(val, G_TYPE_INT)) {
11470 width = g_value_get_int (val);
11472 val = g_hash_table_lookup (hash, "gnm:geometry-height");
11473 if (val != NULL && G_VALUE_HOLDS(val, G_TYPE_INT)) {
11474 height = g_value_get_int (val);
11476 if (width > 0 && height > 0)
11477 wb_view_preferred_size (state->wb_view, width, height);
11481 static void
11482 odf_apply_ooo_table_config (char const *key, GValue *val, OOParseState *state)
11484 if (G_VALUE_HOLDS(val,G_TYPE_HASH_TABLE)) {
11485 GHashTable *hash = g_value_get_boxed (val);
11486 Sheet *sheet = workbook_sheet_by_name (state->pos.wb, key);
11488 if (hash != NULL && sheet != NULL) {
11489 SheetView *sv = sheet_get_view (sheet, state->wb_view);
11490 GnmCellPos pos;
11491 GValue *item;
11492 int pos_left = 0, pos_bottom = 0;
11493 int vsm = 0, hsm = 0, vsp = -1, hsp = -1;
11495 if (!odf_has_gnm_foreign (state)) {
11496 item = g_hash_table_lookup (hash, "TabColor");
11497 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_INT)) {
11498 GOColor color = g_value_get_int (item);
11499 color = color << 8;
11500 sheet->tab_color = gnm_color_new_go (color);
11502 item = g_hash_table_lookup (hash, "CursorPositionX");
11503 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_INT)) {
11504 GValue *itemy = g_hash_table_lookup (hash, "CursorPositionY");
11505 if (itemy != NULL && G_VALUE_HOLDS(itemy, G_TYPE_INT)) {
11506 GnmRange r;
11507 pos.col = g_value_get_int (item);
11508 pos.row = g_value_get_int (itemy);
11509 r.start = pos;
11510 r.end = pos;
11512 sv_selection_reset (sv);
11513 sv_selection_add_range (sv, &r);
11514 gnm_sheet_view_set_edit_pos
11515 (sheet_get_view (sheet, state->wb_view),
11516 &pos);
11519 item = g_hash_table_lookup (hash, "HasColumnRowHeaders");
11520 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_BOOLEAN)) {
11521 gboolean val = g_value_get_boolean (item);
11522 g_object_set (sheet, "display-row-header", val, NULL);
11523 g_object_set (sheet, "display-column-header", val, NULL);
11527 item = g_hash_table_lookup (hash, "ShowGrid");
11528 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_BOOLEAN))
11529 g_object_set (sheet, "display-grid", g_value_get_boolean (item), NULL);
11531 item = g_hash_table_lookup (hash, "ShowZeroValues");
11532 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_BOOLEAN))
11533 g_object_set (sheet, "display-zeros", g_value_get_boolean (item), NULL);
11535 item = g_hash_table_lookup (hash, "ZoomValue");
11536 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_INT))
11537 g_object_set (sheet, "zoom-factor", g_value_get_int (item)/100., NULL);
11539 item = g_hash_table_lookup (hash, "HorizontalSplitMode");
11540 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_INT))
11541 vsm = g_value_get_int (item);
11542 item = g_hash_table_lookup (hash, "VerticalSplitMode");
11543 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_INT))
11544 hsm = g_value_get_int (item);
11546 if (vsm > 0 || hsm > 0) {
11547 item = g_hash_table_lookup (hash, "VerticalSplitPosition");
11548 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_INT))
11549 vsp = g_value_get_int (item);
11550 item = g_hash_table_lookup (hash, "HorizontalSplitPosition");
11551 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_INT))
11552 hsp = g_value_get_int (item);
11553 if (vsp > 0 || hsp > 0) {
11554 GnmCellPos fpos = {0, 0};
11555 pos.col = hsp;
11556 pos.row = vsp;
11557 gnm_sheet_view_freeze_panes (sv, &fpos, &pos);
11560 item = g_hash_table_lookup (hash, "PositionRight");
11561 } else {
11562 item = g_hash_table_lookup (hash, "PositionLeft");
11564 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_INT))
11565 pos_left = g_value_get_int (item);
11567 item = g_hash_table_lookup (hash, "PositionBottom");
11568 if (item != NULL && G_VALUE_HOLDS(item, G_TYPE_INT))
11569 pos_bottom = g_value_get_int (item);
11571 gnm_sheet_view_set_initial_top_left (sv, pos_left, pos_bottom);
11576 static void
11577 odf_apply_ooo_config (OOParseState *state)
11579 GValue *val;
11580 GHashTable *hash;
11582 if ((state->settings.settings == NULL) ||
11583 NULL == (val = g_hash_table_lookup (state->settings.settings, "ooo:view-settings")) ||
11584 !G_VALUE_HOLDS(val,G_TYPE_HASH_TABLE))
11585 return;
11586 hash = g_value_get_boxed (val);
11588 if ((hash == NULL) ||
11589 NULL == (val = g_hash_table_lookup (hash, "Views")) ||
11590 !G_VALUE_HOLDS(val,G_TYPE_HASH_TABLE))
11591 return;
11592 hash = g_value_get_boxed (val);
11594 if ((hash == NULL) ||
11595 NULL == (val = g_hash_table_lookup (hash, "Unnamed_Config_Set-0")) ||
11596 !G_VALUE_HOLDS(val,G_TYPE_HASH_TABLE))
11597 return;
11598 hash = g_value_get_boxed (val);
11600 if (hash == NULL)
11601 return;
11603 val = g_hash_table_lookup (hash, "ActiveTable");
11605 if (NULL != val && G_VALUE_HOLDS(val,G_TYPE_STRING)) {
11606 const gchar *name = g_value_get_string (val);
11607 Sheet *sheet = workbook_sheet_by_name (state->pos.wb, name);
11608 if (sheet != NULL)
11609 wb_view_sheet_focus (state->wb_view, sheet);
11612 if (NULL == (val = g_hash_table_lookup (hash, "Tables")) ||
11613 !G_VALUE_HOLDS(val,G_TYPE_HASH_TABLE))
11614 return;
11615 hash = g_value_get_boxed (val);
11617 if (hash == NULL)
11618 return;
11620 g_hash_table_foreach (hash, (GHFunc) odf_apply_ooo_table_config, state);
11623 /**************************************************************************/
11625 static void
11626 oo_marker (GsfXMLIn *xin, xmlChar const **attrs)
11628 OOParseState *state = (OOParseState *)xin->user_state;
11629 OOMarker *marker = g_new0 (OOMarker, 1);
11630 int type = GO_ARROW_NONE;
11631 double a = 0., b = 0., c = 0.;
11632 char const *name = NULL;
11634 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11635 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11636 OO_NS_DRAW, "name"))
11637 name = CXML2C (attrs[1]);
11638 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11639 OO_NS_SVG, "viewBox"))
11640 marker->view_box = g_strdup (CXML2C (attrs[1]));
11641 else if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]),
11642 OO_NS_SVG, "d"))
11643 marker->d = g_strdup (CXML2C (attrs[1]));
11644 else if (oo_attr_int_range (xin, attrs, OO_GNUM_NS_EXT, "arrow-type", &type,
11645 GO_ARROW_KITE, GO_ARROW_OVAL));
11646 else if (oo_attr_float (xin, attrs, OO_GNUM_NS_EXT,
11647 "arrow-a", &a));
11648 else if (oo_attr_float (xin, attrs, OO_GNUM_NS_EXT,
11649 "arrow-b", &b));
11650 else if (oo_attr_float (xin, attrs, OO_GNUM_NS_EXT,
11651 "arrow-c", &c));
11652 if (type != GO_ARROW_NONE) {
11653 marker->arrow = g_new0 (GOArrow, 1);
11654 go_arrow_init (marker->arrow, type, a, b, c);
11656 if (name != NULL) {
11657 g_hash_table_replace (state->chart.arrow_markers,
11658 g_strdup (name), marker);
11659 } else
11660 oo_marker_free (marker);
11664 /****************** These are the preparse functions ***********************/
11666 static void
11667 odf_sheet_suggest_size (GsfXMLIn *xin, int *cols, int *rows)
11669 int c = GNM_MIN_COLS;
11670 int r = GNM_MIN_ROWS;
11672 while (c < *cols && c < GNM_MAX_COLS)
11673 c *= 2;
11675 while (r < *rows && r < GNM_MAX_ROWS)
11676 r *= 2;
11678 while (!gnm_sheet_valid_size (c, r))
11679 gnm_sheet_suggest_size (&c, &r);
11681 if (xin != NULL && (*cols > c || *rows > r))
11682 oo_warning (xin, _("The sheet size of %i columns and %i rows used in this file "
11683 "exceeds Gnumeric's maximum supported sheet size"), *cols, *rows);
11685 *cols = c;
11686 *rows = r;
11689 static void
11690 odf_preparse_create_sheet (GsfXMLIn *xin)
11692 OOParseState *state = (OOParseState *)xin->user_state;
11693 int rows, cols;
11694 char *table_name = state->object_name;
11695 Sheet *sheet;
11696 sheet_order_t *sot = g_new(sheet_order_t, 1);
11698 cols = state->extent_data.col + 1;
11699 rows = state->extent_data.row + 1;
11700 sot->cols = cols;
11701 sot->rows = rows;
11702 odf_sheet_suggest_size (xin, &cols, &rows);
11704 if (table_name != NULL) {
11705 sheet = workbook_sheet_by_name (state->pos.wb, table_name);
11706 if (NULL == sheet) {
11707 sheet = sheet_new (state->pos.wb, table_name, cols, rows);
11708 workbook_sheet_attach (state->pos.wb, sheet);
11709 } else {
11710 /* We have a corrupted file with a duplicate sheet name */
11711 char *new_name, *base;
11713 base = g_strdup_printf (_("%s_IN_CORRUPTED_FILE"), table_name);
11714 new_name = workbook_sheet_get_free_name (state->pos.wb,
11715 base, FALSE, FALSE);
11716 g_free (base);
11718 oo_warning (xin, _("This file is corrupted with a "
11719 "duplicate sheet name \"%s\", "
11720 "now renamed to \"%s\"."),
11721 table_name, new_name);
11722 sheet = sheet_new (state->pos.wb, new_name, cols, rows);
11723 workbook_sheet_attach (state->pos.wb, sheet);
11724 g_free (new_name);
11726 } else {
11727 table_name = workbook_sheet_get_free_name (state->pos.wb,
11728 _("SHEET_IN_CORRUPTED_FILE"),
11729 TRUE, FALSE);
11730 sheet = sheet_new (state->pos.wb, table_name, cols, rows);
11731 workbook_sheet_attach (state->pos.wb, sheet);
11733 /* We are missing the table name. This is bad! */
11734 oo_warning (xin, _("This file is corrupted with an "
11735 "unnamed sheet "
11736 "now named \"%s\"."),
11737 table_name);
11739 g_free (table_name);
11740 state->object_name = NULL;
11742 /* Store sheets in correct order in case we implicitly
11743 * created one out of order */
11744 sot->sheet = sheet;
11745 state->sheet_order = g_slist_prepend
11746 (state->sheet_order, sot);
11748 state->pos.sheet = sheet;
11750 #if 0
11751 g_printerr ("Created sheet %s\n", sheet->name_unquoted);
11752 #endif
11756 static void
11757 oo_named_exprs_preparse (GsfXMLIn *xin, xmlChar const **attrs)
11759 OOParseState *state = (OOParseState *)xin->user_state;
11761 if (state->pos.sheet == NULL && state->object_name != NULL) {
11762 // Create sheet, but not for global name section
11763 odf_preparse_create_sheet (xin);
11767 static void
11768 oo_named_expr_preparse (GsfXMLIn *xin, xmlChar const **attrs)
11770 oo_named_expr_common (xin, attrs, TRUE);
11773 static void
11774 odf_preparse_table_start (GsfXMLIn *xin, xmlChar const **attrs)
11776 OOParseState *state = (OOParseState *)xin->user_state;
11778 state->pos.eval.col = 0;
11779 state->pos.eval.row = 0;
11780 state->pos.sheet = NULL;
11781 state->extent_data.col = 0;
11782 state->extent_data.row = 0;
11783 state->object_name = NULL;
11785 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11786 if (gsf_xml_in_namecmp (xin, CXML2C (attrs[0]), OO_NS_TABLE, "name"))
11787 state->object_name = g_strdup (CXML2C (attrs[1]));
11790 static void
11791 odf_preparse_spreadsheet_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
11795 static void
11796 odf_preparse_table_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
11798 OOParseState *state = (OOParseState *)xin->user_state;
11800 if (state->pos.sheet == NULL)
11801 odf_preparse_create_sheet (xin);
11803 state->pos.sheet = NULL;
11807 static void
11808 odf_preparse_row_start (GsfXMLIn *xin, xmlChar const **attrs)
11810 OOParseState *state = (OOParseState *)xin->user_state;
11812 state->pos.eval.col = 0;
11813 state->row_inc = 1;
11815 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11816 if (oo_attr_int_range (xin, attrs, OO_NS_TABLE, "number-rows-repeated", &state->row_inc,
11817 0, INT_MAX - state->pos.eval.row));
11820 static void
11821 odf_preparse_row_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *blob)
11823 OOParseState *state = (OOParseState *)xin->user_state;
11824 state->pos.eval.row += state->row_inc;
11827 static void
11828 odf_preparse_cell_start (GsfXMLIn *xin, xmlChar const **attrs)
11830 OOParseState *state = (OOParseState *)xin->user_state;
11832 state->col_inc = 1;
11834 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11835 if (oo_attr_int_range (xin, attrs, OO_NS_TABLE, "number-columns-repeated",
11836 &state->col_inc, 0, INT_MAX - state->pos.eval.col));
11838 oo_update_data_extent (state, state->col_inc, state->row_inc);
11839 state->pos.eval.col += state->col_inc;
11842 static void
11843 odf_preparse_covered_cell_start (GsfXMLIn *xin, xmlChar const **attrs)
11845 OOParseState *state = (OOParseState *)xin->user_state;
11847 state->col_inc = 1;
11848 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11849 if (oo_attr_int_range (xin, attrs, OO_NS_TABLE, "number-columns-repeated",
11850 &state->col_inc, 0, INT_MAX - state->pos.eval.col));
11851 state->pos.eval.col += state->col_inc;
11856 /**************************************************************************/
11858 static void
11859 odf_find_version (GsfXMLIn *xin, xmlChar const **attrs)
11861 OOParseState *state = (OOParseState *)xin->user_state;
11863 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
11864 if (oo_attr_float (xin, attrs, OO_NS_OFFICE,
11865 "version", &state->ver_odf));
11868 /**************************************************************************/
11870 static GsfXMLInNode const styles_dtd[] = {
11871 GSF_XML_IN_NODE_FULL (START, START, -1, NULL, GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
11873 /* ooo-1.x */
11874 GSF_XML_IN_NODE (START, OFFICE_FONTS_OOO1, OO_NS_OFFICE, "font-decls", GSF_XML_NO_CONTENT, NULL, NULL),
11875 GSF_XML_IN_NODE (OFFICE_FONTS_OOO1, FONT_DECL_OOO1, OO_NS_STYLE, "font-decl", GSF_XML_NO_CONTENT, NULL, NULL),
11877 /* ooo-2.x, ooo-3.x */
11878 GSF_XML_IN_NODE (START, OFFICE_DOC_STYLES, OO_NS_OFFICE, "document-styles", GSF_XML_NO_CONTENT, &odf_find_version, NULL),
11879 GSF_XML_IN_NODE (OFFICE_DOC_STYLES, OFFICE_FONTS_OOO1, OO_NS_OFFICE, "font-decls", GSF_XML_NO_CONTENT, NULL, NULL),
11880 GSF_XML_IN_NODE (OFFICE_DOC_STYLES, OFFICE_FONTS, OO_NS_OFFICE, "font-face-decls", GSF_XML_NO_CONTENT, NULL, NULL),
11881 GSF_XML_IN_NODE (OFFICE_FONTS, FONT_DECL, OO_NS_STYLE, "font-face", GSF_XML_NO_CONTENT, NULL, NULL),
11883 GSF_XML_IN_NODE (OFFICE_DOC_STYLES, OFFICE_STYLES, OO_NS_OFFICE, "styles", GSF_XML_NO_CONTENT, NULL, NULL),
11884 GSF_XML_IN_NODE (OFFICE_STYLES, FILLIMAGE, OO_NS_DRAW, "fill-image", GSF_XML_NO_CONTENT, &oo_fill_image, NULL),
11885 GSF_XML_IN_NODE (OFFICE_STYLES, DASH, OO_NS_DRAW, "stroke-dash", GSF_XML_NO_CONTENT, &oo_dash, NULL),
11886 GSF_XML_IN_NODE (OFFICE_STYLES, HATCH, OO_NS_DRAW, "hatch", GSF_XML_NO_CONTENT, &oo_hatch, NULL),
11887 GSF_XML_IN_NODE (OFFICE_STYLES, GRADIENT, OO_NS_DRAW, "gradient", GSF_XML_NO_CONTENT, &oo_gradient, NULL),
11888 GSF_XML_IN_NODE (OFFICE_STYLES, MARKER, OO_NS_DRAW, "marker", GSF_XML_NO_CONTENT, &oo_marker, NULL),
11889 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE, OO_NS_STYLE, "style", GSF_XML_NO_CONTENT, &oo_style, &oo_style_end),
11890 GSF_XML_IN_NODE (STYLE, TABLE_CELL_PROPS, OO_NS_STYLE, "table-cell-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11891 GSF_XML_IN_NODE (STYLE, TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11892 GSF_XML_IN_NODE (STYLE, PARAGRAPH_PROPS, OO_NS_STYLE, "paragraph-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11893 GSF_XML_IN_NODE (PARAGRAPH_PROPS, PARA_TABS, OO_NS_STYLE, "tab-stops", GSF_XML_NO_CONTENT, NULL, NULL),
11894 GSF_XML_IN_NODE (STYLE, STYLE_PROP, OO_NS_STYLE, "properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11895 GSF_XML_IN_NODE (STYLE_PROP, STYLE_TAB_STOPS, OO_NS_STYLE, "tab-stops", GSF_XML_NO_CONTENT, NULL, NULL),
11897 GSF_XML_IN_NODE (OFFICE_STYLES, DEFAULT_STYLE, OO_NS_STYLE, "default-style", GSF_XML_NO_CONTENT, &oo_style, &oo_style_end),
11898 GSF_XML_IN_NODE (DEFAULT_STYLE, DEFAULT_TABLE_CELL_PROPS, OO_NS_STYLE, "table-cell-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11899 GSF_XML_IN_NODE (DEFAULT_STYLE, DEFAULT_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, NULL, NULL),
11900 GSF_XML_IN_NODE (DEFAULT_STYLE, DEFAULT_GRAPHIC_PROPS, OO_NS_STYLE, "graphic-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11901 GSF_XML_IN_NODE (DEFAULT_STYLE, DEFAULT_PARAGRAPH_PROPS, OO_NS_STYLE, "paragraph-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11902 GSF_XML_IN_NODE (DEFAULT_PARAGRAPH_PROPS, DEFAULT_PARA_TABS, OO_NS_STYLE, "tab-stops", GSF_XML_NO_CONTENT, NULL, NULL),
11903 GSF_XML_IN_NODE (DEFAULT_STYLE, DEFAULT_STYLE_PROP, OO_NS_STYLE, "properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11904 GSF_XML_IN_NODE (DEFAULT_STYLE_PROP, STYLE_TAB_STOPS, OO_NS_STYLE, "tab-stops", GSF_XML_NO_CONTENT, NULL, NULL),
11905 GSF_XML_IN_NODE (DEFAULT_STYLE, DEFAULT_TABLE_COL_PROPS, OO_NS_STYLE, "table-column-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11906 GSF_XML_IN_NODE (DEFAULT_STYLE, DEFAULT_TABLE_ROW_PROPS, OO_NS_STYLE, "table-row-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
11908 GSF_XML_IN_NODE (OFFICE_STYLES, NUMBER_STYLE, OO_NS_NUMBER, "number-style", GSF_XML_NO_CONTENT, &odf_number_style, &odf_number_style_end),
11909 #if HAVE_OO_NS_LOCALC_EXT
11910 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_NUMBERFILL_CHARACTER, OO_NS_LOCALC_EXT, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
11911 GSF_XML_IN_NODE (NUMBER_STYLE, LOEXT_TEXT, OO_NS_LOCALC_EXT, "text", GSF_XML_NO_CONTENT, NULL, NULL),
11912 #endif
11913 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_NUMBER, OO_NS_NUMBER, "number", GSF_XML_NO_CONTENT, &odf_number, NULL),
11914 GSF_XML_IN_NODE (NUMBER_STYLE_NUMBER, NUMBER_EMBEDDED_TEXT, OO_NS_NUMBER, "embedded-text", GSF_XML_CONTENT, &odf_embedded_text_start, &odf_embedded_text_end),
11915 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
11916 GSF_XML_IN_NODE (NUMBER_STYLE_TEXT, FORMAT_TEXT_INVISIBLE, OO_GNUM_NS_EXT, "invisible", GSF_XML_NO_CONTENT, &odf_format_invisible_text, NULL),
11917 GSF_XML_IN_NODE (NUMBER_STYLE_TEXT, FORMAT_TEXT_REPEATED, OO_GNUM_NS_EXT, "repeated", GSF_XML_NO_CONTENT, NULL, &odf_format_repeated_text_end),
11918 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_FRACTION, OO_NS_NUMBER, "fraction", GSF_XML_NO_CONTENT, &odf_fraction, NULL),
11919 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_SCI_STYLE_PROP, OO_NS_NUMBER, "scientific-number", GSF_XML_NO_CONTENT, &odf_scientific, NULL),
11920 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_PROP, OO_NS_STYLE, "properties", GSF_XML_NO_CONTENT, NULL, NULL),
11921 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, &odf_map, NULL),
11922 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
11923 GSF_XML_IN_NODE (NUMBER_STYLE, FORMAT_TEXT_INVISIBLE, OO_GNUM_NS_EXT, "invisible", GSF_XML_2ND, NULL, NULL),
11924 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
11926 GSF_XML_IN_NODE (OFFICE_STYLES, DATE_STYLE, OO_NS_NUMBER, "date-style", GSF_XML_NO_CONTENT, &oo_date_style, &oo_date_style_end),
11927 GSF_XML_IN_NODE (DATE_STYLE, DATE_DAY, OO_NS_NUMBER, "day", GSF_XML_NO_CONTENT, &oo_date_day, NULL),
11928 GSF_XML_IN_NODE (DATE_STYLE, DATE_MONTH, OO_NS_NUMBER, "month", GSF_XML_NO_CONTENT, &oo_date_month, NULL),
11929 GSF_XML_IN_NODE (DATE_STYLE, DATE_YEAR, OO_NS_NUMBER, "year", GSF_XML_NO_CONTENT, &oo_date_year, NULL),
11930 GSF_XML_IN_NODE (DATE_STYLE, DATE_ERA, OO_NS_NUMBER, "era", GSF_XML_NO_CONTENT, &oo_date_era, NULL),
11931 GSF_XML_IN_NODE (DATE_STYLE, DATE_DAY_OF_WEEK, OO_NS_NUMBER, "day-of-week", GSF_XML_NO_CONTENT, &oo_date_day_of_week, NULL),
11932 GSF_XML_IN_NODE (DATE_STYLE, DATE_WEEK_OF_YEAR, OO_NS_NUMBER, "week-of-year", GSF_XML_NO_CONTENT, &oo_date_week_of_year, NULL),
11933 GSF_XML_IN_NODE (DATE_STYLE, DATE_QUARTER, OO_NS_NUMBER, "quarter", GSF_XML_NO_CONTENT, &oo_date_quarter, NULL),
11934 GSF_XML_IN_NODE (DATE_STYLE, DATE_HOURS, OO_NS_NUMBER, "hours", GSF_XML_NO_CONTENT, &oo_date_hours, NULL),
11935 GSF_XML_IN_NODE (DATE_STYLE, DATE_MINUTES, OO_NS_NUMBER, "minutes", GSF_XML_NO_CONTENT, &oo_date_minutes, NULL),
11936 GSF_XML_IN_NODE (DATE_STYLE, DATE_SECONDS, OO_NS_NUMBER, "seconds", GSF_XML_NO_CONTENT, &oo_date_seconds, NULL),
11937 GSF_XML_IN_NODE (DATE_STYLE, DATE_AM_PM, OO_NS_NUMBER, "am-pm", GSF_XML_NO_CONTENT, &oo_date_am_pm, NULL),
11938 GSF_XML_IN_NODE (DATE_STYLE, DATE_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
11939 GSF_XML_IN_NODE (DATE_STYLE, DATE_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
11940 GSF_XML_IN_NODE (DATE_STYLE, DATE_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
11941 GSF_XML_IN_NODE (DATE_STYLE, DATE_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
11943 GSF_XML_IN_NODE (OFFICE_STYLES, TIME_STYLE, OO_NS_NUMBER, "time-style", GSF_XML_NO_CONTENT, &oo_date_style, &oo_date_style_end),
11944 GSF_XML_IN_NODE (TIME_STYLE, TIME_HOURS, OO_NS_NUMBER, "hours", GSF_XML_NO_CONTENT, &oo_date_hours, NULL),
11945 GSF_XML_IN_NODE (TIME_STYLE, TIME_MINUTES, OO_NS_NUMBER, "minutes", GSF_XML_NO_CONTENT, &oo_date_minutes, NULL),
11946 GSF_XML_IN_NODE (TIME_STYLE, TIME_SECONDS, OO_NS_NUMBER, "seconds", GSF_XML_NO_CONTENT, &oo_date_seconds, NULL),
11947 GSF_XML_IN_NODE (TIME_STYLE, TIME_AM_PM, OO_NS_NUMBER, "am-pm", GSF_XML_NO_CONTENT, &oo_date_am_pm, NULL),
11948 GSF_XML_IN_NODE (TIME_STYLE, TIME_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
11949 GSF_XML_IN_NODE (TIME_STYLE, TIME_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
11950 GSF_XML_IN_NODE (TIME_STYLE, TIME_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
11951 GSF_XML_IN_NODE (TIME_STYLE, TIME_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
11953 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_BOOL, OO_NS_NUMBER, "boolean-style", GSF_XML_NO_CONTENT, NULL, NULL),
11954 GSF_XML_IN_NODE (STYLE_BOOL, BOOL_PROP, OO_NS_NUMBER, "boolean", GSF_XML_NO_CONTENT, NULL, NULL),
11956 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_CURRENCY, OO_NS_NUMBER, "currency-style", GSF_XML_NO_CONTENT, &odf_number_style, &odf_number_style_end),
11957 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_STYLE, OO_NS_NUMBER, "number", GSF_XML_NO_CONTENT, &odf_number, NULL),
11958 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_STYLE_PROP, OO_NS_STYLE, "properties", GSF_XML_NO_CONTENT, NULL, NULL),
11959 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, &odf_map, NULL),
11960 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_SYMBOL, OO_NS_NUMBER, "currency-symbol", GSF_XML_CONTENT, NULL, &odf_currency_symbol_end),
11961 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
11962 GSF_XML_IN_NODE (CURRENCY_TEXT, FORMAT_TEXT_INVISIBLE, OO_GNUM_NS_EXT, "invisible", GSF_XML_2ND, NULL, NULL),
11963 GSF_XML_IN_NODE (CURRENCY_TEXT, FORMAT_TEXT_REPEATED, OO_GNUM_NS_EXT, "repeated", GSF_XML_2ND, NULL, NULL),
11964 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
11965 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
11967 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_PERCENTAGE, OO_NS_NUMBER, "percentage-style", GSF_XML_NO_CONTENT, &odf_number_percentage_style, &odf_number_style_end),
11968 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_STYLE_PROP, OO_NS_NUMBER, "number", GSF_XML_NO_CONTENT, &odf_number, NULL),
11969 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
11970 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, &odf_map, NULL),
11971 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
11972 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
11974 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_TEXT, OO_NS_NUMBER, "text-style", GSF_XML_NO_CONTENT, &odf_number_style, &odf_number_style_end),
11975 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_CONTENT, OO_NS_NUMBER, "text-content", GSF_XML_NO_CONTENT, &odf_text_content, NULL),
11976 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_PROP, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
11977 GSF_XML_IN_NODE (STYLE_TEXT_PROP, FORMAT_TEXT_INVISIBLE, OO_GNUM_NS_EXT, "invisible", GSF_XML_2ND, NULL, NULL),
11978 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, &odf_map, NULL),
11979 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
11981 GSF_XML_IN_NODE (OFFICE_DOC_STYLES, AUTOMATIC_STYLES, OO_NS_OFFICE, "automatic-styles", GSF_XML_NO_CONTENT, NULL, NULL),
11982 GSF_XML_IN_NODE (AUTOMATIC_STYLES, PAGE_MASTER, OO_NS_STYLE, "page-master", GSF_XML_NO_CONTENT, NULL, NULL), /* ooo1 */
11983 GSF_XML_IN_NODE (PAGE_MASTER, PAGE_MASTER_PROPS, OO_NS_STYLE, "properties", GSF_XML_NO_CONTENT, NULL, NULL),
11984 GSF_XML_IN_NODE (PAGE_MASTER_PROPS, PAGE_MASTER_BG_IMAGE, OO_NS_STYLE, "background-image", GSF_XML_NO_CONTENT, NULL, NULL),
11985 GSF_XML_IN_NODE (PAGE_MASTER, PAGE_MASTER_HEADER, OO_NS_STYLE, "header-style", GSF_XML_NO_CONTENT, NULL, NULL),
11986 GSF_XML_IN_NODE (PAGE_MASTER_HEADER, PAGE_MASTER_PROPS, OO_NS_STYLE, "properties", GSF_XML_2ND, NULL, NULL),
11987 GSF_XML_IN_NODE (PAGE_MASTER, PAGE_MASTER_FOOTER, OO_NS_STYLE, "footer-style", GSF_XML_NO_CONTENT, NULL, NULL),
11988 GSF_XML_IN_NODE (PAGE_MASTER_FOOTER, PAGE_MASTER_PROPS, OO_NS_STYLE, "properties", GSF_XML_2ND, NULL, NULL),
11990 GSF_XML_IN_NODE (AUTOMATIC_STYLES, STYLE, OO_NS_STYLE, "style", GSF_XML_2ND, NULL, NULL),
11991 GSF_XML_IN_NODE (AUTOMATIC_STYLES, PAGE_LAYOUT, OO_NS_STYLE, "page-layout", GSF_XML_NO_CONTENT, &odf_page_layout, &odf_page_layout_end),
11992 GSF_XML_IN_NODE (PAGE_LAYOUT, PAGE_LAYOUT_PROPS, OO_NS_STYLE, "page-layout-properties", GSF_XML_NO_CONTENT, &odf_page_layout_properties, NULL),
11993 GSF_XML_IN_NODE (PAGE_LAYOUT_PROPS, BACK_IMAGE, OO_NS_STYLE, "background-image", GSF_XML_NO_CONTENT, NULL, NULL),
11994 GSF_XML_IN_NODE (PAGE_LAYOUT, HEADER_STYLE, OO_NS_STYLE, "header-style", GSF_XML_NO_CONTENT, NULL, NULL),
11995 GSF_XML_IN_NODE (HEADER_STYLE, HEADER_PROPERTIES, OO_NS_STYLE, "header-footer-properties", GSF_XML_NO_CONTENT, odf_header_properties, NULL),
11996 GSF_XML_IN_NODE (HEADER_PROPERTIES, HF_BACK_IMAGE, OO_NS_STYLE, "background-image", GSF_XML_NO_CONTENT, NULL, NULL),
11997 GSF_XML_IN_NODE (PAGE_LAYOUT, FOOTER_STYLE, OO_NS_STYLE, "footer-style", GSF_XML_NO_CONTENT, NULL, NULL),
11998 GSF_XML_IN_NODE (FOOTER_STYLE, FOOTER_PROPERTIES, OO_NS_STYLE, "header-footer-properties", GSF_XML_NO_CONTENT, odf_footer_properties, NULL),
11999 GSF_XML_IN_NODE (FOOTER_PROPERTIES, HF_BACK_IMAGE, OO_NS_STYLE, "background-image", GSF_XML_2ND, NULL, NULL),
12000 GSF_XML_IN_NODE (AUTOMATIC_STYLES, NUMBER_STYLE, OO_NS_NUMBER, "number-style", GSF_XML_2ND, NULL, NULL),
12001 GSF_XML_IN_NODE (AUTOMATIC_STYLES, DATE_STYLE, OO_NS_NUMBER, "date-style", GSF_XML_2ND, NULL, NULL),
12002 GSF_XML_IN_NODE (AUTOMATIC_STYLES, TIME_STYLE, OO_NS_NUMBER, "time-style", GSF_XML_2ND, NULL, NULL),
12003 GSF_XML_IN_NODE (AUTOMATIC_STYLES, STYLE_BOOL, OO_NS_NUMBER, "boolean-style", GSF_XML_2ND, NULL, NULL),
12004 GSF_XML_IN_NODE (AUTOMATIC_STYLES, STYLE_CURRENCY, OO_NS_NUMBER, "currency-style", GSF_XML_2ND, NULL, NULL),
12005 GSF_XML_IN_NODE (AUTOMATIC_STYLES, STYLE_PERCENTAGE, OO_NS_NUMBER, "percentage-style", GSF_XML_2ND, NULL, NULL),
12006 GSF_XML_IN_NODE (AUTOMATIC_STYLES, STYLE_TEXT, OO_NS_NUMBER, "text-style", GSF_XML_2ND, NULL, NULL),
12008 GSF_XML_IN_NODE (OFFICE_DOC_STYLES, MASTER_STYLES, OO_NS_OFFICE, "master-styles", GSF_XML_NO_CONTENT, NULL, NULL),
12009 GSF_XML_IN_NODE (MASTER_STYLES, MASTER_PAGE, OO_NS_STYLE, "master-page", GSF_XML_NO_CONTENT, &odf_master_page, &odf_master_page_end),
12010 GSF_XML_IN_NODE (MASTER_PAGE, MASTER_PAGE_HEADER_LEFT, OO_NS_STYLE, "header-left", GSF_XML_NO_CONTENT, &odf_header_footer_left, NULL),
12011 GSF_XML_IN_NODE (MASTER_PAGE_HEADER_LEFT, MASTER_PAGE_HEADER_FOOTER_LEFT_P, OO_NS_TEXT, "p", GSF_XML_NO_CONTENT, NULL, NULL),
12012 GSF_XML_IN_NODE (MASTER_PAGE_HEADER_FOOTER_LEFT_P, MASTER_PAGE_HEADER_FOOTER_LEFT_SPAN, OO_NS_TEXT, "span", GSF_XML_NO_CONTENT, NULL, NULL),
12013 GSF_XML_IN_NODE (MASTER_PAGE_HEADER_FOOTER_LEFT_SPAN, MASTER_PAGE_HEADER_FOOTER_LEFT_PAGE_NUMBER, OO_NS_TEXT, "page-number", GSF_XML_NO_CONTENT, NULL, NULL),
12014 GSF_XML_IN_NODE (MASTER_PAGE_HEADER_FOOTER_LEFT_SPAN, MASTER_PAGE_HEADER_FOOTER_LEFT_SHEET_NAME, OO_NS_TEXT, "sheet-name", GSF_XML_NO_CONTENT, NULL, NULL),
12015 GSF_XML_IN_NODE (MASTER_PAGE, MASTER_PAGE_FOOTER_LEFT, OO_NS_STYLE, "footer-left", GSF_XML_NO_CONTENT, &odf_header_footer_left, NULL),
12016 GSF_XML_IN_NODE (MASTER_PAGE_FOOTER_LEFT, MASTER_PAGE_HEADER_FOOTER_LEFT_P, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12017 GSF_XML_IN_NODE_FULL (MASTER_PAGE, MASTER_PAGE_HEADER, OO_NS_STYLE, "header", GSF_XML_NO_CONTENT, FALSE, FALSE, &odf_header_footer, &odf_header_footer_end, 0),
12018 GSF_XML_IN_NODE_FULL (MASTER_PAGE_HEADER, MASTER_PAGE_HF_R_LEFT, OO_NS_STYLE, "region-left", GSF_XML_NO_CONTENT, FALSE, FALSE, &odf_hf_region, &odf_hf_region_end, 0),
12019 GSF_XML_IN_NODE (MASTER_PAGE_HF_R_LEFT, MASTER_PAGE_HF_P, OO_NS_TEXT, "p", GSF_XML_CONTENT, &odf_text_content_start, &odf_text_content_end),
12020 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, TEXT_S, OO_NS_TEXT, "s", GSF_XML_NO_CONTENT, &odf_text_space, NULL),
12021 GSF_XML_IN_NODE_FULL (MASTER_PAGE_HF_P, TEXT_LINE_BREAK, OO_NS_TEXT, "line-break", GSF_XML_NO_CONTENT, FALSE, FALSE, &odf_text_symbol, NULL, .v_str = "\n"),
12022 GSF_XML_IN_NODE_FULL (MASTER_PAGE_HF_P, TEXT_TAB, OO_NS_TEXT, "tab", GSF_XML_SHARED_CONTENT, FALSE, FALSE, odf_text_symbol, NULL, .v_str = "\t"),
12023 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, HF_TITLE, OO_NS_TEXT, "title", GSF_XML_NO_CONTENT, &odf_hf_title, NULL),
12024 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, HF_DATE, OO_NS_TEXT, "date", GSF_XML_NO_CONTENT, &odf_hf_date, NULL),
12025 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, HF_TIME, OO_NS_TEXT, "time", GSF_XML_NO_CONTENT, &odf_hf_time, NULL),
12026 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, HF_SHEET_NAME, OO_NS_TEXT, "sheet-name", GSF_XML_NO_CONTENT, &odf_hf_sheet_name, NULL),
12027 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, HF_PAGE_NUMBER, OO_NS_TEXT, "page-number", GSF_XML_NO_CONTENT, &odf_hf_page_number, NULL),
12028 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, HF_PAGE_COUNT, OO_NS_TEXT, "page-count", GSF_XML_NO_CONTENT, &odf_hf_page_count, NULL),
12029 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, HF_FILE_NAME, OO_NS_TEXT, "file-name", GSF_XML_NO_CONTENT, &odf_hf_file, NULL),
12030 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, HF_EXPRESSION, OO_NS_TEXT, "expression", GSF_XML_NO_CONTENT, &odf_hf_expression, NULL),
12031 GSF_XML_IN_NODE (MASTER_PAGE_HF_P, TEXT_SPAN, OO_NS_TEXT, "span", GSF_XML_SHARED_CONTENT, &odf_text_span_start, &odf_text_span_end),
12032 GSF_XML_IN_NODE (TEXT_SPAN, TEXT_SPAN, OO_NS_TEXT, "span", GSF_XML_2ND, NULL, NULL),
12033 GSF_XML_IN_NODE (TEXT_SPAN, TEXT_S, OO_NS_TEXT, "s", GSF_XML_2ND, NULL, NULL),
12034 GSF_XML_IN_NODE (TEXT_SPAN, TEXT_LINE_BREAK, OO_NS_TEXT, "line-break", GSF_XML_2ND, NULL, NULL),
12035 GSF_XML_IN_NODE (TEXT_SPAN, TEXT_TAB, OO_NS_TEXT, "tab", GSF_XML_2ND, NULL, NULL),
12036 GSF_XML_IN_NODE (TEXT_SPAN, HF_SHEET_NAME, OO_NS_TEXT, "sheet-name", GSF_XML_2ND, NULL, NULL),
12037 GSF_XML_IN_NODE (TEXT_SPAN, HF_PAGE_NUMBER, OO_NS_TEXT, "page-number", GSF_XML_2ND, NULL, NULL),
12038 GSF_XML_IN_NODE (TEXT_SPAN, HF_PAGE_COUNT, OO_NS_TEXT, "page-count", GSF_XML_2ND, NULL, NULL),
12039 GSF_XML_IN_NODE (TEXT_SPAN, HF_FILE_NAME, OO_NS_TEXT, "file-name", GSF_XML_2ND, NULL, NULL),
12040 GSF_XML_IN_NODE (TEXT_SPAN, HF_EXPRESSION, OO_NS_TEXT, "expression", GSF_XML_2ND, NULL, NULL),
12042 GSF_XML_IN_NODE_FULL (MASTER_PAGE_HEADER, MASTER_PAGE_HF_R_RIGHT, OO_NS_STYLE, "region-right", GSF_XML_NO_CONTENT, FALSE, FALSE, &odf_hf_region, &odf_hf_region_end, 2),
12043 GSF_XML_IN_NODE (MASTER_PAGE_HF_R_RIGHT, MASTER_PAGE_HF_P, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12044 GSF_XML_IN_NODE_FULL (MASTER_PAGE_HEADER, MASTER_PAGE_HF_R_CENTER, OO_NS_STYLE, "region-center", GSF_XML_NO_CONTENT, FALSE, FALSE, &odf_hf_region, &odf_hf_region_end, 1),
12045 GSF_XML_IN_NODE (MASTER_PAGE_HF_R_CENTER, MASTER_PAGE_HF_P, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12046 GSF_XML_IN_NODE (MASTER_PAGE_HEADER, MASTER_PAGE_HF_P, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12047 GSF_XML_IN_NODE_FULL (MASTER_PAGE, MASTER_PAGE_FOOTER, OO_NS_STYLE, "footer", GSF_XML_NO_CONTENT, FALSE, FALSE, &odf_header_footer, &odf_header_footer_end, 1),
12048 GSF_XML_IN_NODE (MASTER_PAGE_FOOTER, MASTER_PAGE_HF_R_LEFT, OO_NS_STYLE, "region-left", GSF_XML_2ND, NULL, NULL),
12049 GSF_XML_IN_NODE (MASTER_PAGE_FOOTER, MASTER_PAGE_HF_R_RIGHT, OO_NS_STYLE, "region-right", GSF_XML_2ND, NULL, NULL),
12050 GSF_XML_IN_NODE (MASTER_PAGE_FOOTER, MASTER_PAGE_HF_R_CENTER, OO_NS_STYLE, "region-center", GSF_XML_2ND, NULL, NULL),
12051 GSF_XML_IN_NODE (MASTER_PAGE_FOOTER, MASTER_PAGE_HF_P, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12052 GSF_XML_IN_NODE_END
12055 static GsfXMLInNode const ooo1_content_dtd [] = {
12056 GSF_XML_IN_NODE_FULL (START, START, -1, NULL, GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
12057 GSF_XML_IN_NODE (START, OFFICE, OO_NS_OFFICE, "document-content", GSF_XML_NO_CONTENT, NULL, NULL),
12058 GSF_XML_IN_NODE (OFFICE, SCRIPT, OO_NS_OFFICE, "script", GSF_XML_NO_CONTENT, NULL, NULL),
12059 GSF_XML_IN_NODE (OFFICE, OFFICE_FONTS, OO_NS_OFFICE, "font-decls", GSF_XML_NO_CONTENT, NULL, NULL),
12060 GSF_XML_IN_NODE (OFFICE_FONTS, FONT_DECL, OO_NS_STYLE, "font-decl", GSF_XML_NO_CONTENT, NULL, NULL),
12061 GSF_XML_IN_NODE (OFFICE, OFFICE_STYLES, OO_NS_OFFICE, "automatic-styles", GSF_XML_NO_CONTENT, NULL, NULL),
12062 GSF_XML_IN_NODE (OFFICE_STYLES, PAGE_MASTER, OO_NS_STYLE, "page-master", GSF_XML_NO_CONTENT, NULL, NULL),
12063 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE, OO_NS_STYLE, "style", GSF_XML_NO_CONTENT, &oo_style, &oo_style_end),
12064 GSF_XML_IN_NODE (STYLE, STYLE_PROP, OO_NS_STYLE, "properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
12065 GSF_XML_IN_NODE (STYLE_PROP, STYLE_TAB_STOPS, OO_NS_STYLE, "tab-stops", GSF_XML_NO_CONTENT, NULL, NULL),
12067 GSF_XML_IN_NODE (OFFICE_STYLES, NUMBER_STYLE, OO_NS_NUMBER, "number-style", GSF_XML_NO_CONTENT, NULL, NULL),
12068 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_NUMBER, OO_NS_NUMBER, "number", GSF_XML_NO_CONTENT, NULL, NULL),
12069 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_TEXT, OO_NS_NUMBER, "text", GSF_XML_NO_CONTENT, NULL, NULL),
12070 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_FRACTION, OO_NS_NUMBER, "fraction", GSF_XML_NO_CONTENT, NULL, NULL),
12071 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_SCI_STYLE_PROP, OO_NS_NUMBER, "scientific-number", GSF_XML_NO_CONTENT, NULL, NULL),
12072 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_PROP, OO_NS_STYLE, "properties", GSF_XML_NO_CONTENT, NULL, NULL),
12073 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
12074 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12076 GSF_XML_IN_NODE (OFFICE_STYLES, DATE_STYLE, OO_NS_NUMBER, "date-style", GSF_XML_NO_CONTENT, &oo_date_style, &oo_date_style_end),
12077 GSF_XML_IN_NODE (DATE_STYLE, DATE_DAY, OO_NS_NUMBER, "day", GSF_XML_NO_CONTENT, &oo_date_day, NULL),
12078 GSF_XML_IN_NODE (DATE_STYLE, DATE_MONTH, OO_NS_NUMBER, "month", GSF_XML_NO_CONTENT, &oo_date_month, NULL),
12079 GSF_XML_IN_NODE (DATE_STYLE, DATE_YEAR, OO_NS_NUMBER, "year", GSF_XML_NO_CONTENT, &oo_date_year, NULL),
12080 GSF_XML_IN_NODE (DATE_STYLE, DATE_ERA, OO_NS_NUMBER, "era", GSF_XML_NO_CONTENT, &oo_date_era, NULL),
12081 GSF_XML_IN_NODE (DATE_STYLE, DATE_DAY_OF_WEEK, OO_NS_NUMBER, "day-of-week", GSF_XML_NO_CONTENT, &oo_date_day_of_week, NULL),
12082 GSF_XML_IN_NODE (DATE_STYLE, DATE_WEEK_OF_YEAR, OO_NS_NUMBER, "week-of-year", GSF_XML_NO_CONTENT, &oo_date_week_of_year, NULL),
12083 GSF_XML_IN_NODE (DATE_STYLE, DATE_QUARTER, OO_NS_NUMBER, "quarter", GSF_XML_NO_CONTENT, &oo_date_quarter, NULL),
12084 GSF_XML_IN_NODE (DATE_STYLE, DATE_HOURS, OO_NS_NUMBER, "hours", GSF_XML_NO_CONTENT, &oo_date_hours, NULL),
12085 GSF_XML_IN_NODE (DATE_STYLE, DATE_MINUTES, OO_NS_NUMBER, "minutes", GSF_XML_NO_CONTENT, &oo_date_minutes, NULL),
12086 GSF_XML_IN_NODE (DATE_STYLE, DATE_SECONDS, OO_NS_NUMBER, "seconds", GSF_XML_NO_CONTENT, &oo_date_seconds, NULL),
12087 GSF_XML_IN_NODE (DATE_STYLE, DATE_AM_PM, OO_NS_NUMBER, "am-pm", GSF_XML_NO_CONTENT, &oo_date_am_pm, NULL),
12088 GSF_XML_IN_NODE (DATE_STYLE, DATE_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
12089 GSF_XML_IN_NODE (DATE_STYLE, DATE_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, NULL, NULL),
12090 GSF_XML_IN_NODE (DATE_STYLE, DATE_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
12091 GSF_XML_IN_NODE (DATE_STYLE, DATE_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12093 GSF_XML_IN_NODE (OFFICE_STYLES, TIME_STYLE, OO_NS_NUMBER, "time-style", GSF_XML_NO_CONTENT, &oo_date_style, &oo_date_style_end),
12094 GSF_XML_IN_NODE (TIME_STYLE, TIME_HOURS, OO_NS_NUMBER, "hours", GSF_XML_NO_CONTENT, &oo_date_hours, NULL),
12095 GSF_XML_IN_NODE (TIME_STYLE, TIME_MINUTES, OO_NS_NUMBER, "minutes", GSF_XML_NO_CONTENT, &oo_date_minutes, NULL),
12096 GSF_XML_IN_NODE (TIME_STYLE, TIME_SECONDS, OO_NS_NUMBER, "seconds", GSF_XML_NO_CONTENT, &oo_date_seconds, NULL),
12097 GSF_XML_IN_NODE (TIME_STYLE, TIME_AM_PM, OO_NS_NUMBER, "am-pm", GSF_XML_NO_CONTENT, &oo_date_am_pm, NULL),
12098 GSF_XML_IN_NODE (TIME_STYLE, TIME_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
12099 GSF_XML_IN_NODE (TIME_STYLE, TIME_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, NULL, NULL),
12100 GSF_XML_IN_NODE (TIME_STYLE, TIME_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
12101 GSF_XML_IN_NODE (TIME_STYLE, TIME_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12103 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_BOOL, OO_NS_NUMBER, "boolean-style", GSF_XML_NO_CONTENT, NULL, NULL),
12104 GSF_XML_IN_NODE (STYLE_BOOL, BOOL_PROP, OO_NS_NUMBER, "boolean", GSF_XML_NO_CONTENT, NULL, NULL),
12105 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_CURRENCY, OO_NS_NUMBER, "currency-style", GSF_XML_NO_CONTENT, NULL, NULL),
12106 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_STYLE, OO_NS_NUMBER, "number", GSF_XML_NO_CONTENT, NULL, NULL),
12107 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_STYLE_PROP, OO_NS_STYLE, "properties", GSF_XML_NO_CONTENT, NULL, NULL),
12108 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
12109 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_SYMBOL, OO_NS_NUMBER, "currency-symbol", GSF_XML_NO_CONTENT, NULL, NULL),
12110 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_TEXT, OO_NS_NUMBER, "text", GSF_XML_NO_CONTENT, NULL, NULL),
12111 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12112 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_PERCENTAGE, OO_NS_NUMBER, "percentage-style", GSF_XML_NO_CONTENT, NULL, NULL),
12113 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_STYLE_PROP, OO_NS_NUMBER, "number", GSF_XML_NO_CONTENT, NULL, NULL),
12114 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_TEXT, OO_NS_NUMBER, "text", GSF_XML_NO_CONTENT, NULL, NULL),
12115 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12116 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_TEXT, OO_NS_NUMBER, "text-style", GSF_XML_NO_CONTENT, NULL, NULL),
12117 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_CONTENT, OO_NS_NUMBER, "text-content", GSF_XML_NO_CONTENT, NULL, NULL),
12118 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_PROP, OO_NS_NUMBER, "text", GSF_XML_NO_CONTENT, NULL, NULL),
12119 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
12121 GSF_XML_IN_NODE (OFFICE, OFFICE_BODY, OO_NS_OFFICE, "body", GSF_XML_NO_CONTENT, NULL, NULL),
12122 GSF_XML_IN_NODE (OFFICE_BODY, TABLE_CALC_SETTINGS, OO_NS_TABLE, "calculation-settings", GSF_XML_NO_CONTENT, NULL, NULL),
12123 GSF_XML_IN_NODE (TABLE_CALC_SETTINGS, DATE_CONVENTION, OO_NS_TABLE, "null-date", GSF_XML_NO_CONTENT, &oo_date_convention, NULL),
12124 GSF_XML_IN_NODE (TABLE_CALC_SETTINGS, ITERATION, OO_NS_TABLE, "iteration", GSF_XML_NO_CONTENT, oo_iteration, NULL),
12125 GSF_XML_IN_NODE (OFFICE_BODY, VALIDATIONS, OO_NS_TABLE, "content-validations", GSF_XML_NO_CONTENT, NULL, NULL),
12126 GSF_XML_IN_NODE (VALIDATIONS, VALIDATION, OO_NS_TABLE, "content-validation", GSF_XML_NO_CONTENT, NULL, NULL),
12127 GSF_XML_IN_NODE (VALIDATION, VALIDATION_MSG, OO_NS_TABLE, "error-message", GSF_XML_NO_CONTENT, NULL, NULL),
12129 GSF_XML_IN_NODE (OFFICE_BODY, TABLE, OO_NS_TABLE, "table", GSF_XML_NO_CONTENT, &oo_table_start, &oo_table_end),
12130 GSF_XML_IN_NODE (TABLE, FORMS, OO_NS_OFFICE, "forms", GSF_XML_NO_CONTENT, NULL, NULL),
12131 GSF_XML_IN_NODE (TABLE, TABLE_COL, OO_NS_TABLE, "table-column", GSF_XML_NO_CONTENT, &oo_col_start, NULL),
12132 GSF_XML_IN_NODE (TABLE, TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_NO_CONTENT, &oo_row_start, &oo_row_end),
12133 GSF_XML_IN_NODE (TABLE_ROW, TABLE_CELL, OO_NS_TABLE, "table-cell", GSF_XML_NO_CONTENT, &oo_cell_start, &oo_cell_end),
12134 GSF_XML_IN_NODE (TABLE_CELL, CELL_CONTROL, OO_NS_DRAW, "control", GSF_XML_NO_CONTENT, NULL, NULL),
12135 GSF_XML_IN_NODE (TABLE_CELL, CELL_TEXT, OO_NS_TEXT, "p", GSF_XML_CONTENT, &oo_cell_content_start, &oo_cell_content_end),
12136 GSF_XML_IN_NODE (CELL_TEXT, CELL_TEXT_S, OO_NS_TEXT, "s", GSF_XML_NO_CONTENT, NULL, NULL),
12137 GSF_XML_IN_NODE (CELL_TEXT, CELL_TEXT_SPAN, OO_NS_TEXT, "span", GSF_XML_SHARED_CONTENT, NULL, NULL),
12138 GSF_XML_IN_NODE (TABLE_CELL, CELL_OBJECT, OO_NS_DRAW, "object", GSF_XML_NO_CONTENT, NULL, NULL), /* ignore for now */
12139 GSF_XML_IN_NODE (TABLE_CELL, CELL_GRAPHIC, OO_NS_DRAW, "g", GSF_XML_NO_CONTENT, NULL, NULL), /* ignore for now */
12140 GSF_XML_IN_NODE (CELL_GRAPHIC, CELL_GRAPHIC, OO_NS_DRAW, "g", GSF_XML_2ND, NULL, NULL),
12141 GSF_XML_IN_NODE (CELL_GRAPHIC, DRAW_POLYLINE, OO_NS_DRAW, "polyline", GSF_XML_NO_CONTENT, NULL, NULL),
12142 GSF_XML_IN_NODE (TABLE_CELL, DRAW_LINE, OO_NS_DRAW, "line", GSF_XML_NO_CONTENT, &odf_line, &odf_line_end),
12143 GSF_XML_IN_NODE (DRAW_LINE, IGNORED_TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_NO_CONTENT, NULL, NULL), /* ignore for now */
12144 GSF_XML_IN_NODE (TABLE_ROW, TABLE_COVERED_CELL, OO_NS_TABLE, "covered-table-cell", GSF_XML_NO_CONTENT, &oo_covered_cell_start, &oo_covered_cell_end),
12145 GSF_XML_IN_NODE (TABLE_COVERED_CELL, DRAW_LINE, OO_NS_DRAW, "line", GSF_XML_2ND, NULL, NULL),
12146 GSF_XML_IN_NODE (TABLE, TABLE_COL_GROUP, OO_NS_TABLE, "table-column-group", GSF_XML_NO_CONTENT, NULL, NULL),
12147 GSF_XML_IN_NODE (TABLE_COL_GROUP, TABLE_COL_GROUP, OO_NS_TABLE, "table-column-group", GSF_XML_NO_CONTENT, NULL, NULL),
12148 GSF_XML_IN_NODE (TABLE_COL_GROUP, TABLE_COL, OO_NS_TABLE, "table-column", GSF_XML_2ND, NULL, NULL),
12149 GSF_XML_IN_NODE (TABLE, TABLE_ROW_GROUP, OO_NS_TABLE, "table-row-group", GSF_XML_NO_CONTENT, NULL, NULL),
12150 GSF_XML_IN_NODE (TABLE_ROW_GROUP, TABLE_ROW_GROUP, OO_NS_TABLE, "table-row-group", GSF_XML_NO_CONTENT, NULL, NULL),
12151 GSF_XML_IN_NODE (TABLE_ROW_GROUP, TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_2ND, NULL, NULL),
12152 GSF_XML_IN_NODE (OFFICE_BODY, NAMED_EXPRS, OO_NS_TABLE, "named-expressions", GSF_XML_NO_CONTENT, NULL, NULL),
12153 GSF_XML_IN_NODE (NAMED_EXPRS, NAMED_EXPR, OO_NS_TABLE, "named-expression", GSF_XML_NO_CONTENT, &oo_named_expr, NULL),
12154 GSF_XML_IN_NODE (NAMED_EXPRS, NAMED_RANGE, OO_NS_TABLE, "named-range", GSF_XML_NO_CONTENT, &oo_named_expr, NULL),
12155 GSF_XML_IN_NODE (OFFICE_BODY, DB_RANGES, OO_NS_TABLE, "database-ranges", GSF_XML_NO_CONTENT, NULL, NULL),
12156 GSF_XML_IN_NODE (DB_RANGES, DB_RANGE, OO_NS_TABLE, "database-range", GSF_XML_NO_CONTENT, NULL, NULL),
12157 GSF_XML_IN_NODE (DB_RANGE, TABLE_SORT, OO_NS_TABLE, "sort", GSF_XML_NO_CONTENT, NULL, NULL),
12158 GSF_XML_IN_NODE (TABLE_SORT, SORT_BY, OO_NS_TABLE, "sort-by", GSF_XML_NO_CONTENT, NULL, NULL),
12160 GSF_XML_IN_NODE_END
12163 /****************************************************************************/
12165 static GsfXMLInNode const opendoc_settings_dtd [] = {
12166 GSF_XML_IN_NODE_FULL (START, START, -1, NULL, GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
12167 GSF_XML_IN_NODE (START, OFFICE, OO_NS_OFFICE, "document-settings", GSF_XML_NO_CONTENT, NULL, NULL),
12168 GSF_XML_IN_NODE (OFFICE, SETTINGS, OO_NS_OFFICE, "settings", GSF_XML_NO_CONTENT, NULL, NULL),
12169 GSF_XML_IN_NODE (SETTINGS, CONFIG_ITEM_SET, OO_NS_CONFIG, "config-item-set", GSF_XML_NO_CONTENT, &odf_config_item_set, &odf_config_stack_pop),
12170 GSF_XML_IN_NODE (CONFIG_ITEM_SET, CONFIG_ITEM_SET, OO_NS_CONFIG, "config-item-set", GSF_XML_2ND, NULL, NULL),
12171 GSF_XML_IN_NODE (CONFIG_ITEM_SET, CONFIG_ITEM, OO_NS_CONFIG, "config-item", GSF_XML_CONTENT, &odf_config_item, &odf_config_item_end),
12172 GSF_XML_IN_NODE (CONFIG_ITEM_SET, CONFIG_ITEM_MAP_INDEXED, OO_NS_CONFIG, "config-item-map-indexed", GSF_XML_NO_CONTENT, &odf_config_item_set, &odf_config_stack_pop),
12173 GSF_XML_IN_NODE (CONFIG_ITEM_MAP_INDEXED, CONFIG_ITEM_MAP_ENTRY, OO_NS_CONFIG, "config-item-map-entry", GSF_XML_NO_CONTENT, &odf_config_item_set, &odf_config_stack_pop),
12174 GSF_XML_IN_NODE (CONFIG_ITEM_MAP_ENTRY, CONFIG_ITEM_MAP_INDEXED, OO_NS_CONFIG, "config-item-map-indexed", GSF_XML_2ND, NULL, NULL),
12175 GSF_XML_IN_NODE (CONFIG_ITEM_MAP_ENTRY, CONFIG_ITEM, OO_NS_CONFIG, "config-item", GSF_XML_2ND, NULL, NULL),
12176 GSF_XML_IN_NODE (CONFIG_ITEM_MAP_ENTRY, CONFIG_ITEM_MAP_NAMED, OO_NS_CONFIG, "config-item-map-named", GSF_XML_NO_CONTENT, &odf_config_item_set, &odf_config_stack_pop),
12177 GSF_XML_IN_NODE (CONFIG_ITEM_MAP_ENTRY, CONFIG_ITEM_SET, OO_NS_CONFIG, "config-item-set", GSF_XML_2ND, NULL, NULL),
12178 GSF_XML_IN_NODE (CONFIG_ITEM_SET, CONFIG_ITEM_MAP_NAMED, OO_NS_CONFIG, "config-item-map-named", GSF_XML_2ND, NULL, NULL),
12179 GSF_XML_IN_NODE (CONFIG_ITEM_MAP_NAMED, CONFIG_ITEM_MAP_ENTRY, OO_NS_CONFIG, "config-item-map-entry", GSF_XML_2ND, NULL, NULL),
12181 GSF_XML_IN_NODE_END
12184 /****************************************************************************/
12185 /* Generated based on:
12186 * http://www.oasis-open.org/committees/download.php/12572/OpenDocument-v1.0-os.pdf */
12187 static GsfXMLInNode const opendoc_content_dtd [] =
12189 GSF_XML_IN_NODE_FULL (START, START, -1, NULL, GSF_XML_NO_CONTENT, FALSE, TRUE, NULL, NULL, 0),
12190 GSF_XML_IN_NODE (START, OFFICE, OO_NS_OFFICE, "document-content", GSF_XML_NO_CONTENT, &odf_find_version, NULL),
12191 GSF_XML_IN_NODE (OFFICE, SCRIPT, OO_NS_OFFICE, "scripts", GSF_XML_NO_CONTENT, NULL, NULL),
12192 GSF_XML_IN_NODE (OFFICE, OFFICE_FONTS, OO_NS_OFFICE, "font-face-decls", GSF_XML_NO_CONTENT, NULL, NULL),
12193 GSF_XML_IN_NODE (OFFICE_FONTS, FONT_FACE, OO_NS_STYLE, "font-face", GSF_XML_NO_CONTENT, NULL, NULL),
12194 GSF_XML_IN_NODE (OFFICE, OFFICE_STYLES, OO_NS_OFFICE, "automatic-styles", GSF_XML_NO_CONTENT, NULL, NULL),
12195 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE, OO_NS_STYLE, "style", GSF_XML_NO_CONTENT, &oo_style, &oo_style_end),
12196 GSF_XML_IN_NODE (STYLE, TABLE_CELL_PROPS, OO_NS_STYLE, "table-cell-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
12197 GSF_XML_IN_NODE (STYLE, TABLE_COL_PROPS, OO_NS_STYLE, "table-column-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
12198 GSF_XML_IN_NODE (STYLE, TABLE_ROW_PROPS, OO_NS_STYLE, "table-row-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
12199 GSF_XML_IN_NODE (STYLE, CHART_PROPS, OO_NS_STYLE, "chart-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
12200 GSF_XML_IN_NODE (STYLE, TEXT_PROPS, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
12201 GSF_XML_IN_NODE (STYLE, TABLE_PROPS, OO_NS_STYLE, "table-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
12202 GSF_XML_IN_NODE (STYLE, PARAGRAPH_PROPS, OO_NS_STYLE, "paragraph-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
12203 GSF_XML_IN_NODE (PARAGRAPH_PROPS, PARA_TABS, OO_NS_STYLE, "tab-stops", GSF_XML_NO_CONTENT, NULL, NULL),
12204 GSF_XML_IN_NODE (STYLE, GRAPHIC_PROPS, OO_NS_STYLE, "graphic-properties", GSF_XML_NO_CONTENT, &oo_style_prop, NULL),
12205 GSF_XML_IN_NODE (STYLE, STYLE_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, &oo_style_map, NULL),
12206 GSF_XML_IN_NODE (OFFICE_STYLES, NUMBER_STYLE, OO_NS_NUMBER, "number-style", GSF_XML_NO_CONTENT, &odf_number_style, &odf_number_style_end),
12207 #if HAVE_OO_NS_LOCALC_EXT
12208 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_NUMBERFILL_CHARACTER, OO_NS_LOCALC_EXT, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12209 GSF_XML_IN_NODE (NUMBER_STYLE, LOEXT_TEXT, OO_NS_LOCALC_EXT, "text", GSF_XML_NO_CONTENT, NULL, NULL),
12210 #endif
12211 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_NUMBER, OO_NS_NUMBER, "number", GSF_XML_NO_CONTENT, &odf_number, NULL),
12212 GSF_XML_IN_NODE (NUMBER_STYLE_NUMBER, NUMBER_EMBEDDED_TEXT, OO_NS_NUMBER, "embedded-text", GSF_XML_NO_CONTENT, NULL, NULL),
12213 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
12214 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_STYLE_FRACTION, OO_NS_NUMBER, "fraction", GSF_XML_NO_CONTENT, &odf_fraction, NULL),
12215 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_SCI_STYLE_PROP, OO_NS_NUMBER, "scientific-number", GSF_XML_NO_CONTENT, &odf_scientific, NULL),
12216 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, &odf_map, NULL),
12217 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
12218 GSF_XML_IN_NODE (NUMBER_STYLE, NUMBER_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12219 GSF_XML_IN_NODE (OFFICE_STYLES, DATE_STYLE, OO_NS_NUMBER, "date-style", GSF_XML_NO_CONTENT, &oo_date_style, &oo_date_style_end),
12220 GSF_XML_IN_NODE (DATE_STYLE, DATE_DAY, OO_NS_NUMBER, "day", GSF_XML_NO_CONTENT, &oo_date_day, NULL),
12221 GSF_XML_IN_NODE (DATE_STYLE, DATE_MONTH, OO_NS_NUMBER, "month", GSF_XML_NO_CONTENT, &oo_date_month, NULL),
12222 GSF_XML_IN_NODE (DATE_STYLE, DATE_YEAR, OO_NS_NUMBER, "year", GSF_XML_NO_CONTENT, &oo_date_year, NULL),
12223 GSF_XML_IN_NODE (DATE_STYLE, DATE_ERA, OO_NS_NUMBER, "era", GSF_XML_NO_CONTENT, &oo_date_era, NULL),
12224 GSF_XML_IN_NODE (DATE_STYLE, DATE_DAY_OF_WEEK, OO_NS_NUMBER, "day-of-week", GSF_XML_NO_CONTENT, &oo_date_day_of_week, NULL),
12225 GSF_XML_IN_NODE (DATE_STYLE, DATE_WEEK_OF_YEAR, OO_NS_NUMBER, "week-of-year", GSF_XML_NO_CONTENT, &oo_date_week_of_year, NULL),
12226 GSF_XML_IN_NODE (DATE_STYLE, DATE_QUARTER, OO_NS_NUMBER, "quarter", GSF_XML_NO_CONTENT, &oo_date_quarter, NULL),
12227 GSF_XML_IN_NODE (DATE_STYLE, DATE_HOURS, OO_NS_NUMBER, "hours", GSF_XML_NO_CONTENT, &oo_date_hours, NULL),
12228 GSF_XML_IN_NODE (DATE_STYLE, DATE_MINUTES, OO_NS_NUMBER, "minutes", GSF_XML_NO_CONTENT, &oo_date_minutes, NULL),
12229 GSF_XML_IN_NODE (DATE_STYLE, DATE_SECONDS, OO_NS_NUMBER, "seconds", GSF_XML_NO_CONTENT, &oo_date_seconds, NULL),
12230 GSF_XML_IN_NODE (DATE_STYLE, DATE_AM_PM, OO_NS_NUMBER, "am-pm", GSF_XML_NO_CONTENT, &oo_date_am_pm, NULL),
12231 GSF_XML_IN_NODE (DATE_STYLE, DATE_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
12232 GSF_XML_IN_NODE (DATE_STYLE, DATE_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
12233 GSF_XML_IN_NODE (DATE_STYLE, DATE_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
12234 GSF_XML_IN_NODE (DATE_STYLE, DATE_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12235 GSF_XML_IN_NODE (OFFICE_STYLES, TIME_STYLE, OO_NS_NUMBER, "time-style", GSF_XML_NO_CONTENT, &oo_date_style, &oo_date_style_end),
12236 GSF_XML_IN_NODE (TIME_STYLE, TIME_HOURS, OO_NS_NUMBER, "hours", GSF_XML_NO_CONTENT, &oo_date_hours, NULL),
12237 GSF_XML_IN_NODE (TIME_STYLE, TIME_MINUTES, OO_NS_NUMBER, "minutes", GSF_XML_NO_CONTENT, &oo_date_minutes, NULL),
12238 GSF_XML_IN_NODE (TIME_STYLE, TIME_SECONDS, OO_NS_NUMBER, "seconds", GSF_XML_NO_CONTENT, &oo_date_seconds, NULL),
12239 GSF_XML_IN_NODE (TIME_STYLE, TIME_AM_PM, OO_NS_NUMBER, "am-pm", GSF_XML_NO_CONTENT, &oo_date_am_pm, NULL),
12240 GSF_XML_IN_NODE (TIME_STYLE, TIME_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
12241 GSF_XML_IN_NODE (TIME_STYLE, TIME_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
12242 GSF_XML_IN_NODE (TIME_STYLE, TIME_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
12243 GSF_XML_IN_NODE (TIME_STYLE, TIME_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12244 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_BOOL, OO_NS_NUMBER, "boolean-style", GSF_XML_NO_CONTENT, NULL, NULL),
12245 GSF_XML_IN_NODE (STYLE_BOOL, BOOL_PROP, OO_NS_NUMBER, "boolean", GSF_XML_NO_CONTENT, NULL, NULL),
12246 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_CURRENCY, OO_NS_NUMBER, "currency-style", GSF_XML_NO_CONTENT, &odf_number_style, &odf_number_style_end),
12247 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_STYLE, OO_NS_NUMBER, "number", GSF_XML_NO_CONTENT, &odf_number, NULL),
12248 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_STYLE_PROP, OO_NS_STYLE,"properties", GSF_XML_NO_CONTENT, NULL, NULL),
12249 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, &odf_map, NULL),
12250 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_SYMBOL, OO_NS_NUMBER, "currency-symbol", GSF_XML_CONTENT, NULL, &odf_currency_symbol_end),
12251 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
12252 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
12253 GSF_XML_IN_NODE (STYLE_CURRENCY, CURRENCY_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12254 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_PERCENTAGE, OO_NS_NUMBER, "percentage-style", GSF_XML_NO_CONTENT, &odf_number_percentage_style, &odf_number_style_end),
12255 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_STYLE_PROP, OO_NS_NUMBER, "number", GSF_XML_NO_CONTENT, &odf_number, NULL),
12256 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_TEXT, OO_NS_NUMBER, "text", GSF_XML_CONTENT, &odf_date_text_start, &oo_date_text_end),
12257 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, &odf_map, NULL),
12258 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_TEXT_PROP, OO_NS_STYLE, "text-properties", GSF_XML_NO_CONTENT, &odf_number_color, NULL),
12259 GSF_XML_IN_NODE (STYLE_PERCENTAGE, PERCENTAGE_FILL_CHARACTER, OO_NS_NUMBER, "fill-character", GSF_XML_NO_CONTENT, NULL, NULL),
12260 GSF_XML_IN_NODE (OFFICE_STYLES, STYLE_TEXT, OO_NS_NUMBER, "text-style", GSF_XML_NO_CONTENT, NULL, NULL),
12261 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_CONTENT, OO_NS_NUMBER, "text-content", GSF_XML_NO_CONTENT, NULL, NULL),
12262 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_PROP, OO_NS_NUMBER, "text", GSF_XML_NO_CONTENT, NULL, NULL),
12263 GSF_XML_IN_NODE (STYLE_TEXT, STYLE_TEXT_MAP, OO_NS_STYLE, "map", GSF_XML_NO_CONTENT, NULL, NULL),
12265 GSF_XML_IN_NODE (OFFICE, OFFICE_BODY, OO_NS_OFFICE, "body", GSF_XML_NO_CONTENT, NULL, NULL),
12266 GSF_XML_IN_NODE (OFFICE_BODY, SPREADSHEET, OO_NS_OFFICE, "spreadsheet", GSF_XML_NO_CONTENT, NULL, NULL),
12267 GSF_XML_IN_NODE (SPREADSHEET, DATA_PILOT_TABLES, OO_NS_TABLE, "data-pilot-tables", GSF_XML_NO_CONTENT, NULL, NULL),
12268 GSF_XML_IN_NODE (DATA_PILOT_TABLES, DATA_PILOT_TABLE, OO_NS_TABLE, "data-pilot-table", GSF_XML_NO_CONTENT, NULL, NULL),
12269 GSF_XML_IN_NODE (DATA_PILOT_TABLE, DPT_SOURCE_CELL_RANGE, OO_NS_TABLE, "source-cell-range", GSF_XML_NO_CONTENT, NULL, NULL),
12270 GSF_XML_IN_NODE (DATA_PILOT_TABLE, DATA_PILOT_FIELD, OO_NS_TABLE, "data-pilot-field", GSF_XML_NO_CONTENT, NULL, NULL),
12271 GSF_XML_IN_NODE (DATA_PILOT_FIELD, DATA_PILOT_LEVEL, OO_NS_TABLE, "data-pilot-level", GSF_XML_NO_CONTENT, NULL, NULL),
12272 GSF_XML_IN_NODE (DATA_PILOT_LEVEL, DATA_PILOT_LAYOUT_INFO, OO_NS_TABLE, "data-pilot-layout-info", GSF_XML_NO_CONTENT, NULL, NULL),
12273 GSF_XML_IN_NODE (DATA_PILOT_LEVEL, DATA_PILOT_SORT_INFO, OO_NS_TABLE, "data-pilot-sort-info", GSF_XML_NO_CONTENT, NULL, NULL),
12274 GSF_XML_IN_NODE (DATA_PILOT_LEVEL, DATA_PILOT_DISPLAY_INFO, OO_NS_TABLE, "data-pilot-display-info", GSF_XML_NO_CONTENT, NULL, NULL),
12275 GSF_XML_IN_NODE (DATA_PILOT_LEVEL, DATA_PILOT_MEMBERS, OO_NS_TABLE, "data-pilot-members", GSF_XML_NO_CONTENT, NULL, NULL),
12276 GSF_XML_IN_NODE (DATA_PILOT_MEMBERS, DATA_PILOT_MEMBER, OO_NS_TABLE, "data-pilot-member", GSF_XML_NO_CONTENT, NULL, NULL),
12277 GSF_XML_IN_NODE (DATA_PILOT_LEVEL, DATA_PILOT_SUBTOTALS, OO_NS_TABLE, "data-pilot-subtotals", GSF_XML_NO_CONTENT, NULL, NULL),
12278 GSF_XML_IN_NODE (DATA_PILOT_SUBTOTALS, DATA_PILOT_SUBTOTAL, OO_NS_TABLE, "data-pilot-subtotal", GSF_XML_NO_CONTENT, NULL, NULL),
12279 GSF_XML_IN_NODE (DATA_PILOT_FIELD, DATA_PILOT_GROUPS, OO_NS_TABLE, "data-pilot-groups", GSF_XML_NO_CONTENT, NULL, NULL),
12280 GSF_XML_IN_NODE (SPREADSHEET, CONTENT_VALIDATIONS, OO_NS_TABLE, "content-validations", GSF_XML_NO_CONTENT, NULL, NULL),
12281 GSF_XML_IN_NODE (CONTENT_VALIDATIONS, CONTENT_VALIDATION, OO_NS_TABLE, "content-validation", GSF_XML_NO_CONTENT, &odf_validation, NULL),
12282 GSF_XML_IN_NODE (CONTENT_VALIDATION, ERROR_MESSAGE, OO_NS_TABLE, "error-message", GSF_XML_NO_CONTENT, &odf_validation_error_message , &odf_validation_error_message_end),
12283 GSF_XML_IN_NODE (ERROR_MESSAGE, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_CONTENT, &odf_text_content_start, &odf_text_content_end),
12284 GSF_XML_IN_NODE (TEXT_CONTENT, TEXT_S, OO_NS_TEXT, "s", GSF_XML_NO_CONTENT, &odf_text_space, NULL),
12285 GSF_XML_IN_NODE_FULL (TEXT_CONTENT, TEXT_LINE_BREAK, OO_NS_TEXT, "line-break", GSF_XML_NO_CONTENT, FALSE, FALSE, &odf_text_symbol, NULL, .v_str = "\n"),
12286 GSF_XML_IN_NODE_FULL (TEXT_CONTENT, TEXT_TAB, OO_NS_TEXT, "tab", GSF_XML_SHARED_CONTENT, FALSE, FALSE, odf_text_symbol, NULL, .v_str = "\t"),
12287 GSF_XML_IN_NODE (TEXT_CONTENT, TEXT_SPAN, OO_NS_TEXT, "span", GSF_XML_SHARED_CONTENT, &odf_text_span_start, &odf_text_span_end),
12288 GSF_XML_IN_NODE (TEXT_SPAN, TEXT_SPAN, OO_NS_TEXT, "span", GSF_XML_2ND, NULL, NULL),
12289 GSF_XML_IN_NODE (TEXT_SPAN, TEXT_S, OO_NS_TEXT, "s", GSF_XML_2ND, NULL, NULL),
12290 GSF_XML_IN_NODE (TEXT_SPAN, TEXT_LINE_BREAK, OO_NS_TEXT, "line-break", GSF_XML_2ND, NULL, NULL),
12291 GSF_XML_IN_NODE (TEXT_SPAN, TEXT_TAB, OO_NS_TEXT, "tab", GSF_XML_2ND, NULL, NULL),
12292 GSF_XML_IN_NODE (TEXT_SPAN, TEXT_ADDR, OO_NS_TEXT, "a", GSF_XML_SHARED_CONTENT, &oo_cell_content_link, NULL),
12293 GSF_XML_IN_NODE (TEXT_ADDR, TEXT_S, OO_NS_TEXT, "s", GSF_XML_2ND, NULL, NULL),
12294 GSF_XML_IN_NODE (TEXT_ADDR, TEXT_TAB, OO_NS_TEXT, "tab", GSF_XML_2ND, NULL, NULL),
12295 GSF_XML_IN_NODE (TEXT_ADDR, TEXT_SPAN, OO_NS_TEXT, "span", GSF_XML_2ND, NULL, NULL),
12296 GSF_XML_IN_NODE (CONTENT_VALIDATION, HELP_MESSAGE, OO_NS_TABLE, "help-message", GSF_XML_NO_CONTENT, &odf_validation_help_message , &odf_validation_help_message_end),
12297 GSF_XML_IN_NODE (HELP_MESSAGE, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12298 GSF_XML_IN_NODE (SPREADSHEET, CALC_SETTINGS, OO_NS_TABLE, "calculation-settings", GSF_XML_NO_CONTENT, NULL, NULL),
12299 GSF_XML_IN_NODE (CALC_SETTINGS, ITERATION, OO_NS_TABLE, "iteration", GSF_XML_NO_CONTENT, &oo_iteration, NULL),
12300 GSF_XML_IN_NODE (CALC_SETTINGS, DATE_CONVENTION, OO_NS_TABLE, "null-date", GSF_XML_NO_CONTENT, &oo_date_convention, NULL),
12301 GSF_XML_IN_NODE (SPREADSHEET, CHART, OO_NS_CHART, "chart", GSF_XML_NO_CONTENT, NULL, NULL),
12302 GSF_XML_IN_NODE (OFFICE_BODY, OFFICE_CHART, OO_NS_OFFICE, "chart", GSF_XML_NO_CONTENT, NULL, NULL),
12303 GSF_XML_IN_NODE (OFFICE_CHART, CHART_CHART, OO_NS_CHART, "chart", GSF_XML_NO_CONTENT, &oo_chart, &oo_chart_end),
12304 GSF_XML_IN_NODE (CHART_CHART, CHART_TABLE, OO_NS_TABLE, "table", GSF_XML_NO_CONTENT, NULL, NULL),
12305 GSF_XML_IN_NODE (CHART_TABLE, CHART_TABLE_ROWS, OO_NS_TABLE, "table-rows", GSF_XML_NO_CONTENT, NULL, NULL),
12306 GSF_XML_IN_NODE (CHART_TABLE_ROWS, CHART_TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_NO_CONTENT, NULL, NULL),
12307 GSF_XML_IN_NODE (CHART_TABLE_ROW, CHART_TABLE_CELL, OO_NS_TABLE, "table-cell", GSF_XML_NO_CONTENT, NULL, NULL),
12308 GSF_XML_IN_NODE (CHART_TABLE_CELL, CHART_CELL_P, OO_NS_TEXT, "p", GSF_XML_NO_CONTENT, NULL, NULL),
12309 GSF_XML_IN_NODE (CHART_TABLE_CELL, CHART_CELL_DRAW_G, OO_NS_DRAW, "g", GSF_XML_NO_CONTENT, NULL, NULL),
12310 GSF_XML_IN_NODE (CHART_CELL_DRAW_G, CHART_CELL_SVG_DESC, OO_NS_SVG, "desc", GSF_XML_NO_CONTENT, NULL, NULL),
12311 GSF_XML_IN_NODE (CHART_TABLE, CHART_TABLE_COLS, OO_NS_TABLE, "table-columns", GSF_XML_NO_CONTENT, NULL, NULL),
12312 GSF_XML_IN_NODE (CHART_TABLE_COLS, CHART_TABLE_COL, OO_NS_TABLE, "table-column", GSF_XML_NO_CONTENT, NULL, NULL),
12313 GSF_XML_IN_NODE (CHART_TABLE, CHART_TABLE_HROWS, OO_NS_TABLE, "table-header-rows", GSF_XML_NO_CONTENT, NULL, NULL),
12314 GSF_XML_IN_NODE (CHART_TABLE_HROWS, CHART_TABLE_HROW, OO_NS_TABLE, "table-header-row", GSF_XML_NO_CONTENT, NULL, NULL),
12315 GSF_XML_IN_NODE (CHART_TABLE_HROWS, CHART_TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_2ND, NULL, NULL),
12316 GSF_XML_IN_NODE (CHART_TABLE, CHART_TABLE_HCOLS, OO_NS_TABLE, "table-header-columns", GSF_XML_NO_CONTENT, NULL, NULL),
12317 GSF_XML_IN_NODE (CHART_TABLE_HCOLS, CHART_TABLE_HCOL, OO_NS_TABLE, "table-header-column", GSF_XML_NO_CONTENT, NULL, NULL),
12318 GSF_XML_IN_NODE (CHART_TABLE_HCOLS, CHART_TABLE_COL, OO_NS_TABLE, "table-column", GSF_XML_2ND, NULL, NULL),
12320 GSF_XML_IN_NODE_FULL (CHART_CHART, CHART_TITLE, OO_NS_CHART, "title", GSF_XML_NO_CONTENT, FALSE, FALSE, &oo_chart_title, &oo_chart_title_end, .v_int = 0),
12321 GSF_XML_IN_NODE (CHART_TITLE, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12322 GSF_XML_IN_NODE_FULL (CHART_CHART, CHART_SUBTITLE, OO_NS_CHART, "subtitle", GSF_XML_NO_CONTENT, FALSE, FALSE, &oo_chart_title, &oo_chart_title_end, .v_int = 1),
12323 GSF_XML_IN_NODE (CHART_SUBTITLE, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12324 GSF_XML_IN_NODE_FULL (CHART_CHART, CHART_FOOTER, OO_NS_CHART, "footer", GSF_XML_NO_CONTENT, FALSE, FALSE, &oo_chart_title, &oo_chart_title_end, .v_int = 2),
12325 GSF_XML_IN_NODE (CHART_FOOTER, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12326 GSF_XML_IN_NODE (CHART_CHART, CHART_LEGEND, OO_NS_CHART, "legend", GSF_XML_NO_CONTENT, &oo_legend, NULL),
12327 GSF_XML_IN_NODE (CHART_LEGEND, CHART_LEGEND_TITLE, OO_GNUM_NS_EXT, "title", GSF_XML_NO_CONTENT, &oo_chart_title, &oo_chart_title_end),
12328 GSF_XML_IN_NODE (CHART_LEGEND_TITLE, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12329 GSF_XML_IN_NODE (CHART_CHART, CHART_COLOR_SCALE, OO_GNUM_NS_EXT, "color-scale", GSF_XML_NO_CONTENT, &oo_color_scale, NULL),
12330 GSF_XML_IN_NODE (CHART_CHART, CHART_PLOT_AREA, OO_NS_CHART, "plot-area", GSF_XML_NO_CONTENT, &oo_plot_area, &oo_plot_area_end),
12331 GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_SERIES, OO_NS_CHART, "series", GSF_XML_NO_CONTENT, &oo_plot_series, &oo_plot_series_end),
12332 GSF_XML_IN_NODE (CHART_SERIES, SERIES_DOMAIN, OO_NS_CHART, "domain", GSF_XML_NO_CONTENT, &oo_series_domain, NULL),
12333 GSF_XML_IN_NODE (CHART_SERIES, SERIES_DATA_PT, OO_NS_CHART, "data-point", GSF_XML_NO_CONTENT, &oo_series_pt, NULL),
12334 GSF_XML_IN_NODE (CHART_SERIES, SERIES_DATA_ERR, OO_NS_CHART, "error-indicator", GSF_XML_NO_CONTENT, NULL, NULL),
12335 GSF_XML_IN_NODE (CHART_SERIES, SERIES_REGRESSION, OO_NS_CHART, "regression-curve", GSF_XML_NO_CONTENT, &od_series_regression, NULL),
12336 GSF_XML_IN_NODE (SERIES_REGRESSION, SERIES_REG_EQ, OO_NS_CHART, "equation", GSF_XML_NO_CONTENT, &od_series_reg_equation, NULL),
12337 GSF_XML_IN_NODE (SERIES_REGRESSION, SERIES_REG_EQ_GNM, OO_GNUM_NS_EXT, "equation", GSF_XML_NO_CONTENT, &od_series_reg_equation, NULL),
12338 GSF_XML_IN_NODE (CHART_SERIES, SERIES_REGRESSION_MULTIPLE, OO_GNUM_NS_EXT, "regression-curve", GSF_XML_NO_CONTENT, &od_series_regression, NULL),
12339 GSF_XML_IN_NODE (SERIES_REGRESSION_MULTIPLE, SERIES_REG_EQ, OO_NS_CHART, "equation", GSF_XML_2ND, NULL, NULL),
12340 GSF_XML_IN_NODE (SERIES_REGRESSION_MULTIPLE, SERIES_REG_EQ_GNM, OO_GNUM_NS_EXT, "equation", GSF_XML_2ND, NULL, NULL),
12341 GSF_XML_IN_NODE (CHART_SERIES, SERIES_DROPLINES, OO_GNUM_NS_EXT, "droplines", GSF_XML_NO_CONTENT, &oo_series_droplines, NULL),
12342 GSF_XML_IN_NODE (CHART_SERIES, SERIES_SERIESLINES, OO_GNUM_NS_EXT, "serieslines", GSF_XML_NO_CONTENT, &oo_series_serieslines, NULL),
12343 GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_WALL, OO_NS_CHART, "wall", GSF_XML_NO_CONTENT, &oo_chart_wall, NULL),
12344 GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_FLOOR, OO_NS_CHART, "floor", GSF_XML_NO_CONTENT, NULL, NULL),
12345 GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_AXIS, OO_NS_CHART, "axis", GSF_XML_NO_CONTENT, &oo_chart_axis, &oo_chart_axis_end),
12346 GSF_XML_IN_NODE (CHART_PLOT_AREA, GNM_CHART_AXIS, OO_GNUM_NS_EXT, "axis", GSF_XML_NO_CONTENT, &oo_chart_axis, &oo_chart_axis_end),
12347 GSF_XML_IN_NODE (CHART_AXIS, CHART_AXIS_LINE, OO_GNUM_NS_EXT, "axisline", GSF_XML_NO_CONTENT, &oo_chart_axisline, NULL),
12348 GSF_XML_IN_NODE (CHART_AXIS, CHART_GRID, OO_NS_CHART, "grid", GSF_XML_NO_CONTENT, &oo_chart_grid, NULL),
12349 GSF_XML_IN_NODE (CHART_AXIS, CHART_AXIS_CAT, OO_NS_CHART, "categories", GSF_XML_NO_CONTENT, &od_chart_axis_categories, NULL),
12350 GSF_XML_IN_NODE_FULL (CHART_AXIS, CHART_AXIS_TITLE, OO_NS_CHART, "title", GSF_XML_NO_CONTENT, FALSE, FALSE, &oo_chart_title, &oo_chart_title_end, .v_int = 3),
12351 GSF_XML_IN_NODE (GNM_CHART_AXIS, GNM_CHART_AXIS_LINE, OO_GNUM_NS_EXT, "axisline", GSF_XML_NO_CONTENT, &oo_chart_axisline, NULL),
12352 GSF_XML_IN_NODE (GNM_CHART_AXIS, GNM_CHART_GRID, OO_NS_CHART, "grid", GSF_XML_NO_CONTENT, &oo_chart_grid, NULL),
12353 GSF_XML_IN_NODE (GNM_CHART_AXIS, GNM_CHART_AXIS_CAT, OO_NS_CHART, "categories", GSF_XML_NO_CONTENT, &od_chart_axis_categories, NULL),
12354 GSF_XML_IN_NODE_FULL (GNM_CHART_AXIS, GNM_CHART_AXIS_TITLE, OO_NS_CHART, "title", GSF_XML_NO_CONTENT, FALSE, FALSE, &oo_chart_title, &oo_chart_title_end, .v_int = 3),
12355 GSF_XML_IN_NODE (CHART_AXIS_TITLE, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12356 GSF_XML_IN_NODE (CHART_PLOT_AREA, CHART_OOO_COORDINATE_REGION, OO_NS_CHART_OOO, "coordinate-region", GSF_XML_NO_CONTENT, NULL, NULL),
12357 GSF_XML_IN_NODE (SPREADSHEET, TABLE, OO_NS_TABLE, "table", GSF_XML_NO_CONTENT, &oo_table_start, &oo_table_end),
12358 GSF_XML_IN_NODE (TABLE, SHEET_SELECTIONS, OO_GNUM_NS_EXT, "selections", GSF_XML_NO_CONTENT, &odf_selection, &odf_selection_end),
12359 GSF_XML_IN_NODE (SHEET_SELECTIONS, SELECTION, OO_GNUM_NS_EXT, "selection", GSF_XML_NO_CONTENT, &odf_selection_range, NULL),
12360 GSF_XML_IN_NODE (TABLE, TABLE_SOURCE, OO_NS_TABLE, "table-source", GSF_XML_NO_CONTENT, NULL, NULL),
12361 GSF_XML_IN_NODE (TABLE, TABLE_SHAPES, OO_NS_TABLE, "shapes", GSF_XML_NO_CONTENT, &odf_shapes, &odf_shapes_end),
12362 GSF_XML_IN_NODE (TABLE_SHAPES, DRAW_FRAME, OO_NS_DRAW, "frame", GSF_XML_NO_CONTENT, &od_draw_frame_start, &od_draw_frame_end),
12363 GSF_XML_IN_NODE (TABLE_SHAPES, DRAW_CAPTION, OO_NS_DRAW, "caption", GSF_XML_NO_CONTENT, &odf_caption, &od_draw_text_frame_end),
12364 GSF_XML_IN_NODE (DRAW_CAPTION, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12365 GSF_XML_IN_NODE (TABLE_SHAPES, DRAW_CUSTOM_SHAPE, OO_NS_DRAW, "custom-shape", GSF_XML_NO_CONTENT, &odf_custom_shape, &odf_custom_shape_end),
12366 GSF_XML_IN_NODE (DRAW_CUSTOM_SHAPE, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12367 GSF_XML_IN_NODE (DRAW_CUSTOM_SHAPE, DRAW_ENHANCED_GEOMETRY, OO_NS_DRAW, "enhanced-geometry", GSF_XML_NO_CONTENT, &odf_custom_shape_enhanced_geometry, NULL),
12368 GSF_XML_IN_NODE (DRAW_ENHANCED_GEOMETRY, DRAW_ENHANCED_GEOMETRY_EQUATION, OO_NS_DRAW, "equation", GSF_XML_NO_CONTENT, odf_custom_shape_equation, NULL),
12369 GSF_XML_IN_NODE (DRAW_ENHANCED_GEOMETRY, DRAW_ENHANCED_GEOMETRY_HANDLE, OO_NS_DRAW, "handle", GSF_XML_NO_CONTENT, NULL, NULL),
12370 GSF_XML_IN_NODE (TABLE_SHAPES, DRAW_ELLIPSE, OO_NS_DRAW, "ellipse", GSF_XML_NO_CONTENT, &odf_ellipse, &od_draw_text_frame_end),
12371 GSF_XML_IN_NODE (DRAW_ELLIPSE, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12372 GSF_XML_IN_NODE (TABLE_SHAPES, DRAW_LINE, OO_NS_DRAW, "line", GSF_XML_NO_CONTENT, &odf_line, &odf_line_end),
12373 GSF_XML_IN_NODE (DRAW_LINE, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12374 GSF_XML_IN_NODE (TABLE_SHAPES, DRAW_RECT, OO_NS_DRAW, "rect", GSF_XML_NO_CONTENT, &odf_rect, &od_draw_text_frame_end),
12375 GSF_XML_IN_NODE (DRAW_RECT, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12376 GSF_XML_IN_NODE (TABLE, FORMS, OO_NS_OFFICE, "forms", GSF_XML_NO_CONTENT, NULL, NULL),
12377 GSF_XML_IN_NODE (FORMS, FORM, OO_NS_FORM, "form", GSF_XML_NO_CONTENT, NULL, NULL),
12378 GSF_XML_IN_NODE (FORM, FORM_PROPERTIES, OO_NS_FORM, "properties", GSF_XML_NO_CONTENT, NULL, NULL),
12379 GSF_XML_IN_NODE (FORM_PROPERTIES, FORM_PROPERTY, OO_NS_FORM, "property", GSF_XML_NO_CONTENT, &odf_control_property, NULL),
12380 GSF_XML_IN_NODE (FORM_PROPERTIES, FORM_LIST_PROPERTY, OO_NS_FORM, "list-property", GSF_XML_NO_CONTENT, NULL, NULL),
12381 GSF_XML_IN_NODE (FORM, FORM_BUTTON, OO_NS_FORM, "button", GSF_XML_NO_CONTENT, &odf_form_button, &odf_form_control_end),
12382 GSF_XML_IN_NODE (FORM_BUTTON, FORM_PROPERTIES, OO_NS_FORM, "properties", GSF_XML_2ND, NULL, NULL),
12383 GSF_XML_IN_NODE (FORM_BUTTON, BUTTON_OFFICE_EVENT_LISTENERS, OO_NS_OFFICE, "event-listeners", GSF_XML_NO_CONTENT, NULL, NULL),
12384 GSF_XML_IN_NODE (BUTTON_OFFICE_EVENT_LISTENERS, BUTTON_EVENT_LISTENER, OO_NS_SCRIPT, "event-listener", GSF_XML_NO_CONTENT, &odf_button_event_listener, NULL),
12385 GSF_XML_IN_NODE (FORM, FORM_VALUE_RANGE, OO_NS_FORM, "value-range", GSF_XML_NO_CONTENT, &odf_form_value_range, NULL),
12386 GSF_XML_IN_NODE (FORM_VALUE_RANGE, FORM_PROPERTIES, OO_NS_FORM, "properties", GSF_XML_2ND, NULL, NULL),
12387 GSF_XML_IN_NODE (FORM, FORM_CHECKBOX, OO_NS_FORM, "checkbox", GSF_XML_NO_CONTENT, &odf_form_checkbox, NULL),
12388 GSF_XML_IN_NODE (FORM_CHECKBOX, FORM_PROPERTIES, OO_NS_FORM, "properties", GSF_XML_2ND, NULL, NULL),
12389 GSF_XML_IN_NODE (FORM, FORM_RADIO, OO_NS_FORM, "radio", GSF_XML_NO_CONTENT, &odf_form_radio, NULL),
12390 GSF_XML_IN_NODE (FORM_RADIO, FORM_PROPERTIES, OO_NS_FORM, "properties", GSF_XML_2ND, NULL, NULL),
12391 GSF_XML_IN_NODE (FORM, FORM_LISTBOX, OO_NS_FORM, "listbox", GSF_XML_NO_CONTENT, &odf_form_listbox, NULL),
12392 GSF_XML_IN_NODE (FORM_LISTBOX, FORM_PROPERTIES, OO_NS_FORM, "properties", GSF_XML_2ND, NULL, NULL),
12393 GSF_XML_IN_NODE (FORM, FORM_COMBOBOX, OO_NS_FORM, "combobox", GSF_XML_NO_CONTENT, &odf_form_combobox, NULL),
12394 GSF_XML_IN_NODE (FORM_COMBOBOX, FORM_PROPERTIES, OO_NS_FORM, "properties", GSF_XML_2ND, NULL, NULL),
12395 GSF_XML_IN_NODE (FORM, FORM_GENERIC, OO_NS_FORM, "generic-control", GSF_XML_NO_CONTENT, &odf_form_generic, &odf_form_control_end),
12396 GSF_XML_IN_NODE (FORM_GENERIC, FORM_PROPERTIES, OO_NS_FORM, "properties", GSF_XML_2ND, NULL, NULL),
12397 GSF_XML_IN_NODE (TABLE, TABLE_ROWS, OO_NS_TABLE, "table-rows", GSF_XML_NO_CONTENT, NULL, NULL),
12398 GSF_XML_IN_NODE (TABLE, TABLE_H_ROWS, OO_NS_TABLE, "table-header-rows", GSF_XML_NO_CONTENT, &odf_table_header_rows, &odf_table_header_rows_end),
12399 GSF_XML_IN_NODE (TABLE, TABLE_COL, OO_NS_TABLE, "table-column", GSF_XML_NO_CONTENT, &oo_col_start, NULL),
12400 GSF_XML_IN_NODE (TABLE, TABLE_COLS, OO_NS_TABLE, "table-columns", GSF_XML_NO_CONTENT, NULL, NULL),
12401 GSF_XML_IN_NODE (TABLE, TABLE_H_COLS, OO_NS_TABLE, "table-header-columns", GSF_XML_NO_CONTENT, &odf_table_header_cols, &odf_table_header_cols_end),
12402 GSF_XML_IN_NODE (TABLE_H_COLS, TABLE_COL, OO_NS_TABLE, "table-column", GSF_XML_2ND, NULL, NULL),
12403 GSF_XML_IN_NODE (TABLE_COLS, TABLE_COL, OO_NS_TABLE, "table-column", GSF_XML_2ND, NULL, NULL),
12404 GSF_XML_IN_NODE (TABLE, TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_NO_CONTENT, &oo_row_start, &oo_row_end),
12405 GSF_XML_IN_NODE (TABLE, SOFTPAGEBREAK, OO_NS_TEXT, "soft-page-break", GSF_XML_NO_CONTENT, NULL, NULL),
12406 GSF_XML_IN_NODE (TABLE_ROWS, TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_2ND, NULL, NULL),
12407 GSF_XML_IN_NODE (TABLE_H_ROWS, TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_2ND, NULL, NULL),
12408 GSF_XML_IN_NODE (TABLE_ROWS, SOFTPAGEBREAK, OO_NS_TEXT, "soft-page-break", GSF_XML_2ND, NULL, NULL),
12409 GSF_XML_IN_NODE (TABLE_H_ROWS, SOFTPAGEBREAK, OO_NS_TEXT, "soft-page-break", GSF_XML_2ND, NULL, NULL),
12410 GSF_XML_IN_NODE (TABLE_ROW, TABLE_CELL, OO_NS_TABLE, "table-cell", GSF_XML_NO_CONTENT, &oo_cell_start, &oo_cell_end),
12411 GSF_XML_IN_NODE (TABLE_CELL, DETECTIVE, OO_NS_TABLE, "detective", GSF_XML_NO_CONTENT, NULL, NULL),
12412 GSF_XML_IN_NODE (DETECTIVE, DETECTIVE_OPERATION, OO_NS_TABLE, "operation", GSF_XML_NO_CONTENT, NULL, NULL),
12413 GSF_XML_IN_NODE (TABLE_CELL, DRAW_CUSTOM_SHAPE, OO_NS_DRAW, "custom-shape", GSF_XML_2ND, NULL, NULL),
12414 GSF_XML_IN_NODE (TABLE_CELL, CELL_TEXT, OO_NS_TEXT, "p", GSF_XML_CONTENT, &oo_cell_content_start, &oo_cell_content_end),
12415 GSF_XML_IN_NODE (CELL_TEXT, DRAW_CUSTOM_SHAPE, OO_NS_DRAW, "custom-shape", GSF_XML_2ND, NULL, NULL),
12416 GSF_XML_IN_NODE (CELL_TEXT, TEXT_S, OO_NS_TEXT, "s", GSF_XML_2ND, NULL, NULL),
12417 GSF_XML_IN_NODE (CELL_TEXT, TEXT_ADDR, OO_NS_TEXT, "a", GSF_XML_2ND, NULL, NULL),
12418 GSF_XML_IN_NODE (CELL_TEXT, TEXT_LINE_BREAK, OO_NS_TEXT, "line-break", GSF_XML_2ND, NULL, NULL),
12419 GSF_XML_IN_NODE (CELL_TEXT, TEXT_TAB, OO_NS_TEXT, "tab", GSF_XML_2ND,NULL, NULL ),
12420 GSF_XML_IN_NODE (CELL_TEXT, TEXT_SPAN, OO_NS_TEXT, "span", GSF_XML_2ND, NULL, NULL),
12421 GSF_XML_IN_NODE (TABLE_CELL, CELL_OBJECT, OO_NS_DRAW, "object", GSF_XML_NO_CONTENT, NULL, NULL), /* ignore for now */
12422 GSF_XML_IN_NODE (TABLE_CELL, CELL_GRAPHIC, OO_NS_DRAW, "g", GSF_XML_NO_CONTENT, NULL, NULL), /* ignore for now */
12423 GSF_XML_IN_NODE (CELL_GRAPHIC, CELL_GRAPHIC, OO_NS_DRAW, "g", GSF_XML_2ND, NULL, NULL),
12424 GSF_XML_IN_NODE (CELL_GRAPHIC, DRAW_POLYLINE, OO_NS_DRAW, "polyline", GSF_XML_NO_CONTENT, NULL, NULL),
12425 GSF_XML_IN_NODE (TABLE_CELL, DRAW_CONTROL, OO_NS_DRAW, "control", GSF_XML_NO_CONTENT, &od_draw_control_start, NULL),
12426 GSF_XML_IN_NODE (TABLE_CELL, DRAW_RECT, OO_NS_DRAW, "rect", GSF_XML_2ND, NULL, NULL),
12427 GSF_XML_IN_NODE (TABLE_CELL, DRAW_LINE, OO_NS_DRAW, "line", GSF_XML_2ND, NULL, NULL),
12428 GSF_XML_IN_NODE (TABLE_CELL, DRAW_ELLIPSE, OO_NS_DRAW, "ellipse", GSF_XML_2ND, NULL, NULL),
12429 GSF_XML_IN_NODE (TABLE_CELL, DRAW_FRAME, OO_NS_DRAW, "frame", GSF_XML_2ND, NULL, NULL),
12430 GSF_XML_IN_NODE (DRAW_FRAME, DRAW_OBJECT, OO_NS_DRAW, "object", GSF_XML_NO_CONTENT, &od_draw_object, NULL),
12431 GSF_XML_IN_NODE (DRAW_OBJECT, DRAW_OBJECT_TEXT, OO_NS_TEXT, "p", GSF_XML_CONTENT, NULL, NULL),
12433 GSF_XML_IN_NODE (DRAW_FRAME, DRAW_IMAGE, OO_NS_DRAW, "image", GSF_XML_NO_CONTENT, &od_draw_image, NULL),
12434 GSF_XML_IN_NODE (DRAW_IMAGE, DRAW_IMAGE_TEXT,OO_NS_TEXT, "p", GSF_XML_NO_CONTENT, NULL, NULL),
12435 GSF_XML_IN_NODE (DRAW_FRAME, SVG_DESC, OO_NS_SVG, "desc", GSF_XML_NO_CONTENT, NULL, NULL),
12436 GSF_XML_IN_NODE (DRAW_FRAME, SVG_TITLE, OO_NS_SVG, "title", GSF_XML_NO_CONTENT, NULL, NULL),
12437 GSF_XML_IN_NODE (DRAW_FRAME, DRAW_TEXT_BOX, OO_NS_DRAW, "text-box", GSF_XML_NO_CONTENT, &od_draw_text_box, od_draw_text_frame_end),
12438 GSF_XML_IN_NODE (DRAW_TEXT_BOX, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12439 GSF_XML_IN_NODE (TABLE_CELL, CELL_ANNOTATION, OO_NS_OFFICE, "annotation", GSF_XML_NO_CONTENT, &odf_annotation_start, &odf_annotation_end),
12440 GSF_XML_IN_NODE (CELL_ANNOTATION, TEXT_CONTENT, OO_NS_TEXT, "p", GSF_XML_2ND, NULL, NULL),
12441 GSF_XML_IN_NODE (CELL_ANNOTATION, CELL_ANNOTATION_AUTHOR, OO_NS_DC, "creator", GSF_XML_CONTENT, NULL, &odf_annotation_author_end),
12442 GSF_XML_IN_NODE (CELL_ANNOTATION, CELL_ANNOTATION_DATE, OO_NS_DC, "date", GSF_XML_NO_CONTENT, NULL, NULL),
12444 GSF_XML_IN_NODE (TABLE_ROW, TABLE_COVERED_CELL, OO_NS_TABLE, "covered-table-cell", GSF_XML_NO_CONTENT, &oo_covered_cell_start, &oo_covered_cell_end),
12445 GSF_XML_IN_NODE (TABLE_COVERED_CELL, COVERED_CELL_TEXT, OO_NS_TEXT, "p", GSF_XML_NO_CONTENT, NULL, NULL),
12446 GSF_XML_IN_NODE (COVERED_CELL_TEXT, COVERED_CELL_TEXT_S, OO_NS_TEXT, "s", GSF_XML_NO_CONTENT, NULL, NULL),
12447 GSF_XML_IN_NODE (TABLE_COVERED_CELL, DRAW_CONTROL, OO_NS_DRAW, "control", GSF_XML_NO_CONTENT, NULL, NULL),
12449 GSF_XML_IN_NODE (TABLE, TABLE_COL_GROUP, OO_NS_TABLE, "table-column-group", GSF_XML_NO_CONTENT, NULL, NULL),
12450 GSF_XML_IN_NODE (TABLE_COL_GROUP, TABLE_COL_GROUP, OO_NS_TABLE, "table-column-group", GSF_XML_NO_CONTENT, NULL, NULL),
12451 GSF_XML_IN_NODE (TABLE_COL_GROUP, TABLE_H_COLS, OO_NS_TABLE, "table-header-columns", GSF_XML_2ND, NULL, NULL),
12452 GSF_XML_IN_NODE (TABLE_COL_GROUP, TABLE_COL, OO_NS_TABLE, "table-column", GSF_XML_2ND, NULL, NULL),
12453 GSF_XML_IN_NODE (TABLE_ROW_GROUP, TABLE_ROW_GROUP, OO_NS_TABLE, "table-row-group", GSF_XML_NO_CONTENT, NULL, NULL),
12454 GSF_XML_IN_NODE (TABLE, TABLE_ROW_GROUP, OO_NS_TABLE, "table-row-group", GSF_XML_NO_CONTENT, NULL, NULL),
12455 GSF_XML_IN_NODE (TABLE_ROW_GROUP, TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_2ND, NULL, NULL),
12456 GSF_XML_IN_NODE (TABLE, NAMED_EXPRS, OO_NS_TABLE, "named-expressions", GSF_XML_NO_CONTENT, NULL, NULL),
12457 GSF_XML_IN_NODE (SPREADSHEET, NAMED_EXPRS, OO_NS_TABLE, "named-expressions", GSF_XML_2ND, NULL, NULL),
12458 GSF_XML_IN_NODE (NAMED_EXPRS, NAMED_EXPR, OO_NS_TABLE, "named-expression", GSF_XML_NO_CONTENT, &oo_named_expr, NULL),
12459 GSF_XML_IN_NODE (NAMED_EXPRS, NAMED_RANGE, OO_NS_TABLE, "named-range", GSF_XML_NO_CONTENT, &oo_named_expr, NULL),
12461 GSF_XML_IN_NODE (SPREADSHEET, DB_RANGES, OO_NS_TABLE, "database-ranges", GSF_XML_NO_CONTENT, NULL, NULL),
12462 GSF_XML_IN_NODE (DB_RANGES, DB_RANGE, OO_NS_TABLE, "database-range", GSF_XML_NO_CONTENT, &oo_db_range_start, &oo_db_range_end),
12463 GSF_XML_IN_NODE (DB_RANGE, FILTER, OO_NS_TABLE, "filter", GSF_XML_NO_CONTENT, NULL, NULL),
12464 GSF_XML_IN_NODE (FILTER, FILTER_COND, OO_NS_TABLE, "filter-condition", GSF_XML_NO_CONTENT, &oo_filter_cond, NULL),
12465 GSF_XML_IN_NODE (FILTER, FILTER_AND, OO_NS_TABLE, "filter-and", GSF_XML_NO_CONTENT, NULL, NULL),
12466 GSF_XML_IN_NODE (FILTER_AND, FILTER_OR, OO_NS_TABLE, "filter-or", GSF_XML_NO_CONTENT, &odf_filter_or, NULL),
12467 GSF_XML_IN_NODE (FILTER_OR, FILTER_COND_IGNORE, OO_NS_TABLE, "filter-condition", GSF_XML_NO_CONTENT, NULL, NULL),
12468 GSF_XML_IN_NODE (FILTER_OR, FILTER_AND_IGNORE, OO_NS_TABLE, "filter-or", GSF_XML_NO_CONTENT, NULL, NULL),
12469 GSF_XML_IN_NODE (FILTER_AND, FILTER_COND, OO_NS_TABLE, "filter-condition", GSF_XML_2ND, NULL, NULL),
12470 GSF_XML_IN_NODE (FILTER, FILTER_OR, OO_NS_TABLE, "filter-or", GSF_XML_2ND, NULL, NULL),
12471 GSF_XML_IN_NODE (DB_RANGE, TABLE_SORT, OO_NS_TABLE, "sort", GSF_XML_NO_CONTENT, NULL, NULL),
12472 GSF_XML_IN_NODE (TABLE_SORT, SORT_BY, OO_NS_TABLE, "sort-by", GSF_XML_NO_CONTENT, NULL, NULL),
12474 GSF_XML_IN_NODE_END
12477 static GsfXMLInNode const opendoc_content_preparse_overrides[] =
12479 GSF_XML_IN_NODE (OFFICE_BODY, SPREADSHEET, OO_NS_OFFICE, "spreadsheet", GSF_XML_NO_CONTENT, NULL, &odf_preparse_spreadsheet_end),
12480 GSF_XML_IN_NODE (SPREADSHEET, TABLE, OO_NS_TABLE, "table", GSF_XML_NO_CONTENT, &odf_preparse_table_start, &odf_preparse_table_end),
12481 GSF_XML_IN_NODE (TABLE, TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_NO_CONTENT, &odf_preparse_row_start, &odf_preparse_row_end),
12482 GSF_XML_IN_NODE (TABLE_ROW, TABLE_CELL, OO_NS_TABLE, "table-cell", GSF_XML_NO_CONTENT, &odf_preparse_cell_start, NULL),
12483 GSF_XML_IN_NODE (TABLE_ROW, TABLE_COVERED_CELL, OO_NS_TABLE, "covered-table-cell", GSF_XML_NO_CONTENT, &odf_preparse_covered_cell_start, NULL),
12484 GSF_XML_IN_NODE (TABLE, NAMED_EXPRS, OO_NS_TABLE, "named-expressions", GSF_XML_NO_CONTENT, &oo_named_exprs_preparse, NULL),
12485 GSF_XML_IN_NODE (NAMED_EXPRS, NAMED_EXPR, OO_NS_TABLE, "named-expression", GSF_XML_NO_CONTENT, &oo_named_expr_preparse, NULL),
12486 GSF_XML_IN_NODE (NAMED_EXPRS, NAMED_RANGE, OO_NS_TABLE, "named-range", GSF_XML_NO_CONTENT, &oo_named_expr_preparse, NULL),
12487 GSF_XML_IN_NODE_END
12489 static GsfXMLInNode const *opendoc_content_preparse_dtd;
12492 static GsfXMLInNode const ooo1_content_preparse_overrides [] =
12494 GSF_XML_IN_NODE (OFFICE_BODY, TABLE, OO_NS_TABLE, "table", GSF_XML_NO_CONTENT, &odf_preparse_table_start, &odf_preparse_table_end),
12495 GSF_XML_IN_NODE (TABLE, TABLE_ROW, OO_NS_TABLE, "table-row", GSF_XML_NO_CONTENT, &odf_preparse_row_start, &odf_preparse_row_end),
12496 GSF_XML_IN_NODE (TABLE_ROW, TABLE_CELL, OO_NS_TABLE, "table-cell", GSF_XML_NO_CONTENT, &odf_preparse_cell_start, NULL),
12497 GSF_XML_IN_NODE (TABLE_ROW, TABLE_COVERED_CELL, OO_NS_TABLE, "covered-table-cell", GSF_XML_NO_CONTENT, &odf_preparse_covered_cell_start, NULL),
12498 GSF_XML_IN_NODE_END
12500 static GsfXMLInNode const *ooo1_content_preparse_dtd;
12503 static GsfXMLInNode const *get_dtd () { return opendoc_content_dtd; }
12504 static GsfXMLInNode const *get_styles_dtd () { return styles_dtd; }
12506 /****************************************************************************/
12508 static GnmExpr const *
12509 odf_func_address_handler (GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
12511 guint argc = gnm_expr_list_length (args);
12513 if (argc == 4 && convs->sheet_name_sep == '!') {
12514 /* Openoffice was missing the A1 parameter */
12515 GnmExprList *new_args;
12516 GnmFunc *f = gnm_func_lookup_or_add_placeholder ("ADDRESS");
12518 new_args = g_slist_insert ((GSList *) args,
12519 (gpointer) gnm_expr_new_constant (value_new_int (1)),
12521 return gnm_expr_new_funcall (f, new_args);
12523 return NULL;
12526 static GnmExpr const *
12527 odf_func_phi_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
12529 GnmFunc *f = gnm_func_lookup_or_add_placeholder ("NORMDIST");
12531 args = g_slist_append (args,
12532 (gpointer) gnm_expr_new_constant (value_new_int (0)));
12533 args = g_slist_append (args,
12534 (gpointer) gnm_expr_new_constant (value_new_int (1)));
12536 args = g_slist_append (args,
12537 (gpointer) gnm_expr_new_funcall
12538 (gnm_func_lookup_or_add_placeholder ("FALSE"), NULL));
12540 return gnm_expr_new_funcall (f, args);
12543 static GnmExpr const *
12544 odf_func_gauss_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
12546 guint argc = gnm_expr_list_length (args);
12547 GnmFunc *f = gnm_func_lookup_or_add_placeholder ("ERF");
12548 GnmFunc *fs = gnm_func_lookup_or_add_placeholder ("SQRT");
12549 GnmExpr const * expr;
12551 if (argc != 1)
12552 return NULL;
12554 expr = gnm_expr_new_binary (gnm_expr_new_funcall1
12555 (f, gnm_expr_new_binary ((gnm_expr_copy ((GnmExpr const *)(args->data))),
12556 GNM_EXPR_OP_DIV,
12557 gnm_expr_new_funcall1 (fs,
12558 gnm_expr_new_constant
12559 (value_new_int (2))))),
12560 GNM_EXPR_OP_DIV,
12561 gnm_expr_new_constant (value_new_int (2)));
12562 gnm_expr_list_unref (args);
12563 return expr;
12566 static GnmExpr const *
12567 odf_func_floor_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
12569 guint argc = gnm_expr_list_length (args);
12570 GnmExpr const *expr_x;
12571 GnmExpr const *expr_sig;
12572 GnmExpr const *expr_mode;
12573 GnmExpr const *expr_mode_zero;
12574 GnmExpr const *expr_mode_one;
12575 GnmExpr const *expr_if;
12576 GnmFunc *fd_ceiling;
12577 GnmFunc *fd_floor;
12578 GnmFunc *fd_if;
12580 if (argc == 0 || argc > 3)
12581 return NULL;
12583 fd_ceiling = gnm_func_lookup_or_add_placeholder ("CEILING");
12584 fd_floor = gnm_func_lookup_or_add_placeholder ("FLOOR");
12585 fd_if = gnm_func_lookup_or_add_placeholder ("IF");
12587 expr_x = g_slist_nth_data ((GSList *) args, 0);
12588 if (argc > 1)
12589 expr_sig = gnm_expr_copy (g_slist_nth_data ((GSList *) args, 1));
12590 else {
12591 GnmFunc *fd_sign = gnm_func_lookup_or_add_placeholder ("SIGN");
12592 expr_sig = gnm_expr_new_funcall1 (fd_sign, gnm_expr_copy (expr_x));
12595 expr_mode_zero = gnm_expr_new_funcall3
12596 (fd_if,
12597 gnm_expr_new_binary
12598 (gnm_expr_copy (expr_x),
12599 GNM_EXPR_OP_LT,
12600 gnm_expr_new_constant (value_new_int (0))),
12601 gnm_expr_new_funcall2
12602 (fd_ceiling,
12603 gnm_expr_copy (expr_x),
12604 gnm_expr_copy (expr_sig)),
12605 gnm_expr_new_funcall2
12606 (fd_floor,
12607 gnm_expr_copy (expr_x),
12608 gnm_expr_copy (expr_sig)));
12609 if (argc < 3) {
12610 gnm_expr_free (expr_sig);
12611 gnm_expr_list_unref (args);
12612 return expr_mode_zero;
12615 expr_mode_one =
12616 gnm_expr_new_funcall2
12617 (fd_floor,
12618 gnm_expr_copy (expr_x),
12619 gnm_expr_copy (expr_sig));
12621 expr_mode = g_slist_nth_data ((GSList *) args, 2);
12622 if (GNM_EXPR_GET_OPER (expr_mode) == GNM_EXPR_OP_CONSTANT) {
12623 GnmValue const * val = expr_mode->constant.value;
12624 if (VALUE_IS_NUMBER (val)) {
12625 gnm_float value = value_get_as_float (val);
12626 if (value == 0.) {
12627 gnm_expr_free (expr_mode_one);
12628 gnm_expr_list_unref (args);
12629 gnm_expr_free (expr_sig);
12630 return expr_mode_zero;
12631 } else {
12632 gnm_expr_free (expr_mode_zero);
12633 gnm_expr_list_unref (args);
12634 gnm_expr_free (expr_sig);
12635 return expr_mode_one;
12639 expr_if = gnm_expr_new_funcall3
12640 (fd_if,
12641 gnm_expr_new_binary
12642 (gnm_expr_new_constant (value_new_int (0)),
12643 GNM_EXPR_OP_EQUAL,
12644 gnm_expr_copy (expr_mode)),
12645 expr_mode_zero,
12646 expr_mode_one);
12648 gnm_expr_free (expr_sig);
12649 gnm_expr_list_unref (args);
12650 return expr_if;
12653 static GnmExpr const *
12654 odf_func_ceiling_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
12656 guint argc = gnm_expr_list_length (args);
12657 switch (argc) {
12658 case 1: {
12659 GnmFunc *f = gnm_func_lookup_or_add_placeholder ("CEIL");
12660 return gnm_expr_new_funcall (f, args);
12662 case 2: case 3: {
12663 GnmExpr const *expr_mode_zero;
12664 GnmExpr const *expr_mode_one;
12665 GnmExpr const *expr_if;
12666 GnmExpr const *expr_mode;
12667 GnmExpr const *expr_x = g_slist_nth_data ((GSList *) args, 0);
12668 GnmExpr const *expr_sig = g_slist_nth_data ((GSList *) args, 1);
12670 GnmFunc *fd_ceiling = gnm_func_lookup_or_add_placeholder ("CEILING");
12671 GnmFunc *fd_floor = gnm_func_lookup_or_add_placeholder ("FLOOR");
12672 GnmFunc *fd_if = gnm_func_lookup_or_add_placeholder ("IF");
12674 expr_mode_zero = gnm_expr_new_funcall3
12675 (fd_if,
12676 gnm_expr_new_binary
12677 (gnm_expr_copy (expr_x),
12678 GNM_EXPR_OP_LT,
12679 gnm_expr_new_constant (value_new_int (0))),
12680 gnm_expr_new_funcall2
12681 (fd_floor,
12682 gnm_expr_copy (expr_x),
12683 gnm_expr_copy (expr_sig)),
12684 gnm_expr_new_funcall2
12685 (fd_ceiling,
12686 gnm_expr_copy (expr_x),
12687 gnm_expr_copy (expr_sig)));
12688 if (argc == 2) {
12689 gnm_expr_list_unref (args);
12690 return expr_mode_zero;
12693 expr_mode_one =
12694 gnm_expr_new_funcall2
12695 (fd_ceiling,
12696 gnm_expr_copy (expr_x),
12697 gnm_expr_copy (expr_sig));
12699 expr_mode = g_slist_nth_data ((GSList *) args, 2);
12700 if (GNM_EXPR_GET_OPER (expr_mode) == GNM_EXPR_OP_CONSTANT) {
12701 GnmValue const * val = expr_mode->constant.value;
12702 if (VALUE_IS_NUMBER (val)) {
12703 gnm_float value = value_get_as_float (val);
12704 if (value == 0.) {
12705 gnm_expr_free (expr_mode_one);
12706 gnm_expr_list_unref (args);
12707 return expr_mode_zero;
12708 } else {
12709 gnm_expr_free (expr_mode_zero);
12710 gnm_expr_list_unref (args);
12711 return expr_mode_one;
12715 expr_if = gnm_expr_new_funcall3
12716 (fd_if,
12717 gnm_expr_new_binary
12718 (gnm_expr_new_constant (value_new_int (0)),
12719 GNM_EXPR_OP_EQUAL,
12720 gnm_expr_copy (expr_mode)),
12721 expr_mode_zero,
12722 expr_mode_one);
12723 gnm_expr_list_unref (args);
12724 return expr_if;
12726 default:
12727 break;
12729 return NULL;
12732 static GnmExpr const *
12733 odf_func_chisqdist_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
12735 switch (gnm_expr_list_length (args)) {
12736 case 2: {
12737 GnmFunc *f = gnm_func_lookup_or_add_placeholder ("R.PCHISQ");
12738 return gnm_expr_new_funcall (f, args);
12740 case 3: {
12741 GnmExpr const *arg0 = args->data;
12742 GnmExpr const *arg1 = args->next->data;
12743 GnmExpr const *arg2 = args->next->next->data;
12744 GnmFunc *fd_if;
12745 GnmFunc *fd_pchisq;
12746 GnmFunc *fd_dchisq;
12747 GnmExpr const *expr_pchisq;
12748 GnmExpr const *expr_dchisq;
12749 GnmExpr const *res, *simp;
12751 fd_if = gnm_func_lookup_or_add_placeholder ("IF");
12752 fd_pchisq = gnm_func_lookup_or_add_placeholder ("R.PCHISQ");
12753 fd_dchisq = gnm_func_lookup_or_add_placeholder ("R.DCHISQ");
12754 expr_pchisq = gnm_expr_new_funcall2
12755 (fd_pchisq,
12756 gnm_expr_copy (arg0),
12757 gnm_expr_copy (arg1));
12758 expr_dchisq = gnm_expr_new_funcall2
12759 (fd_dchisq,
12760 arg0,
12761 arg1);
12762 res = gnm_expr_new_funcall3
12763 (fd_if,
12764 arg2,
12765 expr_pchisq,
12766 expr_dchisq);
12768 simp = gnm_expr_simplify_if (res);
12769 if (simp) {
12770 gnm_expr_free (res);
12771 res = simp;
12774 g_slist_free (args);
12775 return res;
12777 default:
12778 break;
12780 return NULL;
12783 static GnmExpr const *
12784 odf_func_f_dist_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
12786 switch (gnm_expr_list_length (args)) {
12787 case 4: {
12788 GnmExpr const *arg0 = args->data;
12789 GnmExpr const *arg1 = args->next->data;
12790 GnmExpr const *arg2 = args->next->next->data;
12791 GnmExpr const *arg3 = args->next->next->next->data;
12792 GnmFunc *fd_if;
12793 GnmFunc *fd_pf;
12794 GnmFunc *fd_df;
12795 GnmExpr const *expr_pf;
12796 GnmExpr const *expr_df;
12797 GnmExpr const *res, *simp;
12799 fd_if = gnm_func_lookup_or_add_placeholder ("IF");
12800 fd_pf = gnm_func_lookup_or_add_placeholder ("R.PF");
12801 fd_df = gnm_func_lookup_or_add_placeholder ("R.DF");
12802 expr_pf = gnm_expr_new_funcall3
12803 (fd_pf,
12804 gnm_expr_copy (arg0),
12805 gnm_expr_copy (arg1),
12806 gnm_expr_copy (arg2));
12807 expr_df = gnm_expr_new_funcall3
12808 (fd_df,
12809 arg0,
12810 arg1,
12811 arg2);
12812 res = gnm_expr_new_funcall3
12813 (fd_if,
12814 arg3,
12815 expr_pf,
12816 expr_df);
12818 simp = gnm_expr_simplify_if (res);
12819 if (simp) {
12820 gnm_expr_free (res);
12821 res = simp;
12824 g_slist_free (args);
12825 return res;
12827 default:
12828 break;
12830 return NULL;
12833 static GnmExpr const *
12834 odf_func_dist4_handler (GnmExprList *args, char const *fd_p_name, char const *fd_d_name)
12836 switch (gnm_expr_list_length (args)) {
12837 case 4: {
12838 GnmExpr const *arg0 = args->data;
12839 GnmExpr const *arg1 = args->next->data;
12840 GnmExpr const *arg2 = args->next->next->data;
12841 GnmExpr const *arg3 = args->next->next->next->data;
12842 GnmFunc *fd_if;
12843 GnmFunc *fd_p;
12844 GnmFunc *fd_d;
12845 GnmExpr const *expr_p;
12846 GnmExpr const *expr_d;
12847 GnmExpr const *res, *simp;
12849 fd_if = gnm_func_lookup_or_add_placeholder ("IF");
12850 fd_p = gnm_func_lookup_or_add_placeholder (fd_p_name);
12851 fd_d = gnm_func_lookup_or_add_placeholder (fd_d_name);
12852 expr_p = gnm_expr_new_funcall3
12853 (fd_p,
12854 gnm_expr_copy (arg0),
12855 gnm_expr_copy (arg1),
12856 gnm_expr_copy (arg2));
12857 expr_d = gnm_expr_new_funcall3
12858 (fd_d,
12859 arg0,
12860 arg1,
12861 arg2);
12862 res = gnm_expr_new_funcall3
12863 (fd_if,
12864 arg3,
12865 expr_p,
12866 expr_d);
12868 simp = gnm_expr_simplify_if (res);
12869 if (simp) {
12870 gnm_expr_free (res);
12871 res = simp;
12874 g_slist_free (args);
12875 return res;
12877 default:
12878 break;
12880 return NULL;
12883 static GnmExpr const *
12884 odf_func_dist3_handler (GnmExprList *args, char const *fd_p_name, char const *fd_d_name)
12886 switch (gnm_expr_list_length (args)) {
12887 case 3: {
12888 GnmExpr const *arg0 = args->data;
12889 GnmExpr const *arg1 = args->next->data;
12890 GnmExpr const *arg2 = args->next->next->data;
12891 GnmFunc *fd_if;
12892 GnmFunc *fd_p;
12893 GnmFunc *fd_d;
12894 GnmExpr const *expr_p;
12895 GnmExpr const *expr_d;
12896 GnmExpr const *res, *simp;
12898 fd_if = gnm_func_lookup_or_add_placeholder ("IF");
12899 fd_p = gnm_func_lookup_or_add_placeholder (fd_p_name);
12900 fd_d = gnm_func_lookup_or_add_placeholder (fd_d_name);
12901 expr_p = gnm_expr_new_funcall2
12902 (fd_p,
12903 gnm_expr_copy (arg0),
12904 gnm_expr_copy (arg1));
12905 expr_d = gnm_expr_new_funcall2
12906 (fd_d,
12907 arg0,
12908 arg1);
12909 res = gnm_expr_new_funcall3
12910 (fd_if,
12911 arg2,
12912 expr_p,
12913 expr_d);
12915 simp = gnm_expr_simplify_if (res);
12916 if (simp) {
12917 gnm_expr_free (res);
12918 res = simp;
12921 g_slist_free (args);
12922 return res;
12924 default:
12925 break;
12927 return NULL;
12930 static GnmExpr const *
12931 odf_func_norm_s_dist_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
12933 switch (gnm_expr_list_length (args)) {
12934 case 2: {
12935 GnmExpr const *arg0 = args->data;
12936 GnmExpr const *arg1 = args->next->data;
12937 GnmFunc *fd_if;
12938 GnmFunc *fd_p;
12939 GnmFunc *fd_d;
12940 GnmExpr const *expr_p;
12941 GnmExpr const *expr_d;
12942 GnmExpr const *res, *simp;
12944 fd_if = gnm_func_lookup_or_add_placeholder ("IF");
12945 fd_p = gnm_func_lookup_or_add_placeholder ("R.DNORM");
12946 fd_d = gnm_func_lookup_or_add_placeholder ("NORMSDIST");
12947 expr_p = gnm_expr_new_funcall3
12948 (fd_p,
12949 gnm_expr_copy (arg0),
12950 gnm_expr_new_constant (value_new_int (0)),
12951 gnm_expr_new_constant (value_new_int (1)));
12952 expr_d = gnm_expr_new_funcall1
12953 (fd_d,
12954 arg0);
12955 res = gnm_expr_new_funcall3
12956 (fd_if,
12957 arg1,
12958 expr_p,
12959 expr_d);
12961 simp = gnm_expr_simplify_if (res);
12962 if (simp) {
12963 gnm_expr_free (res);
12964 res = simp;
12967 g_slist_free (args);
12968 return res;
12970 default:
12971 break;
12973 return NULL;
12976 static void
12977 odf_func_concatenate_handler_cb (gpointer data, gpointer user_data)
12979 GnmExpr const *expr = data;
12980 gboolean *check = (gboolean *)(user_data);
12982 if (gnm_expr_is_rangeref (expr))
12983 (*check) = (*check) || (GNM_EXPR_GET_OPER (expr) != GNM_EXPR_OP_CELLREF);
12986 static GnmExpr const *
12987 odf_func_concatenate_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
12989 gboolean has_range = FALSE;
12990 GnmFunc *fd;
12992 g_slist_foreach ((GSList *) args, odf_func_concatenate_handler_cb, (gpointer) &has_range);
12994 if (has_range)
12995 return NULL;
12997 fd = gnm_func_lookup_or_add_placeholder ("CONCATENATE");
12999 return gnm_expr_new_funcall (fd, args);
13002 static GnmExpr const *
13003 odf_func_t_dist_tail_handler (GnmExprList *args, int tails)
13005 switch (gnm_expr_list_length (args)) {
13006 case 2: {
13007 GnmExpr const *arg0 = args->data;
13008 GnmExpr const *arg1 = args->next->data;
13009 GnmFunc *fd;
13010 GnmExpr const *res;
13012 fd = gnm_func_lookup_or_add_placeholder ("TDIST");
13013 res = gnm_expr_new_funcall3
13014 (fd,
13015 arg0,
13016 arg1,
13017 gnm_expr_new_constant (value_new_int (tails)));
13019 g_slist_free (args);
13020 return res;
13022 default:
13023 break;
13025 return NULL;
13028 static GnmExpr const *
13029 odf_func_t_dist_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
13031 return odf_func_dist3_handler (args, "R.PT", "R.DT");
13034 static GnmExpr const *
13035 odf_func_t_dist_rt_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
13037 return odf_func_t_dist_tail_handler (args, 1);
13040 static GnmExpr const *
13041 odf_func_t_dist_2t_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
13043 return odf_func_t_dist_tail_handler (args, 2);
13046 static GnmExpr const *
13047 odf_func_lognorm_dist_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
13049 return odf_func_dist4_handler (args, "LOGNORMDIST", "R.DLNORM");
13052 static GnmExpr const *
13053 odf_func_negbinom_dist_handler (G_GNUC_UNUSED GnmConventions const *convs, G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
13055 return odf_func_dist4_handler (args, "R.PNBINOM", "NEGBINOMDIST");
13058 static GnmExpr const *
13059 odf_func_true_handler (G_GNUC_UNUSED GnmConventions const *convs,
13060 G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
13062 return args ? NULL : gnm_expr_new_constant (value_new_bool (TRUE));
13065 static GnmExpr const *
13066 odf_func_false_handler (G_GNUC_UNUSED GnmConventions const *convs,
13067 G_GNUC_UNUSED Workbook *scope, GnmExprList *args)
13069 return args ? NULL : gnm_expr_new_constant (value_new_bool (FALSE));
13072 static GnmExpr const *
13073 oo_func_map_in (GnmConventions const *convs, Workbook *scope,
13074 char const *name, GnmExprList *args)
13076 static struct {
13077 char const *gnm_name;
13078 gpointer handler;
13079 } const sc_func_handlers[] = {
13080 {"CHISQDIST", odf_func_chisqdist_handler},
13081 {"CEILING", odf_func_ceiling_handler},
13082 {"FLOOR", odf_func_floor_handler},
13083 {"ADDRESS", odf_func_address_handler},
13084 {"PHI", odf_func_phi_handler},
13085 {"GAUSS", odf_func_gauss_handler},
13086 {"TRUE", odf_func_true_handler},
13087 {"FALSE", odf_func_false_handler},
13088 {"CONCATENATE", odf_func_concatenate_handler},
13089 {"COM.MICROSOFT.F.DIST", odf_func_f_dist_handler},
13090 {"COM.MICROSOFT.LOGNORM.DIST", odf_func_lognorm_dist_handler},
13091 {"COM.MICROSOFT.NEGBINOM.DIST", odf_func_negbinom_dist_handler},
13092 {"COM.MICROSOFT.T.DIST", odf_func_t_dist_handler},
13093 {"COM.MICROSOFT.T.DIST.RT", odf_func_t_dist_rt_handler},
13094 {"COM.MICROSOFT.T.DIST.2T", odf_func_t_dist_2t_handler},
13095 {"COM.MICROSOFT.NORM.S.DIST", odf_func_norm_s_dist_handler},
13096 {NULL, NULL}
13099 static struct {
13100 char const *oo_name;
13101 char const *gnm_name;
13102 } const sc_func_renames[] = {
13103 /* The next functions are or were used by OpenOffice but are not in ODF OpenFormula draft 20090508 */
13105 { "INDIRECT_XL", "INDIRECT" },
13106 { "ADDRESS_XL", "ADDRESS" },
13107 { "ERRORTYPE", "ERROR.TYPE" },
13108 { "EASTERSUNDAY", "EASTERSUNDAY" }, /* OOo stores this without prefix! */
13109 { "ORG.OPENOFFICE.EASTERSUNDAY", "EASTERSUNDAY" },
13111 /* The following is a list of the functions defined in ODF OpenFormula draft 20090508 */
13112 /* where we do not have a function with the same name */
13114 { "CONCATENATE","ODF.CONCATENATE" },
13115 { "DDE","ODF.DDE" },
13116 { "MULTIPLE.OPERATIONS","ODF.MULTIPLE.OPERATIONS" },
13118 /* The following is a complete list of the functions defined in ODF OpenFormula draft 20090508 */
13119 /* We should determine whether any mapping is needed. */
13121 { "B","BINOM.DIST.RANGE" },
13122 { "CEILING","ODF.CEILING" }, /* see handler */
13123 { "CHISQINV","R.QCHISQ" },
13124 { "CHISQDIST","ODF.CHISQDIST" }, /* see handler */
13125 { "FALSE","FALSE" }, /* see handler */
13126 { "FLOOR","ODF.FLOOR" }, /* see handler */
13127 { "FORMULA","GET.FORMULA" },
13128 { "GAUSS","ODF.GAUSS" }, /* see handler */
13129 { "LEGACY.CHIDIST","CHIDIST" },
13130 { "LEGACY.CHIINV","CHIINV" },
13131 { "LEGACY.CHITEST","CHITEST" },
13132 { "LEGACY.FDIST","FDIST" },
13133 { "LEGACY.FINV","FINV" },
13134 { "LEGACY.NORMSDIST","NORMSDIST" },
13135 { "LEGACY.NORMSINV","NORMSINV" },
13136 { "LEGACY.TDIST","TDIST" },
13137 { "PDURATION","G_DURATION" },
13138 { "PHI","NORMDIST" }, /* see handler */
13139 { "SUMPRODUCT","ODF.SUMPRODUCT" },
13140 { "TIME","ODF.TIME" },
13141 { "TRUE","TRUE" }, /* see handler */
13142 { "USDOLLAR","DOLLAR" },
13144 /* The following are known to have appeared (usually with a COM.MICROSOFT prefix. */
13146 { "BETA.DIST","BETA.DIST" }, /* We need this mapping to satisfy */
13147 /* the COM.MICROSOFT prefix */
13148 { "BETA.INV","BETAINV" },
13149 { "BINOM.DIST","BINOMDIST" },
13150 { "BINOM.INV","CRITBINOM" },
13151 { "CHISQ.DIST","R.PCHISQ" },
13152 { "CHISQ.DIST.RT","CHIDIST" },
13153 { "CHISQ.INV","R.QCHISQ" },
13154 { "CHISQ.INV.RT","CHIINV" },
13155 { "CHISQ.TEST","CHITEST" },
13156 { "CONCAT", "CONCAT" },
13157 { "CONFIDENCE.NORM","CONFIDENCE" },
13158 { "CONFIDENCE.T","CONFIDENCE.T" },
13159 { "COVARIANCE.P","COVAR" },
13160 { "COVARIANCE.S","COVARIANCE.S" },
13161 { "EXPON.DIST","EXPONDIST" },
13162 { "F.DIST.RT","FDIST" },
13163 { "F.INV.RT","FINV" },
13164 { "F.TEST","FTEST" },
13165 { "GAMMA.DIST","GAMMADIST" },
13166 { "GAMMA.INV","GAMMAINV" },
13167 { "GAMMALN.PRECISE","GAMMALN" },
13168 { "HYPGEOM.DIST","HYPGEOMDIST" },
13169 { "IFS","IFS" },
13170 { "LOGNORM.INV","LOGINV" },
13171 { "MINIFS", "MINIFS" },
13172 { "MAXIFS", "MAXIFS" },
13173 { "MODE.SNGL","MODE" },
13174 { "MODE.MULT","MODE.MULT" },
13175 { "NORM.DIST","NORMDIST" },
13176 { "NORM.INV","NORMINV" },
13177 { "NORM.S.INV","NORMSINV" },
13178 { "PERCENTILE.INC","PERCENTILE" },
13179 { "PERCENTILE.EXC","PERCENTILE.EXC" },
13180 { "PERCENTRANK.INC","PERCENTRANK" },
13181 { "PERCENTRANK.EXC","PERCENTRANK.EXC" },
13182 { "POISSON.DIST","POISSON" },
13183 { "QUARTILE.INC","QUARTILE" },
13184 { "QUARTILE.EXC","QUARTILE.EXC" },
13185 { "RANK.EQ","RANK" },
13186 { "RANK.AVG","RANK.AVG" },
13187 { "STDEV.S","STDEV" },
13188 { "STDEV.P","STDEVP" },
13189 { "SWITCH", "SWITCH" },
13190 { "T.INV","R.QT" },
13191 { "T.INV.2T","TINV" },
13192 { "T.TEST","TTEST" },
13193 { "TEXTJOIN", "TEXTJOIN" },
13194 { "VAR.S","VAR" },
13195 { "VAR.P","VARP" },
13196 { "WEIBULL.DIST","WEIBULL" },
13197 { "Z.TEST","ZTEST" },
13199 /* { "ADDRESS","ADDRESS" }, also see handler */
13200 /* { "ABS","ABS" }, */
13201 /* { "ACCRINT","ACCRINT" }, */
13202 /* { "ACCRINTM","ACCRINTM" }, */
13203 /* { "ACOS","ACOS" }, */
13204 /* { "ACOSH","ACOSH" }, */
13205 /* { "ACOT","ACOT" }, */
13206 /* { "ACOTH","ACOTH" }, */
13207 /* { "AMORDEGRC","AMORDEGRC" }, */
13208 /* { "AMORLINC","AMORLINC" }, */
13209 /* { "AND","AND" }, */
13210 /* { "ARABIC","ARABIC" }, */
13211 /* { "AREAS","AREAS" }, */
13212 /* { "ASC","ASC" }, */
13213 /* { "ASIN","ASIN" }, */
13214 /* { "ASINH","ASINH" }, */
13215 /* { "ATAN","ATAN" }, */
13216 /* { "ATAN2","ATAN2" }, */
13217 /* { "ATANH","ATANH" }, */
13218 /* { "AVEDEV","AVEDEV" }, */
13219 /* { "AVERAGE","AVERAGE" }, */
13220 /* { "AVERAGEA","AVERAGEA" }, */
13221 /* { "AVERAGEIF","AVERAGEIF" }, */
13222 /* { "AVERAGEIFS","AVERAGEIFS" }, */
13223 /* { "BASE","BASE" }, */
13224 /* { "BESSELI","BESSELI" }, */
13225 /* { "BESSELJ","BESSELJ" }, */
13226 /* { "BESSELK","BESSELK" }, */
13227 /* { "BESSELY","BESSELY" }, */
13228 /* { "BETADIST","BETADIST" }, */
13229 /* { "BETAINV","BETAINV" }, */
13230 /* { "BIN2DEC","BIN2DEC" }, */
13231 /* { "BIN2HEX","BIN2HEX" }, */
13232 /* { "BIN2OCT","BIN2OCT" }, */
13233 /* { "BINOMDIST","BINOMDIST" }, */
13234 /* { "BITAND","BITAND" }, */
13235 /* { "BITLSHIFT","BITLSHIFT" }, */
13236 /* { "BITOR","BITOR" }, */
13237 /* { "BITRSHIFT","BITRSHIFT" }, */
13238 /* { "BITXOR","BITXOR" }, */
13239 /* { "CHAR","CHAR" }, */
13240 /* { "CHOOSE","CHOOSE" }, */
13241 /* { "CLEAN","CLEAN" }, */
13242 /* { "CODE","CODE" }, */
13243 /* { "COLUMN","COLUMN" }, */
13244 /* { "COLUMNS","COLUMNS" }, */
13245 /* { "COMBIN","COMBIN" }, */
13246 /* { "COMBINA","COMBINA" }, */
13247 /* { "COMPLEX","COMPLEX" }, */
13248 /* { "CONFIDENCE","CONFIDENCE" }, */
13249 /* { "CONVERT","CONVERT" }, */
13250 /* { "CORREL","CORREL" }, */
13251 /* { "COS","COS" }, */
13252 /* { "COSH","COSH" }, */
13253 /* { "COT","COT" }, */
13254 /* { "COTH","COTH" }, */
13255 /* { "COUNT","COUNT" }, */
13256 /* { "COUNTA","COUNTA" }, */
13257 /* { "COUNTBLANK","COUNTBLANK" }, */
13258 /* { "COUNTIF","COUNTIF" }, */
13259 /* { "COUNTIFS","COUNTIFS" }, */
13260 /* { "COUPDAYBS","COUPDAYBS" }, */
13261 /* { "COUPDAYS","COUPDAYS" }, */
13262 /* { "COUPDAYSNC","COUPDAYSNC" }, */
13263 /* { "COUPNCD","COUPNCD" }, */
13264 /* { "COUPNUM","COUPNUM" }, */
13265 /* { "COUPPCD","COUPPCD" }, */
13266 /* { "COVAR","COVAR" }, */
13267 /* { "CRITBINOM","CRITBINOM" }, */
13268 /* { "CSC","CSC" }, */
13269 /* { "CSCH","CSCH" }, */
13270 /* { "CUMIPMT","CUMIPMT" }, */
13271 /* { "CUMPRINC","CUMPRINC" }, */
13272 /* { "DATE","DATE" }, */
13273 /* { "DATEDIF","DATEDIF" }, */
13274 /* { "DATEVALUE","DATEVALUE" }, */
13275 /* { "DAVERAGE","DAVERAGE" }, */
13276 /* { "DAY","DAY" }, */
13277 /* { "DAYS","DAYS" }, */
13278 /* { "DAYS360","DAYS360" }, */
13279 /* { "DB","DB" }, */
13280 /* { "DCOUNT","DCOUNT" }, */
13281 /* { "DCOUNTA","DCOUNTA" }, */
13282 /* { "DDB","DDB" }, */
13283 /* { "DDE","DDE" }, */
13284 /* { "DEC2BIN","DEC2BIN" }, */
13285 /* { "DEC2HEX","DEC2HEX" }, */
13286 /* { "DEC2OCT","DEC2OCT" }, */
13287 /* { "DECIMAL","DECIMAL" }, */
13288 /* { "DEGREES","DEGREES" }, */
13289 /* { "DELTA","DELTA" }, */
13290 /* { "DEVSQ","DEVSQ" }, */
13291 /* { "DGET","DGET" }, */
13292 /* { "DISC","DISC" }, */
13293 /* { "DMAX","DMAX" }, */
13294 /* { "DMIN","DMIN" }, */
13295 /* { "DOLLARDE","DOLLARDE" }, */
13296 /* { "DOLLARFR","DOLLARFR" }, */
13297 /* { "DPRODUCT","DPRODUCT" }, */
13298 /* { "DSTDEV","DSTDEV" }, */
13299 /* { "DSTDEVP","DSTDEVP" }, */
13300 /* { "DSUM","DSUM" }, */
13301 /* { "DURATION","DURATION" }, */
13302 /* { "DVAR","DVAR" }, */
13303 /* { "DVARP","DVARP" }, */
13304 /* { "EDATE","EDATE" }, */
13305 /* { "EFFECT","EFFECT" }, */
13306 /* { "EOMONTH","EOMONTH" }, */
13307 /* { "ERF","ERF" }, */
13308 /* { "ERFC","ERFC" }, */
13309 /* { "ERROR.TYPE","ERROR.TYPE" }, */
13310 /* { "EUROCONVERT","EUROCONVERT" }, */
13311 /* { "EVEN","EVEN" }, */
13312 /* { "EXACT","EXACT" }, */
13313 /* { "EXP","EXP" }, */
13314 /* { "EXPONDIST","EXPONDIST" }, */
13315 /* { "FACT","FACT" }, */
13316 /* { "FACTDOUBLE","FACTDOUBLE" }, */
13317 /* { "FDIST","FDIST" }, */
13318 /* { "FIND","FIND" }, */
13319 /* { "FINDB","FINDB" }, */
13320 /* { "FINV","FINV" }, */
13321 /* { "FISHER","FISHER" }, */
13322 /* { "FISHERINV","FISHERINV" }, */
13323 /* { "FIXED","FIXED" }, */
13324 /* { "FORECAST","FORECAST" }, */
13325 /* { "FREQUENCY","FREQUENCY" }, */
13326 /* { "FTEST","FTEST" }, */
13327 /* { "FV","FV" }, */
13328 /* { "FVSCHEDULE","FVSCHEDULE" }, */
13329 /* { "GAMMA","GAMMA" }, */
13330 /* { "GAMMADIST","GAMMADIST" }, */
13331 /* { "GAMMAINV","GAMMAINV" }, */
13332 /* { "GAMMALN","GAMMALN" }, */
13333 /* { "GCD","GCD" }, */
13334 /* { "GEOMEAN","GEOMEAN" }, */
13335 /* { "GESTEP","GESTEP" }, */
13336 /* { "GETPIVOTDATA","GETPIVOTDATA" }, */
13337 /* { "GROWTH","GROWTH" }, */
13338 /* { "HARMEAN","HARMEAN" }, */
13339 /* { "HEX2BIN","HEX2BIN" }, */
13340 /* { "HEX2DEC","HEX2DEC" }, */
13341 /* { "HEX2OCT","HEX2OCT" }, */
13342 /* { "HLOOKUP","HLOOKUP" }, */
13343 /* { "HOUR","HOUR" }, */
13344 /* { "HYPERLINK","HYPERLINK" }, */
13345 /* { "HYPGEOMDIST","HYPGEOMDIST" }, */
13346 /* { "IF","IF" }, */
13347 /* { "IFERROR","IFERROR" }, */
13348 /* { "IFNA","IFNA" }, */
13349 /* { "IMABS","IMABS" }, */
13350 /* { "IMAGINARY","IMAGINARY" }, */
13351 /* { "IMARGUMENT","IMARGUMENT" }, */
13352 /* { "IMCONJUGATE","IMCONJUGATE" }, */
13353 /* { "IMCOS","IMCOS" }, */
13354 /* { "IMCOT","IMCOT" }, */
13355 /* { "IMCSC","IMCSC" }, */
13356 /* { "IMCSCH","IMCSCH" }, */
13357 /* { "IMDIV","IMDIV" }, */
13358 /* { "IMEXP","IMEXP" }, */
13359 /* { "IMLN","IMLN" }, */
13360 /* { "IMLOG10","IMLOG10" }, */
13361 /* { "IMLOG2","IMLOG2" }, */
13362 /* { "IMPOWER","IMPOWER" }, */
13363 /* { "IMPRODUCT","IMPRODUCT" }, */
13364 /* { "IMREAL","IMREAL" }, */
13365 /* { "IMSEC","IMSEC" }, */
13366 /* { "IMSECH","IMSECH" }, */
13367 /* { "IMSIN","IMSIN" }, */
13368 /* { "IMSQRT","IMSQRT" }, */
13369 /* { "IMSUB","IMSUB" }, */
13370 /* { "IMSUM","IMSUM" }, */
13371 /* { "IMTAN","IMTAN" }, */
13372 /* { "INDEX","INDEX" }, */
13373 /* { "INDIRECT","INDIRECT" }, */
13374 /* { "INFO","INFO" }, */
13375 /* { "INT","INT" }, */
13376 /* { "INTERCEPT","INTERCEPT" }, */
13377 /* { "INTRATE","INTRATE" }, */
13378 /* { "IPMT","IPMT" }, */
13379 /* { "IRR","IRR" }, */
13380 /* { "ISBLANK","ISBLANK" }, */
13381 /* { "ISERR","ISERR" }, */
13382 /* { "ISERROR","ISERROR" }, */
13383 /* { "ISEVEN","ISEVEN" }, */
13384 /* { "ISFORMULA","ISFORMULA" }, */
13385 /* { "ISLOGICAL","ISLOGICAL" }, */
13386 /* { "ISNA","ISNA" }, */
13387 /* { "ISNONTEXT","ISNONTEXT" }, */
13388 /* { "ISNUMBER","ISNUMBER" }, */
13389 /* { "ISODD","ISODD" }, */
13390 /* { "ISOWEEKNUM","ISOWEEKNUM" }, */
13391 /* { "ISPMT","ISPMT" }, */
13392 /* { "ISREF","ISREF" }, */
13393 /* { "ISTEXT","ISTEXT" }, */
13394 /* { "JIS","JIS" }, */
13395 /* { "KURT","KURT" }, */
13396 /* { "LARGE","LARGE" }, */
13397 /* { "LCM","LCM" }, */
13398 /* { "LEFT","LEFT" }, */
13399 /* { "LEFTB","LEFTB" }, */
13400 /* { "LEN","LEN" }, */
13401 /* { "LENB","LENB" }, */
13402 /* { "LINEST","LINEST" }, */
13403 /* { "LN","LN" }, */
13404 /* { "LOG","LOG" }, */
13405 /* { "LOG10","LOG10" }, */
13406 /* { "LOGEST","LOGEST" }, */
13407 /* { "LOGINV","LOGINV" }, */
13408 /* { "LOGNORMDIST","LOGNORMDIST" }, */
13409 /* { "LOOKUP","LOOKUP" }, */
13410 /* { "LOWER","LOWER" }, */
13411 /* { "MATCH","MATCH" }, */
13412 /* { "MAX","MAX" }, */
13413 /* { "MAXA","MAXA" }, */
13414 /* { "MDETERM","MDETERM" }, */
13415 /* { "MDURATION","MDURATION" }, */
13416 /* { "MEDIAN","MEDIAN" }, */
13417 /* { "MID","MID" }, */
13418 /* { "MIDB","MIDB" }, */
13419 /* { "MIN","MIN" }, */
13420 /* { "MINA","MINA" }, */
13421 /* { "MINUTE","MINUTE" }, */
13422 /* { "MINVERSE","MINVERSE" }, */
13423 /* { "MIRR","MIRR" }, */
13424 /* { "MMULT","MMULT" }, */
13425 /* { "MOD","MOD" }, */
13426 /* { "MODE","MODE" }, */
13427 /* { "MONTH","MONTH" }, */
13428 /* { "MROUND","MROUND" }, */
13429 /* { "MULTINOMIAL","MULTINOMIAL" }, */
13430 /* { "MULTIPLE.OPERATIONS","MULTIPLE.OPERATIONS" }, */
13431 /* { "MUNIT","MUNIT" }, */
13432 /* { "N","N" }, */
13433 /* { "NA","NA" }, */
13434 /* { "NEGBINOMDIST","NEGBINOMDIST" }, */
13435 /* { "NETWORKDAYS","NETWORKDAYS" }, */
13436 /* { "NOMINAL","NOMINAL" }, */
13437 /* { "NORMDIST","NORMDIST" }, */
13438 /* { "NORMINV","NORMINV" }, */
13439 /* { "NOT","NOT" }, */
13440 /* { "NOW","NOW" }, */
13441 /* { "NPER","NPER" }, */
13442 /* { "NPV","NPV" }, */
13443 /* { "NUMBERVALUE","NUMBERVALUE" }, */
13444 /* { "OCT2BIN","OCT2BIN" }, */
13445 /* { "OCT2DEC","OCT2DEC" }, */
13446 /* { "OCT2HEX","OCT2HEX" }, */
13447 /* { "ODD","ODD" }, */
13448 /* { "ODDFPRICE","ODDFPRICE" }, */
13449 /* { "ODDFYIELD","ODDFYIELD" }, */
13450 /* { "ODDLPRICE","ODDLPRICE" }, */
13451 /* { "ODDLYIELD","ODDLYIELD" }, */
13452 /* { "OFFSET","OFFSET" }, */
13453 /* { "OR","OR" }, */
13454 /* { "PEARSON","PEARSON" }, */
13455 /* { "PERCENTILE","PERCENTILE" }, */
13456 /* { "PERCENTRANK","PERCENTRANK" }, */
13457 /* { "PERMUT","PERMUT" }, */
13458 /* { "PERMUTATIONA","PERMUTATIONA" }, */
13459 /* { "PI","PI" }, */
13460 /* { "PMT","PMT" }, */
13461 /* { "POISSON","POISSON" }, */
13462 /* { "POWER","POWER" }, */
13463 /* { "PPMT","PPMT" }, */
13464 /* { "PRICE","PRICE" }, */
13465 /* { "PRICEDISC","PRICEDISC" }, */
13466 /* { "PRICEMAT","PRICEMAT" }, */
13467 /* { "PROB","PROB" }, */
13468 /* { "PRODUCT","PRODUCT" }, */
13469 /* { "PROPER","PROPER" }, */
13470 /* { "PV","PV" }, */
13471 /* { "QUARTILE","QUARTILE" }, */
13472 /* { "QUOTIENT","QUOTIENT" }, */
13473 /* { "RADIANS","RADIANS" }, */
13474 /* { "RAND","RAND" }, */
13475 /* { "RANDBETWEEN","RANDBETWEEN" }, */
13476 /* { "RANK","RANK" }, */
13477 /* { "RATE","RATE" }, */
13478 /* { "RECEIVED","RECEIVED" }, */
13479 /* { "REPLACE","REPLACE" }, */
13480 /* { "REPLACEB","REPLACEB" }, */
13481 /* { "REPT","REPT" }, */
13482 /* { "RIGHT","RIGHT" }, */
13483 /* { "RIGHTB","RIGHTB" }, */
13484 /* { "ROMAN","ROMAN" }, */
13485 /* { "ROUND","ROUND" }, */
13486 /* { "ROUNDDOWN","ROUNDDOWN" }, */
13487 /* { "ROUNDUP","ROUNDUP" }, */
13488 /* { "ROW","ROW" }, */
13489 /* { "ROWS","ROWS" }, */
13490 /* { "RRI","RRI" }, */
13491 /* { "RSQ","RSQ" }, */
13492 /* { "SEARCH","SEARCH" }, */
13493 /* { "SEARCHB","SEARCHB" }, */
13494 /* { "SEC","SEC" }, */
13495 /* { "SECH","SECH" }, */
13496 /* { "SECOND","SECOND" }, */
13497 /* { "SERIESSUM","SERIESSUM" }, */
13498 /* { "SHEET","SHEET" }, */
13499 /* { "SHEETS","SHEETS" }, */
13500 /* { "SIGN","SIGN" }, */
13501 /* { "SIN","SIN" }, */
13502 /* { "SINH","SINH" }, */
13503 /* { "SKEW","SKEW" }, */
13504 /* { "SKEWP","SKEWP" }, */
13505 /* { "SLN","SLN" }, */
13506 /* { "SLOPE","SLOPE" }, */
13507 /* { "SMALL","SMALL" }, */
13508 /* { "SQRT","SQRT" }, */
13509 /* { "SQRTPI","SQRTPI" }, */
13510 /* { "STANDARDIZE","STANDARDIZE" }, */
13511 /* { "STDEV","STDEV" }, */
13512 /* { "STDEVA","STDEVA" }, */
13513 /* { "STDEVP","STDEVP" }, */
13514 /* { "STDEVPA","STDEVPA" }, */
13515 /* { "STEYX","STEYX" }, */
13516 /* { "SUBSTITUTE","SUBSTITUTE" }, */
13517 /* { "SUBTOTAL","SUBTOTAL" }, */
13518 /* { "SUM","SUM" }, */
13519 /* { "SUMIF","SUMIF" }, */
13520 /* { "SUMIFS","SUMIFS" }, */
13521 /* { "SUMPRODUCT","SUMPRODUCT" }, */
13522 /* { "SUMSQ","SUMSQ" }, */
13523 /* { "SUMX2MY2","SUMX2MY2" }, */
13524 /* { "SUMX2PY2","SUMX2PY2" }, */
13525 /* { "SUMXMY2","SUMXMY2" }, */
13526 /* { "SYD","SYD" }, */
13527 /* { "T","T" }, */
13528 /* { "TAN","TAN" }, */
13529 /* { "TANH","TANH" }, */
13530 /* { "TBILLEQ","TBILLEQ" }, */
13531 /* { "TBILLPRICE","TBILLPRICE" }, */
13532 /* { "TBILLYIELD","TBILLYIELD" }, */
13533 /* { "TEXT","TEXT" }, */
13534 /* { "TIME","TIME" }, */
13535 /* { "TIMEVALUE","TIMEVALUE" }, */
13536 /* { "TINV","TINV" }, */
13537 /* { "TODAY","TODAY" }, */
13538 /* { "TRANSPOSE","TRANSPOSE" }, */
13539 /* { "TREND","TREND" }, */
13540 /* { "TRIM","TRIM" }, */
13541 /* { "TRIMMEAN","TRIMMEAN" }, */
13542 /* { "TRUNC","TRUNC" }, */
13543 /* { "TTEST","TTEST" }, */
13544 /* { "TYPE","TYPE" }, */
13545 /* { "UNICHAR","UNICHAR" }, */
13546 /* { "UNICODE","UNICODE" }, */
13547 /* { "UPPER","UPPER" }, */
13548 /* { "VALUE","VALUE" }, */
13549 /* { "VAR","VAR" }, */
13550 /* { "VARA","VARA" }, */
13551 /* { "VARP","VARP" }, */
13552 /* { "VARPA","VARPA" }, */
13553 /* { "VDB","VDB" }, */
13554 /* { "VLOOKUP","VLOOKUP" }, */
13555 /* { "WEEKDAY","WEEKDAY" }, */
13556 /* { "WEEKNUM","WEEKNUM" }, */
13557 /* { "WEIBULL","WEIBULL" }, */
13558 /* { "WORKDAY","WORKDAY" }, */
13559 /* { "XIRR","XIRR" }, */
13560 /* { "XNPV","XNPV" }, */
13561 /* { "XOR","XOR" }, */
13562 /* { "YEAR","YEAR" }, */
13563 /* { "YEARFRAC","YEARFRAC" }, */
13564 /* { "YIELD","YIELD" }, */
13565 /* { "YIELDDISC","YIELDDISC" }, */
13566 /* { "YIELDMAT","YIELDMAT" }, */
13567 /* { "ZTEST","ZTEST" }, */
13568 { NULL, NULL }
13570 static char const OOoAnalysisPrefix[] = "com.sun.star.sheet.addin.Analysis.get";
13571 static char const GnumericPrefix[] = "ORG.GNUMERIC.";
13572 static char const MicrosoftPrefix[] = "COM.MICROSOFT.";
13574 GnmFunc *f = NULL;
13575 int i;
13576 GnmExpr const * (*handler) (GnmConventions const *convs, Workbook *scope, GnmExprList *args);
13577 ODFConventions *oconv = (ODFConventions *)convs;
13578 GHashTable *namemap;
13579 GHashTable *handlermap;
13581 if (NULL == oconv->state->openformula_namemap) {
13582 namemap = g_hash_table_new (go_ascii_strcase_hash,
13583 go_ascii_strcase_equal);
13584 for (i = 0; sc_func_renames[i].oo_name; i++)
13585 g_hash_table_insert (namemap,
13586 (gchar *) sc_func_renames[i].oo_name,
13587 (gchar *) sc_func_renames[i].gnm_name);
13588 oconv->state->openformula_namemap = namemap;
13589 } else
13590 namemap = oconv->state->openformula_namemap;
13592 if (NULL == oconv->state->openformula_handlermap) {
13593 guint i;
13594 handlermap = g_hash_table_new (go_ascii_strcase_hash,
13595 go_ascii_strcase_equal);
13596 for (i = 0; sc_func_handlers[i].gnm_name; i++)
13597 g_hash_table_insert (handlermap,
13598 (gchar *) sc_func_handlers[i].gnm_name,
13599 sc_func_handlers[i].handler);
13600 oconv->state->openformula_handlermap = handlermap;
13601 } else
13602 handlermap = oconv->state->openformula_handlermap;
13604 handler = g_hash_table_lookup (handlermap, name);
13605 if (handler != NULL) {
13606 GnmExpr const * res = handler (convs, scope, args);
13607 if (res != NULL)
13608 return res;
13611 if (0 == g_ascii_strncasecmp (name, GnumericPrefix, sizeof (GnumericPrefix)-1)) {
13612 f = gnm_func_lookup_or_add_placeholder (name+sizeof (GnumericPrefix)-1);
13613 } else if (0 == g_ascii_strncasecmp (name, OOoAnalysisPrefix, sizeof (OOoAnalysisPrefix)-1)) {
13614 f = gnm_func_lookup_or_add_placeholder (name+sizeof (OOoAnalysisPrefix)-1);
13615 } else if (0 == g_ascii_strncasecmp (name, MicrosoftPrefix, sizeof (MicrosoftPrefix)-1)) {
13616 char const *new_name;
13617 if (NULL != namemap &&
13618 NULL != (new_name = g_hash_table_lookup (namemap, name+sizeof (MicrosoftPrefix)-1)))
13619 f = gnm_func_lookup_or_add_placeholder (new_name);
13622 if (NULL == f) {
13623 char const *new_name;
13624 if (NULL != namemap &&
13625 NULL != (new_name = g_hash_table_lookup (namemap, name)))
13626 name = new_name;
13627 f = gnm_func_lookup_or_add_placeholder (name);
13630 return gnm_expr_new_funcall (f, args);
13633 static gboolean
13634 identified_google_docs (GsfInfile *zip)
13636 /* As of 2011/10/1 google-docs does not store any generator info so */
13637 /* we cannot use that for identification */
13638 gboolean google_docs = FALSE;
13639 GsfInput *content = gsf_infile_child_by_name
13640 (zip, "content.xml");
13641 if (content) {
13642 /* pick arbitrary size limit of 0.5k for the manifest to avoid
13643 * potential of any funny business */
13644 size_t size = MIN (gsf_input_size (content), 512);
13645 char const *mf = gsf_input_read (content, size, NULL);
13646 if (mf)
13647 google_docs =
13648 (NULL != g_strstr_len
13649 (mf, -1,
13650 "urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
13651 g_object_unref (content);
13653 return google_docs;
13656 static OOVer
13657 determine_oo_version (GsfInfile *zip, OOVer def)
13659 char const *header;
13660 size_t size;
13661 GsfInput *mimetype = gsf_infile_child_by_name (zip, "mimetype");
13662 if (!mimetype) {
13663 /* Google-docs is the mimetype so we need to check that */
13664 if (identified_google_docs (zip))
13665 return OOO_VER_OPENDOC;
13666 /* Really old versions also had no mimetype. Allow that,
13667 except in the probe. */
13668 return def;
13671 /* pick arbitrary size limit of 2k for the mimetype to avoid
13672 * potential of any funny business */
13673 size = MIN (gsf_input_size (mimetype), 2048);
13674 header = gsf_input_read (mimetype, size, NULL);
13676 if (header) {
13677 unsigned ui;
13679 for (ui = 0 ; ui < G_N_ELEMENTS (OOVersions) ; ui++)
13680 if (size == strlen (OOVersions[ui].mime_type) &&
13681 !memcmp (OOVersions[ui].mime_type, header, size)) {
13682 g_object_unref (mimetype);
13683 return OOVersions[ui].version;
13687 g_object_unref (mimetype);
13688 return OOO_VER_UNKNOWN;
13691 void
13692 openoffice_file_open (GOFileOpener const *fo, GOIOContext *io_context,
13693 WorkbookView *wb_view, GsfInput *input);
13694 G_MODULE_EXPORT void
13695 openoffice_file_open (G_GNUC_UNUSED GOFileOpener const *fo, GOIOContext *io_context,
13696 WorkbookView *wb_view, GsfInput *input)
13698 GsfXMLInDoc *doc;
13699 GsfInput *contents = NULL;
13700 GsfInput *styles = NULL;
13701 GsfDocMetaData *meta_data;
13702 GsfInfile *zip;
13703 GnmLocale *locale;
13704 OOParseState state;
13705 GError *err = NULL;
13706 int i;
13707 gboolean content_malformed = FALSE;
13709 zip = gsf_infile_zip_new (input, &err);
13710 if (zip == NULL) {
13711 g_return_if_fail (err != NULL);
13712 go_cmd_context_error_import (GO_CMD_CONTEXT (io_context),
13713 err->message);
13714 g_error_free (err);
13715 return;
13718 state.ver = determine_oo_version (zip, OOO_VER_1);
13719 if (state.ver == OOO_VER_UNKNOWN) {
13720 /* TODO : include the unknown type in the message when
13721 * we move the error handling into the importer object */
13722 go_cmd_context_error_import (GO_CMD_CONTEXT (io_context),
13723 _("Unknown mimetype for openoffice file."));
13724 g_object_unref (zip);
13725 return;
13726 } else if (state.ver == OOO_VER_OPENDOC)
13727 state.ver_odf = 1.2; /* Probably most common at this time */
13728 else state.ver_odf = 0.;
13730 contents = gsf_infile_child_by_name (zip, "content.xml");
13731 if (contents == NULL) {
13732 go_cmd_context_error_import (GO_CMD_CONTEXT (io_context),
13733 _("No stream named content.xml found."));
13734 g_object_unref (zip);
13735 return;
13738 styles = gsf_infile_child_by_name (zip, "styles.xml");
13739 if (styles == NULL) {
13740 go_cmd_context_error_import (GO_CMD_CONTEXT (io_context),
13741 _("No stream named styles.xml found."));
13742 g_object_unref (contents);
13743 g_object_unref (zip);
13744 return;
13747 locale = gnm_push_C_locale ();
13749 /* init */
13750 state.debug = gnm_debug_flag ("opendocumentimport");
13751 state.hd_ft_left_warned = FALSE;
13752 state.context = io_context;
13753 state.wb_view = wb_view;
13754 state.pos.wb = wb_view_get_workbook (wb_view);
13755 state.zip = zip;
13756 state.pos.sheet = NULL;
13757 state.pos.eval.col = -1;
13758 state.pos.eval.row = -1;
13759 state.cell_comment = NULL;
13760 state.sharer = gnm_expr_sharer_new ();
13761 state.chart.name = NULL;
13762 state.chart.cs_enhanced_path = NULL;
13763 state.chart.cs_modifiers = NULL;
13764 state.chart.cs_viewbox = NULL;
13765 state.chart.cs_type = NULL;
13766 state.chart.cs_variables = NULL;
13767 for (i = 0; i < OO_CHART_STYLE_INHERITANCE; i++)
13768 state.chart.i_plot_styles[i] = NULL;
13769 state.styles.sheet = g_hash_table_new_full (g_str_hash, g_str_equal,
13770 (GDestroyNotify) g_free,
13771 (GDestroyNotify) oo_sheet_style_free);
13772 state.styles.text = g_hash_table_new_full (g_str_hash, g_str_equal,
13773 (GDestroyNotify) g_free,
13774 (GDestroyNotify) pango_attr_list_unref);
13775 state.styles.col = g_hash_table_new_full (g_str_hash, g_str_equal,
13776 (GDestroyNotify) g_free,
13777 (GDestroyNotify) g_free);
13778 state.styles.row = g_hash_table_new_full (g_str_hash, g_str_equal,
13779 (GDestroyNotify) g_free,
13780 (GDestroyNotify) g_free);
13781 state.styles.cell = g_hash_table_new_full (g_str_hash, g_str_equal,
13782 (GDestroyNotify) g_free,
13783 (GDestroyNotify) odf_oo_cell_style_unref);
13784 state.styles.cell_datetime = g_hash_table_new_full (g_str_hash, g_str_equal,
13785 (GDestroyNotify) g_free,
13786 (GDestroyNotify) odf_oo_cell_style_unref);
13787 state.styles.cell_date = g_hash_table_new_full (g_str_hash, g_str_equal,
13788 (GDestroyNotify) g_free,
13789 (GDestroyNotify) odf_oo_cell_style_unref);
13790 state.styles.cell_time = g_hash_table_new_full (g_str_hash, g_str_equal,
13791 (GDestroyNotify) g_free,
13792 (GDestroyNotify) odf_oo_cell_style_unref);
13793 state.styles.master_pages = g_hash_table_new_full (g_str_hash, g_str_equal,
13794 (GDestroyNotify) g_free,
13795 (GDestroyNotify) gnm_print_info_free);
13796 state.styles.page_layouts = g_hash_table_new_full (g_str_hash, g_str_equal,
13797 (GDestroyNotify) g_free,
13798 (GDestroyNotify) gnm_print_info_free);
13799 state.formats = g_hash_table_new_full (g_str_hash, g_str_equal,
13800 (GDestroyNotify) g_free,
13801 (GDestroyNotify) go_format_unref);
13802 state.validations = g_hash_table_new_full (g_str_hash, g_str_equal,
13803 (GDestroyNotify) g_free,
13804 (GDestroyNotify) odf_validation_free);
13805 state.chart.so = NULL;
13806 state.chart.saved_graph_styles = NULL;
13807 state.chart.saved_hatches = NULL;
13808 state.chart.saved_dash_styles = NULL;
13809 state.chart.saved_fill_image_styles = NULL;
13810 state.chart.saved_gradient_styles = NULL;
13811 state.chart.graph_styles = g_hash_table_new_full
13812 (g_str_hash, g_str_equal,
13813 (GDestroyNotify) g_free,
13814 (GDestroyNotify) oo_chart_style_free);
13815 state.chart.hatches = g_hash_table_new_full
13816 (g_str_hash, g_str_equal,
13817 (GDestroyNotify) g_free,
13818 (GDestroyNotify) g_free);
13819 state.chart.dash_styles = g_hash_table_new_full
13820 (g_str_hash, g_str_equal,
13821 (GDestroyNotify) g_free,
13822 NULL);
13823 state.chart.fill_image_styles = g_hash_table_new_full
13824 (g_str_hash, g_str_equal,
13825 (GDestroyNotify) g_free,
13826 (GDestroyNotify) g_free);
13827 state.chart.gradient_styles = g_hash_table_new_full
13828 (g_str_hash, g_str_equal,
13829 (GDestroyNotify) g_free,
13830 (GDestroyNotify) g_free);
13831 state.controls = g_hash_table_new_full
13832 (g_str_hash, g_str_equal,
13833 (GDestroyNotify) g_free,
13834 (GDestroyNotify) oo_control_free);
13835 state.chart.arrow_markers = g_hash_table_new_full
13836 (g_str_hash, g_str_equal,
13837 (GDestroyNotify) g_free,
13838 (GDestroyNotify) oo_marker_free);
13839 state.strings = g_hash_table_new_full
13840 (g_str_hash, g_str_equal,
13841 (GDestroyNotify) g_free,
13842 (GDestroyNotify) g_free);
13843 state.cur_style.cells = NULL;
13844 state.cur_style.col_rows = NULL;
13845 state.cur_style.sheets = NULL;
13846 state.default_style.cells = NULL;
13847 state.default_style.rows = NULL;
13848 state.default_style.columns = NULL;
13849 state.default_style.graphics = NULL;
13850 state.cur_style.type = OO_STYLE_UNKNOWN;
13851 state.cur_style.requires_disposal = FALSE;
13852 state.sheet_order = NULL;
13853 for (i = 0; i<NUM_FORMULAE_SUPPORTED; i++)
13854 state.convs[i] = NULL;
13855 state.openformula_namemap = NULL;
13856 state.openformula_handlermap = NULL;
13857 state.cur_format.accum = NULL;
13858 state.cur_format.percentage = FALSE;
13859 state.filter = NULL;
13860 state.print.page_breaks.h = state.print.page_breaks.v = NULL;
13861 state.last_progress_update = 0;
13862 state.last_error = NULL;
13863 state.cur_control = NULL;
13864 state.chart_list = NULL;
13866 state.text_p_stack = NULL;
13867 state.text_p_for_cell.permanent = TRUE;
13868 state.text_p_for_cell.span_style_stack = NULL;
13869 state.text_p_for_cell.span_style_list = NULL;
13870 state.text_p_for_cell.gstr = NULL;
13871 state.text_p_for_cell.attrs = NULL;
13873 state.table_n = -1;
13875 go_io_progress_message (state.context, _("Reading file..."));
13876 go_io_value_progress_set (state.context, gsf_input_size (contents), 0);
13878 if (state.ver == OOO_VER_OPENDOC) {
13879 GsfInput *meta_file = gsf_infile_child_by_name (zip, "meta.xml");
13880 if (NULL != meta_file) {
13881 meta_data = gsf_doc_meta_data_new ();
13882 err = gsf_doc_meta_data_read_from_odf (meta_data, meta_file);
13883 if (NULL != err) {
13884 go_io_warning (io_context,
13885 _("Invalid metadata '%s'"), err->message);
13886 g_error_free (err);
13887 } else
13888 go_doc_set_meta_data (GO_DOC (state.pos.wb), meta_data);
13890 g_object_unref (meta_data);
13891 g_object_unref (meta_file);
13895 doc = gsf_xml_in_doc_new ((state.ver == OOO_VER_1)
13896 ? ooo1_content_preparse_dtd
13897 : opendoc_content_preparse_dtd,
13898 gsf_odf_get_ns ());
13899 content_malformed = !gsf_xml_in_doc_parse (doc, contents, &state);
13900 gsf_xml_in_doc_free (doc);
13901 odf_clear_conventions (&state); /* contain references to xin */
13902 state.sheet_order = g_slist_reverse (state.sheet_order);
13904 if (NULL != styles) {
13905 GsfXMLInDoc *doc = gsf_xml_in_doc_new (styles_dtd,
13906 gsf_odf_get_ns ());
13907 gsf_xml_in_doc_parse (doc, styles, &state);
13908 gsf_xml_in_doc_free (doc);
13909 odf_clear_conventions (&state); /* contain references to xin */
13910 g_object_unref (styles);
13913 if (!content_malformed) {
13914 gsf_input_seek (contents, 0, G_SEEK_SET);
13915 doc = gsf_xml_in_doc_new ((state.ver == OOO_VER_1)
13916 ? ooo1_content_dtd
13917 : opendoc_content_dtd,
13918 gsf_odf_get_ns ());
13919 content_malformed = !gsf_xml_in_doc_parse (doc, contents, &state);
13920 gsf_xml_in_doc_free (doc);
13921 odf_clear_conventions (&state);
13923 odf_fix_expr_names (&state);
13927 if (!content_malformed) {
13928 GsfInput *settings;
13929 char const *filesaver;
13931 /* look for the view settings */
13932 state.settings.settings
13933 = g_hash_table_new_full (g_str_hash, g_str_equal,
13934 (GDestroyNotify) g_free,
13935 (GDestroyNotify) destroy_gvalue);
13936 state.settings.stack = NULL;
13937 settings = gsf_infile_child_by_name (zip, "settings.xml");
13938 if (settings != NULL) {
13939 GsfXMLInDoc *sdoc = gsf_xml_in_doc_new
13940 (opendoc_settings_dtd, gsf_odf_get_ns ());
13941 gsf_xml_in_doc_parse (sdoc, settings, &state);
13942 gsf_xml_in_doc_free (sdoc);
13943 odf_clear_conventions (&state); /* contain references to xin */
13944 g_object_unref (settings);
13946 if (state.settings.stack != NULL) {
13947 go_cmd_context_error_import (GO_CMD_CONTEXT (io_context),
13948 _("settings.xml stream is malformed!"));
13949 g_slist_free_full (state.settings.stack,
13950 (GDestroyNotify) g_hash_table_unref);
13951 state.settings.stack = NULL;
13954 /* Use the settings here! */
13955 if (state.debug)
13956 g_hash_table_foreach (state.settings.settings,
13957 (GHFunc)dump_settings_hash, (char *)"");
13958 if (!odf_has_gnm_foreign (&state)) {
13959 filesaver = odf_created_by_gnumeric (&state) ?
13960 "Gnumeric_OpenCalc:openoffice"
13961 : "Gnumeric_OpenCalc:odf";
13962 } else {
13963 filesaver = "Gnumeric_OpenCalc:odf";
13965 odf_apply_ooo_config (&state);
13966 odf_apply_gnm_config (&state);
13968 workbook_set_saveinfo (state.pos.wb, GO_FILE_FL_AUTO,
13969 go_file_saver_for_id
13970 (filesaver));
13972 g_hash_table_destroy (state.settings.settings);
13973 state.settings.settings = NULL;
13977 go_io_progress_unset (state.context);
13978 g_free (state.last_error);
13980 /* This should be empty! */
13981 while (state.text_p_stack)
13982 odf_pop_text_p (&state);
13984 if (state.default_style.cells)
13985 odf_oo_cell_style_unref (state.default_style.cells);
13986 g_free (state.default_style.rows);
13987 g_free (state.default_style.columns);
13988 oo_chart_style_free (state.default_style.graphics);
13989 odf_free_cur_style (&state);
13990 g_hash_table_destroy (state.styles.sheet);
13991 g_hash_table_destroy (state.styles.text);
13992 g_hash_table_destroy (state.styles.col);
13993 g_hash_table_destroy (state.styles.row);
13994 g_hash_table_destroy (state.styles.cell);
13995 g_hash_table_destroy (state.styles.cell_datetime);
13996 g_hash_table_destroy (state.styles.cell_date);
13997 g_hash_table_destroy (state.styles.cell_time);
13998 g_hash_table_destroy (state.styles.master_pages);
13999 g_hash_table_destroy (state.styles.page_layouts);
14000 g_slist_free_full (state.chart.saved_graph_styles,
14001 (GDestroyNotify)g_hash_table_destroy);
14002 g_hash_table_destroy (state.chart.graph_styles);
14003 g_hash_table_destroy (state.chart.hatches);
14004 g_hash_table_destroy (state.chart.dash_styles);
14005 g_hash_table_destroy (state.chart.fill_image_styles);
14006 g_hash_table_destroy (state.chart.gradient_styles);
14007 g_hash_table_destroy (state.formats);
14008 g_hash_table_destroy (state.controls);
14009 g_hash_table_destroy (state.validations);
14010 g_hash_table_destroy (state.strings);
14011 g_hash_table_destroy (state.chart.arrow_markers);
14012 g_slist_free_full (state.sheet_order, (GDestroyNotify)g_free);
14013 if (state.openformula_namemap)
14014 g_hash_table_destroy (state.openformula_namemap);
14015 if (state.openformula_handlermap)
14016 g_hash_table_destroy (state.openformula_handlermap);
14017 g_object_unref (contents);
14018 gnm_expr_sharer_destroy (state.sharer);
14019 g_free (state.chart.cs_enhanced_path);
14020 g_free (state.chart.cs_modifiers);
14021 g_free (state.chart.cs_viewbox);
14022 g_free (state.chart.cs_type);
14023 if (state.chart.so)
14024 g_object_unref (state.chart.so);
14025 if (state.chart_list)
14026 g_slist_free_full (state.chart_list, odf_destroy_object_offset);
14027 if (state.chart.cs_variables)
14028 g_hash_table_destroy (state.chart.cs_variables);
14029 g_slist_free (state.text_p_for_cell.span_style_stack);
14030 g_slist_free_full (state.text_p_for_cell.span_style_list, g_free);
14031 if (state.text_p_for_cell.gstr)
14032 g_string_free (state.text_p_for_cell.gstr, TRUE);
14033 if (state.text_p_for_cell.attrs)
14034 pango_attr_list_unref (state.text_p_for_cell.attrs);
14036 g_object_unref (zip);
14038 if (content_malformed)
14039 go_io_error_string (io_context, _("XML document not well formed!"));
14041 i = workbook_sheet_count (state.pos.wb);
14042 while (i-- > 0)
14043 sheet_flag_recompute_spans (workbook_sheet_by_index (state.pos.wb, i));
14044 workbook_queue_all_recalc (state.pos.wb);
14046 gnm_pop_C_locale (locale);
14049 gboolean
14050 openoffice_file_probe (GOFileOpener const *fo, GsfInput *input, GOFileProbeLevel pl);
14052 gboolean
14053 openoffice_file_probe (G_GNUC_UNUSED GOFileOpener const *fo, GsfInput *input, G_GNUC_UNUSED GOFileProbeLevel pl)
14055 GsfInfile *zip;
14056 OOVer ver;
14058 gboolean old_ext_ok = FALSE;
14059 char const *name = gsf_input_name (input);
14060 if (name) {
14061 name = gsf_extension_pointer (name);
14062 old_ext_ok = (name != NULL &&
14063 (g_ascii_strcasecmp (name, "sxc") == 0 ||
14064 g_ascii_strcasecmp (name, "stc") == 0));
14067 zip = gsf_infile_zip_new (input, NULL);
14068 if (zip == NULL)
14069 return FALSE;
14071 ver = determine_oo_version
14072 (zip, old_ext_ok ? OOO_VER_1 : OOO_VER_UNKNOWN);
14074 g_object_unref (zip);
14076 return ver != OOO_VER_UNKNOWN;
14079 static char *
14080 make_node_id (GsfXMLInNode const *node)
14082 return g_strconcat (node->id, " -- ", node->parent_id, NULL);
14085 static GsfXMLInNode const *
14086 create_preparse_dtd (const GsfXMLInNode *orig, const GsfXMLInNode *overrides)
14088 int i, N;
14089 GHashTable *loc_hash =
14090 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
14091 GsfXMLInNode *res;
14093 for (N = 0; orig[N].id != NULL; N++) {
14094 g_hash_table_replace (loc_hash, make_node_id (orig + N),
14095 GINT_TO_POINTER (N));
14099 res = g_memdup (orig, (N + 1) * sizeof (GsfXMLInNode));
14100 for (i = 0; i < N; i++) {
14101 res[i].start = NULL;
14102 res[i].end = NULL;
14103 res[i].has_content = GSF_XML_NO_CONTENT;
14106 for (i = 0; overrides[i].id != NULL; i++) {
14107 char *id = make_node_id (overrides + i);
14108 int loc = GPOINTER_TO_INT (g_hash_table_lookup (loc_hash, id));
14109 if (loc)
14110 res[loc] = overrides[i];
14111 g_free (id);
14114 g_hash_table_destroy (loc_hash);
14116 return res;
14119 G_MODULE_EXPORT void
14120 go_plugin_init (G_GNUC_UNUSED GOPlugin *plugin, G_GNUC_UNUSED GOCmdContext *cc)
14122 magic_transparent = style_color_auto_back ();
14124 opendoc_content_preparse_dtd =
14125 create_preparse_dtd (opendoc_content_dtd, opendoc_content_preparse_overrides);
14127 ooo1_content_preparse_dtd =
14128 create_preparse_dtd (ooo1_content_dtd, ooo1_content_preparse_overrides);
14131 G_MODULE_EXPORT void
14132 go_plugin_shutdown (G_GNUC_UNUSED GOPlugin *plugin, G_GNUC_UNUSED GOCmdContext *cc)
14134 style_color_unref (magic_transparent);
14135 magic_transparent = NULL;
14136 g_free ((gpointer)opendoc_content_preparse_dtd);
14137 opendoc_content_preparse_dtd = NULL;
14138 g_free ((gpointer)ooo1_content_preparse_dtd);
14139 ooo1_content_preparse_dtd = NULL;