Code cleanups.
[gnumeric.git] / src / xml-sax-write.c
blob69086307172e7457d3a153bfe471b36df38fd969
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 // Do we write result values? For now this is clipboard only
76 gboolean write_value_result;
78 GsfXMLOut *output;
79 } GnmOutputXML;
81 #define GNM "gnm:"
83 /* Precision to use when saving point measures. */
84 #define POINT_SIZE_PRECISION 4
86 void
87 gnm_xml_out_add_gocolor (GsfXMLOut *o, char const *id, GOColor c)
90 * This uses format "rrrr:gggg:bbbb" or "rrrr:gggg:bbbb:aaaa"
91 * using hex numbers, i.e., the numbers are in the range from
92 * 0 to FFFF.
94 * Note, that while go_xml_out_add_color exists, we cannot use
95 * it as it using a 0-255 scaling and always includes alpha.
97 unsigned r, g, b, a;
98 char buf[4 * 4 * sizeof (unsigned int) + 1];
100 GO_COLOR_TO_RGBA (c, &r, &g, &b, &a);
102 sprintf (buf, "%X:%X:%X%c%X",
103 r * 0x101, g * 0x101, b * 0x101,
104 (a == 0xff ? 0 : ':'),
105 a * 0x101);
106 gsf_xml_out_add_cstr_unchecked (o, id, buf);
109 static void
110 gnm_xml_out_add_color (GsfXMLOut *o, char const *id, GnmColor const *c)
112 gnm_xml_out_add_gocolor (o, id, c->go_color);
115 static void
116 gnm_xml_out_add_cellpos (GsfXMLOut *o, char const *id, GnmCellPos const *p)
118 g_return_if_fail (p != NULL);
119 gsf_xml_out_add_cstr_unchecked (o, id, cellpos_as_string (p));
122 static void
123 xml_out_add_range (GsfXMLOut *xml, GnmRange const *r)
125 g_return_if_fail (range_is_sane (r));
127 gsf_xml_out_add_int (xml, "startCol", r->start.col);
128 gsf_xml_out_add_int (xml, "startRow", r->start.row);
129 gsf_xml_out_add_int (xml, "endCol", r->end.col);
130 gsf_xml_out_add_int (xml, "endRow", r->end.row);
133 static void
134 xml_out_add_points (GsfXMLOut *xml, char const *name, double val)
136 gsf_xml_out_add_float (xml, name, val, POINT_SIZE_PRECISION);
139 static void
140 xml_write_boolean_attribute (GnmOutputXML *state, char const *name, gboolean value)
142 gsf_xml_out_start_element (state->output, GNM "Attribute");
143 gsf_xml_out_simple_element (state->output, GNM "name", name);
144 gsf_xml_out_simple_element (state->output, GNM "value", value ? "TRUE" : "FALSE");
145 gsf_xml_out_end_element (state->output); /* </Attribute> */
148 static void
149 xml_write_version (GnmOutputXML *state)
151 gsf_xml_out_start_element (state->output, GNM "Version");
152 gsf_xml_out_add_int (state->output, "Epoch", GNM_VERSION_EPOCH);
153 gsf_xml_out_add_int (state->output, "Major", GNM_VERSION_MAJOR);
154 gsf_xml_out_add_int (state->output, "Minor", GNM_VERSION_MINOR);
155 gsf_xml_out_add_cstr_unchecked (state->output, "Full", GNM_VERSION_FULL);
156 gsf_xml_out_end_element (state->output); /* </Version> */
159 static void
160 xml_write_attributes (GnmOutputXML *state)
162 gsf_xml_out_start_element (state->output, GNM "Attributes");
163 xml_write_boolean_attribute
164 (state, "WorkbookView::show_horizontal_scrollbar",
165 state->wb_view->show_horizontal_scrollbar);
166 xml_write_boolean_attribute
167 (state, "WorkbookView::show_vertical_scrollbar",
168 state->wb_view->show_vertical_scrollbar);
169 xml_write_boolean_attribute
170 (state, "WorkbookView::show_notebook_tabs",
171 state->wb_view->show_notebook_tabs);
172 xml_write_boolean_attribute
173 (state, "WorkbookView::do_auto_completion",
174 state->wb_view->do_auto_completion);
175 xml_write_boolean_attribute
176 (state, "WorkbookView::is_protected",
177 state->wb_view->is_protected);
178 gsf_xml_out_end_element (state->output); /* </Attributes> */
181 static void
182 xml_write_meta_data (GnmOutputXML *state)
184 gsf_doc_meta_data_write_to_odf (go_doc_get_meta_data (GO_DOC (state->wb)),
185 state->output);
188 /* DEPRECATED in 1.7.11 */
189 static void
190 xml_write_conventions (GnmOutputXML *state)
192 GODateConventions const *conv = workbook_date_conv (state->wb);
193 if (conv->use_1904)
194 gsf_xml_out_simple_element (state->output, GNM "DateConvention", "1904");
197 static void
198 xml_write_sheet_names (GnmOutputXML *state)
200 int i, n = workbook_sheet_count (state->wb);
201 Sheet *sheet;
203 gsf_xml_out_start_element (state->output, GNM "SheetNameIndex");
204 for (i = 0 ; i < n ; i++) {
205 sheet = workbook_sheet_by_index (state->wb, i);
206 gsf_xml_out_start_element (state->output, GNM "SheetName");
209 * Note, that we explicitly namespace these attributes.
210 * That is not wrong, per se, but note that Gnumeric until
211 * 1.12.22 will not read files without this explicit name-
212 * space and that the abbreviation must be "gnm".
214 if (sheet->sheet_type == GNM_SHEET_OBJECT)
215 gsf_xml_out_add_cstr (state->output, GNM "SheetType", "object");
216 gsf_xml_out_add_int (state->output, GNM "Cols",
217 gnm_sheet_get_max_cols (sheet));
218 gsf_xml_out_add_int (state->output, GNM "Rows",
219 gnm_sheet_get_max_rows (sheet));
220 gsf_xml_out_add_cstr (state->output, NULL, sheet->name_unquoted);
221 gsf_xml_out_end_element (state->output); /* </gnm:SheetName> */
223 gsf_xml_out_end_element (state->output); /* </gnm:SheetNameIndex> */
226 static void
227 xml_write_name (GnmOutputXML *state, GnmNamedExpr *nexpr)
229 char *expr_str;
231 g_return_if_fail (nexpr != NULL);
233 gsf_xml_out_start_element (state->output, GNM "Name");
234 gsf_xml_out_simple_element (state->output, GNM "name",
235 expr_name_name (nexpr));
236 expr_str = expr_name_as_string (nexpr, NULL, state->convs);
237 gsf_xml_out_simple_element (state->output, GNM "value", expr_str);
238 g_free (expr_str);
239 gsf_xml_out_simple_element (state->output, GNM "position",
240 cellpos_as_string (&nexpr->pos.eval));
241 gsf_xml_out_end_element (state->output); /* </gnm:Name> */
244 static void
245 xml_write_named_expressions (GnmOutputXML *state, GnmNamedExprCollection *scope)
247 GSList *names =
248 g_slist_sort (gnm_named_expr_collection_list (scope),
249 (GCompareFunc)expr_name_cmp_by_name);
250 GSList *p;
252 if (!names)
253 return;
255 gsf_xml_out_start_element (state->output, GNM "Names");
256 for (p = names; p; p = p->next) {
257 GnmNamedExpr *nexpr = p->data;
258 xml_write_name (state, nexpr);
260 gsf_xml_out_end_element (state->output); /* </gnm:Names> */
261 g_slist_free (names);
264 static void
265 xml_write_geometry (GnmOutputXML *state)
267 if (state->wb_view->preferred_width > 0 ||
268 state->wb_view->preferred_height > 0) {
269 gsf_xml_out_start_element (state->output, GNM "Geometry");
270 gsf_xml_out_add_int (state->output, "Width", state->wb_view->preferred_width);
271 gsf_xml_out_add_int (state->output, "Height", state->wb_view->preferred_height);
272 gsf_xml_out_end_element (state->output); /* </gnm:Geometry> */
276 static void
277 xml_write_print_unit (GnmOutputXML *state, char const *name,
278 double points, GtkUnit unit)
280 gsf_xml_out_start_element (state->output, name);
281 xml_out_add_points (state->output, "Points", points);
282 gsf_xml_out_add_cstr_unchecked (state->output, "PrefUnit",
283 unit_to_unit_name (unit));
284 gsf_xml_out_end_element (state->output);
287 static void
288 xml_write_print_repeat_range (GnmOutputXML *state,
289 char const *name,
290 const char *range)
292 if (range && *range) {
293 gsf_xml_out_start_element (state->output, name);
294 gsf_xml_out_add_cstr_unchecked (state->output, "value", range);
295 gsf_xml_out_end_element (state->output);
299 static void
300 xml_write_print_hf (GnmOutputXML *state, char const *name,
301 GnmPrintHF const *hf)
303 gsf_xml_out_start_element (state->output, name);
304 gsf_xml_out_add_cstr (state->output, "Left", hf->left_format);
305 gsf_xml_out_add_cstr (state->output, "Middle", hf->middle_format);
306 gsf_xml_out_add_cstr (state->output, "Right", hf->right_format);
307 gsf_xml_out_end_element (state->output);
311 static void
312 xml_write_breaks (GnmOutputXML *state, GnmPageBreaks *breaks)
314 GArray const *details = breaks->details;
315 GnmPageBreak const *binfo;
316 unsigned i;
318 gsf_xml_out_start_element (state->output,
319 (breaks->is_vert) ? GNM "vPageBreaks" : GNM "hPageBreaks");
320 gsf_xml_out_add_int (state->output, "count", details->len);
322 for (i = 0 ; i < details->len ; i++) {
323 binfo = &g_array_index (details, GnmPageBreak, i);
324 gsf_xml_out_start_element (state->output, GNM "break");
325 gsf_xml_out_add_int (state->output, "pos", binfo->pos);
326 if (binfo->type == GNM_PAGE_BREAK_MANUAL)
327 gsf_xml_out_add_cstr_unchecked (state->output, "type", "manual");
328 else if (binfo->type == GNM_PAGE_BREAK_DATA_SLICE)
329 gsf_xml_out_add_cstr_unchecked (state->output, "type", "data-slice");
330 else if (binfo->type == GNM_PAGE_BREAK_AUTO)
331 gsf_xml_out_add_cstr_unchecked (state->output, "type", "auto");
332 gsf_xml_out_end_element (state->output); /* </break> */
334 gsf_xml_out_end_element (state->output);
337 static void
338 xml_write_print_info (GnmOutputXML *state, GnmPrintInformation *pi)
340 char *paper_name;
341 char const *uri;
342 double header;
343 double footer;
344 double left;
345 double right;
346 double edge_to_above_footer;
347 double edge_to_below_header;
348 GtkPageOrientation orient;
350 g_return_if_fail (pi != NULL);
352 gsf_xml_out_start_element (state->output, GNM "PrintInformation");
354 gsf_xml_out_start_element (state->output, GNM "Margins");
356 print_info_get_margins (pi, &header, &footer, &left, &right,
357 &edge_to_below_header, &edge_to_above_footer);
358 xml_write_print_unit (state, GNM "top", edge_to_below_header,
359 pi->desired_display.header);
360 xml_write_print_unit (state, GNM "bottom", edge_to_above_footer,
361 pi->desired_display.footer);
362 xml_write_print_unit (state, GNM "left", left,
363 pi->desired_display.left);
364 xml_write_print_unit (state, GNM "right", right,
365 pi->desired_display.right);
366 xml_write_print_unit (state, GNM "header", header,
367 pi->desired_display.top);
368 xml_write_print_unit (state, GNM "footer", footer,
369 pi->desired_display.bottom);
370 gsf_xml_out_end_element (state->output);
372 gsf_xml_out_start_element (state->output, GNM "Scale");
373 if (pi->scaling.type == PRINT_SCALE_PERCENTAGE) {
374 gsf_xml_out_add_cstr_unchecked (state->output, "type", "percentage");
375 go_xml_out_add_double (state->output, "percentage", pi->scaling.percentage.x);
376 } else {
377 gsf_xml_out_add_cstr_unchecked (state->output, "type", "size_fit");
378 go_xml_out_add_double (state->output, "cols", pi->scaling.dim.cols);
379 go_xml_out_add_double (state->output, "rows", pi->scaling.dim.rows);
381 gsf_xml_out_end_element (state->output);
383 gsf_xml_out_start_element (state->output, GNM "vcenter");
384 gsf_xml_out_add_int (state->output, "value", pi->center_vertically);
385 gsf_xml_out_end_element (state->output);
387 gsf_xml_out_start_element (state->output, GNM "hcenter");
388 gsf_xml_out_add_int (state->output, "value", pi->center_horizontally);
389 gsf_xml_out_end_element (state->output);
391 gsf_xml_out_start_element (state->output, GNM "grid");
392 gsf_xml_out_add_int (state->output, "value", pi->print_grid_lines);
393 gsf_xml_out_end_element (state->output);
395 gsf_xml_out_start_element (state->output, GNM "even_if_only_styles");
396 gsf_xml_out_add_int (state->output, "value", pi->print_even_if_only_styles);
397 gsf_xml_out_end_element (state->output);
399 gsf_xml_out_start_element (state->output, GNM "monochrome");
400 gsf_xml_out_add_int (state->output, "value", pi->print_black_and_white);
401 gsf_xml_out_end_element (state->output);
403 gsf_xml_out_start_element (state->output, GNM "draft");
404 gsf_xml_out_add_int (state->output, "value", pi->print_as_draft);
405 gsf_xml_out_end_element (state->output);
407 gsf_xml_out_start_element (state->output, GNM "titles");
408 gsf_xml_out_add_int (state->output, "value", pi->print_titles);
409 gsf_xml_out_end_element (state->output);
411 gsf_xml_out_start_element (state->output, GNM "do_not_print");
412 gsf_xml_out_add_int (state->output, "value", pi->do_not_print);
413 gsf_xml_out_end_element (state->output);
415 gsf_xml_out_start_element (state->output, GNM "print_range");
416 gsf_xml_out_add_enum (state->output, "value",
417 GNM_PRINT_RANGE_TYPE,
418 print_info_get_printrange (pi) );
419 gsf_xml_out_end_element (state->output);
421 xml_write_print_repeat_range (state, GNM "repeat_top", pi->repeat_top);
422 xml_write_print_repeat_range (state, GNM "repeat_left", pi->repeat_left);
424 /* this was once an enum, hence the silly strings */
425 gsf_xml_out_simple_element (state->output, GNM "order",
426 pi->print_across_then_down ? "r_then_d" :"d_then_r");
428 orient = print_info_get_paper_orientation (pi);
429 gsf_xml_out_simple_element (state->output, GNM "orientation",
430 (orient == GTK_PAGE_ORIENTATION_PORTRAIT
431 || orient == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT)
432 ? "portrait" : "landscape");
433 #warning TODO: we should also handle inversion
435 xml_write_print_hf (state, GNM "Header", pi->header);
436 xml_write_print_hf (state, GNM "Footer", pi->footer);
438 paper_name = print_info_get_paper (pi);
439 if (paper_name)
440 gsf_xml_out_simple_element (state->output, GNM "paper",
441 paper_name);
442 g_free (paper_name);
444 uri = print_info_get_printtofile_uri (pi);
445 if (uri)
446 gsf_xml_out_simple_element (state->output, GNM "print-to-uri",
447 uri);
449 if (NULL != pi->page_breaks.v)
450 xml_write_breaks (state, pi->page_breaks.v);
451 if (NULL != pi->page_breaks.h)
452 xml_write_breaks (state, pi->page_breaks.h);
454 gsf_xml_out_start_element (state->output, GNM "comments");
455 gsf_xml_out_add_enum (state->output, "placement",
456 GNM_PRINT_COMMENT_PLACEMENT_TYPE,
457 pi->comment_placement);
458 gsf_xml_out_end_element (state->output);
460 gsf_xml_out_start_element (state->output, GNM "errors");
461 gsf_xml_out_add_enum (state->output, "PrintErrorsAs",
462 GNM_PRINT_ERRORS_TYPE,
463 pi->error_display);
464 gsf_xml_out_end_element (state->output);
466 gsf_xml_out_end_element (state->output);
469 static void
470 xml_write_style (GnmOutputXML *state, GnmStyle const *style)
472 static char const *border_names[] = {
473 GNM "Top",
474 GNM "Bottom",
475 GNM "Left",
476 GNM "Right",
477 GNM "Rev-Diagonal",
478 GNM "Diagonal"
480 GnmValidation const *v;
481 GnmHLink const *lnk;
482 GnmInputMsg const *im;
483 GnmStyleConditions const *sc;
484 GnmStyleBorderType t;
485 unsigned i;
486 gboolean started;
488 gsf_xml_out_start_element (state->output, GNM "Style");
490 if (gnm_style_is_element_set (style, MSTYLE_ALIGN_H))
491 gsf_xml_out_add_enum (state->output, "HAlign",
492 GNM_ALIGN_H_TYPE,
493 gnm_style_get_align_h (style));
494 if (gnm_style_is_element_set (style, MSTYLE_ALIGN_V))
495 gsf_xml_out_add_enum (state->output, "VAlign",
496 GNM_ALIGN_V_TYPE,
497 gnm_style_get_align_v (style));
498 if (gnm_style_is_element_set (style, MSTYLE_WRAP_TEXT))
499 gsf_xml_out_add_bool (state->output, "WrapText",
500 gnm_style_get_wrap_text (style));
501 if (gnm_style_is_element_set (style, MSTYLE_SHRINK_TO_FIT))
502 gsf_xml_out_add_bool (state->output, "ShrinkToFit",
503 gnm_style_get_shrink_to_fit (style));
504 if (gnm_style_is_element_set (style, MSTYLE_ROTATION))
505 gsf_xml_out_add_int (state->output, "Rotation",
506 gnm_style_get_rotation (style));
507 if (gnm_style_is_element_set (style, MSTYLE_PATTERN))
508 gsf_xml_out_add_int (state->output, "Shade",
509 gnm_style_get_pattern (style));
510 if (gnm_style_is_element_set (style, MSTYLE_INDENT))
511 gsf_xml_out_add_int (state->output, "Indent", gnm_style_get_indent (style));
512 if (gnm_style_is_element_set (style, MSTYLE_CONTENTS_LOCKED))
513 gsf_xml_out_add_bool (state->output, "Locked",
514 gnm_style_get_contents_locked (style));
515 if (gnm_style_is_element_set (style, MSTYLE_CONTENTS_HIDDEN))
516 gsf_xml_out_add_bool (state->output, "Hidden",
517 gnm_style_get_contents_hidden (style));
518 if (gnm_style_is_element_set (style, MSTYLE_FONT_COLOR))
519 gnm_xml_out_add_color (state->output, "Fore",
520 gnm_style_get_font_color (style));
521 if (gnm_style_is_element_set (style, MSTYLE_COLOR_BACK))
522 gnm_xml_out_add_color (state->output, "Back",
523 gnm_style_get_back_color (style));
524 if (gnm_style_is_element_set (style, MSTYLE_COLOR_PATTERN))
525 gnm_xml_out_add_color (state->output, "PatternColor",
526 gnm_style_get_pattern_color (style));
527 if (gnm_style_is_element_set (style, MSTYLE_FORMAT)) {
528 const char *fmt = go_format_as_XL (gnm_style_get_format (style));
529 gsf_xml_out_add_cstr (state->output, "Format", fmt);
532 if (gnm_style_is_element_set (style, MSTYLE_FONT_NAME) ||
533 gnm_style_is_element_set (style, MSTYLE_FONT_SIZE) ||
534 gnm_style_is_element_set (style, MSTYLE_FONT_BOLD) ||
535 gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC) ||
536 gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
537 gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH) ||
538 gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT)) {
539 gsf_xml_out_start_element (state->output, GNM "Font");
541 if (gnm_style_is_element_set (style, MSTYLE_FONT_SIZE))
542 xml_out_add_points (state->output, "Unit", gnm_style_get_font_size (style));
543 if (gnm_style_is_element_set (style, MSTYLE_FONT_BOLD))
544 gsf_xml_out_add_int (state->output, "Bold", gnm_style_get_font_bold (style));
545 if (gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC))
546 gsf_xml_out_add_int (state->output, "Italic", gnm_style_get_font_italic (style));
547 if (gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE))
548 gsf_xml_out_add_int (state->output, "Underline", (int)gnm_style_get_font_uline (style));
549 if (gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
550 gsf_xml_out_add_int (state->output, "StrikeThrough", gnm_style_get_font_strike (style));
551 if (gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT))
552 gsf_xml_out_add_int (state->output, "Script", (int)gnm_style_get_font_script (style));
554 if (gnm_style_is_element_set (style, MSTYLE_FONT_NAME)) {
555 const char *fontname = gnm_style_get_font_name (style);
556 gsf_xml_out_add_cstr (state->output, NULL, fontname);
559 gsf_xml_out_end_element (state->output);
562 if (gnm_style_is_element_set (style, MSTYLE_HLINK) &&
563 NULL != (lnk = gnm_style_get_hlink (style))) {
564 gsf_xml_out_start_element (state->output, GNM "HyperLink");
565 gsf_xml_out_add_cstr (state->output, "type", g_type_name (G_OBJECT_TYPE (lnk)));
566 gsf_xml_out_add_cstr (state->output, "target", gnm_hlink_get_target (lnk));
567 if (gnm_hlink_get_tip (lnk) != NULL)
568 gsf_xml_out_add_cstr (state->output, "tip", gnm_hlink_get_tip (lnk));
569 gsf_xml_out_end_element (state->output);
572 if (gnm_style_is_element_set (style, MSTYLE_VALIDATION) &&
573 NULL != (v = gnm_style_get_validation (style))) {
574 GnmParsePos pp;
575 char *tmp;
577 gsf_xml_out_start_element (state->output, GNM "Validation");
578 gsf_xml_out_add_enum (state->output, "Style",
579 GNM_VALIDATION_STYLE_TYPE, v->style);
580 gsf_xml_out_add_enum (state->output, "Type",
581 GNM_VALIDATION_TYPE_TYPE, v->type);
583 switch (v->type) {
584 case GNM_VALIDATION_TYPE_AS_INT:
585 case GNM_VALIDATION_TYPE_AS_NUMBER:
586 case GNM_VALIDATION_TYPE_AS_DATE:
587 case GNM_VALIDATION_TYPE_AS_TIME:
588 case GNM_VALIDATION_TYPE_TEXT_LENGTH:
589 gsf_xml_out_add_enum (state->output, "Operator",
590 GNM_VALIDATION_OP_TYPE, v->op);
591 break;
592 default:
593 break;
596 gsf_xml_out_add_bool (state->output, "AllowBlank", v->allow_blank);
597 gsf_xml_out_add_bool (state->output, "UseDropdown", v->use_dropdown);
599 if (v->title != NULL && v->title->str[0] != '\0')
600 gsf_xml_out_add_cstr (state->output, "Title", v->title->str);
601 if (v->msg != NULL && v->msg->str[0] != '\0')
602 gsf_xml_out_add_cstr (state->output, "Message", v->msg->str);
604 parse_pos_init_sheet (&pp, (Sheet *)state->sheet);
606 if (v->deps[0].texpr != NULL &&
607 (tmp = gnm_expr_top_as_string (v->deps[0].texpr, &pp, state->convs)) != NULL) {
608 gsf_xml_out_simple_element (state->output, GNM "Expression0", tmp);
609 g_free (tmp);
611 if (v->deps[1].texpr != NULL &&
612 (tmp = gnm_expr_top_as_string (v->deps[1].texpr, &pp, state->convs)) != NULL) {
613 gsf_xml_out_simple_element (state->output, GNM "Expression1", tmp);
614 g_free (tmp);
616 gsf_xml_out_end_element (state->output); /* </Validation> */
619 if (gnm_style_is_element_set (style, MSTYLE_INPUT_MSG) &&
620 NULL != (im = gnm_style_get_input_msg (style))) {
621 char const *txt;
622 gsf_xml_out_start_element (state->output, GNM "InputMessage");
623 if (NULL != (txt = gnm_input_msg_get_title (im)))
624 gsf_xml_out_add_cstr (state->output, "Title", txt);
625 if (NULL != (txt = gnm_input_msg_get_msg (im)))
626 gsf_xml_out_add_cstr (state->output, "Message", txt);
627 gsf_xml_out_end_element (state->output); /* </InputMessage> */
630 if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS) &&
631 NULL != (sc = gnm_style_get_conditions (style))) {
632 GPtrArray const *conds = gnm_style_conditions_details (sc);
633 if (conds != NULL) {
634 GnmParsePos pp;
635 parse_pos_init_sheet (&pp, (Sheet *)state->sheet);
637 for (i = 0 ; i < conds->len ; i++) {
638 unsigned ui;
639 GnmStyleCond const *cond =
640 g_ptr_array_index (conds, i);
641 gsf_xml_out_start_element (state->output, GNM "Condition");
642 gsf_xml_out_add_int (state->output, "Operator", cond->op);
643 for (ui = 0; ui < 2; ui++) {
644 GnmExprTop const *texpr = gnm_style_cond_get_expr (cond, ui);
645 char *tmp = texpr
646 ? gnm_expr_top_as_string (texpr, &pp, state->convs)
647 : NULL;
648 const char *attr = (ui == 0)
649 ? GNM "Expression0"
650 : GNM "Expression1";
651 if (tmp) {
652 gsf_xml_out_simple_element (state->output, attr, tmp);
653 g_free (tmp);
656 xml_write_style (state, cond->overlay);
657 gsf_xml_out_end_element (state->output); /* </Condition> */
662 started = FALSE;
663 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
664 GnmBorder const *border;
665 if (gnm_style_is_element_set (style, i) &&
666 NULL != (border = gnm_style_get_border (style, i)) &&
667 GNM_STYLE_BORDER_NONE != (t = border->line_type)) {
668 GnmColor const *col = border->color;
670 if (!started) {
671 gsf_xml_out_start_element (state->output, GNM "StyleBorder");
672 started = TRUE;
675 gsf_xml_out_start_element (state->output,
676 border_names [i - MSTYLE_BORDER_TOP]);
677 gsf_xml_out_add_int (state->output, "Style", t);
678 gnm_xml_out_add_color (state->output, "Color", col);
679 gsf_xml_out_end_element (state->output);
682 if (started)
683 gsf_xml_out_end_element (state->output);
685 gsf_xml_out_end_element (state->output);
688 static void
689 xml_write_style_region (GnmOutputXML *state, GnmStyleRegion const *region)
691 gsf_xml_out_start_element (state->output, GNM "StyleRegion");
692 xml_out_add_range (state->output, &region->range);
693 if (region->style != NULL)
694 xml_write_style (state, region->style);
695 gsf_xml_out_end_element (state->output);
698 static int
699 cb_sheet_style_order (GnmStyleRegion const *a, GnmStyleRegion const *b)
701 GnmRange const *ra = &a->range;
702 GnmRange const *rb = &b->range;
703 int res;
705 res = ra->start.col - rb->start.col;
707 if (res == 0)
708 res = ra->start.row - rb->start.row;
710 return res;
713 static void
714 xml_write_styles (GnmOutputXML *state)
716 GnmStyleList *styles =
717 g_slist_sort (sheet_style_get_range (state->sheet, NULL),
718 (GCompareFunc)cb_sheet_style_order);
719 if (styles != NULL) {
720 GnmStyleList *ptr;
722 gsf_xml_out_start_element (state->output, GNM "Styles");
723 for (ptr = styles; ptr; ptr = ptr->next)
724 xml_write_style_region (state, ptr->data);
725 gsf_xml_out_end_element (state->output);
726 style_list_free (styles);
730 typedef struct {
731 GnmOutputXML *state;
732 gboolean is_column;
733 ColRowInfo prev;
734 int prev_pos, rle_count;
735 GnmCellRegion const *cr;
736 } closure_write_colrow;
738 static gboolean
739 xml_write_colrow_info (GnmColRowIter const *iter, closure_write_colrow *closure)
741 ColRowInfo const *prev = &closure->prev;
742 GsfXMLOut *output = closure->state->output;
743 ColRowInfo const *def =
744 sheet_colrow_get_default (closure->state->sheet,
745 closure->is_column);
747 closure->rle_count++;
748 if (NULL != iter && col_row_info_equal (prev, iter->cri))
749 return FALSE;
751 if (closure->prev_pos != -1 && !col_row_info_equal (prev, def)) {
752 if (closure->is_column)
753 gsf_xml_out_start_element (output, GNM "ColInfo");
754 else
755 gsf_xml_out_start_element (output, GNM "RowInfo");
757 gsf_xml_out_add_int (output, "No", closure->prev_pos);
758 xml_out_add_points (output, "Unit", prev->size_pts);
759 if (prev->hard_size)
760 gsf_xml_out_add_bool (output, "HardSize", TRUE);
761 if (!prev->visible)
762 gsf_xml_out_add_bool (output, "Hidden", TRUE);
763 if (prev->is_collapsed)
764 gsf_xml_out_add_bool (output, "Collapsed", TRUE);
765 if (prev->outline_level > 0)
766 gsf_xml_out_add_int (output, "OutlineLevel", prev->outline_level);
768 if (closure->rle_count > 1)
769 gsf_xml_out_add_int (output, "Count", closure->rle_count);
770 gsf_xml_out_end_element (output);
773 closure->rle_count = 0;
774 if (NULL != iter) {
775 closure->prev = *iter->cri;
776 closure->prev_pos = iter->pos;
779 return FALSE;
782 static void
783 xml_write_cols_rows (GnmOutputXML *state, GnmCellRegion const *cr)
785 const Sheet *sheet = state->sheet;
786 int i;
788 for (i = 0; i < 2; i++) {
789 closure_write_colrow closure;
790 gboolean is_cols = (i == 0);
792 gsf_xml_out_start_element (state->output,
793 is_cols ? GNM "Cols" : GNM "Rows");
795 if (sheet)
796 xml_out_add_points
797 (state->output, "DefaultSizePts",
798 sheet_colrow_get_default (sheet, is_cols)->size_pts);
800 closure.state = state;
801 closure.cr = cr;
802 closure.is_column = is_cols;
803 memset (&closure.prev, 0, sizeof (closure.prev));
804 closure.prev_pos = -1;
805 closure.rle_count = 0;
806 if (cr)
807 colrow_state_list_foreach
808 (is_cols ? cr->col_state : cr->row_state,
809 sheet, is_cols,
810 is_cols ? cr->base.col : cr->base.row,
811 (ColRowHandler)&xml_write_colrow_info,
812 &closure);
813 else
814 sheet_colrow_foreach
815 (sheet, is_cols, 0, -1,
816 (ColRowHandler)&xml_write_colrow_info,
817 &closure);
818 xml_write_colrow_info (NULL, &closure); /* flush */
819 gsf_xml_out_end_element (state->output); /* </gnm:Cols> */
823 static void
824 xml_write_selection_info (GnmOutputXML *state)
826 GSList *ptr, *copy;
827 SheetView const *sv = sheet_get_view (state->sheet, state->wb_view);
828 if (!sv) return; /* Hidden. */
830 gsf_xml_out_start_element (state->output, GNM "Selections");
831 gsf_xml_out_add_int (state->output, "CursorCol", sv->edit_pos_real.col);
832 gsf_xml_out_add_int (state->output, "CursorRow", sv->edit_pos_real.row);
834 /* Insert the selections in REVERSE order */
835 copy = g_slist_reverse (g_slist_copy (sv->selections));
836 for (ptr = copy; ptr; ptr = ptr->next) {
837 GnmRange const *r = ptr->data;
838 gsf_xml_out_start_element (state->output, GNM "Selection");
839 xml_out_add_range (state->output, r);
840 gsf_xml_out_end_element (state->output); /* </gnm:Selection> */
842 g_slist_free (copy);
844 gsf_xml_out_end_element (state->output); /* </gnm:Selections> */
847 static void
848 xml_write_cell_and_position (GnmOutputXML *state,
849 GnmExprTop const *texpr, GnmValue const *val,
850 GnmParsePos const *pp)
852 gboolean write_contents = TRUE;
853 gboolean const is_shared_expr = (texpr != NULL) &&
854 gnm_expr_top_is_shared (texpr);
856 /* Only the top left corner of an array needs to be saved (>= 0.53) */
857 if (texpr && gnm_expr_top_is_array_elem (texpr, NULL, NULL))
858 return; /* DOM version would write <Cell Col= Row=/> */
860 gsf_xml_out_start_element (state->output, GNM "Cell");
861 gsf_xml_out_add_int (state->output, "Row", pp->eval.row);
862 gsf_xml_out_add_int (state->output, "Col", pp->eval.col);
864 /* As of version 0.53 we save the ID of shared expressions */
865 if (is_shared_expr) {
866 gpointer id = g_hash_table_lookup (state->expr_map, (gpointer) texpr);
868 if (id == NULL) {
869 id = GINT_TO_POINTER (g_hash_table_size (state->expr_map) + 1);
870 g_hash_table_insert (state->expr_map, (gpointer)texpr, id);
871 } else
872 write_contents = FALSE;
874 gsf_xml_out_add_int (state->output, "ExprID", GPOINTER_TO_INT (id));
877 /* As of version 0.53 we save the size of the array as attributes */
878 /* As of version 0.57 the attributes are in the Cell not the Content */
879 if (texpr && gnm_expr_top_is_array_corner (texpr)) {
880 int cols, rows;
881 gnm_expr_top_get_array_size (texpr, &cols, &rows);
882 gsf_xml_out_add_int (state->output, "Rows", rows);
883 gsf_xml_out_add_int (state->output, "Cols", cols);
886 if (write_contents) {
887 gboolean write_value = !texpr || state->write_value_result;
888 GString *str = state->cell_str;
890 g_string_truncate (str, 0);
892 if (write_value) {
893 if (val != NULL) {
894 gsf_xml_out_add_int (state->output, "ValueType", val->v_any.type);
895 if (VALUE_FMT (val) != NULL) {
896 const char *fmt = go_format_as_XL (VALUE_FMT (val));
897 gsf_xml_out_add_cstr (state->output, "ValueFormat", fmt);
899 value_get_as_gstring (val, str, state->convs);
900 if (texpr) {
901 gsf_xml_out_add_cstr (state->output, "Value", str->str);
902 g_string_truncate (str, 0);
904 } else {
905 g_warning ("%s has no value ?", cellpos_as_string (&pp->eval));
909 if (texpr) {
910 GnmConventionsOut out;
911 out.accum = str;
912 out.pp = pp;
913 out.convs = state->convs;
915 g_string_append_c (str, '=');
916 gnm_expr_top_as_gstring (texpr, &out);
919 gsf_xml_out_add_cstr (state->output, NULL, str->str);
921 gsf_xml_out_end_element (state->output); /* </gnm:Cell> */
924 static GnmValue *
925 cb_write_cell (GnmCellIter const *iter, GnmOutputXML *state)
927 GnmExprTop const *texpr = iter->cell->base.texpr;
928 GnmValue const *value = iter->cell->value;
930 if (texpr == NULL && VALUE_IS_EMPTY (value))
931 return NULL;
933 xml_write_cell_and_position (state, texpr, value, &iter->pp);
934 return NULL;
937 static void
938 xml_write_cells (GnmOutputXML *state)
940 gsf_xml_out_start_element (state->output, GNM "Cells");
941 sheet_foreach_cell_in_region ((Sheet *)state->sheet,
942 CELL_ITER_IGNORE_NONEXISTENT,
943 0, 0, -1, -1,
944 (CellIterFunc) cb_write_cell, state);
945 gsf_xml_out_end_element (state->output); /* </gnm:Cells> */
948 static void
949 xml_write_merged_regions (GnmOutputXML *state)
951 GSList *ptr = state->sheet->list_merged;
952 if (ptr == NULL)
953 return;
954 gsf_xml_out_start_element (state->output, GNM "MergedRegions");
955 for (; ptr != NULL ; ptr = ptr->next)
956 gsf_xml_out_simple_element (state->output,
957 GNM "Merge", range_as_string (ptr->data));
958 gsf_xml_out_end_element (state->output); /* </gnm:MergedRegions> */
961 static void
962 xml_write_sheet_layout (GnmOutputXML *state)
964 SheetView const *sv = sheet_get_view (state->sheet, state->wb_view);
965 if (!sv) return; /* Hidden. */
967 gsf_xml_out_start_element (state->output, GNM "SheetLayout");
968 gnm_xml_out_add_cellpos (state->output, "TopLeft", &sv->initial_top_left);
970 if (sv_is_frozen (sv)) {
971 gsf_xml_out_start_element (state->output, GNM "FreezePanes");
972 gnm_xml_out_add_cellpos (state->output, "FrozenTopLeft", &sv->frozen_top_left);
973 gnm_xml_out_add_cellpos (state->output, "UnfrozenTopLeft", &sv->unfrozen_top_left);
974 gsf_xml_out_end_element (state->output); /* </gnm:FreezePanes> */
976 gsf_xml_out_end_element (state->output); /* </gnm:SheetLayout> */
979 static void
980 xml_write_filter_expr (GnmOutputXML *state,
981 GnmFilterCondition const *cond, unsigned i)
983 static char const *filter_cond_name[] = { "eq", "gt", "lt", "gte", "lte", "ne" };
985 * WARNING WARNING WARING
986 * Value and ValueType are _reversed !!!
988 static struct { char const *op, *valtype, *val; } filter_expr_attrs[] = {
989 { "Op0", "Value0", "ValueType0" },
990 { "Op1", "Value1", "ValueType1" }
993 GString *text = g_string_new (NULL);
994 value_get_as_gstring (cond->value[i], text, state->convs);
995 gsf_xml_out_add_cstr_unchecked (state->output,
996 filter_expr_attrs[i].op, filter_cond_name [cond->op[i]]);
997 gsf_xml_out_add_int (state->output,
998 filter_expr_attrs[i].valtype, cond->value[i]->v_any.type);
999 gsf_xml_out_add_cstr (state->output,
1000 filter_expr_attrs[i].val, text->str);
1001 g_string_free (text, TRUE);
1004 static void
1005 xml_write_filter_field (GnmOutputXML *state,
1006 GnmFilterCondition const *cond, unsigned i)
1008 gsf_xml_out_start_element (state->output, GNM "Field");
1009 gsf_xml_out_add_int (state->output, "Index", i);
1011 switch (GNM_FILTER_OP_TYPE_MASK & cond->op[0]) {
1012 case 0: gsf_xml_out_add_cstr_unchecked (state->output, "Type", "expr");
1013 xml_write_filter_expr (state, cond, 0);
1014 if (cond->op[1] != GNM_FILTER_UNUSED) {
1015 xml_write_filter_expr (state, cond, 1);
1016 gsf_xml_out_add_bool (state->output, "IsAnd", cond->is_and);
1018 break;
1019 case GNM_FILTER_OP_BLANKS:
1020 gsf_xml_out_add_cstr_unchecked (state->output, "Type", "blanks");
1021 break;
1022 case GNM_FILTER_OP_NON_BLANKS:
1023 gsf_xml_out_add_cstr_unchecked (state->output, "Type", "nonblanks");
1024 break;
1025 case GNM_FILTER_OP_TOP_N:
1026 gsf_xml_out_add_cstr_unchecked (state->output, "Type", "bucket");
1027 gsf_xml_out_add_bool (state->output, "top",
1028 cond->op[0] & 1 ? TRUE : FALSE);
1029 gsf_xml_out_add_bool (state->output, "items",
1030 cond->op[0] & 2 ? TRUE : FALSE);
1031 go_xml_out_add_double (state->output, "count", cond->count);
1032 break;
1035 gsf_xml_out_end_element (state->output); /* </gnm:Field> */
1038 static void
1039 xml_write_sheet_filters (GnmOutputXML *state)
1041 GSList *ptr;
1042 GnmFilter *filter;
1043 GnmFilterCondition const *cond;
1044 unsigned i;
1046 if (state->sheet->filters == NULL)
1047 return;
1049 gsf_xml_out_start_element (state->output, GNM "Filters");
1051 for (ptr = state->sheet->filters; ptr != NULL ; ptr = ptr->next) {
1052 filter = ptr->data;
1053 gsf_xml_out_start_element (state->output, GNM "Filter");
1054 gsf_xml_out_add_cstr_unchecked (state->output, "Area",
1055 range_as_string (&filter->r));
1057 for (i = filter->fields->len ; i-- > 0 ; ) {
1058 cond = gnm_filter_get_condition (filter, i);
1059 if (cond != NULL && cond->op[0] != GNM_FILTER_UNUSED)
1060 xml_write_filter_field (state, cond, i);
1063 gsf_xml_out_end_element (state->output); /* </gnm:Filter> */
1066 gsf_xml_out_end_element (state->output); /* </gnm:Filters> */
1069 static void
1070 xml_write_solver (GnmOutputXML *state)
1072 GnmSolverParameters *param = state->sheet->solver_parameters;
1073 GSList *ptr;
1074 GnmCellRef const *target;
1075 GnmValue const *input;
1077 if (param == NULL)
1078 return;
1080 gsf_xml_out_start_element (state->output, GNM "Solver");
1082 target = gnm_solver_param_get_target (param);
1083 if (target != NULL) {
1084 GnmExpr const *expr = gnm_expr_new_cellref (target);
1085 GnmParsePos pp;
1086 char *txt = gnm_expr_as_string
1087 (expr,
1088 parse_pos_init_sheet (&pp, state->sheet),
1089 state->convs);
1090 gsf_xml_out_add_cstr (state->output, "Target", txt);
1091 g_free (txt);
1092 gnm_expr_free (expr);
1095 gsf_xml_out_add_int (state->output, "ModelType", param->options.model_type);
1096 gsf_xml_out_add_int (state->output, "ProblemType", param->problem_type);
1097 input = gnm_solver_param_get_input (param);
1098 if (input)
1099 gsf_xml_out_add_cstr (state->output, "Inputs",
1100 value_peek_string (input));
1101 gsf_xml_out_add_int (state->output, "MaxTime",
1102 param->options.max_time_sec);
1103 gsf_xml_out_add_int (state->output, "MaxIter",
1104 param->options.max_iter);
1105 gsf_xml_out_add_bool (state->output, "NonNeg",
1106 param->options.assume_non_negative);
1107 gsf_xml_out_add_bool (state->output, "Discr",
1108 param->options.assume_discrete);
1109 gsf_xml_out_add_bool (state->output, "AutoScale",
1110 param->options.automatic_scaling);
1111 gsf_xml_out_add_bool (state->output, "ProgramR",
1112 param->options.program_report);
1113 gsf_xml_out_add_bool (state->output, "SensitivityR",
1114 param->options.sensitivity_report);
1116 for (ptr = param->constraints; ptr != NULL ; ptr = ptr->next) {
1117 GnmSolverConstraint const *c = ptr->data;
1118 int type;
1119 GString *str = g_string_new (NULL);
1121 /* Historical values. Not a bit field. */
1122 switch (c->type) {
1123 default: type = 0; break;
1124 case GNM_SOLVER_LE: type = 1; break;
1125 case GNM_SOLVER_GE: type = 2; break;
1126 case GNM_SOLVER_EQ: type = 4; break;
1127 case GNM_SOLVER_INTEGER: type = 8; break;
1128 case GNM_SOLVER_BOOLEAN: type = 16; break;
1131 gsf_xml_out_start_element (state->output, GNM "Constr");
1132 gsf_xml_out_add_int (state->output, "Type", type);
1134 gnm_solver_constraint_side_as_str (c, state->sheet, str, TRUE);
1135 gsf_xml_out_add_cstr (state->output, "lhs", str->str);
1137 if (gnm_solver_constraint_has_rhs (c)) {
1138 g_string_truncate (str, 0);
1139 gnm_solver_constraint_side_as_str (c, state->sheet,
1140 str, FALSE);
1141 gsf_xml_out_add_cstr (state->output, "rhs", str->str);
1144 gsf_xml_out_end_element (state->output); /* </gnm:Constr> */
1146 g_string_free (str, TRUE);
1149 gsf_xml_out_end_element (state->output); /* </gnm:Solver> */
1152 static void
1153 xml_write_scenario (GnmOutputXML *state, GnmScenario const *sc)
1155 GSList *l;
1156 GnmParsePos pp;
1158 parse_pos_init_sheet (&pp, sc->sheet);
1160 gsf_xml_out_start_element (state->output, GNM "Scenario");
1162 gsf_xml_out_add_cstr (state->output, "Name", sc->name);
1163 if (sc->comment)
1164 gsf_xml_out_add_cstr (state->output, "Comment", sc->comment);
1166 for (l = sc->items; l; l = l->next) {
1167 GnmScenarioItem const *sci = l->data;
1168 GnmValue const *val = sci->value;
1169 GString *str;
1170 GnmConventionsOut out;
1172 if (!gnm_scenario_item_valid (sci, NULL))
1173 continue;
1175 str = g_string_new (NULL);
1176 gsf_xml_out_start_element (state->output, GNM "Item");
1178 out.accum = str;
1179 out.pp = &pp;
1180 out.convs = state->convs;
1182 gnm_expr_top_as_gstring (sci->dep.texpr, &out);
1183 gsf_xml_out_add_cstr (state->output, "Range", str->str);
1185 if (val) {
1186 gsf_xml_out_add_int (state->output,
1187 "ValueType",
1188 val->v_any.type);
1189 if (VALUE_FMT (val) != NULL) {
1190 const char *fmt = go_format_as_XL (VALUE_FMT (val));
1191 gsf_xml_out_add_cstr (state->output, "ValueFormat", fmt);
1193 g_string_truncate (str, 0);
1194 value_get_as_gstring (val, str, state->convs);
1195 gsf_xml_out_add_cstr (state->output, NULL, str->str);
1198 gsf_xml_out_end_element (state->output); /* </gnm:Item> */
1199 g_string_free (str, TRUE);
1202 gsf_xml_out_end_element (state->output); /* </gnm:Scenario> */
1206 static void
1207 xml_write_scenarios (GnmOutputXML *state)
1209 GList *ptr;
1211 if (state->sheet->scenarios == NULL)
1212 return;
1214 gsf_xml_out_start_element (state->output, GNM "Scenarios");
1216 for (ptr = state->sheet->scenarios ; ptr != NULL ; ptr = ptr->next) {
1217 GnmScenario const *sc = ptr->data;
1218 xml_write_scenario (state, sc);
1221 gsf_xml_out_end_element (state->output); /* </gnm:Scenarios> */
1224 static int
1225 so_by_pos (SheetObject *a, SheetObject *b)
1227 GnmRange const *ra = &a->anchor.cell_bound;
1228 GnmRange const *rb = &b->anchor.cell_bound;
1229 int i;
1230 i = ra->start.col - rb->start.col;
1231 if (!i) i = ra->start.row - rb->start.row;
1232 if (!i) i = ra->end.col - rb->end.col;
1233 if (!i) i = ra->end.row - rb->end.row;
1234 return i;
1237 static void
1238 xml_write_objects (GnmOutputXML *state, GSList *objects)
1240 gboolean needs_container = TRUE;
1241 char buffer[4*(DBL_DIG+10)];
1242 char const *type_name;
1243 char *tmp;
1244 GSList *ptr;
1245 GSList *with_zorder = NULL;
1246 GSList *without_zorder = NULL;
1249 * Most objects are selectable and the order therefore matters.
1250 * We write those in reverse order because sheet_object_set_sheet
1251 * will reverse them on input.
1253 * Cell comments are separated out and sorted. This helps
1254 * consistency.
1256 * Yet other objects have no export method and we drop those on
1257 * the floor.
1259 for (ptr = objects ;ptr != NULL ; ptr = ptr->next) {
1260 SheetObject *so = ptr->data;
1261 SheetObjectClass *klass = GNM_SO_CLASS (G_OBJECT_GET_CLASS (so));
1262 if (klass == NULL || klass->write_xml_sax == NULL)
1263 continue;
1265 if (GNM_IS_CELL_COMMENT (so))
1266 without_zorder = g_slist_prepend (without_zorder, so);
1267 else
1268 with_zorder = g_slist_prepend (with_zorder, so);
1270 without_zorder = g_slist_sort (without_zorder, (GCompareFunc)so_by_pos);
1271 objects = g_slist_concat (without_zorder, with_zorder);
1273 for (ptr = objects ;ptr != NULL ; ptr = ptr->next) {
1274 SheetObject *so = ptr->data;
1275 SheetObjectClass *klass = GNM_SO_CLASS (G_OBJECT_GET_CLASS (so));
1276 GnmRange cell_bound = so->anchor.cell_bound;
1278 switch (so->anchor.mode) {
1279 case GNM_SO_ANCHOR_TWO_CELLS:
1280 break;
1281 case GNM_SO_ANCHOR_ONE_CELL:
1282 cell_bound.end = cell_bound.start;
1283 break;
1284 case GNM_SO_ANCHOR_ABSOLUTE:
1285 range_init (&cell_bound, 0, 0, 0, 0);
1286 break;
1287 default:
1288 g_assert_not_reached ();
1291 if (needs_container) {
1292 needs_container = FALSE;
1293 gsf_xml_out_start_element (state->output, GNM "Objects");
1296 /* A hook so that things can sometimes change names */
1297 type_name = klass->xml_export_name;
1298 if (type_name == NULL)
1299 type_name = G_OBJECT_TYPE_NAME (so);
1301 tmp = g_strconcat (GNM, type_name, NULL);
1302 gsf_xml_out_start_element (state->output, tmp);
1303 if (so->name)
1304 gsf_xml_out_add_cstr (state->output, "Name", so->name);
1305 if (so->anchor.mode != GNM_SO_ANCHOR_ABSOLUTE)
1306 gsf_xml_out_add_cstr (state->output, "ObjectBound", range_as_string (&cell_bound));
1307 if (so->anchor.mode != GNM_SO_ANCHOR_TWO_CELLS)
1308 gsf_xml_out_add_enum (state->output,
1309 "AnchorMode",
1310 GNM_SHEET_OBJECT_ANCHOR_MODE_TYPE,
1311 so->anchor.mode);
1312 snprintf (buffer, sizeof (buffer), "%.3g %.3g %.3g %.3g",
1313 so->anchor.offset [0], so->anchor.offset [1],
1314 so->anchor.offset [2], so->anchor.offset [3]);
1315 gsf_xml_out_add_cstr (state->output, "ObjectOffset", buffer);
1317 gsf_xml_out_add_int (state->output, "Direction",
1318 so->anchor.base.direction);
1319 gsf_xml_out_add_int
1320 (state->output, "Print",
1321 (so->flags & SHEET_OBJECT_PRINT) ? 1 : 0);
1323 (*klass->write_xml_sax) (so, state->output, state->convs);
1325 gsf_xml_out_end_element (state->output); /* </gnm:{typename}> */
1326 g_free (tmp);
1328 g_slist_free (objects);
1330 if (!needs_container)
1331 gsf_xml_out_end_element (state->output); /* </gnm:Objects> */
1334 static void
1335 xml_write_sheet (GnmOutputXML *state, Sheet const *sheet)
1337 GnmColor *c;
1339 state->sheet = sheet;
1340 gsf_xml_out_start_element (state->output, GNM "Sheet");
1342 gsf_xml_out_add_bool (state->output,
1343 "DisplayFormulas", sheet->display_formulas);
1344 gsf_xml_out_add_bool (state->output,
1345 "HideZero", sheet->hide_zero);
1346 gsf_xml_out_add_bool (state->output,
1347 "HideGrid", sheet->hide_grid);
1348 gsf_xml_out_add_bool (state->output,
1349 "HideColHeader", sheet->hide_col_header);
1350 gsf_xml_out_add_bool (state->output,
1351 "HideRowHeader", sheet->hide_row_header);
1352 gsf_xml_out_add_bool (state->output,
1353 "DisplayOutlines", sheet->display_outlines);
1354 gsf_xml_out_add_bool (state->output,
1355 "OutlineSymbolsBelow", sheet->outline_symbols_below);
1356 gsf_xml_out_add_bool (state->output,
1357 "OutlineSymbolsRight", sheet->outline_symbols_right);
1358 if (sheet->text_is_rtl)
1359 gsf_xml_out_add_bool (state->output,
1360 "RTL_Layout", sheet->text_is_rtl);
1361 if (sheet->is_protected)
1362 gsf_xml_out_add_bool (state->output,
1363 "Protected", sheet->is_protected);
1365 /* TODO : Make this an enum internally eventually */
1366 if (sheet->convs->r1c1_addresses)
1367 gsf_xml_out_add_cstr_unchecked (state->output,
1368 "ExprConvention", "gnumeric:R1C1");
1370 gsf_xml_out_add_enum (state->output,
1371 "Visibility", GNM_SHEET_VISIBILITY_TYPE, sheet->visibility);
1373 if (sheet->tab_color != NULL)
1374 gnm_xml_out_add_color (state->output, "TabColor", sheet->tab_color);
1375 if (sheet->tab_text_color != NULL)
1376 gnm_xml_out_add_color (state->output, "TabTextColor", sheet->tab_text_color);
1377 if (NULL != (c = sheet_style_get_auto_pattern_color (sheet))) {
1378 gnm_xml_out_add_color (state->output, "GridColor", c);
1379 style_color_unref (c);
1382 gsf_xml_out_simple_element (state->output,
1383 GNM "Name", sheet->name_unquoted);
1384 gsf_xml_out_simple_int_element (state->output,
1385 GNM "MaxCol", sheet->cols.max_used);
1386 gsf_xml_out_simple_int_element (state->output,
1387 GNM "MaxRow", sheet->rows.max_used);
1388 gsf_xml_out_simple_float_element (state->output,
1389 GNM "Zoom", sheet->last_zoom_factor_used, 4);
1391 xml_write_named_expressions (state, sheet->names);
1392 xml_write_print_info (state, sheet->print_info);
1393 xml_write_styles (state);
1394 xml_write_cols_rows (state, NULL);
1395 xml_write_selection_info (state);
1396 xml_write_objects (state, sheet->sheet_objects);
1397 xml_write_cells (state);
1399 xml_write_merged_regions (state);
1400 xml_write_sheet_layout (state);
1401 xml_write_sheet_filters (state);
1402 xml_write_solver (state);
1403 xml_write_scenarios (state);
1405 gsf_xml_out_end_element (state->output); /* </gnm:Sheet> */
1406 state->sheet = NULL;
1409 static void
1410 xml_write_sheets (GnmOutputXML *state)
1412 int i, n = workbook_sheet_count (state->wb);
1413 gsf_xml_out_start_element (state->output, GNM "Sheets");
1414 for (i = 0 ; i < n ; i++)
1415 xml_write_sheet (state, workbook_sheet_by_index (state->wb, i));
1416 gsf_xml_out_end_element (state->output); /* </gnm:Sheets> */
1419 static void
1420 xml_write_uidata (GnmOutputXML *state)
1422 gsf_xml_out_start_element (state->output, GNM "UIData");
1423 gsf_xml_out_add_int (state->output, "SelectedTab",
1424 wb_view_cur_sheet (state->wb_view)->index_in_wb);
1425 gsf_xml_out_end_element (state->output); /* </gnm:UIData> */
1428 static void
1429 xml_write_date_conventions_as_attr (GnmOutputXML *state,
1430 GODateConventions const *conv)
1432 if (conv->use_1904)
1433 gsf_xml_out_add_cstr_unchecked (state->output,
1434 GNM "DateConvention", "Apple:1904");
1437 static void
1438 xml_write_number_system (GnmOutputXML *state)
1441 * These numbers define how to interpret decimal values in the
1442 * file. They are not yet used, but should be used when the
1443 * number system of the loading Gnumeric is different from the
1444 * number system of the saving Gnumeric.
1446 gsf_xml_out_add_int (state->output, "FloatRadix", FLT_RADIX);
1447 gsf_xml_out_add_int (state->output, "FloatDigits", GNM_MANT_DIG);
1451 static void
1452 xml_write_calculation (GnmOutputXML *state)
1454 gsf_xml_out_start_element (state->output, GNM "Calculation");
1455 gsf_xml_out_add_bool (state->output,
1456 "ManualRecalc", !state->wb->recalc_auto);
1457 gsf_xml_out_add_bool (state->output,
1458 "EnableIteration", state->wb->iteration.enabled);
1459 gsf_xml_out_add_int (state->output,
1460 "MaxIterations", state->wb->iteration.max_number);
1461 go_xml_out_add_double (state->output,
1462 "IterationTolerance", state->wb->iteration.tolerance);
1463 xml_write_date_conventions_as_attr (state,
1464 workbook_date_conv (state->wb));
1465 xml_write_number_system (state);
1466 gsf_xml_out_end_element (state->output); /* </gnm:Calculation> */
1469 GnmConventions *
1470 gnm_xml_io_conventions (void)
1472 GnmConventions *res = gnm_conventions_new ();
1473 gnm_float l10;
1475 res->decimal_sep_dot = TRUE;
1476 res->input.range_ref = rangeref_parse;
1477 res->output.range_ref = gnm_1_0_rangeref_as_string;
1478 res->range_sep_colon = TRUE;
1479 res->arg_sep = ',';
1480 res->array_col_sep = ',';
1481 res->array_row_sep = ';';
1482 res->output.translated = FALSE;
1484 l10 = gnm_log10 (FLT_RADIX);
1485 res->output.decimal_digits = (int)gnm_ceil (GNM_MANT_DIG * l10) +
1486 (l10 == (int)l10 ? 0 : 1);
1488 return res;
1491 static void
1492 gnm_xml_file_save_full (G_GNUC_UNUSED GOFileSaver const *fs,
1493 G_GNUC_UNUSED GOIOContext *io_context,
1494 GoView const *view, GsfOutput *output,
1495 gboolean compress)
1497 GnmOutputXML state;
1498 GsfOutput *gzout = NULL;
1499 GnmLocale *locale;
1500 WorkbookView *wb_view = GNM_WORKBOOK_VIEW (view);
1502 if (compress) {
1503 gzout = gsf_output_gzip_new (output, NULL);
1504 output = gzout;
1507 state.wb_view = wb_view;
1508 state.wb = wb_view_get_workbook (wb_view);
1509 state.sheet = NULL;
1510 state.output = gsf_xml_out_new (output);
1511 state.convs = gnm_xml_io_conventions ();
1512 state.expr_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1513 state.cell_str = g_string_new (NULL);
1514 state.write_value_result = FALSE;
1515 go_doc_init_write (GO_DOC (state.wb), state.output);
1517 locale = gnm_push_C_locale ();
1519 gsf_xml_out_start_element (state.output, GNM "Workbook");
1522 * As long as we want older versions of Gnumeric to be able to read
1523 * the files we produce, we should not increase the version number
1524 * in the file we write. Until 1.12.21, v10 was the highest listed
1525 * xml-sax-read.c's content_ns.
1527 gsf_xml_out_add_cstr_unchecked (state.output, "xmlns:gnm",
1528 "http://www.gnumeric.org/v10.dtd");
1529 #if 0 /* seems to break meta data */
1530 /* default namespace added for 1.8 */
1531 gsf_xml_out_add_cstr_unchecked (state.output, "xmlns",
1532 "http://www.gnumeric.org/v10.dtd");
1533 #endif
1534 gsf_xml_out_add_cstr_unchecked (state.output, "xmlns:xsi",
1535 "http://www.w3.org/2001/XMLSchema-instance");
1536 gsf_xml_out_add_cstr_unchecked (state.output, "xsi:schemaLocation",
1537 "http://www.gnumeric.org/v9.xsd");
1539 xml_write_version (&state);
1540 xml_write_attributes (&state);
1541 xml_write_meta_data (&state);
1542 xml_write_conventions (&state); /* DEPRECATED, moved to Calculation */
1543 xml_write_calculation (&state);
1544 xml_write_sheet_names (&state);
1545 xml_write_named_expressions (&state, state.wb->names);
1546 xml_write_geometry (&state);
1547 xml_write_sheets (&state);
1548 xml_write_uidata (&state);
1549 go_doc_write (GO_DOC (state.wb), state.output);
1551 gsf_xml_out_end_element (state.output); /* </Workbook> */
1553 gnm_pop_C_locale (locale);
1555 g_hash_table_destroy (state.expr_map);
1556 g_string_free (state.cell_str, TRUE);
1557 gnm_conventions_unref (state.convs);
1558 g_object_unref (state.output);
1560 if (gzout) {
1561 gsf_output_close (gzout);
1562 g_object_unref (gzout);
1566 static void
1567 gnm_xml_file_save (GOFileSaver const *fs, GOIOContext *io_context,
1568 GoView const *view, GsfOutput *output)
1570 gboolean compress;
1571 char const *extension = NULL;
1573 /* If the suffix is .xml disable compression */
1574 if (NULL != gsf_output_name (output))
1575 extension = gsf_extension_pointer (gsf_output_name (output));
1576 if (NULL != extension && g_ascii_strcasecmp (extension, "xml") == 0)
1577 compress = FALSE;
1578 else
1579 compress = (gnm_conf_get_core_xml_compression_level () > 0);
1581 gnm_xml_file_save_full (fs, io_context, view, output, compress);
1584 static void
1585 gnm_xml_file_save_xml (GOFileSaver const *fs, GOIOContext *io_context,
1586 GoView const *view, GsfOutput *output)
1588 gnm_xml_file_save_full (fs, io_context, view, output, FALSE);
1591 /**************************************************************************/
1593 typedef struct {
1594 GnmOutputXML state;
1595 GnmCellRegion const *cr;
1596 GnmParsePos pp;
1597 } XMLCellCopyState;
1599 static void
1600 cb_xml_write_cell_region_cells (GnmCellCopy *cc,
1601 G_GNUC_UNUSED gconstpointer ignore,
1602 XMLCellCopyState *state)
1604 state->pp.eval.col = state->cr->base.col + cc->offset.col;
1605 state->pp.eval.row = state->cr->base.row + cc->offset.row;
1606 xml_write_cell_and_position (&state->state,
1607 cc->texpr, cc->val, &state->pp);
1611 * gnm_cellregion_to_xml:
1612 * @cr: the content to store.
1614 * Returns: (transfer full): %NULL on error
1616 GsfOutputMemory *
1617 gnm_cellregion_to_xml (GnmCellRegion const *cr)
1619 XMLCellCopyState state;
1620 GnmStyleList *s_ptr;
1621 GSList *ptr;
1622 GsfOutput *buf = gsf_output_memory_new ();
1623 GnmLocale *locale;
1624 GODoc *doc = NULL;
1626 g_return_val_if_fail (cr != NULL, NULL);
1627 g_return_val_if_fail (IS_SHEET (cr->origin_sheet), NULL);
1629 state.state.wb_view = NULL;
1630 state.state.wb = NULL;
1631 state.state.sheet = cr->origin_sheet;
1632 state.state.output = gsf_xml_out_new (buf);
1633 state.state.convs = gnm_xml_io_conventions ();
1634 state.state.expr_map = g_hash_table_new (g_direct_hash, g_direct_equal);
1635 state.state.cell_str = g_string_new (NULL);
1636 state.state.write_value_result = TRUE;
1638 locale = gnm_push_C_locale ();
1639 if (cr->origin_sheet) {
1640 /* hoping this always occur */
1641 doc = GO_DOC (cr->origin_sheet->workbook);
1642 go_doc_init_write (doc, state.state.output);
1645 gsf_xml_out_start_element (state.state.output, GNM "ClipboardRange");
1647 /* backwards compat, must be first */
1648 gsf_xml_out_add_cstr_unchecked (state.state.output, "xmlns:gnm",
1649 "http://www.gnumeric.org/v10.dtd");
1650 /* default namespace added for 1.8 */
1651 gsf_xml_out_add_cstr_unchecked (state.state.output, "xmlns",
1652 "http://www.gnumeric.org/v10.dtd");
1654 gsf_xml_out_add_int (state.state.output, "Cols", cr->cols);
1655 gsf_xml_out_add_int (state.state.output, "Rows", cr->rows);
1656 gsf_xml_out_add_int (state.state.output, "BaseCol", cr->base.col);
1657 gsf_xml_out_add_int (state.state.output, "BaseRow", cr->base.row);
1658 if (cr->origin_sheet)
1659 xml_write_date_conventions_as_attr
1660 (&state.state,
1661 workbook_date_conv (cr->origin_sheet->workbook));
1662 xml_write_number_system (&state.state);
1663 if (cr->not_as_contents)
1664 gsf_xml_out_add_bool (state.state.output, "NotAsContent", TRUE);
1666 xml_write_cols_rows (&state.state, cr);
1668 if (cr->styles != NULL) {
1669 gsf_xml_out_start_element (state.state.output, GNM "Styles");
1670 for (s_ptr = cr->styles ; s_ptr != NULL ; s_ptr = s_ptr->next)
1671 xml_write_style_region (&state.state, s_ptr->data);
1672 gsf_xml_out_end_element (state.state.output); /* </Styles> */
1675 if (cr->merged != NULL) {
1676 gsf_xml_out_start_element (state.state.output, GNM "MergedRegions");
1677 for (ptr = cr->merged ; ptr != NULL ; ptr = ptr->next) {
1678 gsf_xml_out_start_element (state.state.output, GNM "Merge");
1679 gsf_xml_out_add_cstr_unchecked (state.state.output, NULL,
1680 range_as_string (ptr->data));
1681 gsf_xml_out_end_element (state.state.output); /* </Merge> */
1683 gsf_xml_out_end_element (state.state.output); /* </gnm:MergedRegions> */
1686 /* NOTE SNEAKY : ensure that sheet names have explicit workbooks */
1687 state.pp.wb = NULL;
1688 state.pp.sheet = cr->origin_sheet;
1689 state.cr = cr;
1690 if (cr->cell_content != NULL) {
1691 gsf_xml_out_start_element (state.state.output, GNM "Cells");
1692 g_hash_table_foreach (cr->cell_content,
1693 (GHFunc) cb_xml_write_cell_region_cells, &state);
1694 gsf_xml_out_end_element (state.state.output); /* </Cells> */
1697 xml_write_objects (&state.state, cr->objects);
1699 if (NULL != doc)
1700 go_doc_write (doc, state.state.output);
1701 gsf_xml_out_end_element (state.state.output); /* </ClipboardRange> */
1703 gnm_pop_C_locale (locale);
1705 g_hash_table_destroy (state.state.expr_map);
1706 g_string_free (state.state.cell_str, TRUE);
1707 gnm_conventions_unref (state.state.convs);
1708 g_object_unref (state.state.output);
1710 gsf_output_close (buf);
1712 return GSF_OUTPUT_MEMORY (buf);
1715 #define XML_SAX_ID "Gnumeric_XmlIO:sax"
1716 #define XML_SAX_ID_0 "Gnumeric_XmlIO:sax:0"
1718 void
1719 gnm_xml_sax_write_init (void)
1721 GOFileSaver *saver = go_file_saver_new
1722 (XML_SAX_ID,
1723 "gnumeric",
1724 _("Gnumeric XML (*.gnumeric)"),
1725 GO_FILE_FL_AUTO, gnm_xml_file_save);
1726 g_object_set (G_OBJECT (saver),
1727 "mime-type", "application/x-gnumeric",
1728 NULL);
1730 go_file_saver_register_as_default (saver, 50);
1731 g_object_unref (saver);
1733 saver = go_file_saver_new
1734 (XML_SAX_ID_0,
1735 "xml",
1736 _("Gnumeric XML uncompressed (*.xml)"),
1737 GO_FILE_FL_AUTO, gnm_xml_file_save_xml);
1738 g_object_set (G_OBJECT (saver),
1739 "mime-type", "application/xml",
1740 NULL);
1742 go_file_saver_register (saver);
1743 g_object_unref (saver);
1746 void
1747 gnm_xml_sax_write_shutdown (void)
1749 go_file_saver_unregister (go_file_saver_for_id (XML_SAX_ID));
1750 go_file_saver_unregister (go_file_saver_for_id (XML_SAX_ID_0));