Introspection: updates.
[gnumeric.git] / src / xml-sax-write.c
blob3e23c768c71996cf2d38ccba819b6cfc23d25a33
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * xml-sax-write.c : export .gnumeric and the clipboard subset using the sax
5 * like wrappers in libgsf
7 * Copyright (C) 2003-2007 Jody Goldberg (jody@gnome.org)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) version 3.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 * USA
25 /*****************************************************************************/
27 #include <gnumeric-config.h>
28 #include <gnumeric.h>
29 #include <glib/gi18n-lib.h>
30 #include <xml-sax.h>
31 #include <workbook-view.h>
32 #include <gnm-format.h>
33 #include <workbook.h>
34 #include <workbook-priv.h> /* Workbook::names */
35 #include <cell.h>
36 #include <sheet.h>
37 #include <sheet-view.h>
38 #include <sheet-style.h>
39 #include <style-color.h>
40 #include <style-conditions.h>
41 #include <expr.h>
42 #include <expr-name.h>
43 #include <value.h>
44 #include <ranges.h>
45 #include <mstyle.h>
46 #include <style-border.h>
47 #include <validation.h>
48 #include <hlink.h>
49 #include <input-msg.h>
50 #include <tools/gnm-solver.h>
51 #include <sheet-filter.h>
52 #include <sheet-object-impl.h>
53 #include <sheet-object-cell-comment.h>
54 #include <print-info.h>
55 #include <gutils.h>
56 #include <clipboard.h>
57 #include <tools/scenarios.h>
58 #include <gnumeric-conf.h>
60 #include <goffice/goffice.h>
61 #include <gsf/gsf-libxml.h>
62 #include <gsf/gsf-output-gzip.h>
63 #include <gsf/gsf-doc-meta-data.h>
64 #include <gsf/gsf-opendoc-utils.h>
65 #include <gsf/gsf-utils.h>
67 typedef struct {
68 WorkbookView const *wb_view; /* View for the new workbook */
69 Workbook const *wb; /* The new workbook */
70 Sheet const *sheet;
71 GnmConventions *convs;
72 GHashTable *expr_map;
73 GString *cell_str; /* Scratch pad. */
75 GsfXMLOut *output;
76 } GnmOutputXML;
78 #define GNM "gnm:"
80 /* Precision to use when saving point measures. */
81 #define POINT_SIZE_PRECISION 4
83 void
84 gnm_xml_out_add_gocolor (GsfXMLOut *o, char const *id, GOColor c)
87 * This uses format "rrrr:gggg:bbbb" or "rrrr:gggg:bbbb:aaaa"
88 * using hex numbers, i.e., the numbers are in the range from
89 * 0 to FFFF.
91 * Note, that while go_xml_out_add_color exists, we cannot use
92 * it as it using a 0-255 scaling and always includes alpha.
94 unsigned r, g, b, a;
95 char buf[4 * 4 * sizeof (unsigned int) + 1];
97 GO_COLOR_TO_RGBA (c, &r, &g, &b, &a);
99 sprintf (buf, "%X:%X:%X%c%X",
100 r * 0x101, g * 0x101, b * 0x101,
101 (a == 0xff ? 0 : ':'),
102 a * 0x101);
103 gsf_xml_out_add_cstr_unchecked (o, id, buf);
106 static void
107 gnm_xml_out_add_color (GsfXMLOut *o, char const *id, GnmColor const *c)
109 gnm_xml_out_add_gocolor (o, id, c->go_color);
112 static void
113 gnm_xml_out_add_cellpos (GsfXMLOut *o, char const *id, GnmCellPos const *p)
115 g_return_if_fail (p != NULL);
116 gsf_xml_out_add_cstr_unchecked (o, id, cellpos_as_string (p));
119 static void
120 xml_out_add_range (GsfXMLOut *xml, GnmRange const *r)
122 g_return_if_fail (range_is_sane (r));
124 gsf_xml_out_add_int (xml, "startCol", r->start.col);
125 gsf_xml_out_add_int (xml, "startRow", r->start.row);
126 gsf_xml_out_add_int (xml, "endCol", r->end.col);
127 gsf_xml_out_add_int (xml, "endRow", r->end.row);
130 static void
131 xml_out_add_points (GsfXMLOut *xml, char const *name, double val)
133 gsf_xml_out_add_float (xml, name, val, POINT_SIZE_PRECISION);
136 static void
137 xml_write_boolean_attribute (GnmOutputXML *state, char const *name, gboolean value)
139 gsf_xml_out_start_element (state->output, GNM "Attribute");
140 gsf_xml_out_simple_element (state->output, GNM "name", name);
141 gsf_xml_out_simple_element (state->output, GNM "value", value ? "TRUE" : "FALSE");
142 gsf_xml_out_end_element (state->output); /* </Attribute> */
145 static void
146 xml_write_version (GnmOutputXML *state)
148 gsf_xml_out_start_element (state->output, GNM "Version");
149 gsf_xml_out_add_int (state->output, "Epoch", GNM_VERSION_EPOCH);
150 gsf_xml_out_add_int (state->output, "Major", GNM_VERSION_MAJOR);
151 gsf_xml_out_add_int (state->output, "Minor", GNM_VERSION_MINOR);
152 gsf_xml_out_add_cstr_unchecked (state->output, "Full", GNM_VERSION_FULL);
153 gsf_xml_out_end_element (state->output); /* </Version> */
156 static void
157 xml_write_attributes (GnmOutputXML *state)
159 gsf_xml_out_start_element (state->output, GNM "Attributes");
160 xml_write_boolean_attribute
161 (state, "WorkbookView::show_horizontal_scrollbar",
162 state->wb_view->show_horizontal_scrollbar);
163 xml_write_boolean_attribute
164 (state, "WorkbookView::show_vertical_scrollbar",
165 state->wb_view->show_vertical_scrollbar);
166 xml_write_boolean_attribute
167 (state, "WorkbookView::show_notebook_tabs",
168 state->wb_view->show_notebook_tabs);
169 xml_write_boolean_attribute
170 (state, "WorkbookView::do_auto_completion",
171 state->wb_view->do_auto_completion);
172 xml_write_boolean_attribute
173 (state, "WorkbookView::is_protected",
174 state->wb_view->is_protected);
175 gsf_xml_out_end_element (state->output); /* </Attributes> */
178 static void
179 xml_write_meta_data (GnmOutputXML *state)
181 gsf_doc_meta_data_write_to_odf (go_doc_get_meta_data (GO_DOC (state->wb)),
182 state->output);
185 /* DEPRECATED in 1.7.11 */
186 static void
187 xml_write_conventions (GnmOutputXML *state)
189 GODateConventions const *conv = workbook_date_conv (state->wb);
190 if (conv->use_1904)
191 gsf_xml_out_simple_element (state->output, GNM "DateConvention", "1904");
194 static void
195 xml_write_sheet_names (GnmOutputXML *state)
197 int i, n = workbook_sheet_count (state->wb);
198 Sheet *sheet;
200 gsf_xml_out_start_element (state->output, GNM "SheetNameIndex");
201 for (i = 0 ; i < n ; i++) {
202 sheet = workbook_sheet_by_index (state->wb, i);
203 gsf_xml_out_start_element (state->output, GNM "SheetName");
206 * Note, that we explicitly namespace these attributes.
207 * That is not wrong, per se, but note that Gnumeric until
208 * 1.12.22 will not read files without this explicit name-
209 * space and that the abbreviation must be "gnm".
211 if (sheet->sheet_type == GNM_SHEET_OBJECT)
212 gsf_xml_out_add_cstr (state->output, GNM "SheetType", "object");
213 gsf_xml_out_add_int (state->output, GNM "Cols",
214 gnm_sheet_get_max_cols (sheet));
215 gsf_xml_out_add_int (state->output, GNM "Rows",
216 gnm_sheet_get_max_rows (sheet));
217 gsf_xml_out_add_cstr (state->output, NULL, sheet->name_unquoted);
218 gsf_xml_out_end_element (state->output); /* </gnm:SheetName> */
220 gsf_xml_out_end_element (state->output); /* </gnm:SheetNameIndex> */
223 static void
224 xml_write_name (GnmOutputXML *state, GnmNamedExpr *nexpr)
226 char *expr_str;
228 g_return_if_fail (nexpr != NULL);
230 gsf_xml_out_start_element (state->output, GNM "Name");
231 gsf_xml_out_simple_element (state->output, GNM "name",
232 expr_name_name (nexpr));
233 expr_str = expr_name_as_string (nexpr, NULL, state->convs);
234 gsf_xml_out_simple_element (state->output, GNM "value", expr_str);
235 g_free (expr_str);
236 gsf_xml_out_simple_element (state->output, GNM "position",
237 cellpos_as_string (&nexpr->pos.eval));
238 gsf_xml_out_end_element (state->output); /* </gnm:Name> */
241 static void
242 xml_write_named_expressions (GnmOutputXML *state, GnmNamedExprCollection *scope)
244 GSList *names =
245 g_slist_sort (gnm_named_expr_collection_list (scope),
246 (GCompareFunc)expr_name_cmp_by_name);
247 GSList *p;
249 if (!names)
250 return;
252 gsf_xml_out_start_element (state->output, GNM "Names");
253 for (p = names; p; p = p->next) {
254 GnmNamedExpr *nexpr = p->data;
255 xml_write_name (state, nexpr);
257 gsf_xml_out_end_element (state->output); /* </gnm:Names> */
258 g_slist_free (names);
261 static void
262 xml_write_geometry (GnmOutputXML *state)
264 if (state->wb_view->preferred_width > 0 ||
265 state->wb_view->preferred_height > 0) {
266 gsf_xml_out_start_element (state->output, GNM "Geometry");
267 gsf_xml_out_add_int (state->output, "Width", state->wb_view->preferred_width);
268 gsf_xml_out_add_int (state->output, "Height", state->wb_view->preferred_height);
269 gsf_xml_out_end_element (state->output); /* </gnm:Geometry> */
273 static void
274 xml_write_print_unit (GnmOutputXML *state, char const *name,
275 double points, GtkUnit unit)
277 gsf_xml_out_start_element (state->output, name);
278 xml_out_add_points (state->output, "Points", points);
279 gsf_xml_out_add_cstr_unchecked (state->output, "PrefUnit",
280 unit_to_unit_name (unit));
281 gsf_xml_out_end_element (state->output);
284 static void
285 xml_write_print_repeat_range (GnmOutputXML *state,
286 char const *name,
287 const char *range)
289 if (range && *range) {
290 gsf_xml_out_start_element (state->output, name);
291 gsf_xml_out_add_cstr_unchecked (state->output, "value", range);
292 gsf_xml_out_end_element (state->output);
296 static void
297 xml_write_print_hf (GnmOutputXML *state, char const *name,
298 GnmPrintHF const *hf)
300 gsf_xml_out_start_element (state->output, name);
301 gsf_xml_out_add_cstr (state->output, "Left", hf->left_format);
302 gsf_xml_out_add_cstr (state->output, "Middle", hf->middle_format);
303 gsf_xml_out_add_cstr (state->output, "Right", hf->right_format);
304 gsf_xml_out_end_element (state->output);
308 static void
309 xml_write_breaks (GnmOutputXML *state, GnmPageBreaks *breaks)
311 GArray const *details = breaks->details;
312 GnmPageBreak const *binfo;
313 unsigned i;
315 gsf_xml_out_start_element (state->output,
316 (breaks->is_vert) ? GNM "vPageBreaks" : GNM "hPageBreaks");
317 gsf_xml_out_add_int (state->output, "count", details->len);
319 for (i = 0 ; i < details->len ; i++) {
320 binfo = &g_array_index (details, GnmPageBreak, i);
321 gsf_xml_out_start_element (state->output, GNM "break");
322 gsf_xml_out_add_int (state->output, "pos", binfo->pos);
323 if (binfo->type == GNM_PAGE_BREAK_MANUAL)
324 gsf_xml_out_add_cstr_unchecked (state->output, "type", "manual");
325 else if (binfo->type == GNM_PAGE_BREAK_DATA_SLICE)
326 gsf_xml_out_add_cstr_unchecked (state->output, "type", "data-slice");
327 else if (binfo->type == GNM_PAGE_BREAK_AUTO)
328 gsf_xml_out_add_cstr_unchecked (state->output, "type", "auto");
329 gsf_xml_out_end_element (state->output); /* </break> */
331 gsf_xml_out_end_element (state->output);
334 static void
335 xml_write_print_info (GnmOutputXML *state, GnmPrintInformation *pi)
337 char *paper_name;
338 char const *uri;
339 double header;
340 double footer;
341 double left;
342 double right;
343 double edge_to_above_footer;
344 double edge_to_below_header;
345 GtkPageOrientation orient;
347 g_return_if_fail (pi != NULL);
349 gsf_xml_out_start_element (state->output, GNM "PrintInformation");
351 gsf_xml_out_start_element (state->output, GNM "Margins");
353 print_info_get_margins (pi, &header, &footer, &left, &right,
354 &edge_to_below_header, &edge_to_above_footer);
355 xml_write_print_unit (state, GNM "top", edge_to_below_header,
356 pi->desired_display.header);
357 xml_write_print_unit (state, GNM "bottom", edge_to_above_footer,
358 pi->desired_display.footer);
359 xml_write_print_unit (state, GNM "left", left,
360 pi->desired_display.left);
361 xml_write_print_unit (state, GNM "right", right,
362 pi->desired_display.right);
363 xml_write_print_unit (state, GNM "header", header,
364 pi->desired_display.top);
365 xml_write_print_unit (state, GNM "footer", footer,
366 pi->desired_display.bottom);
367 gsf_xml_out_end_element (state->output);
369 gsf_xml_out_start_element (state->output, GNM "Scale");
370 if (pi->scaling.type == PRINT_SCALE_PERCENTAGE) {
371 gsf_xml_out_add_cstr_unchecked (state->output, "type", "percentage");
372 go_xml_out_add_double (state->output, "percentage", pi->scaling.percentage.x);
373 } else {
374 gsf_xml_out_add_cstr_unchecked (state->output, "type", "size_fit");
375 go_xml_out_add_double (state->output, "cols", pi->scaling.dim.cols);
376 go_xml_out_add_double (state->output, "rows", pi->scaling.dim.rows);
378 gsf_xml_out_end_element (state->output);
380 gsf_xml_out_start_element (state->output, GNM "vcenter");
381 gsf_xml_out_add_int (state->output, "value", pi->center_vertically);
382 gsf_xml_out_end_element (state->output);
384 gsf_xml_out_start_element (state->output, GNM "hcenter");
385 gsf_xml_out_add_int (state->output, "value", pi->center_horizontally);
386 gsf_xml_out_end_element (state->output);
388 gsf_xml_out_start_element (state->output, GNM "grid");
389 gsf_xml_out_add_int (state->output, "value", pi->print_grid_lines);
390 gsf_xml_out_end_element (state->output);
392 gsf_xml_out_start_element (state->output, GNM "even_if_only_styles");
393 gsf_xml_out_add_int (state->output, "value", pi->print_even_if_only_styles);
394 gsf_xml_out_end_element (state->output);
396 gsf_xml_out_start_element (state->output, GNM "monochrome");
397 gsf_xml_out_add_int (state->output, "value", pi->print_black_and_white);
398 gsf_xml_out_end_element (state->output);
400 gsf_xml_out_start_element (state->output, GNM "draft");
401 gsf_xml_out_add_int (state->output, "value", pi->print_as_draft);
402 gsf_xml_out_end_element (state->output);
404 gsf_xml_out_start_element (state->output, GNM "titles");
405 gsf_xml_out_add_int (state->output, "value", pi->print_titles);
406 gsf_xml_out_end_element (state->output);
408 gsf_xml_out_start_element (state->output, GNM "do_not_print");
409 gsf_xml_out_add_int (state->output, "value", pi->do_not_print);
410 gsf_xml_out_end_element (state->output);
412 gsf_xml_out_start_element (state->output, GNM "print_range");
413 gsf_xml_out_add_enum (state->output, "value",
414 GNM_PRINT_RANGE_TYPE,
415 print_info_get_printrange (pi) );
416 gsf_xml_out_end_element (state->output);
418 xml_write_print_repeat_range (state, GNM "repeat_top", pi->repeat_top);
419 xml_write_print_repeat_range (state, GNM "repeat_left", pi->repeat_left);
421 /* this was once an enum, hence the silly strings */
422 gsf_xml_out_simple_element (state->output, GNM "order",
423 pi->print_across_then_down ? "r_then_d" :"d_then_r");
425 orient = print_info_get_paper_orientation (pi);
426 gsf_xml_out_simple_element (state->output, GNM "orientation",
427 (orient == GTK_PAGE_ORIENTATION_PORTRAIT
428 || orient == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT)
429 ? "portrait" : "landscape");
430 #warning TODO: we should also handle inversion
432 xml_write_print_hf (state, GNM "Header", pi->header);
433 xml_write_print_hf (state, GNM "Footer", pi->footer);
435 paper_name = print_info_get_paper (pi);
436 if (paper_name)
437 gsf_xml_out_simple_element (state->output, GNM "paper",
438 paper_name);
439 g_free (paper_name);
441 uri = print_info_get_printtofile_uri (pi);
442 if (uri)
443 gsf_xml_out_simple_element (state->output, GNM "print-to-uri",
444 uri);
446 if (NULL != pi->page_breaks.v)
447 xml_write_breaks (state, pi->page_breaks.v);
448 if (NULL != pi->page_breaks.h)
449 xml_write_breaks (state, pi->page_breaks.h);
451 gsf_xml_out_start_element (state->output, GNM "comments");
452 gsf_xml_out_add_enum (state->output, "placement",
453 GNM_PRINT_COMMENT_PLACEMENT_TYPE,
454 pi->comment_placement);
455 gsf_xml_out_end_element (state->output);
457 gsf_xml_out_start_element (state->output, GNM "errors");
458 gsf_xml_out_add_enum (state->output, "PrintErrorsAs",
459 GNM_PRINT_ERRORS_TYPE,
460 pi->error_display);
461 gsf_xml_out_end_element (state->output);
463 gsf_xml_out_end_element (state->output);
466 static void
467 xml_write_style (GnmOutputXML *state, GnmStyle const *style)
469 static char const *border_names[] = {
470 GNM "Top",
471 GNM "Bottom",
472 GNM "Left",
473 GNM "Right",
474 GNM "Rev-Diagonal",
475 GNM "Diagonal"
477 GnmValidation const *v;
478 GnmHLink const *lnk;
479 GnmInputMsg const *im;
480 GnmStyleConditions const *sc;
481 GnmStyleBorderType t;
482 unsigned i;
483 gboolean started;
485 gsf_xml_out_start_element (state->output, GNM "Style");
487 if (gnm_style_is_element_set (style, MSTYLE_ALIGN_H))
488 gsf_xml_out_add_enum (state->output, "HAlign",
489 GNM_ALIGN_H_TYPE,
490 gnm_style_get_align_h (style));
491 if (gnm_style_is_element_set (style, MSTYLE_ALIGN_V))
492 gsf_xml_out_add_enum (state->output, "VAlign",
493 GNM_ALIGN_V_TYPE,
494 gnm_style_get_align_v (style));
495 if (gnm_style_is_element_set (style, MSTYLE_WRAP_TEXT))
496 gsf_xml_out_add_bool (state->output, "WrapText",
497 gnm_style_get_wrap_text (style));
498 if (gnm_style_is_element_set (style, MSTYLE_SHRINK_TO_FIT))
499 gsf_xml_out_add_bool (state->output, "ShrinkToFit",
500 gnm_style_get_shrink_to_fit (style));
501 if (gnm_style_is_element_set (style, MSTYLE_ROTATION))
502 gsf_xml_out_add_int (state->output, "Rotation",
503 gnm_style_get_rotation (style));
504 if (gnm_style_is_element_set (style, MSTYLE_PATTERN))
505 gsf_xml_out_add_int (state->output, "Shade",
506 gnm_style_get_pattern (style));
507 if (gnm_style_is_element_set (style, MSTYLE_INDENT))
508 gsf_xml_out_add_int (state->output, "Indent", gnm_style_get_indent (style));
509 if (gnm_style_is_element_set (style, MSTYLE_CONTENTS_LOCKED))
510 gsf_xml_out_add_bool (state->output, "Locked",
511 gnm_style_get_contents_locked (style));
512 if (gnm_style_is_element_set (style, MSTYLE_CONTENTS_HIDDEN))
513 gsf_xml_out_add_bool (state->output, "Hidden",
514 gnm_style_get_contents_hidden (style));
515 if (gnm_style_is_element_set (style, MSTYLE_FONT_COLOR))
516 gnm_xml_out_add_color (state->output, "Fore",
517 gnm_style_get_font_color (style));
518 if (gnm_style_is_element_set (style, MSTYLE_COLOR_BACK))
519 gnm_xml_out_add_color (state->output, "Back",
520 gnm_style_get_back_color (style));
521 if (gnm_style_is_element_set (style, MSTYLE_COLOR_PATTERN))
522 gnm_xml_out_add_color (state->output, "PatternColor",
523 gnm_style_get_pattern_color (style));
524 if (gnm_style_is_element_set (style, MSTYLE_FORMAT)) {
525 const char *fmt = go_format_as_XL (gnm_style_get_format (style));
526 gsf_xml_out_add_cstr (state->output, "Format", fmt);
529 if (gnm_style_is_element_set (style, MSTYLE_FONT_NAME) ||
530 gnm_style_is_element_set (style, MSTYLE_FONT_SIZE) ||
531 gnm_style_is_element_set (style, MSTYLE_FONT_BOLD) ||
532 gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC) ||
533 gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
534 gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH) ||
535 gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT)) {
536 gsf_xml_out_start_element (state->output, GNM "Font");
538 if (gnm_style_is_element_set (style, MSTYLE_FONT_SIZE))
539 xml_out_add_points (state->output, "Unit", gnm_style_get_font_size (style));
540 if (gnm_style_is_element_set (style, MSTYLE_FONT_BOLD))
541 gsf_xml_out_add_int (state->output, "Bold", gnm_style_get_font_bold (style));
542 if (gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC))
543 gsf_xml_out_add_int (state->output, "Italic", gnm_style_get_font_italic (style));
544 if (gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE))
545 gsf_xml_out_add_int (state->output, "Underline", (int)gnm_style_get_font_uline (style));
546 if (gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
547 gsf_xml_out_add_int (state->output, "StrikeThrough", gnm_style_get_font_strike (style));
548 if (gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT))
549 gsf_xml_out_add_int (state->output, "Script", (int)gnm_style_get_font_script (style));
551 if (gnm_style_is_element_set (style, MSTYLE_FONT_NAME)) {
552 const char *fontname = gnm_style_get_font_name (style);
553 gsf_xml_out_add_cstr (state->output, NULL, fontname);
556 gsf_xml_out_end_element (state->output);
559 if (gnm_style_is_element_set (style, MSTYLE_HLINK) &&
560 NULL != (lnk = gnm_style_get_hlink (style))) {
561 gsf_xml_out_start_element (state->output, GNM "HyperLink");
562 gsf_xml_out_add_cstr (state->output, "type", g_type_name (G_OBJECT_TYPE (lnk)));
563 gsf_xml_out_add_cstr (state->output, "target", gnm_hlink_get_target (lnk));
564 if (gnm_hlink_get_tip (lnk) != NULL)
565 gsf_xml_out_add_cstr (state->output, "tip", gnm_hlink_get_tip (lnk));
566 gsf_xml_out_end_element (state->output);
569 if (gnm_style_is_element_set (style, MSTYLE_VALIDATION) &&
570 NULL != (v = gnm_style_get_validation (style))) {
571 GnmParsePos pp;
572 char *tmp;
574 gsf_xml_out_start_element (state->output, GNM "Validation");
575 gsf_xml_out_add_enum (state->output, "Style",
576 GNM_VALIDATION_STYLE_TYPE, v->style);
577 gsf_xml_out_add_enum (state->output, "Type",
578 GNM_VALIDATION_TYPE_TYPE, v->type);
580 switch (v->type) {
581 case GNM_VALIDATION_TYPE_AS_INT :
582 case GNM_VALIDATION_TYPE_AS_NUMBER :
583 case GNM_VALIDATION_TYPE_AS_DATE :
584 case GNM_VALIDATION_TYPE_AS_TIME :
585 case GNM_VALIDATION_TYPE_TEXT_LENGTH :
586 gsf_xml_out_add_enum (state->output, "Operator",
587 GNM_VALIDATION_OP_TYPE, v->op);
588 break;
589 default :
590 break;
593 gsf_xml_out_add_bool (state->output, "AllowBlank", v->allow_blank);
594 gsf_xml_out_add_bool (state->output, "UseDropdown", v->use_dropdown);
596 if (v->title != NULL && v->title->str[0] != '\0')
597 gsf_xml_out_add_cstr (state->output, "Title", v->title->str);
598 if (v->msg != NULL && v->msg->str[0] != '\0')
599 gsf_xml_out_add_cstr (state->output, "Message", v->msg->str);
601 parse_pos_init_sheet (&pp, (Sheet *)state->sheet);
603 if (v->deps[0].texpr != NULL &&
604 (tmp = gnm_expr_top_as_string (v->deps[0].texpr, &pp, state->convs)) != NULL) {
605 gsf_xml_out_simple_element (state->output, GNM "Expression0", tmp);
606 g_free (tmp);
608 if (v->deps[1].texpr != NULL &&
609 (tmp = gnm_expr_top_as_string (v->deps[1].texpr, &pp, state->convs)) != NULL) {
610 gsf_xml_out_simple_element (state->output, GNM "Expression1", tmp);
611 g_free (tmp);
613 gsf_xml_out_end_element (state->output); /* </Validation> */
616 if (gnm_style_is_element_set (style, MSTYLE_INPUT_MSG) &&
617 NULL != (im = gnm_style_get_input_msg (style))) {
618 char const *txt;
619 gsf_xml_out_start_element (state->output, GNM "InputMessage");
620 if (NULL != (txt = gnm_input_msg_get_title (im)))
621 gsf_xml_out_add_cstr (state->output, "Title", txt);
622 if (NULL != (txt = gnm_input_msg_get_msg (im)))
623 gsf_xml_out_add_cstr (state->output, "Message", txt);
624 gsf_xml_out_end_element (state->output); /* </InputMessage> */
627 if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS) &&
628 NULL != (sc = gnm_style_get_conditions (style))) {
629 GPtrArray const *conds = gnm_style_conditions_details (sc);
630 if (conds != NULL) {
631 GnmParsePos pp;
632 parse_pos_init_sheet (&pp, (Sheet *)state->sheet);
634 for (i = 0 ; i < conds->len ; i++) {
635 unsigned ui;
636 GnmStyleCond const *cond =
637 g_ptr_array_index (conds, i);
638 gsf_xml_out_start_element (state->output, GNM "Condition");
639 gsf_xml_out_add_int (state->output, "Operator", cond->op);
640 for (ui = 0; ui < 2; ui++) {
641 GnmExprTop const *texpr = gnm_style_cond_get_expr (cond, ui);
642 char *tmp = texpr
643 ? gnm_expr_top_as_string (texpr, &pp, state->convs)
644 : NULL;
645 const char *attr = (ui == 0)
646 ? GNM "Expression0"
647 : GNM "Expression1";
648 if (tmp) {
649 gsf_xml_out_simple_element (state->output, attr, tmp);
650 g_free (tmp);
653 xml_write_style (state, cond->overlay);
654 gsf_xml_out_end_element (state->output); /* </Condition> */
659 started = FALSE;
660 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
661 GnmBorder const *border;
662 if (gnm_style_is_element_set (style, i) &&
663 NULL != (border = gnm_style_get_border (style, i)) &&
664 GNM_STYLE_BORDER_NONE != (t = border->line_type)) {
665 GnmColor const *col = border->color;
667 if (!started) {
668 gsf_xml_out_start_element (state->output, GNM "StyleBorder");
669 started = TRUE;
672 gsf_xml_out_start_element (state->output,
673 border_names [i - MSTYLE_BORDER_TOP]);
674 gsf_xml_out_add_int (state->output, "Style", t);
675 gnm_xml_out_add_color (state->output, "Color", col);
676 gsf_xml_out_end_element (state->output);
679 if (started)
680 gsf_xml_out_end_element (state->output);
682 gsf_xml_out_end_element (state->output);
685 static void
686 xml_write_style_region (GnmOutputXML *state, GnmStyleRegion const *region)
688 gsf_xml_out_start_element (state->output, GNM "StyleRegion");
689 xml_out_add_range (state->output, &region->range);
690 if (region->style != NULL)
691 xml_write_style (state, region->style);
692 gsf_xml_out_end_element (state->output);
695 static int
696 cb_sheet_style_order (GnmStyleRegion const *a, GnmStyleRegion const *b)
698 GnmRange const *ra = &a->range;
699 GnmRange const *rb = &b->range;
700 int res;
702 res = ra->start.col - rb->start.col;
704 if (res == 0)
705 res = ra->start.row - rb->start.row;
707 return res;
710 static void
711 xml_write_styles (GnmOutputXML *state)
713 GnmStyleList *styles =
714 g_slist_sort (sheet_style_get_range (state->sheet, NULL),
715 (GCompareFunc)cb_sheet_style_order);
716 if (styles != NULL) {
717 GnmStyleList *ptr;
719 gsf_xml_out_start_element (state->output, GNM "Styles");
720 for (ptr = styles; ptr; ptr = ptr->next)
721 xml_write_style_region (state, ptr->data);
722 gsf_xml_out_end_element (state->output);
723 style_list_free (styles);
727 typedef struct {
728 GnmOutputXML *state;
729 gboolean is_column;
730 ColRowInfo const *prev;
731 int prev_pos, rle_count;
732 } closure_write_colrow;
734 static gboolean
735 xml_write_colrow_info (GnmColRowIter const *iter, closure_write_colrow *closure)
737 ColRowInfo const *prev = closure->prev;
738 GsfXMLOut *output = closure->state->output;
739 ColRowInfo const *def =
740 sheet_colrow_get_default (closure->state->sheet,
741 closure->is_column);
743 closure->rle_count++;
744 if (NULL != iter && col_row_info_equal (prev, iter->cri))
745 return FALSE;
747 if (prev != NULL && !col_row_info_equal (prev, def)) {
748 if (closure->is_column)
749 gsf_xml_out_start_element (output, GNM "ColInfo");
750 else
751 gsf_xml_out_start_element (output, GNM "RowInfo");
753 gsf_xml_out_add_int (output, "No", closure->prev_pos);
754 xml_out_add_points (output, "Unit", prev->size_pts);
755 if (prev->hard_size)
756 gsf_xml_out_add_bool (output, "HardSize", TRUE);
757 if (!prev->visible)
758 gsf_xml_out_add_bool (output, "Hidden", TRUE);
759 if (prev->is_collapsed)
760 gsf_xml_out_add_bool (output, "Collapsed", TRUE);
761 if (prev->outline_level > 0)
762 gsf_xml_out_add_int (output, "OutlineLevel", prev->outline_level);
764 if (closure->rle_count > 1)
765 gsf_xml_out_add_int (output, "Count", closure->rle_count);
766 gsf_xml_out_end_element (output);
769 closure->rle_count = 0;
770 if (NULL != iter) {
771 closure->prev = iter->cri;
772 closure->prev_pos = iter->pos;
775 return FALSE;
778 static void
779 xml_write_cols_rows (GnmOutputXML *state)
781 closure_write_colrow closure;
782 gsf_xml_out_start_element (state->output, GNM "Cols");
783 xml_out_add_points (state->output, "DefaultSizePts",
784 sheet_col_get_default_size_pts (state->sheet));
785 closure.state = state;
786 closure.is_column = TRUE;
787 closure.prev = NULL;
788 closure.prev_pos = -1;
789 closure.rle_count = 0;
790 col_row_collection_foreach (&state->sheet->cols, 0, gnm_sheet_get_last_col (state->sheet),
791 (ColRowHandler)&xml_write_colrow_info, &closure);
792 xml_write_colrow_info (NULL, &closure); /* flush */
793 gsf_xml_out_end_element (state->output); /* </gnm:Cols> */
795 gsf_xml_out_start_element (state->output, GNM "Rows");
796 xml_out_add_points (state->output, "DefaultSizePts",
797 sheet_row_get_default_size_pts (state->sheet));
798 closure.state = state;
799 closure.is_column = FALSE;
800 closure.prev = NULL;
801 closure.prev_pos = -1;
802 closure.rle_count = 0;
803 col_row_collection_foreach (&state->sheet->rows, 0, gnm_sheet_get_last_row (state->sheet),
804 (ColRowHandler)&xml_write_colrow_info, &closure);
805 xml_write_colrow_info (NULL, &closure); /* flush */
806 gsf_xml_out_end_element (state->output); /* </gnm:Rows> */
809 static void
810 xml_write_selection_info (GnmOutputXML *state)
812 GSList *ptr, *copy;
813 SheetView const *sv = sheet_get_view (state->sheet, state->wb_view);
814 if (!sv) return; /* Hidden. */
816 gsf_xml_out_start_element (state->output, GNM "Selections");
817 gsf_xml_out_add_int (state->output, "CursorCol", sv->edit_pos_real.col);
818 gsf_xml_out_add_int (state->output, "CursorRow", sv->edit_pos_real.row);
820 /* Insert the selections in REVERSE order */
821 copy = g_slist_reverse (g_slist_copy (sv->selections));
822 for (ptr = copy; ptr; ptr = ptr->next) {
823 GnmRange const *r = ptr->data;
824 gsf_xml_out_start_element (state->output, GNM "Selection");
825 xml_out_add_range (state->output, r);
826 gsf_xml_out_end_element (state->output); /* </gnm:Selection> */
828 g_slist_free (copy);
830 gsf_xml_out_end_element (state->output); /* </gnm:Selections> */
833 static void
834 xml_write_cell_and_position (GnmOutputXML *state,
835 GnmExprTop const *texpr, GnmValue const *val,
836 GnmParsePos const *pp)
838 gboolean write_contents = TRUE;
839 gboolean const is_shared_expr = (texpr != NULL) &&
840 gnm_expr_top_is_shared (texpr);
842 /* Only the top left corner of an array needs to be saved (>= 0.53) */
843 if (texpr && gnm_expr_top_is_array_elem (texpr, NULL, NULL))
844 return; /* DOM version would write <Cell Col= Row=/> */
846 gsf_xml_out_start_element (state->output, GNM "Cell");
847 gsf_xml_out_add_int (state->output, "Row", pp->eval.row);
848 gsf_xml_out_add_int (state->output, "Col", pp->eval.col);
850 /* As of version 0.53 we save the ID of shared expressions */
851 if (is_shared_expr) {
852 gpointer id = g_hash_table_lookup (state->expr_map, (gpointer) texpr);
854 if (id == NULL) {
855 id = GINT_TO_POINTER (g_hash_table_size (state->expr_map) + 1);
856 g_hash_table_insert (state->expr_map, (gpointer)texpr, id);
857 } else
858 write_contents = FALSE;
860 gsf_xml_out_add_int (state->output, "ExprID", GPOINTER_TO_INT (id));
863 /* As of version 0.53 we save the size of the array as attributes */
864 /* As of version 0.57 the attributes are in the Cell not the Content */
865 if (texpr && gnm_expr_top_is_array_corner (texpr)) {
866 int cols, rows;
867 gnm_expr_top_get_array_size (texpr, &cols, &rows);
868 gsf_xml_out_add_int (state->output, "Rows", rows);
869 gsf_xml_out_add_int (state->output, "Cols", cols);
872 if (write_contents) {
873 GString *str = state->cell_str;
875 g_string_truncate (str, 0);
877 if (!texpr) {
878 if (val != NULL) {
879 gsf_xml_out_add_int (state->output, "ValueType", val->v_any.type);
880 if (VALUE_FMT (val) != NULL) {
881 const char *fmt = go_format_as_XL (VALUE_FMT (val));
882 gsf_xml_out_add_cstr (state->output, "ValueFormat", fmt);
884 value_get_as_gstring (val, str, state->convs);
885 } else {
886 g_warning ("%s has no value ?", cellpos_as_string (&pp->eval));
888 } else {
889 GnmConventionsOut out;
890 out.accum = str;
891 out.pp = pp;
892 out.convs = state->convs;
894 g_string_append_c (str, '=');
895 gnm_expr_top_as_gstring (texpr, &out);
898 gsf_xml_out_add_cstr (state->output, NULL, str->str);
900 gsf_xml_out_end_element (state->output); /* </gnm:Cell> */
903 static GnmValue *
904 cb_write_cell (GnmCellIter const *iter, GnmOutputXML *state)
906 GnmExprTop const *texpr = iter->cell->base.texpr;
907 GnmValue const *value = iter->cell->value;
909 if (texpr == NULL && VALUE_IS_EMPTY (value))
910 return NULL;
912 xml_write_cell_and_position (state, texpr, value, &iter->pp);
913 return NULL;
916 static void
917 xml_write_cells (GnmOutputXML *state)
919 gsf_xml_out_start_element (state->output, GNM "Cells");
920 sheet_foreach_cell_in_range ((Sheet *)state->sheet, CELL_ITER_IGNORE_NONEXISTENT,
921 0, 0, gnm_sheet_get_last_col (state->sheet), gnm_sheet_get_last_row (state->sheet),
922 (CellIterFunc) cb_write_cell, state);
923 gsf_xml_out_end_element (state->output); /* </gnm:Cells> */
926 static void
927 xml_write_merged_regions (GnmOutputXML *state)
929 GSList *ptr = state->sheet->list_merged;
930 if (ptr == NULL)
931 return;
932 gsf_xml_out_start_element (state->output, GNM "MergedRegions");
933 for (; ptr != NULL ; ptr = ptr->next)
934 gsf_xml_out_simple_element (state->output,
935 GNM "Merge", range_as_string (ptr->data));
936 gsf_xml_out_end_element (state->output); /* </gnm:MergedRegions> */
939 static void
940 xml_write_sheet_layout (GnmOutputXML *state)
942 SheetView const *sv = sheet_get_view (state->sheet, state->wb_view);
943 if (!sv) return; /* Hidden. */
945 gsf_xml_out_start_element (state->output, GNM "SheetLayout");
946 gnm_xml_out_add_cellpos (state->output, "TopLeft", &sv->initial_top_left);
948 if (sv_is_frozen (sv)) {
949 gsf_xml_out_start_element (state->output, GNM "FreezePanes");
950 gnm_xml_out_add_cellpos (state->output, "FrozenTopLeft", &sv->frozen_top_left);
951 gnm_xml_out_add_cellpos (state->output, "UnfrozenTopLeft", &sv->unfrozen_top_left);
952 gsf_xml_out_end_element (state->output); /* </gnm:FreezePanes> */
954 gsf_xml_out_end_element (state->output); /* </gnm:SheetLayout> */
957 static void
958 xml_write_filter_expr (GnmOutputXML *state,
959 GnmFilterCondition const *cond, unsigned i)
961 static char const *filter_cond_name[] = { "eq", "gt", "lt", "gte", "lte", "ne" };
963 * WARNING WARNING WARING
964 * Value and ValueType are _reversed !!!
966 static struct { char const *op, *valtype, *val; } filter_expr_attrs[] = {
967 { "Op0", "Value0", "ValueType0" },
968 { "Op1", "Value1", "ValueType1" }
971 GString *text = g_string_new (NULL);
972 value_get_as_gstring (cond->value[i], text, state->convs);
973 gsf_xml_out_add_cstr_unchecked (state->output,
974 filter_expr_attrs[i].op, filter_cond_name [cond->op[i]]);
975 gsf_xml_out_add_int (state->output,
976 filter_expr_attrs[i].valtype, cond->value[i]->v_any.type);
977 gsf_xml_out_add_cstr (state->output,
978 filter_expr_attrs[i].val, text->str);
979 g_string_free (text, TRUE);
982 static void
983 xml_write_filter_field (GnmOutputXML *state,
984 GnmFilterCondition const *cond, unsigned i)
986 gsf_xml_out_start_element (state->output, GNM "Field");
987 gsf_xml_out_add_int (state->output, "Index", i);
989 switch (GNM_FILTER_OP_TYPE_MASK & cond->op[0]) {
990 case 0: gsf_xml_out_add_cstr_unchecked (state->output, "Type", "expr");
991 xml_write_filter_expr (state, cond, 0);
992 if (cond->op[1] != GNM_FILTER_UNUSED) {
993 xml_write_filter_expr (state, cond, 1);
994 gsf_xml_out_add_bool (state->output, "IsAnd", cond->is_and);
996 break;
997 case GNM_FILTER_OP_BLANKS:
998 gsf_xml_out_add_cstr_unchecked (state->output, "Type", "blanks");
999 break;
1000 case GNM_FILTER_OP_NON_BLANKS:
1001 gsf_xml_out_add_cstr_unchecked (state->output, "Type", "nonblanks");
1002 break;
1003 case GNM_FILTER_OP_TOP_N:
1004 gsf_xml_out_add_cstr_unchecked (state->output, "Type", "bucket");
1005 gsf_xml_out_add_bool (state->output, "top",
1006 cond->op[0] & 1 ? TRUE : FALSE);
1007 gsf_xml_out_add_bool (state->output, "items",
1008 cond->op[0] & 2 ? TRUE : FALSE);
1009 go_xml_out_add_double (state->output, "count", cond->count);
1010 break;
1013 gsf_xml_out_end_element (state->output); /* </gnm:Field> */
1016 static void
1017 xml_write_sheet_filters (GnmOutputXML *state)
1019 GSList *ptr;
1020 GnmFilter *filter;
1021 GnmFilterCondition const *cond;
1022 unsigned i;
1024 if (state->sheet->filters == NULL)
1025 return;
1027 gsf_xml_out_start_element (state->output, GNM "Filters");
1029 for (ptr = state->sheet->filters; ptr != NULL ; ptr = ptr->next) {
1030 filter = ptr->data;
1031 gsf_xml_out_start_element (state->output, GNM "Filter");
1032 gsf_xml_out_add_cstr_unchecked (state->output, "Area",
1033 range_as_string (&filter->r));
1035 for (i = filter->fields->len ; i-- > 0 ; ) {
1036 cond = gnm_filter_get_condition (filter, i);
1037 if (cond != NULL && cond->op[0] != GNM_FILTER_UNUSED)
1038 xml_write_filter_field (state, cond, i);
1041 gsf_xml_out_end_element (state->output); /* </gnm:Filter> */
1044 gsf_xml_out_end_element (state->output); /* </gnm:Filters> */
1047 static void
1048 xml_write_solver (GnmOutputXML *state)
1050 GnmSolverParameters *param = state->sheet->solver_parameters;
1051 GSList *ptr;
1052 GnmCellRef const *target;
1053 GnmValue const *input;
1055 if (param == NULL)
1056 return;
1058 gsf_xml_out_start_element (state->output, GNM "Solver");
1060 target = gnm_solver_param_get_target (param);
1061 if (target != NULL) {
1062 GnmExpr const *expr = gnm_expr_new_cellref (target);
1063 GnmParsePos pp;
1064 char *txt = gnm_expr_as_string
1065 (expr,
1066 parse_pos_init_sheet (&pp, state->sheet),
1067 state->convs);
1068 gsf_xml_out_add_cstr (state->output, "Target", txt);
1069 g_free (txt);
1070 gnm_expr_free (expr);
1073 gsf_xml_out_add_int (state->output, "ModelType", param->options.model_type);
1074 gsf_xml_out_add_int (state->output, "ProblemType", param->problem_type);
1075 input = gnm_solver_param_get_input (param);
1076 if (input)
1077 gsf_xml_out_add_cstr (state->output, "Inputs",
1078 value_peek_string (input));
1079 gsf_xml_out_add_int (state->output, "MaxTime",
1080 param->options.max_time_sec);
1081 gsf_xml_out_add_int (state->output, "MaxIter",
1082 param->options.max_iter);
1083 gsf_xml_out_add_bool (state->output, "NonNeg",
1084 param->options.assume_non_negative);
1085 gsf_xml_out_add_bool (state->output, "Discr",
1086 param->options.assume_discrete);
1087 gsf_xml_out_add_bool (state->output, "AutoScale",
1088 param->options.automatic_scaling);
1089 gsf_xml_out_add_bool (state->output, "ProgramR",
1090 param->options.program_report);
1091 gsf_xml_out_add_bool (state->output, "SensitivityR",
1092 param->options.sensitivity_report);
1094 for (ptr = param->constraints; ptr != NULL ; ptr = ptr->next) {
1095 GnmSolverConstraint const *c = ptr->data;
1096 int type;
1097 GString *str = g_string_new (NULL);
1099 /* Historical values. Not a bit field. */
1100 switch (c->type) {
1101 default: type = 0; break;
1102 case GNM_SOLVER_LE: type = 1; break;
1103 case GNM_SOLVER_GE: type = 2; break;
1104 case GNM_SOLVER_EQ: type = 4; break;
1105 case GNM_SOLVER_INTEGER: type = 8; break;
1106 case GNM_SOLVER_BOOLEAN: type = 16; break;
1109 gsf_xml_out_start_element (state->output, GNM "Constr");
1110 gsf_xml_out_add_int (state->output, "Type", type);
1112 gnm_solver_constraint_side_as_str (c, state->sheet, str, TRUE);
1113 gsf_xml_out_add_cstr (state->output, "lhs", str->str);
1115 if (gnm_solver_constraint_has_rhs (c)) {
1116 g_string_truncate (str, 0);
1117 gnm_solver_constraint_side_as_str (c, state->sheet,
1118 str, FALSE);
1119 gsf_xml_out_add_cstr (state->output, "rhs", str->str);
1122 gsf_xml_out_end_element (state->output); /* </gnm:Constr> */
1124 g_string_free (str, TRUE);
1127 gsf_xml_out_end_element (state->output); /* </gnm:Solver> */
1130 static void
1131 xml_write_scenario (GnmOutputXML *state, GnmScenario const *sc)
1133 GSList *l;
1134 GnmParsePos pp;
1136 parse_pos_init_sheet (&pp, sc->sheet);
1138 gsf_xml_out_start_element (state->output, GNM "Scenario");
1140 gsf_xml_out_add_cstr (state->output, "Name", sc->name);
1141 if (sc->comment)
1142 gsf_xml_out_add_cstr (state->output, "Comment", sc->comment);
1144 for (l = sc->items; l; l = l->next) {
1145 GnmScenarioItem const *sci = l->data;
1146 GnmValue const *val = sci->value;
1147 GString *str;
1148 GnmConventionsOut out;
1150 if (!gnm_scenario_item_valid (sci, NULL))
1151 continue;
1153 str = g_string_new (NULL);
1154 gsf_xml_out_start_element (state->output, GNM "Item");
1156 out.accum = str;
1157 out.pp = &pp;
1158 out.convs = state->convs;
1160 gnm_expr_top_as_gstring (sci->dep.texpr, &out);
1161 gsf_xml_out_add_cstr (state->output, "Range", str->str);
1163 if (val) {
1164 gsf_xml_out_add_int (state->output,
1165 "ValueType",
1166 val->v_any.type);
1167 if (VALUE_FMT (val) != NULL) {
1168 const char *fmt = go_format_as_XL (VALUE_FMT (val));
1169 gsf_xml_out_add_cstr (state->output, "ValueFormat", fmt);
1171 g_string_truncate (str, 0);
1172 value_get_as_gstring (val, str, state->convs);
1173 gsf_xml_out_add_cstr (state->output, NULL, str->str);
1176 gsf_xml_out_end_element (state->output); /* </gnm:Item> */
1177 g_string_free (str, TRUE);
1180 gsf_xml_out_end_element (state->output); /* </gnm:Scenario> */
1184 static void
1185 xml_write_scenarios (GnmOutputXML *state)
1187 GList *ptr;
1189 if (state->sheet->scenarios == NULL)
1190 return;
1192 gsf_xml_out_start_element (state->output, GNM "Scenarios");
1194 for (ptr = state->sheet->scenarios ; ptr != NULL ; ptr = ptr->next) {
1195 GnmScenario const *sc = ptr->data;
1196 xml_write_scenario (state, sc);
1199 gsf_xml_out_end_element (state->output); /* </gnm:Scenarios> */
1202 static int
1203 so_by_pos (SheetObject *a, SheetObject *b)
1205 GnmRange const *ra = &a->anchor.cell_bound;
1206 GnmRange const *rb = &b->anchor.cell_bound;
1207 int i;
1208 i = ra->start.col - rb->start.col;
1209 if (!i) i = ra->start.row - rb->start.row;
1210 if (!i) i = ra->end.col - rb->end.col;
1211 if (!i) i = ra->end.row - rb->end.row;
1212 return i;
1215 static void
1216 xml_write_objects (GnmOutputXML *state, GSList *objects)
1218 gboolean needs_container = TRUE;
1219 char buffer[4*(DBL_DIG+10)];
1220 char const *type_name;
1221 char *tmp;
1222 GSList *ptr;
1223 GSList *with_zorder = NULL;
1224 GSList *without_zorder = NULL;
1227 * Most objects are selectable and the order therefore matters.
1228 * We write those in reverse order because sheet_object_set_sheet
1229 * will reverse them on input.
1231 * Cell comments are separated out and sorted. This helps
1232 * consistency.
1234 * Yet other objects have no export method and we drop those on
1235 * the floor.
1237 for (ptr = objects ;ptr != NULL ; ptr = ptr->next) {
1238 SheetObject *so = ptr->data;
1239 SheetObjectClass *klass = GNM_SO_CLASS (G_OBJECT_GET_CLASS (so));
1240 if (klass == NULL || klass->write_xml_sax == NULL)
1241 continue;
1243 if (GNM_IS_CELL_COMMENT (so))
1244 without_zorder = g_slist_prepend (without_zorder, so);
1245 else
1246 with_zorder = g_slist_prepend (with_zorder, so);
1248 without_zorder = g_slist_sort (without_zorder, (GCompareFunc)so_by_pos);
1249 objects = g_slist_concat (without_zorder, with_zorder);
1251 for (ptr = objects ;ptr != NULL ; ptr = ptr->next) {
1252 SheetObject *so = ptr->data;
1253 SheetObjectClass *klass = GNM_SO_CLASS (G_OBJECT_GET_CLASS (so));
1254 GnmRange cell_bound = so->anchor.cell_bound;
1256 switch (so->anchor.mode) {
1257 case GNM_SO_ANCHOR_TWO_CELLS:
1258 break;
1259 case GNM_SO_ANCHOR_ONE_CELL:
1260 cell_bound.end = cell_bound.start;
1261 break;
1262 case GNM_SO_ANCHOR_ABSOLUTE:
1263 range_init (&cell_bound, 0, 0, 0, 0);
1264 break;
1265 default:
1266 g_assert_not_reached ();
1269 if (needs_container) {
1270 needs_container = FALSE;
1271 gsf_xml_out_start_element (state->output, GNM "Objects");
1274 /* A hook so that things can sometimes change names */
1275 type_name = klass->xml_export_name;
1276 if (type_name == NULL)
1277 type_name = G_OBJECT_TYPE_NAME (so);
1279 tmp = g_strconcat (GNM, type_name, NULL);
1280 gsf_xml_out_start_element (state->output, tmp);
1281 if (so->name)
1282 gsf_xml_out_add_cstr (state->output, "Name", so->name);
1283 if (so->anchor.mode != GNM_SO_ANCHOR_ABSOLUTE)
1284 gsf_xml_out_add_cstr (state->output, "ObjectBound", range_as_string (&cell_bound));
1285 if (so->anchor.mode != GNM_SO_ANCHOR_TWO_CELLS)
1286 gsf_xml_out_add_enum (state->output,
1287 "AnchorMode",
1288 GNM_SHEET_OBJECT_ANCHOR_MODE_TYPE,
1289 so->anchor.mode);
1290 snprintf (buffer, sizeof (buffer), "%.3g %.3g %.3g %.3g",
1291 so->anchor.offset [0], so->anchor.offset [1],
1292 so->anchor.offset [2], so->anchor.offset [3]);
1293 gsf_xml_out_add_cstr (state->output, "ObjectOffset", buffer);
1295 gsf_xml_out_add_int (state->output, "Direction",
1296 so->anchor.base.direction);
1297 gsf_xml_out_add_int
1298 (state->output, "Print",
1299 (so->flags & SHEET_OBJECT_PRINT) ? 1 : 0);
1301 (*klass->write_xml_sax) (so, state->output, state->convs);
1303 gsf_xml_out_end_element (state->output); /* </gnm:{typename}> */
1304 g_free (tmp);
1306 g_slist_free (objects);
1308 if (!needs_container)
1309 gsf_xml_out_end_element (state->output); /* </gnm:Objects> */
1312 static void
1313 xml_write_sheet (GnmOutputXML *state, Sheet const *sheet)
1315 GnmColor *c;
1317 state->sheet = sheet;
1318 gsf_xml_out_start_element (state->output, GNM "Sheet");
1320 gsf_xml_out_add_bool (state->output,
1321 "DisplayFormulas", sheet->display_formulas);
1322 gsf_xml_out_add_bool (state->output,
1323 "HideZero", sheet->hide_zero);
1324 gsf_xml_out_add_bool (state->output,
1325 "HideGrid", sheet->hide_grid);
1326 gsf_xml_out_add_bool (state->output,
1327 "HideColHeader", sheet->hide_col_header);
1328 gsf_xml_out_add_bool (state->output,
1329 "HideRowHeader", sheet->hide_row_header);
1330 gsf_xml_out_add_bool (state->output,
1331 "DisplayOutlines", sheet->display_outlines);
1332 gsf_xml_out_add_bool (state->output,
1333 "OutlineSymbolsBelow", sheet->outline_symbols_below);
1334 gsf_xml_out_add_bool (state->output,
1335 "OutlineSymbolsRight", sheet->outline_symbols_right);
1336 if (sheet->text_is_rtl)
1337 gsf_xml_out_add_bool (state->output,
1338 "RTL_Layout", sheet->text_is_rtl);
1339 if (sheet->is_protected)
1340 gsf_xml_out_add_bool (state->output,
1341 "Protected", sheet->is_protected);
1343 /* TODO : Make this an enum internally eventually */
1344 if (sheet->convs->r1c1_addresses)
1345 gsf_xml_out_add_cstr_unchecked (state->output,
1346 "ExprConvention", "gnumeric:R1C1");
1348 gsf_xml_out_add_enum (state->output,
1349 "Visibility", GNM_SHEET_VISIBILITY_TYPE, sheet->visibility);
1351 if (sheet->tab_color != NULL)
1352 gnm_xml_out_add_color (state->output, "TabColor", sheet->tab_color);
1353 if (sheet->tab_text_color != NULL)
1354 gnm_xml_out_add_color (state->output, "TabTextColor", sheet->tab_text_color);
1355 if (NULL != (c = sheet_style_get_auto_pattern_color (sheet))) {
1356 gnm_xml_out_add_color (state->output, "GridColor", c);
1357 style_color_unref (c);
1360 gsf_xml_out_simple_element (state->output,
1361 GNM "Name", sheet->name_unquoted);
1362 gsf_xml_out_simple_int_element (state->output,
1363 GNM "MaxCol", sheet->cols.max_used);
1364 gsf_xml_out_simple_int_element (state->output,
1365 GNM "MaxRow", sheet->rows.max_used);
1366 gsf_xml_out_simple_float_element (state->output,
1367 GNM "Zoom", sheet->last_zoom_factor_used, 4);
1369 xml_write_named_expressions (state, sheet->names);
1370 xml_write_print_info (state, sheet->print_info);
1371 xml_write_styles (state);
1372 xml_write_cols_rows (state);
1373 xml_write_selection_info (state);
1374 xml_write_objects (state, sheet->sheet_objects);
1375 xml_write_cells (state);
1377 xml_write_merged_regions (state);
1378 xml_write_sheet_layout (state);
1379 xml_write_sheet_filters (state);
1380 xml_write_solver (state);
1381 xml_write_scenarios (state);
1383 gsf_xml_out_end_element (state->output); /* </gnm:Sheet> */
1384 state->sheet = NULL;
1387 static void
1388 xml_write_sheets (GnmOutputXML *state)
1390 int i, n = workbook_sheet_count (state->wb);
1391 gsf_xml_out_start_element (state->output, GNM "Sheets");
1392 for (i = 0 ; i < n ; i++)
1393 xml_write_sheet (state, workbook_sheet_by_index (state->wb, i));
1394 gsf_xml_out_end_element (state->output); /* </gnm:Sheets> */
1397 static void
1398 xml_write_uidata (GnmOutputXML *state)
1400 gsf_xml_out_start_element (state->output, GNM "UIData");
1401 gsf_xml_out_add_int (state->output, "SelectedTab",
1402 wb_view_cur_sheet (state->wb_view)->index_in_wb);
1403 gsf_xml_out_end_element (state->output); /* </gnm:UIData> */
1406 static void
1407 xml_write_date_conventions_as_attr (GnmOutputXML *state,
1408 GODateConventions const *conv)
1410 if (conv->use_1904)
1411 gsf_xml_out_add_cstr_unchecked (state->output,
1412 GNM "DateConvention", "Apple:1904");
1415 static void
1416 xml_write_number_system (GnmOutputXML *state)
1419 * These numbers define how to interpret decimal values in the
1420 * file. They are not yet used, but should be used when the
1421 * number system of the loading Gnumeric is different from the
1422 * number system of the saving Gnumeric.
1424 gsf_xml_out_add_int (state->output, "FloatRadix", FLT_RADIX);
1425 gsf_xml_out_add_int (state->output, "FloatDigits", GNM_MANT_DIG);
1429 static void
1430 xml_write_calculation (GnmOutputXML *state)
1432 gsf_xml_out_start_element (state->output, GNM "Calculation");
1433 gsf_xml_out_add_bool (state->output,
1434 "ManualRecalc", !state->wb->recalc_auto);
1435 gsf_xml_out_add_bool (state->output,
1436 "EnableIteration", state->wb->iteration.enabled);
1437 gsf_xml_out_add_int (state->output,
1438 "MaxIterations", state->wb->iteration.max_number);
1439 go_xml_out_add_double (state->output,
1440 "IterationTolerance", state->wb->iteration.tolerance);
1441 xml_write_date_conventions_as_attr (state,
1442 workbook_date_conv (state->wb));
1443 xml_write_number_system (state);
1444 gsf_xml_out_end_element (state->output); /* </gnm:Calculation> */
1447 GnmConventions *
1448 gnm_xml_io_conventions (void)
1450 GnmConventions *res = gnm_conventions_new ();
1451 gnm_float l10;
1453 res->decimal_sep_dot = TRUE;
1454 res->input.range_ref = rangeref_parse;
1455 res->output.range_ref = gnm_1_0_rangeref_as_string;
1456 res->range_sep_colon = TRUE;
1457 res->arg_sep = ',';
1458 res->array_col_sep = ',';
1459 res->array_row_sep = ';';
1460 res->output.translated = FALSE;
1462 l10 = gnm_log10 (FLT_RADIX);
1463 res->output.decimal_digits = (int)gnm_ceil (GNM_MANT_DIG * l10) +
1464 (l10 == (int)l10 ? 0 : 1);
1466 return res;
1469 static void
1470 gnm_xml_file_save_full (G_GNUC_UNUSED GOFileSaver const *fs,
1471 G_GNUC_UNUSED GOIOContext *io_context,
1472 GoView const *view, GsfOutput *output,
1473 gboolean compress)
1475 GnmOutputXML state;
1476 GsfOutput *gzout = NULL;
1477 GnmLocale *locale;
1478 WorkbookView *wb_view = GNM_WORKBOOK_VIEW (view);
1480 if (compress) {
1481 gzout = gsf_output_gzip_new (output, NULL);
1482 output = gzout;
1485 state.wb_view = wb_view;
1486 state.wb = wb_view_get_workbook (wb_view);
1487 state.sheet = NULL;
1488 state.output = gsf_xml_out_new (output);
1489 state.convs = gnm_xml_io_conventions ();
1490 state.expr_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1491 state.cell_str = g_string_new (NULL);
1492 go_doc_init_write (GO_DOC (state.wb), state.output);
1494 locale = gnm_push_C_locale ();
1496 gsf_xml_out_start_element (state.output, GNM "Workbook");
1499 * As long as we want older versions of Gnumeric to be able to read
1500 * the files we produce, we should not increase the version number
1501 * in the file we write. Until 1.12.21, v10 was the highest listed
1502 * xml-sax-read.c's content_ns.
1504 gsf_xml_out_add_cstr_unchecked (state.output, "xmlns:gnm",
1505 "http://www.gnumeric.org/v10.dtd");
1506 #if 0 /* seems to break meta data */
1507 /* default namespace added for 1.8 */
1508 gsf_xml_out_add_cstr_unchecked (state.output, "xmlns",
1509 "http://www.gnumeric.org/v10.dtd");
1510 #endif
1511 gsf_xml_out_add_cstr_unchecked (state.output, "xmlns:xsi",
1512 "http://www.w3.org/2001/XMLSchema-instance");
1513 gsf_xml_out_add_cstr_unchecked (state.output, "xsi:schemaLocation",
1514 "http://www.gnumeric.org/v9.xsd");
1516 xml_write_version (&state);
1517 xml_write_attributes (&state);
1518 xml_write_meta_data (&state);
1519 xml_write_conventions (&state); /* DEPRECATED, moved to Calculation */
1520 xml_write_calculation (&state);
1521 xml_write_sheet_names (&state);
1522 xml_write_named_expressions (&state, state.wb->names);
1523 xml_write_geometry (&state);
1524 xml_write_sheets (&state);
1525 xml_write_uidata (&state);
1526 go_doc_write (GO_DOC (state.wb), state.output);
1528 gsf_xml_out_end_element (state.output); /* </Workbook> */
1530 gnm_pop_C_locale (locale);
1532 g_hash_table_destroy (state.expr_map);
1533 g_string_free (state.cell_str, TRUE);
1534 gnm_conventions_unref (state.convs);
1535 g_object_unref (state.output);
1537 if (gzout) {
1538 gsf_output_close (gzout);
1539 g_object_unref (gzout);
1543 static void
1544 gnm_xml_file_save (GOFileSaver const *fs, GOIOContext *io_context,
1545 GoView const *view, GsfOutput *output)
1547 gboolean compress;
1548 char const *extension = NULL;
1550 /* If the suffix is .xml disable compression */
1551 if (NULL != gsf_output_name (output))
1552 extension = gsf_extension_pointer (gsf_output_name (output));
1553 if (NULL != extension && g_ascii_strcasecmp (extension, "xml") == 0)
1554 compress = FALSE;
1555 else
1556 compress = (gnm_conf_get_core_xml_compression_level () > 0);
1558 gnm_xml_file_save_full (fs, io_context, view, output, compress);
1561 static void
1562 gnm_xml_file_save_xml (GOFileSaver const *fs, GOIOContext *io_context,
1563 GoView const *view, GsfOutput *output)
1565 gnm_xml_file_save_full (fs, io_context, view, output, FALSE);
1568 /**************************************************************************/
1570 typedef struct {
1571 GnmOutputXML state;
1572 GnmCellRegion const *cr;
1573 GnmParsePos pp;
1574 } XMLCellCopyState;
1576 static void
1577 cb_xml_write_cell_region_cells (GnmCellCopy *cc,
1578 G_GNUC_UNUSED gconstpointer ignore,
1579 XMLCellCopyState *state)
1581 state->pp.eval.col = state->cr->base.col + cc->offset.col;
1582 state->pp.eval.row = state->cr->base.row + cc->offset.row;
1583 xml_write_cell_and_position (&state->state,
1584 cc->texpr, cc->val, &state->pp);
1588 * gnm_cellregion_to_xml:
1589 * @cr: the content to store.
1591 * Caller is responsible for free-ing the result.
1592 * Returns: (transfer full): NULL on error
1594 GsfOutputMemory *
1595 gnm_cellregion_to_xml (GnmCellRegion const *cr)
1597 XMLCellCopyState state;
1598 GnmStyleList *s_ptr;
1599 GSList *ptr;
1600 GsfOutput *buf = gsf_output_memory_new ();
1601 GnmLocale *locale;
1602 GODoc *doc = NULL;
1604 g_return_val_if_fail (cr != NULL, NULL);
1605 g_return_val_if_fail (IS_SHEET (cr->origin_sheet), NULL);
1607 state.state.wb_view = NULL;
1608 state.state.wb = NULL;
1609 state.state.sheet = cr->origin_sheet;
1610 state.state.output = gsf_xml_out_new (buf);
1611 state.state.convs = gnm_xml_io_conventions ();
1612 state.state.expr_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1613 state.state.cell_str = g_string_new (NULL);
1615 locale = gnm_push_C_locale ();
1616 if (cr->origin_sheet) {
1617 /* hoping this always occur */
1618 doc = GO_DOC (cr->origin_sheet->workbook);
1619 go_doc_init_write (doc, state.state.output);
1622 gsf_xml_out_start_element (state.state.output, GNM "ClipboardRange");
1624 /* backwards compat, must be first */
1625 gsf_xml_out_add_cstr_unchecked (state.state.output, "xmlns:gnm",
1626 "http://www.gnumeric.org/v10.dtd");
1627 /* default namespace added for 1.8 */
1628 gsf_xml_out_add_cstr_unchecked (state.state.output, "xmlns",
1629 "http://www.gnumeric.org/v10.dtd");
1631 gsf_xml_out_add_int (state.state.output, "Cols", cr->cols);
1632 gsf_xml_out_add_int (state.state.output, "Rows", cr->rows);
1633 gsf_xml_out_add_int (state.state.output, "BaseCol", cr->base.col);
1634 gsf_xml_out_add_int (state.state.output, "BaseRow", cr->base.row);
1635 if (cr->origin_sheet)
1636 xml_write_date_conventions_as_attr
1637 (&state.state,
1638 workbook_date_conv (cr->origin_sheet->workbook));
1639 xml_write_number_system (&state.state);
1640 if (cr->not_as_contents)
1641 gsf_xml_out_add_bool (state.state.output, "NotAsContent", TRUE);
1643 if (cr->styles != NULL) {
1644 gsf_xml_out_start_element (state.state.output, GNM "Styles");
1645 for (s_ptr = cr->styles ; s_ptr != NULL ; s_ptr = s_ptr->next)
1646 xml_write_style_region (&state.state, s_ptr->data);
1647 gsf_xml_out_end_element (state.state.output); /* </Styles> */
1650 if (cr->merged != NULL) {
1651 gsf_xml_out_start_element (state.state.output, GNM "MergedRegions");
1652 for (ptr = cr->merged ; ptr != NULL ; ptr = ptr->next) {
1653 gsf_xml_out_start_element (state.state.output, GNM "Merge");
1654 gsf_xml_out_add_cstr_unchecked (state.state.output, NULL,
1655 range_as_string (ptr->data));
1656 gsf_xml_out_end_element (state.state.output); /* </Merge> */
1658 gsf_xml_out_end_element (state.state.output); /* </gnm:MergedRegions> */
1661 /* NOTE SNEAKY : ensure that sheet names have explicit workbooks */
1662 state.pp.wb = NULL;
1663 state.pp.sheet = cr->origin_sheet;
1664 state.cr = cr;
1665 if (cr->cell_content != NULL) {
1666 gsf_xml_out_start_element (state.state.output, GNM "Cells");
1667 g_hash_table_foreach (cr->cell_content,
1668 (GHFunc) cb_xml_write_cell_region_cells, &state);
1669 gsf_xml_out_end_element (state.state.output); /* </Cells> */
1672 xml_write_objects (&state.state, cr->objects);
1674 if (NULL != doc)
1675 go_doc_write (doc, state.state.output);
1676 gsf_xml_out_end_element (state.state.output); /* </ClipboardRange> */
1678 gnm_pop_C_locale (locale);
1680 g_hash_table_destroy (state.state.expr_map);
1681 g_string_free (state.state.cell_str, TRUE);
1682 gnm_conventions_unref (state.state.convs);
1683 g_object_unref (state.state.output);
1685 gsf_output_close (buf);
1687 return GSF_OUTPUT_MEMORY (buf);
1690 #define XML_SAX_ID "Gnumeric_XmlIO:sax"
1691 #define XML_SAX_ID_0 "Gnumeric_XmlIO:sax:0"
1693 void
1694 gnm_xml_sax_write_init (void)
1696 GOFileSaver *saver = go_file_saver_new
1697 (XML_SAX_ID,
1698 "gnumeric",
1699 _("Gnumeric XML (*.gnumeric)"),
1700 GO_FILE_FL_AUTO, gnm_xml_file_save);
1701 g_object_set (G_OBJECT (saver),
1702 "mime-type", "application/x-gnumeric",
1703 NULL);
1705 go_file_saver_register_as_default (saver, 50);
1706 g_object_unref (saver);
1708 saver = go_file_saver_new
1709 (XML_SAX_ID_0,
1710 "xml",
1711 _("Gnumeric XML uncompressed (*.xml)"),
1712 GO_FILE_FL_AUTO, gnm_xml_file_save_xml);
1713 g_object_set (G_OBJECT (saver),
1714 "mime-type", "application/xml",
1715 NULL);
1717 go_file_saver_register (saver);
1718 g_object_unref (saver);
1721 void
1722 gnm_xml_sax_write_shutdown (void)
1724 go_file_saver_unregister (go_file_saver_for_id (XML_SAX_ID));
1725 go_file_saver_unregister (go_file_saver_for_id (XML_SAX_ID_0));