xml-io.c 95.1 KB
Newer Older
1
/* vim: set sw=8: */
2 3 4
/*
 * xml-io.c: save/read gnumeric Sheets using an XML encoding.
 *
5 6 7
 * Authors:
 *   Daniel Veillard <Daniel.Veillard@w3.org>
 *   Miguel de Icaza <miguel@gnu.org>
Jody Goldberg's avatar
Jody Goldberg committed
8
 *   Jody Goldberg <jody@gnome.org>
9
 */
10 11
#include <gnumeric-config.h>
#include "gnumeric.h"
Jody Goldberg's avatar
Jody Goldberg committed
12
#include "xml-io.h"
13

14
#include "style-color.h"
15
#include "style-border.h"
16
#include "style.h"
Jody Goldberg's avatar
Jody Goldberg committed
17 18
#include "sheet.h"
#include "sheet-style.h"
19
#include "sheet-object.h"
20
#include "sheet-object-cell-comment.h"
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
21
#include "str.h"
22
#include "print-info.h"
23
#include "file.h"
24
#include "expr.h"
25
#include "expr-name.h"
26
#include "cell.h"
27
#include "value.h"
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
28
#include "validation.h"
29
#include "sheet-merge.h"
30 31 32
#include "io-context.h"
#include "command-context.h"
#include "workbook-control.h"
33
#include "workbook-view.h"
34
#include "workbook.h"
35
#include "selection.h"
36
#include "clipboard.h"
Jody Goldberg's avatar
Jody Goldberg committed
37
#include "format.h"
Jody Goldberg's avatar
Jody Goldberg committed
38
#include "ranges.h"
Chyla Zbigniew's avatar
Chyla Zbigniew committed
39
#include "file.h"
Jody Goldberg's avatar
Jody Goldberg committed
40
#include "str.h"
41
#include "plugin-util.h"
Jody Goldberg's avatar
Jody Goldberg committed
42

Jody Goldberg's avatar
Jody Goldberg committed
43 44 45
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/xmlmemory.h>
46 47
#include <sys/types.h>
#include <sys/stat.h>
48
#include <unistd.h>
49
#include <fcntl.h>
50
#include <errno.h>
51
#include <zlib.h>
52
#include <string.h>
Jody Goldberg's avatar
Jody Goldberg committed
53
#include <gal/util/e-xml-utils.h>
54
#include <gal/widgets/e-colors.h>
55 56
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
57
#include <libgnome/gnome-config.h>
Jody Goldberg's avatar
Jody Goldberg committed
58 59 60
#include <locale.h>
#include <math.h>
#include <limits.h>
61 62 63
#ifdef ENABLE_BONOBO
#include <bonobo/bonobo-exception.h>
#endif
64

65 66 67
/* Precision to use when saving point measures. */
#define POINT_SIZE_PRECISION 3

68
/* FIXME - tune the values below */
69 70
#define XML_INPUT_BUFFER_SIZE      4096
#define N_ELEMENTS_BETWEEN_UPDATES 20
71

72 73
/* ------------------------------------------------------------------------- */

74 75
static GnumFileOpener *xml_opener = NULL;
static GnumFileSaver  *xml_saver = NULL;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
76

77
GnumFileOpener *
78
gnumeric_xml_get_opener (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
79
{
80
	return xml_opener;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
81 82
}

83
GnumFileSaver *
84
gnumeric_xml_get_saver (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
85
{
86
	return xml_saver;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
87 88
}

89 90
/* ------------------------------------------------------------------------- */

91 92 93
XmlParseContext *
xml_parse_ctx_new_full (xmlDocPtr             doc,
			xmlNsPtr              ns,
Michael Meeks's avatar
Michael Meeks committed
94
			GnumericXMLVersion    version,
95 96 97 98 99 100
			XmlSheetObjectReadFn  read_fn,
			XmlSheetObjectWriteFn write_fn,
			gpointer              user_data)
{
	XmlParseContext *ctxt = g_new0 (XmlParseContext, 1);

101 102 103 104 105
	ctxt->version      = version;
	ctxt->doc          = doc;
	ctxt->ns           = ns;
	ctxt->expr_map     = g_hash_table_new (g_direct_hash, g_direct_equal);
	ctxt->shared_exprs = g_ptr_array_new ();
106

107 108 109
	ctxt->write_fn     = write_fn;
	ctxt->read_fn      = read_fn;
	ctxt->user_data    = user_data;
110 111 112 113 114 115 116 117

	return ctxt;
}

XmlParseContext *
xml_parse_ctx_new (xmlDocPtr doc,
		   xmlNsPtr  ns)
{
Michael Meeks's avatar
Michael Meeks committed
118
	return xml_parse_ctx_new_full (
Jody Goldberg's avatar
Jody Goldberg committed
119
		doc, ns, GNUM_XML_LATEST, NULL, NULL, NULL);
120
}
121

122 123 124 125 126 127
void
xml_parse_ctx_destroy (XmlParseContext *ctxt)
{
	g_return_if_fail (ctxt != NULL);

	g_hash_table_destroy (ctxt->expr_map);
128 129
	g_ptr_array_free (ctxt->shared_exprs, TRUE);

130 131
	g_free (ctxt);
}
132

133 134
/* ------------------------------------------------------------------------- */

135 136 137
/*
 * Internal stuff: xml helper functions.
 */
138

139
static void
140
xml_arg_set (GtkArg *arg, const gchar *string)
141 142
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
143 144 145 146 147 148 149
	case GTK_TYPE_CHAR:
		GTK_VALUE_CHAR (*arg) = string[0];
		break;
	case GTK_TYPE_UCHAR:
		GTK_VALUE_UCHAR (*arg) = string[0];
		break;
	case GTK_TYPE_BOOL:
Jody Goldberg's avatar
Jody Goldberg committed
150
		if (!strcmp (string, "TRUE"))
JP Rosevear's avatar
JP Rosevear committed
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
			GTK_VALUE_BOOL (*arg) = TRUE;
		else
			GTK_VALUE_BOOL (*arg) = FALSE;
		break;
	case GTK_TYPE_INT:
		GTK_VALUE_INT (*arg) = atoi (string);
		break;
	case GTK_TYPE_UINT:
		GTK_VALUE_UINT (*arg) = atoi (string);
		break;
	case GTK_TYPE_LONG:
		GTK_VALUE_LONG (*arg) = atol (string);
		break;
	case GTK_TYPE_ULONG:
		GTK_VALUE_ULONG (*arg) = atol (string);
		break;
	case GTK_TYPE_FLOAT:
		GTK_VALUE_FLOAT (*arg) = atof (string);
		break;
	case GTK_TYPE_DOUBLE:
		GTK_VALUE_DOUBLE (*arg) = atof (string);
		break;
	case GTK_TYPE_STRING:
		GTK_VALUE_STRING (*arg) = g_strdup (string);
		break;
176 177 178 179 180 181 182
	}
}

