xml-sax-write.c 46.6 KB
Newer Older
1 2 3
/* vim: set sw=8: */

/*
4
 * xml-sax-write.c : export .gnumeric and the clipboard subset using a the sax
5
 *			like wrappers in libgsf
6
 *
7
 * Copyright (C) 2003-2007 Jody Goldberg (jody@gnome.org)
8 9 10 11 12 13 14 15 16 17 18 19
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21 22 23 24 25
 * USA
 */

/*****************************************************************************/

26
#include <gnumeric-config.h>
27
#include <gnumeric.h>
28
#include <xml-sax.h>
29
#include <workbook-view.h>
30
#include <gnm-format.h>
31 32
#include <workbook.h>
#include <workbook-priv.h> /* Workbook::names */
33
#include <cell.h>
34
#include <sheet.h>
35
#include <sheet-view.h>
36 37
#include <sheet-style.h>
#include <style-color.h>
38
#include <style-conditions.h>
39 40
#include <expr.h>
#include <expr-impl.h>
41
#include <expr-name.h>
42
#include <value.h>
43
#include <str.h>
44 45 46 47 48
#include <ranges.h>
#include <mstyle.h>
#include <style-border.h>
#include <validation.h>
#include <hlink.h>
49
#include <input-msg.h>
50 51
#include <solver.h>
#include <sheet-filter.h>
52
#include <sheet-object-impl.h>
53
#include <print-info.h>
54
#include <xml-io.h>
55
#include <gutils.h>
56
#include <clipboard.h>
57
#include <tools/scenarios.h>
58
#include <gnumeric-gconf.h>
59

60
#include <goffice/app/file.h>
61 62
#include <gsf/gsf-libxml.h>
#include <gsf/gsf-output-gzip.h>
63
#include <gsf/gsf-doc-meta-data.h>
64
#include <gsf/gsf-opendoc-utils.h>
65 66 67 68 69
#include <gsf/gsf-utils.h>

typedef struct {
	WorkbookView const *wb_view;	/* View for the new workbook */
	Workbook const	   *wb;		/* The new workbook */
70
	Sheet const	   *sheet;
71
	GnmConventions	   *convs;
72
	GHashTable	   *expr_map;
73
	GString		   *cell_str;   /* Scratch pad.  */
74

75
	GsfXMLOut *output;
76 77
} GnmOutputXML;

78
#define GNM "gnm:"
79

80 81 82 83
/* Precision to use when saving point measures. */
#define POINT_SIZE_PRECISION 4

static void
Jody Goldberg's avatar
Jody Goldberg committed
84
xml_out_add_range (GsfXMLOut *xml, GnmRange const *r)
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
{
	g_return_if_fail (range_is_sane (r));

	gsf_xml_out_add_int (xml, "startCol", r->start.col);
	gsf_xml_out_add_int (xml, "startRow", r->start.row);
	gsf_xml_out_add_int (xml, "endCol",   r->end.col);
	gsf_xml_out_add_int (xml, "endRow",   r->end.row);
}

static void
xml_out_add_points (GsfXMLOut *xml, char const *name, double val)
{
	gsf_xml_out_add_float (xml, name, val, POINT_SIZE_PRECISION);
}

100 101 102
static void
xml_write_attribute (GnmOutputXML *state, char const *name, char const *value)
{
103
	gsf_xml_out_start_element (state->output, GNM "Attribute");
104
	/* backwards compatibility with 1.0.x which uses gtk-1.2 GTK_TYPE_BOOLEAN */
105 106 107
	gsf_xml_out_simple_element (state->output, GNM "type", "4");
	gsf_xml_out_simple_element (state->output, GNM "name", name);
	gsf_xml_out_simple_element (state->output, GNM "value", value);
108
	gsf_xml_out_end_element (state->output); /* </Attribute> */
109 110
}

111 112 113
static void
xml_write_version (GnmOutputXML *state)
{
114
	gsf_xml_out_start_element (state->output, GNM "Version");
115 116 117
	gsf_xml_out_add_int (state->output, "Epoch", GNM_VERSION_EPOCH);
	gsf_xml_out_add_int (state->output, "Major", GNM_VERSION_MAJOR);
	gsf_xml_out_add_int (state->output, "Minor", GNM_VERSION_MINOR);
118
	gsf_xml_out_add_cstr_unchecked (state->output, "Full", GNM_VERSION_FULL);
119 120 121
	gsf_xml_out_end_element (state->output); /* </Version> */
}

122 123 124
static void
xml_write_attributes (GnmOutputXML *state)
{
125
	gsf_xml_out_start_element (state->output, GNM "Attributes");
126 127 128 129 130 131 132 133 134 135
	xml_write_attribute (state, "WorkbookView::show_horizontal_scrollbar",
		state->wb_view->show_horizontal_scrollbar ? "TRUE" : "FALSE");
	xml_write_attribute (state, "WorkbookView::show_vertical_scrollbar",
		state->wb_view->show_vertical_scrollbar ? "TRUE" : "FALSE");
	xml_write_attribute (state, "WorkbookView::show_notebook_tabs",
		state->wb_view->show_notebook_tabs ? "TRUE" : "FALSE");
	xml_write_attribute (state, "WorkbookView::do_auto_completion",
		state->wb_view->do_auto_completion ? "TRUE" : "FALSE");
	xml_write_attribute (state, "WorkbookView::is_protected",
		state->wb_view->is_protected ? "TRUE" : "FALSE");
136
	gsf_xml_out_end_element (state->output); /* </Attributes> */
137 138 139
}

static void
140
xml_write_meta_data (GnmOutputXML *state)
141
{
142
	gsf_opendoc_metadata_write (state->output,
143
		go_doc_get_meta_data (GO_DOC (state->wb)));
144 145
}

146
/* DEPRECATED in 1.7.11 */
147 148 149
static void
xml_write_conventions (GnmOutputXML *state)
{
150
	GODateConventions const *conv = workbook_date_conv (state->wb);
151
	if (conv->use_1904)
152
		gsf_xml_out_simple_element (state->output, GNM "DateConvention", "1904");
153 154 155 156 157 158 159 160
}

static void
xml_write_sheet_names (GnmOutputXML *state)
{
	int i, n = workbook_sheet_count (state->wb);
	Sheet *sheet;

161
	gsf_xml_out_start_element (state->output, GNM "SheetNameIndex");
162 163
	for (i = 0 ; i < n ; i++) {
		sheet = workbook_sheet_by_index (state->wb, i);
164
		gsf_xml_out_simple_element (state->output, GNM "SheetName",
165 166
			sheet->name_unquoted);
	}
167
	gsf_xml_out_end_element (state->output); /* </gnm:SheetNameIndex> */
168 169 170
}