static char *
xml_arg_get (GtkArg *arg)
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
183 184 185
	case GTK_TYPE_CHAR:
		return g_strdup (&GTK_VALUE_CHAR (*arg));
	case GTK_TYPE_UCHAR:
Jody Goldberg's avatar
Jody Goldberg committed
186
		return g_strdup ((gchar *)&GTK_VALUE_UCHAR (*arg));
JP Rosevear's avatar
JP Rosevear committed
187 188 189 190 191 192 193 194 195 196
	case GTK_TYPE_BOOL:
		if (GTK_VALUE_BOOL (*arg))
			return g_strdup ("TRUE");
		else
			return g_strdup ("FALSE");
	case GTK_TYPE_INT:
		return g_strdup_printf("%i", GTK_VALUE_INT (*arg));
	case GTK_TYPE_UINT:
		return g_strdup_printf("%u", GTK_VALUE_UINT (*arg));
	case GTK_TYPE_LONG:
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
197
		return g_strdup_printf("%li", GTK_VALUE_LONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
198
	case GTK_TYPE_ULONG:
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
199
		return g_strdup_printf("%lu", GTK_VALUE_ULONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
200 201 202 203 204 205
	case GTK_TYPE_FLOAT:
		return g_strdup_printf("%f", GTK_VALUE_FLOAT (*arg));
	case GTK_TYPE_DOUBLE:
		return g_strdup_printf("%f", GTK_VALUE_DOUBLE (*arg));
	case GTK_TYPE_STRING:
		return g_strdup (GTK_VALUE_STRING (*arg));
206 207 208 209
	}

	return NULL;
}
Jody Goldberg's avatar
Jody Goldberg committed
210

211 212
/* Get an xmlChar * value for a node carried as an attibute
 * result must be xmlFree
213
 */
214
xmlChar *
215
xml_node_get_cstr (xmlNodePtr node, char const *name)
216
{
Jody Goldberg's avatar
Jody Goldberg committed
217
	return name ? xmlGetProp (node, (xmlChar *)name) : xmlNodeGetContent (node);
218 219
}
void
220
xml_node_set_cstr (xmlNodePtr node, char const *name, char const *val)
221
{
222
	if (name)
Jody Goldberg's avatar
Jody Goldberg committed
223
		xmlSetProp (node, (xmlChar *)name, (xmlChar *)val);
224
	else
Jody Goldberg's avatar
Jody Goldberg committed
225
		xmlNodeSetContent (node, (xmlChar *)val);
226 227
}

228
gboolean
229
xml_node_get_int (xmlNodePtr node, char const *name, int *val)
230
{
231
	xmlChar *buf;
232
	char *end;
233

234 235 236
	buf = xml_node_get_cstr (node, name);
	if (buf == NULL)
		return FALSE;
237 238

	errno = 0; /* strto(ld) sets errno, but does not clear it.  */
Jody Goldberg's avatar
Jody Goldberg committed
239
	*val = strtol ((char *)buf, &end, 10);
240
	xmlFree (buf);
241

242
	return ((char *)buf != end) && (errno != ERANGE);
243
}
244

245
void
246
xml_node_set_int (xmlNodePtr node, char const *name, int val)
247
{
248 249
	char str[4 * sizeof (int)];
	sprintf (str, "%d", val);
Jody Goldberg's avatar
Jody Goldberg committed
250
	xml_node_set_cstr (node, name, str);
251 252
}

253
gboolean
254
xml_node_get_double (xmlNodePtr node, char const *name, double *val)
255
{
256
	xmlChar *buf;
257
	char *end;
258

259 260
	buf = xml_node_get_cstr (node, name);
	if (buf == NULL)
261
		return FALSE;
262 263

	errno = 0; /* strto(ld) sets errno, but does not clear it.  */
Jody Goldberg's avatar
Jody Goldberg committed
264
	*val = strtod ((char *)buf, &end);
265
	xmlFree (buf);
266

267
	return ((char *)buf != end) && (errno != ERANGE);
268
}
269

270
void
271
xml_node_set_double (xmlNodePtr node, char const *name, double val,
272
		     int precision)
273
{
274
	char str[101 + DBL_DIG];
275

276 277
	if (precision < 0 || precision > DBL_DIG)
		precision = DBL_DIG;
278

279
	if (fabs (val) < 1e9 && fabs (val) > 1e-5)
280
		snprintf (str, 100 + DBL_DIG, "%.*g", precision, val);
281 282
	else
		snprintf (str, 100 + DBL_DIG, "%f", val);
283

Jody Goldberg's avatar
Jody Goldberg committed
284
	xml_node_set_cstr (node, name, str);
285 286
}

287 288 289 290 291 292 293
StyleColor *
xml_node_get_color (xmlNodePtr node, char const *name)
{
	StyleColor *res = NULL;
	xmlChar *color;
	int red, green, blue;

Jody Goldberg's avatar
Jody Goldberg committed
294
	color = xmlGetProp (node, (xmlChar *)name);
295 296
	if (color == NULL)
		return 0;
Jody Goldberg's avatar
Jody Goldberg committed
297
	if (sscanf ((char *)color, "%X:%X:%X", &red, &green, &blue) == 3)
298 299 300 301 302 303 304 305 306 307 308 309
		res = style_color_new (red, green, blue);
	xmlFree (color);
	return res;
}
void
xml_node_set_color (xmlNodePtr node, char const *name, StyleColor const *val)
{
	char str[4 * sizeof (val->color)];
	sprintf (str, "%X:%X:%X", val->color.red, val->color.green, val->color.blue);
	xml_node_set_cstr (node, name, str);
}

310 311 312 313 314 315 316 317 318 319
static gboolean
xml_node_get_cellpos (xmlNodePtr node, char const *name, CellPos *val)
{
	xmlChar *buf;
	int dummy;
	gboolean res;

	buf = xml_node_get_cstr (node, name);
	if (val == NULL)
		return FALSE;
Jody Goldberg's avatar
Jody Goldberg committed
320
	res = parse_cell_name ((char *)buf, &val->col, &val->row, TRUE, &dummy);
321 322 323 324 325 326 327 328 329 330
	xmlFree (buf);
	return res;
}

static void
xml_node_set_cellpos (xmlNodePtr node, char const *name, CellPos const *val)
{
	xml_node_set_cstr (node, name, cell_pos_name (val));
}

331 332 333 334
/*
 * Set a double value for a node with POINT_SIZE_PRECISION digits precision.
 */
static void
335
xml_node_set_points (xmlNodePtr node, char const *name, double val)
336
{
337
	xml_node_set_double (node, name, val, POINT_SIZE_PRECISION);
338 339
}

340
static void
341
xml_node_set_print_unit (xmlNodePtr node, char const *name,
342
			 PrintUnit const *pu)
343 344
{
	xmlNodePtr  child;
Jody Goldberg's avatar
Jody Goldberg committed
345
	char       *txt = "points";
Jody Goldberg's avatar
Jody Goldberg committed
346
	xmlChar       *tstr;
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365

	if (pu == NULL || name == NULL)
		return;

	switch (pu->desired_display) {
	case UNIT_POINTS:
		txt = "points";
		break;
	case UNIT_MILLIMETER:
		txt = "mm";
		break;
	case UNIT_CENTIMETER:
		txt = "cm";
		break;
	case UNIT_INCH:
		txt = "in";
		break;
	}

Jody Goldberg's avatar
Jody Goldberg committed
366
	child = xmlNewChild (node, NULL, (xmlChar *)name, NULL);
367

368
	xml_node_set_points (child, "Points", pu->points);
369

Jody Goldberg's avatar
Jody Goldberg committed
370 371
	tstr = xmlEncodeEntitiesReentrant (node->doc, (xmlChar *)txt);
	xml_node_set_cstr (child, "PrefUnit", (char *)tstr);
372
	if (tstr) xmlFree (tstr);
373 374 375
}