static void
171
xml_write_name (GnmOutputXML *state, GnmNamedExpr *nexpr)
172 173 174 175 176
{
	char *expr_str;

	g_return_if_fail (nexpr != NULL);

177 178
	gsf_xml_out_start_element (state->output, GNM "Name");
	gsf_xml_out_simple_element (state->output, GNM "name",
179
		nexpr->name->str);
180
	expr_str = expr_name_as_string (nexpr, NULL, state->convs);
181
	gsf_xml_out_simple_element (state->output, GNM "value", expr_str);
182
	g_free (expr_str);
183
	gsf_xml_out_simple_element (state->output, GNM "position",
184
		cellpos_as_string (&nexpr->pos.eval));
185
	gsf_xml_out_end_element (state->output); /* </gnm:Name> */
186 187 188 189 190 191
}

static void
xml_write_named_expressions (GnmOutputXML *state, GnmNamedExprCollection *scope)
{
	if (scope != NULL) {
192 193 194 195 196
		GSList *names =
			g_slist_sort (gnm_named_expr_collection_list (scope),
				      (GCompareFunc)expr_name_cmp_by_name);
		GSList *p;

197
		gsf_xml_out_start_element (state->output, GNM "Names");
198 199 200 201
		for (p = names; p; p = p->next) {
			GnmNamedExpr *nexpr = p->data;
			xml_write_name (state, nexpr);
		}
202
		gsf_xml_out_end_element (state->output); /* </gnm:Names> */
203
		g_slist_free (names);
204 205 206 207 208 209
	}
}

static void
xml_write_geometry (GnmOutputXML *state)
{
210
	gsf_xml_out_start_element (state->output, GNM "Geometry");
211 212
	gsf_xml_out_add_int (state->output, "Width", state->wb_view->preferred_width);
	gsf_xml_out_add_int (state->output, "Height", state->wb_view->preferred_height);
213
	gsf_xml_out_end_element (state->output); /* </gnm:Geometry> */
214 215 216 217
}

static void
xml_write_print_unit (GnmOutputXML *state, char const *name,
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
218
		      double points, GtkUnit unit)
219 220 221
{
	gsf_xml_out_start_element (state->output, name);
	xml_out_add_points (state->output, "Points", points);
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
222 223
	gsf_xml_out_add_cstr_unchecked (state->output, "PrefUnit",
					unit_to_unit_name (unit));
224 225 226 227 228 229 230 231 232 233 234
	gsf_xml_out_end_element (state->output);
}

static void
xml_write_print_repeat_range (GnmOutputXML *state,
			      char const *name,
			      PrintRepeatRange *range)
{
	if (range->use) {
		gsf_xml_out_start_element (state->output, name);
		gsf_xml_out_add_cstr_unchecked (state->output, "value",
235
			range_as_string (&range->range));
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
		gsf_xml_out_end_element (state->output);
	}
}

static void
xml_write_print_hf (GnmOutputXML *state, char const *name,
		    PrintHF const *hf)
{
	gsf_xml_out_start_element (state->output, name);
	gsf_xml_out_add_cstr (state->output, "Left", hf->left_format);
	gsf_xml_out_add_cstr (state->output, "Middle", hf->middle_format);
	gsf_xml_out_add_cstr (state->output, "Right", hf->right_format);
	gsf_xml_out_end_element (state->output);

}
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275

static void
xml_write_breaks (GnmOutputXML *state, GnmPageBreaks *breaks)
{
	GArray const *details = breaks->details;
	GnmPageBreak const *binfo;
	unsigned i;

	gsf_xml_out_start_element (state->output,
		(breaks->is_vert) ? GNM "vPageBreaks" : GNM "hPageBreaks");
	gsf_xml_out_add_int (state->output, "count", details->len);

	for (i = 0 ; i < details->len ; i++) {
		binfo = &g_array_index (details, GnmPageBreak, i);
		gsf_xml_out_start_element (state->output, GNM "break");
		gsf_xml_out_add_int (state->output, "pos", binfo->pos);
		if (binfo->type == GNM_PAGE_BREAK_MANUAL)
			gsf_xml_out_add_cstr_unchecked  (state->output, "type", "manual");
		else if (binfo->type == GNM_PAGE_BREAK_DATA_SLICE)
			gsf_xml_out_add_cstr_unchecked  (state->output, "type", "data-slice");
		gsf_xml_out_end_element (state->output); /* </break> */
	}
	gsf_xml_out_end_element (state->output);
}

276 277 278
static void
xml_write_print_info (GnmOutputXML *state, PrintInformation *pi)
{
279
	char  *paper_name;
280 281 282 283
	double header;
	double footer;
	double left;
	double right;
284 285
	double edge_to_above_footer;
	double edge_to_below_header;
286
	GtkPageOrientation orient;
287 288 289

	g_return_if_fail (pi != NULL);

290
	gsf_xml_out_start_element (state->output, GNM "PrintInformation");
291

292
	gsf_xml_out_start_element (state->output, GNM "Margins");
293 294 295 296

	print_info_get_margins (pi, &header, &footer, &left, &right,
				&edge_to_below_header, &edge_to_above_footer);
	xml_write_print_unit (state, GNM "top", edge_to_below_header,
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
297
			      pi->desired_display.header);
298
	xml_write_print_unit (state, GNM "bottom", edge_to_above_footer,
Andreas J. Guelzow's avatar
Andreas J. Guelzow committed
299 300 301 302 303 304 305 306 307
			      pi->desired_display.footer);
	xml_write_print_unit (state, GNM "left", left,
			      pi->desired_display.left);
	xml_write_print_unit (state, GNM "right", right,
			      pi->desired_display.right);
	xml_write_print_unit (state, GNM "header", header,
			      pi->desired_display.top);
	xml_write_print_unit (state, GNM "footer", footer,
			      pi->desired_display.bottom);
308 309
	gsf_xml_out_end_element (state->output);

310
	gsf_xml_out_start_element (state->output, GNM "Scale");
311
	if (pi->scaling.type == PRINT_SCALE_PERCENTAGE) {
312 313 314 315 316 317
		gsf_xml_out_add_cstr_unchecked  (state->output, "type", "percentage");
		gsf_xml_out_add_float  (state->output, "percentage", pi->scaling.percentage.x, -1);
	} else {
		gsf_xml_out_add_cstr_unchecked  (state->output, "type", "size_fit");
		gsf_xml_out_add_float  (state->output, "cols", pi->scaling.dim.cols, -1);
		gsf_xml_out_add_float  (state->output, "rows", pi->scaling.dim.rows, -1);
318
	}
319 320
	gsf_xml_out_end_element (state->output);

321
	gsf_xml_out_start_element (state->output, GNM "vcenter");
322 323 324
	gsf_xml_out_add_int  (state->output, "value", pi->center_vertically);
	gsf_xml_out_end_element (state->output);

325
	gsf_xml_out_start_element (state->output, GNM "hcenter");
326 327 328
	gsf_xml_out_add_int  (state->output, "value", pi->center_horizontally);
	gsf_xml_out_end_element (state->output);

329
	gsf_xml_out_start_element (state->output, GNM "grid");
330 331 332
	gsf_xml_out_add_int  (state->output, "value",    pi->print_grid_lines);
	gsf_xml_out_end_element (state->output);

333
	gsf_xml_out_start_element (state->output, GNM "even_if_only_styles");
334 335 336
	gsf_xml_out_add_int  (state->output, "value",    pi->print_even_if_only_styles);
	gsf_xml_out_end_element (state->output);

337
	gsf_xml_out_start_element (state->output, GNM "monochrome");
338 339 340
	gsf_xml_out_add_int  (state->output, "value",    pi->print_black_and_white);
	gsf_xml_out_end_element (state->output);

341
	gsf_xml_out_start_element (state->output, GNM "draft");
342 343 344
	gsf_xml_out_add_int  (state->output, "value",    pi->print_as_draft);
	gsf_xml_out_end_element (state->output);

345
	gsf_xml_out_start_element (state->output, GNM "titles");
346 347 348
	gsf_xml_out_add_int  (state->output, "value",    pi->print_titles);
	gsf_xml_out_end_element (state->output);

349 350 351 352 353
	gsf_xml_out_start_element (state->output, GNM "do_not_print");
	gsf_xml_out_add_int  (state->output, "value",    pi->do_not_print);
	gsf_xml_out_end_element (state->output);


354 355
	xml_write_print_repeat_range (state, GNM "repeat_top", &pi->repeat_top);
	xml_write_print_repeat_range (state, GNM "repeat_left", &pi->repeat_left);
356

357
	/* this was once an enum, hence the silly strings */
358
	gsf_xml_out_simple_element (state->output, GNM "order",
359
		pi->print_across_then_down ? "r_then_d" :"d_then_r");
360 361

	orient = print_info_get_paper_orientation (pi);
362
	gsf_xml_out_simple_element (state->output, GNM "orientation",
363 364 365 366
		(orient == GTK_PAGE_ORIENTATION_PORTRAIT
		 || orient == GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT)
				    ? "portrait" : "landscape");
#warning TODO: we should also handle inversion
367

368 369
	xml_write_print_hf (state, GNM "Header", pi->header);
	xml_write_print_hf (state, GNM "Footer", pi->footer);
370

371 372
	paper_name = print_info_get_paper (pi);
	if (paper_name)
373
		gsf_xml_out_simple_element (state->output, GNM "paper", paper_name);
374
	g_free (paper_name);
375

376 377 378 379
	if (NULL != pi->page_breaks.v)
		xml_write_breaks (state, pi->page_breaks.v);
	if (NULL != pi->page_breaks.h)
		xml_write_breaks (state, pi->page_breaks.h);
380 381 382 383
	gsf_xml_out_end_element (state->output);
}