static void
376
xml_node_get_print_unit (xmlNodePtr node, PrintUnit * const pu)
377
{
Jody Goldberg's avatar
Jody Goldberg committed
378
	gchar       *txt;
379

380 381 382
	g_return_if_fail (pu != NULL);
	g_return_if_fail (node != NULL);

383
	xml_node_get_double (node, "Points", &pu->points);
Jody Goldberg's avatar
Jody Goldberg committed
384
	txt = (gchar *)xmlGetProp  (node, (xmlChar *)"PrefUnit");
385 386 387
	if (txt) {
		if (!g_strcasecmp (txt, "points"))
			pu->desired_display = UNIT_POINTS;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
388
		else if (!strcmp (txt, "mm"))
389
			pu->desired_display = UNIT_MILLIMETER;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
390
		else if (!strcmp (txt, "cm"))
391
			pu->desired_display = UNIT_CENTIMETER;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
392
		else if (!strcmp (txt, "in"))
393
			pu->desired_display = UNIT_INCH;
394
		xmlFree (txt);
395 396 397
	}
}

398
static gboolean
399
xml_node_get_range (xmlNodePtr tree, Range *r)
400
{
401
	gboolean res =
402 403 404 405
	    xml_node_get_int (tree, "startCol", &r->start.col) &&
	    xml_node_get_int (tree, "startRow", &r->start.row) &&
	    xml_node_get_int (tree, "endCol",   &r->end.col) &&
	    xml_node_get_int (tree, "endRow",   &r->end.row);
406

407 408
	/* Older versions of gnumeric had some boundary problems */
	range_ensure_sanity (r);
409 410

	return res;
411 412 413
}

static void
414
xml_node_set_range (xmlNodePtr tree, Range const *r)
415
{
Jody Goldberg's avatar
Jody Goldberg committed
416 417
	g_return_if_fail (range_is_sane (r));

418 419 420 421
	xml_node_set_int (tree, "startCol", r->start.col);
	xml_node_set_int (tree, "startRow", r->start.row);
	xml_node_set_int (tree, "endCol",   r->end.col);
	xml_node_set_int (tree, "endRow",   r->end.row);
422 423 424
}

static void
425
xml_read_selection_info (XmlParseContext *ctxt, xmlNodePtr tree)
426 427 428
{
	Range r;
	int row, col;
429
	Sheet *sheet = ctxt->sheet;
Jody Goldberg's avatar
Jody Goldberg committed
430
	xmlNodePtr sel, selections = e_xml_get_child_by_name (tree, (xmlChar *)"Selections");
431

432 433 434
	if (selections == NULL)
		return;

435
	sheet_selection_reset (sheet);
436
	for (sel = selections->xmlChildrenNode; sel; sel = sel->next)
437
		if (xml_node_get_range (sel, &r))
438 439 440 441
			sheet_selection_add_range (sheet,
						   r.start.col, r.start.row,
						   r.start.col, r.start.row,
						   r.end.col, r.end.row);
442

443 444
	if (xml_node_get_int (selections, "CursorCol", &col) &&
	    xml_node_get_int (selections, "CursorRow", &row))
445
		sheet_set_edit_pos (sheet, col, row);
446 447 448
}

static void
449
xml_write_selection_info (XmlParseContext *ctxt, Sheet const *sheet, xmlNodePtr tree)
450 451
{
	GList *ptr, *copy;
Jody Goldberg's avatar
Jody Goldberg committed
452
	tree = xmlNewChild (tree, ctxt->ns, (xmlChar *)"Selections", NULL);
453 454 455 456 457

	/* Insert the selections in REVERSE order */
	copy = g_list_copy (sheet->selections);
	ptr = g_list_reverse (copy);
	for (; ptr != NULL ; ptr = ptr->next) {
458
		Range const *r = ptr->data;
Jody Goldberg's avatar
Jody Goldberg committed
459
		xmlNodePtr child = xmlNewChild (tree, ctxt->ns, (xmlChar *)"Selection", NULL);
460
		xml_node_set_range (child, r);
461 462 463
	}
	g_list_free (copy);

464 465
	xml_node_set_int (tree, "CursorCol", sheet->edit_pos_real.col);
	xml_node_set_int (tree, "CursorRow", sheet->edit_pos_real.row);
466 467
}

468 469 470
/*
 * Create an XML subtree of doc equivalent to the given StyleBorder.
 */
471
static char const *StyleSideNames[6] =
472 473 474 475
{
 	"Top",
 	"Bottom",
 	"Left",
Michael Meeks's avatar
Michael Meeks committed
476 477 478
 	"Right",
	"Diagonal",
	"Rev-Diagonal"
Arturo Espinosa's avatar
Arturo Espinosa committed
479
};
480

481
static xmlNodePtr
482
xml_write_style_border (XmlParseContext *ctxt,
483
			const MStyle *style)
484 485 486
{
	xmlNodePtr cur;
	xmlNodePtr side;
487
	int        i;
488

489
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
490
		StyleBorder const *border;
491
		if (mstyle_is_element_set (style, i) &&
492
		    NULL != (border = mstyle_get_border (style, i))) {
Arturo Espinosa's avatar
Arturo Espinosa committed
493
			break;
494
		}
495
	}
496
	if (i > MSTYLE_BORDER_DIAGONAL)
Arturo Espinosa's avatar
Arturo Espinosa committed
497
		return NULL;
498

Jody Goldberg's avatar
Jody Goldberg committed
499
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, (xmlChar *)"StyleBorder", NULL);
500

501
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
502
		StyleBorder const *border;