static void
384
xml_write_style (GnmOutputXML *state, GnmStyle const *style)
385 386
{
	static char const *border_names[] = {
387 388 389 390 391 392
		GNM "Top",
		GNM "Bottom",
		GNM "Left",
		GNM "Right",
		GNM "Diagonal",
		GNM "Rev-Diagonal"
393
	};
Jody Goldberg's avatar
Jody Goldberg committed
394
	GnmValidation const *v;
395 396 397 398
	GnmHLink   const *link;
	GnmInputMsg const *im;
	GnmStyleConditions const *sc;
	GnmStyleCond const *cond;
399 400
	GnmBorder const *border;
	GnmStyleBorderType t;
401 402 403
	GnmParsePos    pp;
	char	   *tmp;
	unsigned i;
404
	gboolean started;
405

406
	gsf_xml_out_start_element (state->output, GNM "Style");
407

408 409 410 411 412 413 414 415 416 417 418 419 420 421
	if (gnm_style_is_element_set (style, MSTYLE_ALIGN_H))
		gsf_xml_out_add_int (state->output, "HAlign", gnm_style_get_align_h (style));
	if (gnm_style_is_element_set (style, MSTYLE_ALIGN_V))
		gsf_xml_out_add_int (state->output, "VAlign", gnm_style_get_align_v (style));
	if (gnm_style_is_element_set (style, MSTYLE_WRAP_TEXT))
		gsf_xml_out_add_bool (state->output, "WrapText", gnm_style_get_wrap_text (style));
	if (gnm_style_is_element_set (style, MSTYLE_SHRINK_TO_FIT))
		gsf_xml_out_add_bool (state->output, "ShrinkToFit", gnm_style_get_shrink_to_fit (style));
	if (gnm_style_is_element_set (style, MSTYLE_ROTATION))
		gsf_xml_out_add_int (state->output, "Rotation", gnm_style_get_rotation (style));
	if (gnm_style_is_element_set (style, MSTYLE_PATTERN))
		gsf_xml_out_add_int (state->output, "Shade", gnm_style_get_pattern (style));
	if (gnm_style_is_element_set (style, MSTYLE_INDENT))
		gsf_xml_out_add_int (state->output, "Indent", gnm_style_get_indent (style));
422 423 424 425
	if (gnm_style_is_element_set (style, MSTYLE_CONTENTS_LOCKED))
		gsf_xml_out_add_bool (state->output, "Locked", gnm_style_get_contents_locked (style));
	if (gnm_style_is_element_set (style, MSTYLE_CONTENTS_HIDDEN))
		gsf_xml_out_add_bool (state->output, "Hidden", gnm_style_get_contents_hidden (style));
426 427 428 429 430 431 432
	if (gnm_style_is_element_set (style, MSTYLE_FONT_COLOR))
		gnm_xml_out_add_color (state->output, "Fore", gnm_style_get_font_color (style));
	if (gnm_style_is_element_set (style, MSTYLE_COLOR_BACK))
		gnm_xml_out_add_color (state->output, "Back", gnm_style_get_back_color (style));
	if (gnm_style_is_element_set (style, MSTYLE_COLOR_PATTERN))
		gnm_xml_out_add_color (state->output, "PatternColor", gnm_style_get_pattern_color (style));
	if (gnm_style_is_element_set (style, MSTYLE_FORMAT)) {
433
		const char *fmt = go_format_as_XL (gnm_style_get_format (style));
434 435 436
		gsf_xml_out_add_cstr (state->output, "Format", fmt);
	}

437 438 439 440 441
	if (gnm_style_is_element_set (style, MSTYLE_FONT_NAME) ||
	    gnm_style_is_element_set (style, MSTYLE_FONT_SIZE) ||
	    gnm_style_is_element_set (style, MSTYLE_FONT_BOLD) ||
	    gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC) ||
	    gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
442 443
	    gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH) ||
	    gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT)) {
444 445
		char const *fontname;

446
		gsf_xml_out_start_element (state->output, GNM "Font");
447

448 449 450 451 452 453 454 455 456 457
		if (gnm_style_is_element_set (style, MSTYLE_FONT_SIZE))
			xml_out_add_points (state->output, "Unit", gnm_style_get_font_size (style));
		if (gnm_style_is_element_set (style, MSTYLE_FONT_BOLD))
			gsf_xml_out_add_int (state->output, "Bold", gnm_style_get_font_bold (style));
		if (gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC))
			gsf_xml_out_add_int (state->output, "Italic", gnm_style_get_font_italic (style));
		if (gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE))
			gsf_xml_out_add_int (state->output, "Underline", (int)gnm_style_get_font_uline (style));
		if (gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
			gsf_xml_out_add_int (state->output, "StrikeThrough", gnm_style_get_font_strike (style));
458 459
		if (gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT))
			gsf_xml_out_add_int (state->output, "Script", (int)gnm_style_get_font_script (style));