Jody Goldberg's avatar
Jody Goldberg committed
503
		if (mstyle_is_element_set (style, i) &&
504
		    NULL != (border = mstyle_get_border (style, i))) {
Jody Goldberg's avatar
Jody Goldberg committed
505 506
			StyleBorderType t = border->line_type;
			StyleColor *col   = border->color;
507
 			side = xmlNewChild (cur, ctxt->ns,
Jody Goldberg's avatar
Jody Goldberg committed
508
					    (xmlChar *)(StyleSideNames [i - MSTYLE_BORDER_TOP]),
509
 					    NULL);
510
			xml_node_set_int (side, "Style", t);
511
			if (t != STYLE_BORDER_NONE)
512
				xml_node_set_color (side, "Color", col);
513
 		}
514 515
	}
	return cur;
516 517 518 519 520
}

/*
 * Create a StyleBorder equivalent to the XML subtree of doc.
 */
521
static void
522
xml_read_style_border (XmlParseContext *ctxt, xmlNodePtr tree, MStyle *mstyle)
523 524
{
	xmlNodePtr side;
525
	int        i;
526 527 528

	if (strcmp (tree->name, "StyleBorder")){
		fprintf (stderr,
529
			 "xml_read_style_border: invalid element type %s, 'StyleBorder' expected`\n",
530 531
			 tree->name);
	}
532

533
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
534
 		if ((side = e_xml_get_child_by_name (tree,
Jody Goldberg's avatar
Jody Goldberg committed
535
					      (xmlChar *)(StyleSideNames [i - MSTYLE_BORDER_TOP]))) != NULL) {
536 537
			int		 t;
			StyleColor      *color = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
538
			StyleBorder    *border;
539
			xml_node_get_int (side, "Style", &t);
540
			if (t != STYLE_BORDER_NONE)
541
				color = xml_node_get_color (side, "Color");
542
			border = style_border_fetch ((StyleBorderType)t, color,
Michael Meeks's avatar
Michael Meeks committed
543
						     style_border_get_orientation (i));
544
			mstyle_set_border (mstyle, i, border);
545
 		}
546
	}
547 548
}

Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
549 550 551 552 553 554
static xmlNodePtr
xml_write_style_condition_chain (XmlParseContext *ctxt, StyleCondition *sc)
{
	xmlNodePtr cur;
	StyleCondition *sci;
	
Jody Goldberg's avatar
Jody Goldberg committed
555
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, (xmlChar *)"StyleConditionChain", NULL);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
556 557 558
	for (sci = sc; sci != NULL; sci = sci->next) {
		xmlNodePtr parent, ptr;

Jody Goldberg's avatar
Jody Goldberg committed
559
		parent = xmlNewChild (cur, ctxt->ns, (xmlChar *)"StyleCondition", NULL);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
560 561 562 563
		xml_node_set_int (parent, "Type", sci->type);
		xml_node_set_int (parent, "NextOperator", sci->next_op);
		switch (sci->type) {
		case SCT_EXPR :
Jody Goldberg's avatar
Jody Goldberg committed
564
			ptr = xmlNewChild (parent, ctxt->ns, (xmlChar *)"Expr", NULL);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
565 566 567 568
			xml_node_set_int (ptr, "Operator", sci->u.expr.op);

			if (sci->u.expr.dep.expression) {
				Sheet *sheet = sci->u.expr.dep.sheet;
569
				ParsePos pos;
Jody Goldberg's avatar
Jody Goldberg committed
570 571
				char *val;
				xmlChar	*tstr;
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
572
			
573 574
				val = expr_tree_as_string (sci->u.expr.dep.expression,
					parse_pos_init (&pos, sheet->workbook, sheet, 0, 0));
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
575
				
Jody Goldberg's avatar
Jody Goldberg committed
576
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, (xmlChar *)val);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
577 578 579 580 581 582
				xml_node_set_cstr (ptr, "Expression", val);
				if (tstr) xmlFree (tstr);
				g_free (val);
			}
			break;
		case SCT_CONSTRAINT :
Jody Goldberg's avatar
Jody Goldberg committed
583
			ptr = xmlNewChild (parent, ctxt->ns, (xmlChar *)"Constraint", NULL);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
584 585 586
			xml_node_set_int (ptr, "Value", sci->u.constraint);
			break;
		case SCT_FLAGS :
Jody Goldberg's avatar
Jody Goldberg committed
587
			ptr = xmlNewChild (parent, ctxt->ns, (xmlChar *)"Flags", NULL);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639
			xml_node_set_int (ptr, "Value", sci->u.flags);
			break;
		default :
			g_warning ("Unknown StyleCondition Type");
		}
	}
	
	return cur;
}