460 461 462

		if (gnm_style_is_element_set (style, MSTYLE_FONT_NAME))
			fontname = gnm_style_get_font_name (style);
463 464 465 466 467 468 469
		else /* backwards compatibility */
			fontname = "Helvetica";

		gsf_xml_out_add_cstr (state->output, NULL, fontname);
		gsf_xml_out_end_element (state->output);
	}

470 471
	if (gnm_style_is_element_set (style, MSTYLE_HLINK) &&
	    NULL != (link = gnm_style_get_hlink (style))) {
472
		gsf_xml_out_start_element (state->output, GNM "HyperLink");
473 474 475 476 477 478 479
		gsf_xml_out_add_cstr (state->output, "type", g_type_name (G_OBJECT_TYPE (link)));
		gsf_xml_out_add_cstr (state->output, "target", gnm_hlink_get_target (link));
		if (gnm_hlink_get_tip (link) != NULL)
			gsf_xml_out_add_cstr (state->output, "tip", gnm_hlink_get_tip (link));
		gsf_xml_out_end_element (state->output);
	}

480 481
	if (gnm_style_is_element_set (style, MSTYLE_VALIDATION) &&
	    NULL != (v = gnm_style_get_validation (style))) {
482
		gsf_xml_out_start_element (state->output, GNM "Validation");
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
		gsf_xml_out_add_int (state->output, "Style", v->style);
		gsf_xml_out_add_int (state->output, "Type", v->type);

		switch (v->type) {
		case VALIDATION_TYPE_AS_INT :
		case VALIDATION_TYPE_AS_NUMBER :
		case VALIDATION_TYPE_AS_DATE :
		case VALIDATION_TYPE_AS_TIME :
		case VALIDATION_TYPE_TEXT_LENGTH :
			gsf_xml_out_add_int (state->output, "Operator", v->op);
			break;
		default :
			break;
		}

		gsf_xml_out_add_bool (state->output, "AllowBlank", v->allow_blank);
		gsf_xml_out_add_bool (state->output, "UseDropdown", v->use_dropdown);

		if (v->title != NULL && v->title->str[0] != '\0')
			gsf_xml_out_add_cstr (state->output, "Title", v->title->str);
		if (v->msg != NULL && v->msg->str[0] != '\0')
			gsf_xml_out_add_cstr (state->output, "Message", v->msg->str);

		parse_pos_init_sheet (&pp, (Sheet *)state->sheet);
507
		if (v->texpr[0] != NULL &&
508
		    (tmp = gnm_expr_top_as_string (v->texpr[0], &pp, state->convs)) != NULL) {
509
			gsf_xml_out_simple_element (state->output, GNM "Expression0", tmp);
510 511
			g_free (tmp);
		}
512
		if (v->texpr[1] != NULL &&
513
		    (tmp = gnm_expr_top_as_string (v->texpr[1], &pp, state->convs)) != NULL) {
514
			gsf_xml_out_simple_element (state->output, GNM "Expression1", tmp);
515 516
			g_free (tmp);
		}
517
		gsf_xml_out_end_element (state->output); /* </Validation> */
518 519
	}

520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
	if (gnm_style_is_element_set (style, MSTYLE_INPUT_MSG) &&
	    NULL != (im = gnm_style_get_input_msg (style))) {
		char const *txt;
		gsf_xml_out_start_element (state->output, GNM "InputMessage");
		if (NULL != (txt = gnm_input_msg_get_title (im)))
			gsf_xml_out_add_cstr (state->output, "Title", txt);
		if (NULL != (txt = gnm_input_msg_get_msg (im)))
			gsf_xml_out_add_cstr (state->output, "Message", txt);
		gsf_xml_out_end_element (state->output); /* </InputMessage> */
	}

	if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS) &&
	    NULL != (sc = gnm_style_get_conditions (style))) {
		GArray const *conds = gnm_style_conditions_details (sc);
		if (conds != NULL)
			for (i = 0 ; i < conds->len ; i++) {
				cond = &g_array_index (conds, GnmStyleCond, i);
				gsf_xml_out_start_element (state->output, GNM "Condition");
				gsf_xml_out_add_int (state->output, "Operator", cond->op);
				parse_pos_init_sheet (&pp, (Sheet *)state->sheet);
540
				if (cond->texpr[0] != NULL &&
541
				    (tmp = gnm_expr_top_as_string (cond->texpr[0], &pp, state->convs)) != NULL) {
542 543 544
					gsf_xml_out_simple_element (state->output, GNM "Expression0", tmp);
					g_free (tmp);
				}
545
				if (cond->texpr[1] != NULL &&
546
				    (tmp = gnm_expr_top_as_string (cond->texpr[1], &pp, state->convs)) != NULL) {
547 548 549
					gsf_xml_out_simple_element (state->output, GNM "Expression1", tmp);
					g_free (tmp);
				}
550
				xml_write_style (state, cond->overlay);
551 552 553 554
				gsf_xml_out_end_element (state->output); /* </Condition> */
			}
	}

555 556 557 558 559 560
	started = FALSE;
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++)
		if (gnm_style_is_element_set (style, i) &&
		    NULL != (border = gnm_style_get_border (style, i)) &&
		    GNM_STYLE_BORDER_NONE != (t = border->line_type)) {
			GnmColor const *col   = border->color;
561

562 563 564
			if (!started) {
				gsf_xml_out_start_element (state->output, GNM "StyleBorder");
				started = TRUE;
565
			}
566 567 568 569 570 571

			gsf_xml_out_start_element (state->output,
				border_names [i - MSTYLE_BORDER_TOP]);
			gsf_xml_out_add_int (state->output, "Style", t);
				gnm_xml_out_add_color (state->output, "Color", col);
			gsf_xml_out_end_element (state->output);
572
		}
573
	if (started)
574
		gsf_xml_out_end_element (state->output);
575

576 577 578 579
	gsf_xml_out_end_element (state->output);
}

static void
Morten Welinder's avatar
Morten Welinder committed
580
xml_write_style_region (GnmOutputXML *state, GnmStyleRegion const *region)
581
{
582
	gsf_xml_out_start_element (state->output, GNM "StyleRegion");
583 584
	xml_out_add_range (state->output, &region->range);
	if (region->style != NULL)
585
		xml_write_style (state, region->style);
586 587 588
	gsf_xml_out_end_element (state->output);
}

589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
static int
cb_sheet_style_order (GnmStyleRegion const *a, GnmStyleRegion const *b)
{
	GnmRange const *ra = &a->range;
	GnmRange const *rb = &b->range;
	int res;

	res = ra->start.col - rb->start.col;

	if (res == 0)
		res = ra->start.row - rb->start.row;

	return res;
}