static StyleCondition *
xml_read_style_condition_chain (XmlParseContext *ctxt, xmlNodePtr tree)
{
	StyleCondition *scf = NULL;
	StyleCondition *scl = NULL;
	xmlNodePtr parent;
	
	if (strcmp (tree->name, "StyleConditionChain")){
		fprintf (stderr,
			 "xml_read_style_condition: invalid element type %s, 'StyleConditionChain' expected`\n",
			 tree->name);
	}
	
	parent = tree->xmlChildrenNode;
	while (parent) {
		StyleConditionBool next_op = 0;
		StyleConditionType type = 0;
		StyleCondition *sc = NULL;
		xmlNodePtr child = parent->xmlChildrenNode;
		
		if (!parent->name || strcmp (parent->name, "StyleCondition"))
			fprintf (stderr,
				 "xml_read_style_condition: invalid element type %s, 'StyleCondition' expected`\n",
				 parent->name);

		xml_node_get_int (parent, "Type", (int*) &type);
		xml_node_get_int (parent, "NextOperator", (int*) &next_op);
		
		switch (type) {
		case SCT_EXPR :
			if (child && child->name && !strcmp (child->name, "Expr")) {
				ExprTree *expr = NULL;
				ParsePos pos, *pp;
				xmlChar *s;
				int op;
			
				xml_node_get_int (child, "Operator", &op);
				s = xml_node_get_cstr (child, "Expression");

				if (s) {
					pp = parse_pos_init (&pos, ctxt->wb,
							     ctxt->sheet, 0, 0);
Jody Goldberg's avatar
Jody Goldberg committed
640
					if ((expr = expr_parse_str_simple ((char *)s, pp)) == NULL)
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
641 642 643 644 645
						fprintf (stderr, "xml_read_style_condition: empty/invalid expression. condition damaged!\n");
					xmlFree (s);
				} else
					g_warning ("StyleConditionExpression without Expression!");
			
646
				sc = style_condition_new_expr (op, expr);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
			} else
				fprintf (stderr,
					 "xml_read_style_condition: invalid element type %s, 'Expr' expected`\n",
					 child->name);
			break;
		case SCT_CONSTRAINT :
			if (child && child->name && !strcmp (child->name, "Constraint")) {
				StyleConditionConstraint constraint;
				
				xml_node_get_int (child, "Value", (int *) &constraint);
				sc = style_condition_new_constraint (constraint);
			} else
				fprintf (stderr,
					 "xml_read_style_condition: invalid element type %s, 'Constraint' expected`\n",
					 child->name);
			break;
		case SCT_FLAGS :
			if (child && child->name && !strcmp (child->name, "Flags")) {
				StyleConditionFlags flags;
				
				xml_node_get_int (child, "Value", (int *) &flags);
				sc = style_condition_new_flags (flags);
			} else
				fprintf (stderr,
					 "xml_read_style_condition: invalid element type %s, 'Flags' expected`\n",
					 child->name);
			break;
		default :
			g_warning ("Unknown StyleCondition type");
		}

		if (!sc) {
			g_warning ("Broken StyleConditionChain!");
			break;
		}
		
		if (scl)
			style_condition_chain (scl, next_op, sc);
		else
			scf = sc;
		scl = sc;

		parent = parent->next;
	}
	
	return scf;
}

695 696 697
/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
698
xmlNodePtr
699
xml_write_style (XmlParseContext *ctxt,
700
		 MStyle *style)
701
{
702
	xmlNodePtr  cur, child;
Jody Goldberg's avatar
Jody Goldberg committed
703
	xmlChar       *tstr;
704

Jody Goldberg's avatar
Jody Goldberg committed
705
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, (xmlChar *)"Style", NULL);
706

707
	if (mstyle_is_element_set (style, MSTYLE_ALIGN_H))
708
		xml_node_set_int (cur, "HAlign", mstyle_get_align_h (style));
709
	if (mstyle_is_element_set (style, MSTYLE_ALIGN_V))
710
		xml_node_set_int (cur, "VAlign", mstyle_get_align_v (style));
711
	if (mstyle_is_element_set (style, MSTYLE_WRAP_TEXT))
712
		xml_node_set_int (cur, "WrapText", mstyle_get_wrap_text (style));
713
	if (mstyle_is_element_set (style, MSTYLE_ORIENTATION))
714
		xml_node_set_int (cur, "Orient", mstyle_get_orientation (style));
715
	if (mstyle_is_element_set (style, MSTYLE_PATTERN))
716
		xml_node_set_int (cur, "Shade", mstyle_get_pattern (style));
Jody Goldberg's avatar
Jody Goldberg committed
717
	if (mstyle_is_element_set (style, MSTYLE_INDENT))
718
		xml_node_set_int (cur, "Indent", mstyle_get_indent (style));
719 720 721 722
	if (mstyle_is_element_set (style, MSTYLE_CONTENT_LOCKED))
		xml_node_set_int (cur, "Locked", mstyle_get_content_locked (style));
	if (mstyle_is_element_set (style, MSTYLE_CONTENT_HIDDEN))
		xml_node_set_int (cur, "Hidden", mstyle_get_content_hidden (style));
723 724

	if (mstyle_is_element_set (style, MSTYLE_COLOR_FORE))
725
		xml_node_set_color (cur, "Fore", mstyle_get_color (style, MSTYLE_COLOR_FORE));
726
	if (mstyle_is_element_set (style, MSTYLE_COLOR_BACK))
727
		xml_node_set_color (cur, "Back", mstyle_get_color (style, MSTYLE_COLOR_BACK));
728
	if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN))
729
		xml_node_set_color (cur, "PatternColor", mstyle_get_color (style, MSTYLE_COLOR_PATTERN));
Jody Goldberg's avatar
Jody Goldberg committed
730 731
	if (mstyle_is_element_set (style, MSTYLE_FORMAT)) {
		char *fmt = style_format_as_XL (mstyle_get_format (style), FALSE);
732
		xml_node_set_cstr (cur, "Format", fmt);
Jody Goldberg's avatar
Jody Goldberg committed
733 734
		g_free (fmt);
	}
735 736

	if (mstyle_is_element_set (style, MSTYLE_FONT_NAME) ||
737
	    mstyle_is_element_set (style, MSTYLE_FONT_SIZE) ||
738 739
	    mstyle_is_element_set (style, MSTYLE_FONT_BOLD) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_ITALIC) ||
740 741
	    mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH)) {
742
		char const *fontname;
743 744 745 746 747

		if (mstyle_is_element_set (style, MSTYLE_FONT_NAME))
			fontname = mstyle_get_font_name (style);
		else /* backwards compatibility */
			fontname = "Helvetica";
748

Jody Goldberg's avatar
Jody Goldberg committed
749 750
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, (xmlChar *)fontname);
		child = xmlNewChild (cur, ctxt->ns, (xmlChar *)"Font", tstr);
751 752
		if (tstr) xmlFree (tstr);

753
		if (mstyle_is_element_set (style, MSTYLE_FONT_SIZE))
754
			xml_node_set_points (child, "Unit",
755 756
					      mstyle_get_font_size (style));
		if (mstyle_is_element_set (style, MSTYLE_FONT_BOLD))
757
			xml_node_set_int (child, "Bold",
758 759
					   mstyle_get_font_bold (style));
		if (mstyle_is_element_set (style, MSTYLE_FONT_ITALIC))
760
			xml_node_set_int (child, "Italic",
761
					   mstyle_get_font_italic (style));
Jody Goldberg's avatar
Jody Goldberg committed
762
		if (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE))
763
			xml_node_set_int (child, "Underline",
Jody Goldberg's avatar
Jody Goldberg committed
764
					   (int)mstyle_get_font_uline (style));
765
		if (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
766
			xml_node_set_int (child, "StrikeThrough",
767
					   mstyle_get_font_strike (style));
768 769
	}

Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
770 771 772 773
	if (mstyle_is_element_set (style, MSTYLE_VALIDATION)) {
		Validation *v = mstyle_get_validation (style);
		xmlNodePtr xsc;
		
Jody Goldberg's avatar
Jody Goldberg committed
774
		child = xmlNewChild (cur, ctxt->ns, (xmlChar *)"Validation", NULL);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
775 776 777
		xml_node_set_int (child, "Style", v->vs);

		if (v->title) {
Jody Goldberg's avatar
Jody Goldberg committed
778 779
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, (xmlChar *)(v->title->str));
			xml_node_set_cstr (child, "Title", (char *)tstr);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
780 781 782 783
			if (tstr) xmlFree (tstr);
		}

		if (v->msg) {
Jody Goldberg's avatar
Jody Goldberg committed
784 785
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, (xmlChar *)(v->msg->str));
			xml_node_set_cstr (child, "Message", (char *)tstr);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
786 787 788 789 790 791 792 793
			if (tstr) xmlFree (tstr);
		}
		
		xsc = xml_write_style_condition_chain (ctxt, v->sc);
		if (xsc)
			xmlAddChild (child, xsc);
	}

794 795 796
	child = xml_write_style_border (ctxt, style);
	if (child)
		xmlAddChild (cur, child);
797 798

	return cur;
799 800
}

801
static xmlNodePtr
802
xml_write_names (XmlParseContext *ctxt, GList *names)
803
{
Jody Goldberg's avatar
Jody Goldberg committed
804 805
	xmlChar *txt;
	char *expr_str;
806 807
	xmlNodePtr  namesContainer, nameNode;
	NamedExpression const *nexpr;
808

Jody Goldberg's avatar
Jody Goldberg committed
809
	namesContainer = xmlNewDocNode (ctxt->doc, ctxt->ns, (xmlChar *)"Names", NULL);
810

811 812
	for (; names != NULL ; names = names->next) {
		nexpr = names->data;
813

814
		g_return_val_if_fail (nexpr != NULL, NULL);
815

Jody Goldberg's avatar
Jody Goldberg committed
816
		nameNode = xmlNewChild (namesContainer, ctxt->ns, (xmlChar *)"Name", NULL);
817

Jody Goldberg's avatar
Jody Goldberg committed
818 819
		txt = xmlEncodeEntitiesReentrant (ctxt->doc, (xmlChar *)nexpr->name->str);
		xmlNewChild (nameNode, ctxt->ns, (xmlChar *)"name", txt);
820
		if (txt) xmlFree (txt);
821

822
		expr_str = expr_name_as_string (nexpr, NULL);
Jody Goldberg's avatar
Jody Goldberg committed
823 824
		txt = xmlEncodeEntitiesReentrant (ctxt->doc, (xmlChar *)expr_str);
		xmlNewChild (nameNode, ctxt->ns, (xmlChar *)"value", txt);
825 826
		if (txt) xmlFree (txt);
		g_free (expr_str);
827

Jody Goldberg's avatar
Jody Goldberg committed
828 829
		xmlNewChild (nameNode, ctxt->ns, (xmlChar *)"position",
			(xmlChar *)cell_pos_name (&nexpr->pos.eval));
830
	}
831

832
	return namesContainer;
833 834 835
}