604 605 606
static void
xml_write_styles (GnmOutputXML *state)
{
607 608 609
	GnmStyleList *styles =
		g_slist_sort (sheet_style_get_list (state->sheet, NULL),
			      (GCompareFunc)cb_sheet_style_order);
610
	if (styles != NULL) {
611 612
		GnmStyleList *ptr;

613
		gsf_xml_out_start_element (state->output, GNM "Styles");
614 615 616
		for (ptr = styles; ptr; ptr = ptr->next)
			xml_write_style_region (state, ptr->data);
		gsf_xml_out_end_element (state->output);
617
		style_list_free (styles);
618 619 620 621 622
	}
}

typedef struct {
	GnmOutputXML *state;
623 624 625
	gboolean	is_column;
	ColRowInfo const *prev;
	int prev_pos, rle_count;
626 627 628
} closure_write_colrow;

static gboolean
629
xml_write_colrow_info (GnmColRowIter const *iter, closure_write_colrow *closure)
630
{
631
	ColRowInfo const *prev = closure->prev;
632 633 634
	GsfXMLOut *output = closure->state->output;

	closure->rle_count++;
635
	if (NULL != iter && colrow_equal (prev, iter->cri))
636 637 638 639
		return FALSE;

	if (prev != NULL) {
		if (closure->is_column)
640
			gsf_xml_out_start_element (output, GNM "ColInfo");
641
		else
642
			gsf_xml_out_start_element (output, GNM "RowInfo");
643

644
		gsf_xml_out_add_int (output, "No", closure->prev_pos);
645 646 647 648 649 650 651
		xml_out_add_points (output, "Unit", prev->size_pts);
		if (prev->hard_size)
			gsf_xml_out_add_bool (output, "HardSize", TRUE);
		if (!prev->visible)
			gsf_xml_out_add_bool (output, "Hidden", TRUE);
		if (prev->is_collapsed)
			gsf_xml_out_add_bool (output, "Collapsed", TRUE);
652
		if (prev->outline_level > 0)
Jody Goldberg's avatar
Jody Goldberg committed
653
			gsf_xml_out_add_int (output, "OutlineLevel", prev->outline_level);
654 655 656 657 658 659 660

		if (closure->rle_count > 1)
			gsf_xml_out_add_int (output, "Count", closure->rle_count);
		gsf_xml_out_end_element (output);
	}

	closure->rle_count = 0;
661 662 663 664
	if (NULL != iter) {
		closure->prev = iter->cri;
		closure->prev_pos = iter->pos;
	}
665 666 667 668 669 670 671 672

	return FALSE;
}

static void
xml_write_cols_rows (GnmOutputXML *state)
{
	closure_write_colrow closure;
673
	gsf_xml_out_start_element (state->output, GNM "Cols");
674 675 676 677
	xml_out_add_points (state->output, "DefaultSizePts",
		sheet_col_get_default_size_pts (state->sheet));
	closure.state = state;
	closure.is_column = TRUE;
678 679
	closure.prev = NULL;
	closure.prev_pos = -1;
680 681 682 683
	closure.rle_count = 0;
	colrow_foreach (&state->sheet->cols, 0, SHEET_MAX_COLS-1,
		(ColRowHandler)&xml_write_colrow_info, &closure);
	xml_write_colrow_info (NULL, &closure); /* flush */
684
	gsf_xml_out_end_element (state->output); /* </gnm:Cols> */
685

686
	gsf_xml_out_start_element (state->output, GNM "Rows");
687 688 689 690
	xml_out_add_points (state->output, "DefaultSizePts",
		sheet_row_get_default_size_pts (state->sheet));
	closure.state = state;
	closure.is_column = FALSE;
691 692
	closure.prev = NULL;
	closure.prev_pos = -1;
693 694 695 696
	closure.rle_count = 0;
	colrow_foreach (&state->sheet->rows, 0, SHEET_MAX_ROWS-1,
		(ColRowHandler)&xml_write_colrow_info, &closure);
	xml_write_colrow_info (NULL, &closure); /* flush */
697
	gsf_xml_out_end_element (state->output); /* </gnm:Rows> */
698 699
}

700 701 702
static void
xml_write_selection_info (GnmOutputXML *state)
{
703 704
	GSList *ptr, *copy;
	SheetView const *sv = sheet_get_view (state->sheet, state->wb_view);
Morten Welinder's avatar
Morten Welinder committed
705
	if (!sv) return;  /* Hidden.  */
706

707
	gsf_xml_out_start_element (state->output, GNM "Selections");
708 709 710 711
	gsf_xml_out_add_int (state->output, "CursorCol", sv->edit_pos_real.col);
	gsf_xml_out_add_int (state->output, "CursorRow", sv->edit_pos_real.row);

	/* Insert the selections in REVERSE order */
712 713
	copy = g_slist_copy (sv->selections);
	ptr = g_slist_reverse (copy);
714
	for (; ptr != NULL ; ptr = ptr->next) {
Jody Goldberg's avatar
Jody Goldberg committed
715
		GnmRange const *r = ptr->data;
716
		gsf_xml_out_start_element (state->output, GNM "Selection");
717
		xml_out_add_range (state->output, r);
718
		gsf_xml_out_end_element (state->output); /* </gnm:Selection> */
719
	}
720
	g_slist_free (copy);
721

722
	gsf_xml_out_end_element (state->output); /* </gnm:Selections> */
723 724 725
}

static void
726
xml_write_cell_and_position (GnmOutputXML *state,
727
			     GnmExprTop const *texpr, GnmValue const *val,
728
			     GnmParsePos const *pp)
729 730
{
	gboolean write_contents = TRUE;
731 732
	gboolean const is_shared_expr = (texpr != NULL) &&
		gnm_expr_top_is_shared (texpr);
733 734

	/* Only the top left corner of an array needs to be saved (>= 0.53) */
Morten Welinder's avatar
Morten Welinder committed
735
	if (texpr && gnm_expr_top_is_array_elem (texpr))
736 737
		return; /* DOM version would write <Cell Col= Row=/> */

738
	gsf_xml_out_start_element (state->output, GNM "Cell");
739
	gsf_xml_out_add_int (state->output, "Row", pp->eval.row);
740
	gsf_xml_out_add_int (state->output, "Col", pp->eval.col);
741 742 743

	/* As of version 0.53 we save the ID of shared expressions */
	if (is_shared_expr) {
744
		gpointer id = g_hash_table_lookup (state->expr_map, (gpointer) texpr);
745 746 747

		if (id == NULL) {
			id = GINT_TO_POINTER (g_hash_table_size (state->expr_map) + 1);
748
			g_hash_table_insert (state->expr_map, (gpointer)texpr, id);
749
		} else
750 751 752 753 754 755 756
			write_contents = FALSE;

		gsf_xml_out_add_int (state->output, "ExprID", GPOINTER_TO_INT (id));
	}

	/* As of version 0.53 we save the size of the array as attributes */
	/* As of version 0.57 the attributes are in the Cell not the Content */
Morten Welinder's avatar
Morten Welinder committed
757
	if (texpr && gnm_expr_top_get_array_corner (texpr)) {
758 759
		gsf_xml_out_add_int (state->output, "Rows", texpr->expr->array_corner.rows);
		gsf_xml_out_add_int (state->output, "Cols", texpr->expr->array_corner.cols);
760 761 762
	}

	if (write_contents) {
763 764 765
		GString *str = state->cell_str;

		g_string_truncate (str, 0);
766

767
		if (!texpr) {
768 769 770
			if (val != NULL) {
				gsf_xml_out_add_int (state->output, "ValueType", val->type);
				if (VALUE_FMT (val) != NULL) {
771
					const char *fmt = go_format_as_XL (VALUE_FMT (val));
772 773
					gsf_xml_out_add_cstr (state->output, "ValueFormat", fmt);
				}
774
				value_get_as_gstring (val, str, state->convs);
775 776
			} else {
				g_warning ("%s has no value ?", cellpos_as_string (&pp->eval));
777
			}
778
		} else {
779 780 781 782 783
			GnmConventionsOut out;
			out.accum = str;
			out.pp    = pp;
			out.convs = state->convs;

784
			g_string_append_c (str, '=');
785
			gnm_expr_top_as_gstring (texpr, &out);
786
		}
787 788 789

		gsf_xml_out_add_cstr (state->output, NULL, str->str);
	}
790
	gsf_xml_out_end_element (state->output); /* </gnm:Cell> */
791 792
}

793 794
static GnmValue *
cb_write_cell (GnmCellIter const *iter, GnmOutputXML *state)
795
{
796
	xml_write_cell_and_position (state,
797 798
		iter->cell->base.texpr, iter->cell->value, &iter->pp);
	return NULL;
799
}
800

801 802 803
static void
xml_write_cells (GnmOutputXML *state)
{
804
	gsf_xml_out_start_element (state->output, GNM "Cells");
805 806 807
	sheet_foreach_cell_in_range ((Sheet *)state->sheet, CELL_ITER_IGNORE_NONEXISTENT,
		0, 0, SHEET_MAX_COLS-1, SHEET_MAX_ROWS-1,
		(CellIterFunc) cb_write_cell, state);
808
	gsf_xml_out_end_element (state->output); /* </gnm:Cells> */
809 810 811 812 813
}

static void
xml_write_merged_regions (GnmOutputXML *state)
{
814 815 816
	GSList *ptr = state->sheet->list_merged;
	if (ptr == NULL)
		return;
817
	gsf_xml_out_start_element (state->output, GNM "MergedRegions");
818 819
	for (; ptr != NULL ; ptr = ptr->next)
		gsf_xml_out_simple_element (state->output,
820
			GNM "Merge", range_as_string (ptr->data));
821
	gsf_xml_out_end_element (state->output); /* </gnm:MergedRegions> */
822 823 824 825 826
}

static void
xml_write_sheet_layout (GnmOutputXML *state)
{
827
	SheetView const *sv = sheet_get_view (state->sheet, state->wb_view);
Morten Welinder's avatar
Morten Welinder committed
828
	if (!sv) return;  /* Hidden.  */
829

830
	gsf_xml_out_start_element (state->output, GNM "SheetLayout");
831
	gnm_xml_out_add_cellpos (state->output, "TopLeft", &sv->initial_top_left);
832 833

	if (sv_is_frozen (sv)) {
834
		gsf_xml_out_start_element (state->output, GNM "FreezePanes");
835 836
		gnm_xml_out_add_cellpos (state->output, "FrozenTopLeft", &sv->frozen_top_left);
		gnm_xml_out_add_cellpos (state->output, "UnfrozenTopLeft", &sv->unfrozen_top_left);
837
		gsf_xml_out_end_element (state->output); /* </gnm:FreezePanes> */
838
	}
839
	gsf_xml_out_end_element (state->output); /* </gnm:SheetLayout> */
840 841 842 843 844 845 846
}

static void
xml_write_filter_expr (GnmOutputXML *state,
		       GnmFilterCondition const *cond, unsigned i)
{
	static char const *filter_cond_name[] = { "eq", "gt", "lt", "gte", "lte", "ne" };
847 848 849 850
	/*
	 * WARNING WARNING WARING
	 * Value and ValueType are _reversed !!!
	 */
851 852 853 854 855 856
	static struct { char const *op, *valtype, *val; } filter_expr_attrs[] = {
		{ "Op0", "Value0", "ValueType0" },
		{ "Op1", "Value1", "ValueType1" }
	};

	GString *text = g_string_new (NULL);
857
	value_get_as_gstring (cond->value[i], text, state->convs);
858 859 860 861 862 863 864 865 866 867
	gsf_xml_out_add_cstr_unchecked (state->output,
		filter_expr_attrs[i].op, filter_cond_name [cond->op[i]]);
	gsf_xml_out_add_int (state->output,
		filter_expr_attrs[i].valtype, cond->value[i]->type);
	gsf_xml_out_add_cstr (state->output,
		filter_expr_attrs[i].val, text->str);
	g_string_free (text, TRUE);
}

static void
Jody Goldberg's avatar
Jody Goldberg committed
868
xml_write_filter_field (GnmOutputXML *state,
869 870
			GnmFilterCondition const *cond, unsigned i)
{
871
	gsf_xml_out_start_element (state->output, GNM "Field");
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
	gsf_xml_out_add_int (state->output, "Index", i);

	switch (GNM_FILTER_OP_TYPE_MASK & cond->op[0]) {
	case 0: gsf_xml_out_add_cstr_unchecked (state->output, "Type", "expr");
		xml_write_filter_expr (state, cond, 0);
		if (cond->op[1] != GNM_FILTER_UNUSED) {
			xml_write_filter_expr (state, cond, 1);
			gsf_xml_out_add_bool (state->output, "IsAnd", cond->is_and);
		}
		break;
	case GNM_FILTER_OP_BLANKS:
		gsf_xml_out_add_cstr_unchecked (state->output, "Type", "blanks");
		break;
	case GNM_FILTER_OP_NON_BLANKS:
		gsf_xml_out_add_cstr_unchecked (state->output, "Type", "nonblanks");
		break;
	case GNM_FILTER_OP_TOP_N:
		gsf_xml_out_add_cstr_unchecked (state->output, "Type", "bucket");
		gsf_xml_out_add_bool (state->output, "top",
			cond->op[0] & 1 ? TRUE : FALSE);
		gsf_xml_out_add_bool (state->output, "items",
			cond->op[0] & 2 ? TRUE : FALSE);
894
		gsf_xml_out_add_float (state->output, "count", cond->count, 4);
895 896 897
		break;
	}

898
	gsf_xml_out_end_element (state->output); /* </gnm:Field> */
899 900 901 902 903
}