static void
836 837
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree,
		Workbook *wb, Sheet *sheet)
838
{
839 840 841
	xmlNode *id;
	xmlNode *expr;
	xmlNode *position;
Jody Goldberg's avatar
Jody Goldberg committed
842 843 844
	xmlNode *name = e_xml_get_child_by_name (tree, (xmlChar *)"Names");
	xmlChar *name_str;
	char	*expr_str;
845

846 847
	if (name == NULL)
		return;
848

849 850 851
	for (name = name->xmlChildrenNode; name ; name = name->next) {
		ParseError  perr;
		ParsePos    pp;
852

853 854
		if (name->name == NULL || strcmp (name->name, "Name"))
			continue;
855

Jody Goldberg's avatar
Jody Goldberg committed
856 857 858
		id = e_xml_get_child_by_name (name, (xmlChar *)"name");
		expr = e_xml_get_child_by_name (name, (xmlChar *)"value");
		position = e_xml_get_child_by_name (name, (xmlChar *)"position");
859

860
		g_return_if_fail (id != NULL && expr != NULL);
861

862
		name_str = xmlNodeGetContent (id);
Jody Goldberg's avatar
Jody Goldberg committed
863
		expr_str = (char *)xmlNodeGetContent (expr);
864
		g_return_if_fail (name_str != NULL && expr_str != NULL);
865

866 867
		parse_pos_init (&pp, wb, sheet, 0, 0);
		if (position != NULL) {
Jody Goldberg's avatar
Jody Goldberg committed
868
			xmlChar *pos_txt = xmlNodeGetContent (position);
869 870
			if (pos_txt != NULL) {
				CellRef tmp;
Jody Goldberg's avatar
Jody Goldberg committed
871
				char const *res = cellref_a1_get (&tmp, (char *)pos_txt, &pp.eval);
872
				if (res != NULL && *res == '\0') {
873 874
					pp.eval.col = tmp.col;
					pp.eval.row = tmp.row;
875
				}
876
				xmlFree (pos_txt);
877 878
			}
		}
879

880
		parse_error_init (&perr);
Jody Goldberg's avatar
Jody Goldberg committed
881
		if (!expr_name_create (&pp, (char *)name_str, expr_str, &perr))
882 883 884 885 886
			g_warning (perr.message);
		parse_error_free (&perr);

		xmlFree (name_str);
		xmlFree (expr_str);
887 888 889
	}
}

890
static xmlNodePtr
891
xml_write_summary (XmlParseContext *ctxt, SummaryInfo *summary_info)
892 893
{
	GList *items, *m;
Jody Goldberg's avatar
Jody Goldberg committed
894
	xmlChar *tstr;
895 896
	xmlNodePtr cur;

897
	if (!summary_info)
898 899
		return NULL;

900
	m = items = summary_info_as_list (summary_info);
901 902 903 904

	if (!items)
		return NULL;

Jody Goldberg's avatar
Jody Goldberg committed
905
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, (xmlChar *)"Summary", NULL);
906 907 908 909 910

	while (items) {
		xmlNodePtr   tmp;
		SummaryItem *sit = items->data;
		if (sit) {
Jody Goldberg's avatar
Jody Goldberg committed
911
			xmlChar *text;
912

Jody Goldberg's avatar
Jody Goldberg committed
913 914 915
			tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, (xmlChar *)"Item", NULL);
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, (xmlChar *)sit->name);
			xmlNewChild (tmp, ctxt->ns, (xmlChar *)"name", tstr);
916
			if (tstr) xmlFree (tstr);
917 918

			if (sit->type == SUMMARY_INT) {
Jody Goldberg's avatar
Jody Goldberg committed
919
				text = (xmlChar *)g_strdup_printf ("%d", sit->v.i);
920
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
Jody Goldberg's avatar
Jody Goldberg committed
921
				xmlNewChild (tmp, ctxt->ns, (xmlChar *)"val-int", tstr);
922
				if (tstr) xmlFree (tstr);
923
			} else {
Jody Goldberg's avatar
Jody Goldberg committed
924
				text = (xmlChar *)summary_item_as_text (sit);
925
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
Jody Goldberg's avatar
Jody Goldberg committed
926
				xmlNewChild (tmp, ctxt->ns, (xmlChar *)"val-string", tstr);
927
				if (tstr) xmlFree (tstr);
928 929
			}
			g_free (text);
930
			xmlAddChild (cur, tmp);
931 932 933 934 935 936 937 938
		}
		items = g_list_next (items);
	}
	g_list_free (m);
	return cur;
}

static void
939
xml_read_summary (XmlParseContext *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
940
{
941
	xmlNodePtr child;
942