static void
xml_write_sheet_filters (GnmOutputXML *state)
{
904 905 906 907 908 909 910 911
	GSList *ptr;
	GnmFilter *filter;
	GnmFilterCondition const *cond;
	unsigned i;

	if (state->sheet->filters == NULL)
		return;

912
	gsf_xml_out_start_element (state->output, GNM "Filters");
913 914 915

	for (ptr = state->sheet->filters; ptr != NULL ; ptr = ptr->next) {
		filter = ptr->data;
916
		gsf_xml_out_start_element (state->output, GNM "Filter");
917
		gsf_xml_out_add_cstr_unchecked (state->output, "Area",
918
			range_as_string (&filter->r));
919 920 921 922

		for (i = filter->fields->len ; i-- > 0 ; ) {
			cond = gnm_filter_get_condition (filter, i);
			if (cond != NULL && cond->op[0] != GNM_FILTER_UNUSED)
Jody Goldberg's avatar
Jody Goldberg committed
923
				xml_write_filter_field (state, cond, i);
924 925
		}

926
		gsf_xml_out_end_element (state->output); /* </gnm:Filter> */
927 928
	}

929
	gsf_xml_out_end_element (state->output); /* </gnm:Filters> */
930 931 932 933 934
}

static void
xml_write_solver (GnmOutputXML *state)
{
935 936 937 938 939 940 941 942
        SolverParameters *param = state->sheet->solver_parameters;
	SolverConstraint const *c;
	int type;
	GSList *ptr;

	if (param == NULL)
		return;

943
	gsf_xml_out_start_element (state->output, GNM "Solver");
944 945

	if (param->target_cell != NULL) {
946
		gsf_xml_out_add_int (state->output, "TargetCol",
947
			param->target_cell->pos.col);
948
		gsf_xml_out_add_int (state->output, "TargetRow",
949 950 951 952 953 954 955 956 957 958
			param->target_cell->pos.row);
	}

	gsf_xml_out_add_int (state->output, "ProblemType", param->problem_type);
	gsf_xml_out_add_cstr (state->output, "Inputs",
		param->input_entry_str);
	gsf_xml_out_add_int (state->output, "MaxTime",
		param->options.max_time_sec);
	gsf_xml_out_add_int (state->output, "MaxIter",
		param->options.max_iter);
959
	gsf_xml_out_add_bool (state->output, "NonNeg",
960
		param->options.assume_non_negative);
961
	gsf_xml_out_add_bool (state->output, "Discr",
962
		param->options.assume_discrete);
963
	gsf_xml_out_add_bool (state->output, "AutoScale",
964
		param->options.automatic_scaling);
965
	gsf_xml_out_add_bool (state->output, "ShowIter",
966
		param->options.show_iter_results);
967
	gsf_xml_out_add_bool (state->output, "AnswerR",
968
		param->options.answer_report);
969
	gsf_xml_out_add_bool (state->output, "SensitivityR",
970
		param->options.sensitivity_report);
971
	gsf_xml_out_add_bool (state->output, "LimitsR",
972
		param->options.limits_report);
973
	gsf_xml_out_add_bool (state->output, "PerformR",
974
		param->options.performance_report);
975
	gsf_xml_out_add_bool (state->output, "ProgramR",
976 977 978
		param->options.program_report);

	for (ptr = param->constraints; ptr != NULL ; ptr = ptr->next) {
979
		c = ptr->data;
980

981
		gsf_xml_out_start_element (state->output, GNM "Constr");
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
		gsf_xml_out_add_int (state->output, "Lcol", c->lhs.col);
		gsf_xml_out_add_int (state->output, "Lrow", c->lhs.row);
		gsf_xml_out_add_int (state->output, "Rcol", c->rhs.col);
		gsf_xml_out_add_int (state->output, "Rrow", c->rhs.row);
		gsf_xml_out_add_int (state->output, "Cols", c->cols);
		gsf_xml_out_add_int (state->output, "Rows", c->rows);

		switch (c->type) {
		default:	 type = 0;	break;
		case SolverLE:   type = 1;	break;
		case SolverGE:   type = 2;	break;
		case SolverEQ:   type = 4;	break;
		case SolverINT:  type = 8;	break;
		case SolverBOOL: type = 16;	break;
		}
		gsf_xml_out_add_int (state->output, "Type", type);
998
		gsf_xml_out_end_element (state->output); /* </gnm:Constr> */
999 1000
	}

1001
	gsf_xml_out_end_element (state->output); /* </gnm:Solver> */
1002 1003 1004 1005 1006
}

static void
xml_write_scenarios (GnmOutputXML *state)
{
1007 1008 1009 1010 1011
	GList   *ptr;

	if (state->sheet->scenarios == NULL)
		return;

1012
	gsf_xml_out_start_element (state->output, GNM "Scenarios");
1013 1014

	for (ptr = state->sheet->scenarios ; ptr != NULL ; ptr = ptr->next) {
1015
		scenario_t const *s = (scenario_t const *)ptr->data;
Morten Welinder's avatar
Morten Welinder committed
1016 1017 1018
#if 0
		int       i, cols, rows;
#endif
1019

1020
		gsf_xml_out_start_element (state->output, GNM "Scenario");
1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
		gsf_xml_out_add_cstr (state->output, "Name", s->name);
		gsf_xml_out_add_cstr (state->output, "Comment", s->comment);

		/* Scenario: changing cells in a string form.  In a string
		 * form so that we can in the future allow it to contain
		 * multiple ranges without modifing the file format.*/
		gsf_xml_out_add_cstr (state->output, "CellsStr", s->cell_sel_str);

#if 0 /* CRACK CRACK CRACK need something cleaner */
		/* Scenario: values. */
		rows = range_height (&s->range);
		cols = range_width (&s->range);
		for (i = 0; i < cols * rows; i++) {
Morten Welinder's avatar
Morten Welinder committed
1034
			GString  *name = g_string_new (NULL);
1035 1036 1037
			g_string_append_printf (name, "V%d", i);
			xml_node_set_value (scen, name->str,
					    s->changing_cells [i]);
Morten Welinder's avatar
Morten Welinder committed
1038
			g_string_free (name, TRUE);
1039 1040
		}
#endif
Morten Welinder's avatar
Morten Welinder committed
1041 1042

		gsf_xml_out_end_element (state->output); /* </gnm:Scenario> */
1043 1044
	}

1045
	gsf_xml_out_end_element (state->output); /* </gnm:Scenarios> */
1046 1047
}

1048
static void
1049
xml_write_objects (GnmOutputXML *state, GSList *objects)
1050 1051 1052 1053 1054 1055 1056
{
	gboolean needs_container = TRUE;
	SheetObject	 *so;
	SheetObjectClass *klass;
	char buffer[4*(DBL_DIG+10)];
	char const *type_name;
	char *tmp;
1057
	GSList *ptr;
1058

1059 1060 1061 1062
	/* reverse the list to maintain order when we prepend the objects in
	 * sheet_object_set_sheet on import */
	objects = g_slist_reverse ( g_slist_copy (objects));
	for (ptr = objects ;ptr != NULL ; ptr = ptr->next) {
1063 1064 1065 1066 1067 1068 1069
		so = ptr->data;
		klass = SHEET_OBJECT_CLASS (G_OBJECT_GET_CLASS (so));
		if (klass == NULL || klass->write_xml_sax == NULL)
			continue;

		if (needs_container) {
			needs_container = FALSE;
1070
			gsf_xml_out_start_element (state->output, GNM "Objects");
1071 1072 1073 1074 1075 1076 1077
		}

		/* A hook so that things can sometimes change names */
		type_name = klass->xml_export_name;
		if (type_name == NULL)
			type_name = G_OBJECT_TYPE_NAME (so);

1078
		tmp = g_strconcat (GNM, type_name, NULL);
1079
		gsf_xml_out_start_element (state->output, tmp);
1080
		gsf_xml_out_add_cstr (state->output, "ObjectBound", range_as_string (&so->anchor.cell_bound));
1081 1082 1083 1084 1085 1086
		snprintf (buffer, sizeof (buffer), "%.3g %.3g %.3g %.3g",
			  so->anchor.offset [0], so->anchor.offset [1],
			  so->anchor.offset [2], so->anchor.offset [3]);
		gsf_xml_out_add_cstr (state->output, "ObjectOffset", buffer);

		gsf_xml_out_add_int (state->output, "Direction",
1087
			so->anchor.base.direction);
1088 1089 1090

		(*klass->write_xml_sax) (so, state->output);

1091
		gsf_xml_out_end_element (state->output); /* </gnm:{typename}> */
1092 1093
		g_free (tmp);
	}
1094
	g_slist_free (objects);
1095 1096

	if (!needs_container)
1097
		gsf_xml_out_end_element (state->output); /* </gnm:Objects> */
1098 1099
}

1100 1101 1102 1103
static void
xml_write_sheet (GnmOutputXML *state, Sheet const *sheet)
{
	state->sheet = sheet;
1104
	gsf_xml_out_start_element (state->output, GNM "Sheet");
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121

	gsf_xml_out_add_bool (state->output,
		"DisplayFormulas",	sheet->display_formulas);
	gsf_xml_out_add_bool (state->output,
		"HideZero",		sheet->hide_zero);
	gsf_xml_out_add_bool (state->output,
		"HideGrid",		sheet->hide_grid);
	gsf_xml_out_add_bool (state->output,
		"HideColHeader",	sheet->hide_col_header);
	gsf_xml_out_add_bool (state->output,
		"HideRowHeader",	sheet->hide_row_header);
	gsf_xml_out_add_bool (state->output,
		"DisplayOutlines",	sheet->display_outlines);
	gsf_xml_out_add_bool (state->output,
		"OutlineSymbolsBelow",	sheet->outline_symbols_below);
	gsf_xml_out_add_bool (state->output,
		"OutlineSymbolsRight",	sheet->outline_symbols_right);
1122 1123 1124
	if (sheet->text_is_rtl)
		gsf_xml_out_add_bool (state->output,
			"RTL_Layout", sheet->text_is_rtl);
1125 1126 1127
	if (sheet->is_protected)
		gsf_xml_out_add_bool (state->output,
			"Protected", sheet->is_protected);
1128 1129 1130 1131 1132 1133

	/* TODO : Make this an enum internally eventually */
	if (sheet->convs->r1c1_addresses)
		gsf_xml_out_add_cstr_unchecked (state->output,
			"ExprConvention", "gnumeric:R1C1");

Morten Welinder's avatar
Morten Welinder committed
1134 1135
	gsf_xml_out_add_enum (state->output,
		"Visibility", GNM_SHEET_VISIBILITY_TYPE, sheet->visibility);
1136 1137

	if (sheet->tab_color != NULL)
1138
		gnm_xml_out_add_color (state->output, "TabColor", sheet->tab_color);
1139
	if (sheet->tab_text_color != NULL)
1140
		gnm_xml_out_add_color (state->output, "TabTextColor", sheet->tab_text_color);
1141 1142

	gsf_xml_out_simple_element (state->output,
1143
		GNM "Name", sheet->name_unquoted);
1144
	gsf_xml_out_simple_int_element (state->output,
1145
		GNM "MaxCol", sheet->cols.max_used);
1146
	gsf_xml_out_simple_int_element (state->output,
1147
		GNM "MaxRow", sheet->rows.max_used);
1148
	gsf_xml_out_simple_float_element (state->output,
1149
		GNM "Zoom", sheet->last_zoom_factor_used, 4);
1150 1151 1152 1153 1154

	xml_write_named_expressions (state, sheet->names);
	xml_write_print_info (state, sheet->print_info);
	xml_write_styles (state);
	xml_write_cols_rows (state);
1155
	xml_write_selection_info (state);
1156
	xml_write_objects (state, sheet->sheet_objects);
1157 1158 1159 1160 1161 1162 1163
	xml_write_cells (state);

	xml_write_merged_regions (state);
	xml_write_sheet_layout (state);
	xml_write_sheet_filters (state);
	xml_write_solver (state);
	xml_write_scenarios (state);
1164

1165
	gsf_xml_out_end_element (state->output); /* </gnm:Sheet> */
1166 1167 1168 1169 1170 1171 1172
	state->sheet = NULL;
}

static void
xml_write_sheets (GnmOutputXML *state)
{
	int i, n = workbook_sheet_count (state->wb);
1173
	gsf_xml_out_start_element (state->output, GNM "Sheets");
1174 1175
	for (i = 0 ; i < n ; i++)
		xml_write_sheet (state, workbook_sheet_by_index (state->wb, i));
1176
	gsf_xml_out_end_element (state->output); /* </gnm:Sheets> */
1177 1178
}

1179 1180 1181
static void
xml_write_uidata (GnmOutputXML *state)
{
1182
	gsf_xml_out_start_element (state->output, GNM "UIData");
1183 1184
	gsf_xml_out_add_int (state->output, "SelectedTab",
		wb_view_cur_sheet (state->wb_view)->index_in_wb);
1185
	gsf_xml_out_end_element (state->output); /* </gnm:UIData> */
1186 1187
}

1188
static void
1189 1190
xml_write_date_conventions_as_attr (GnmOutputXML *state,
				    GODateConventions const *conv)
1191
{
1192 1193 1194 1195
	if (conv->use_1904)
		gsf_xml_out_add_cstr_unchecked (state->output,
			GNM "DateConvention", "Apple:1904");
}
1196

1197 1198 1199
static void
xml_write_calculation (GnmOutputXML *state)
{
1200
	gsf_xml_out_start_element (state->output, GNM "Calculation");
1201
	gsf_xml_out_add_bool (state->output,
1202
		"ManualRecalc",		!state->wb->recalc_auto);
1203
	gsf_xml_out_add_bool (state->output,
1204
		"EnableIteration",	state->wb->iteration.enabled);
1205
	gsf_xml_out_add_int (state->output,
1206
		"MaxIterations",	state->wb->iteration.max_number);
1207
	gsf_xml_out_add_float (state->output,
1208
		"IterationTolerance",	state->wb->iteration.tolerance, -1);
1209 1210
	xml_write_date_conventions_as_attr (state,
					    workbook_date_conv (state->wb));
1211
	gsf_xml_out_end_element (state->output); /* </gnm:Calculation> */
1212 1213
}