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

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

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

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

71 72
/* ------------------------------------------------------------------------- */

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

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

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

88 89
/* ------------------------------------------------------------------------- */

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

100 101 102 103 104
	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 ();
105

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

	return ctxt;
}

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

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

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

129 130
	g_free (ctxt);
}
131

132 133
/* ------------------------------------------------------------------------- */

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

138
static void
139
xml_arg_set (GtkArg *arg, const gchar *string)
140 141
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
142 143 144 145 146 147 148
	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
149
		if (!strcmp (string, "TRUE"))
JP Rosevear's avatar
JP Rosevear committed
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
			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;
175 176 177 178 179 180 181
	}
}

static char *
xml_arg_get (GtkArg *arg)
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
182 183 184 185 186 187 188 189 190 191 192 193 194 195
	case GTK_TYPE_CHAR:
		return g_strdup (&GTK_VALUE_CHAR (*arg));
	case GTK_TYPE_UCHAR:
		return g_strdup (&GTK_VALUE_UCHAR (*arg));
	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
196
		return g_strdup_printf("%li", GTK_VALUE_LONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
197
	case GTK_TYPE_ULONG:
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
198
		return g_strdup_printf("%lu", GTK_VALUE_ULONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
199 200 201 202 203 204
	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));
205 206 207 208
	}

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

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

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

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

	errno = 0; /* strto(ld) sets errno, but does not clear it.  */
	*val = strtol (buf, &end, 10);
239
	xmlFree (buf);
240

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

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

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

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

	errno = 0; /* strto(ld) sets errno, but does not clear it.  */
	*val = strtod (buf, &end);
264
	xmlFree (buf);
265

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

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

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

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

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

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
StyleColor *
xml_node_get_color (xmlNodePtr node, char const *name)
{
	StyleColor *res = NULL;
	xmlChar *color;
	int red, green, blue;

	color = xmlGetProp (node, name);
	if (color == NULL)
		return 0;
	if (sscanf (color, "%X:%X:%X", &red, &green, &blue) == 3)
		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);
}

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
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;
	res = parse_cell_name (buf, &val->col, &val->row, TRUE, &dummy);
	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));
}

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

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

	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;
	}

365
	child = xmlNewChild (node, NULL, name, NULL);
366

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

	tstr = xmlEncodeEntitiesReentrant (node->doc, txt);
370
	xml_node_set_cstr (child, "PrefUnit", tstr);
371
	if (tstr) xmlFree (tstr);
372 373 374
}

static void
375
xml_node_get_print_unit (xmlNodePtr node, PrintUnit * const pu)
376 377
{
	char       *txt;
378

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

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

397
static gboolean
398
xml_node_get_range (xmlNodePtr tree, Range *r)
399
{
400
	gboolean res =
401 402 403 404
	    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);
405

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

	return res;
410 411 412
}

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

417 418 419 420
	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);
421 422 423
}

static void
424
xml_read_selection_info (XmlParseContext *ctxt, xmlNodePtr tree)
425 426 427
{
	Range r;
	int row, col;
428
	Sheet *sheet = ctxt->sheet;
429
	xmlNodePtr sel, selections = e_xml_get_child_by_name (tree, "Selections");
430

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

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

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

static void
448
xml_write_selection_info (XmlParseContext *ctxt, Sheet const *sheet, xmlNodePtr tree)
449 450 451 452 453 454 455 456
{
	GList *ptr, *copy;
	tree = xmlNewChild (tree, ctxt->ns, "Selections", NULL);

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

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

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

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

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

498
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
499

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

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

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

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

Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
static xmlNodePtr
xml_write_style_condition_chain (XmlParseContext *ctxt, StyleCondition *sc)
{
	xmlNodePtr cur;
	StyleCondition *sci;
	
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleConditionChain", NULL);
	for (sci = sc; sci != NULL; sci = sci->next) {
		xmlNodePtr parent, ptr;

		parent = xmlNewChild (cur, ctxt->ns, "StyleCondition", NULL);
		xml_node_set_int (parent, "Type", sci->type);
		xml_node_set_int (parent, "NextOperator", sci->next_op);
		switch (sci->type) {
		case SCT_EXPR :
			ptr = xmlNewChild (parent, ctxt->ns, "Expr", NULL);
			xml_node_set_int (ptr, "Operator", sci->u.expr.op);

			if (sci->u.expr.dep.expression) {
				Sheet *sheet = sci->u.expr.dep.sheet;
568
				ParsePos pos;
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
569 570
				char *val, *tstr;
			
571 572
				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
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 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
				
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, val);
				xml_node_set_cstr (ptr, "Expression", val);
				if (tstr) xmlFree (tstr);
				g_free (val);
			}
			break;
		case SCT_CONSTRAINT :
			ptr = xmlNewChild (parent, ctxt->ns, "Constraint", NULL);
			xml_node_set_int (ptr, "Value", sci->u.constraint);
			break;
		case SCT_FLAGS :
			ptr = xmlNewChild (parent, ctxt->ns, "Flags", NULL);
			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);
638
					if ((expr = expr_parse_str_simple (s, pp)) == NULL)
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
639 640 641 642 643
						fprintf (stderr, "xml_read_style_condition: empty/invalid expression. condition damaged!\n");
					xmlFree (s);
				} else
					g_warning ("StyleConditionExpression without Expression!");
			
644
				sc = style_condition_new_expr (op, expr);
Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
645 646 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
				expr_tree_unref (expr);
			} 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;
}

694 695 696
/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
697
xmlNodePtr
698
xml_write_style (XmlParseContext *ctxt,
699
		 MStyle *style)
700
{
701
	xmlNodePtr  cur, child;
702
	char       *tstr;
703

704
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
705

706
	if (mstyle_is_element_set (style, MSTYLE_ALIGN_H))
707
		xml_node_set_int (cur, "HAlign", mstyle_get_align_h (style));
708
	if (mstyle_is_element_set (style, MSTYLE_ALIGN_V))
709
		xml_node_set_int (cur, "VAlign", mstyle_get_align_v (style));
710
	if (mstyle_is_element_set (style, MSTYLE_WRAP_TEXT))
711
		xml_node_set_int (cur, "WrapText", mstyle_get_wrap_text (style));
712
	if (mstyle_is_element_set (style, MSTYLE_ORIENTATION))
713
		xml_node_set_int (cur, "Orient", mstyle_get_orientation (style));
714
	if (mstyle_is_element_set (style, MSTYLE_PATTERN))
715
		xml_node_set_int (cur, "Shade", mstyle_get_pattern (style));
Jody Goldberg's avatar
Jody Goldberg committed
716
	if (mstyle_is_element_set (style, MSTYLE_INDENT))
717
		xml_node_set_int (cur, "Indent", mstyle_get_indent (style));
718 719 720 721
	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));
722 723

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

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

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

748 749 750 751
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, fontname);
		child = xmlNewChild (cur, ctxt->ns, "Font", tstr);
		if (tstr) xmlFree (tstr);

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

Almer S. Tigelaar's avatar
Almer S. Tigelaar committed
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
	if (mstyle_is_element_set (style, MSTYLE_VALIDATION)) {
		Validation *v = mstyle_get_validation (style);
		xmlNodePtr xsc;
		
		child = xmlNewChild (cur, ctxt->ns, "Validation", NULL);
		xml_node_set_int (child, "Style", v->vs);

		if (v->title) {
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, v->title->str);
			xml_node_set_cstr (child, "Title", tstr);
			if (tstr) xmlFree (tstr);
		}

		if (v->msg) {
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, v->msg->str);
			xml_node_set_cstr (child, "Message", tstr);
			if (tstr) xmlFree (tstr);
		}
		
		xsc = xml_write_style_condition_chain (ctxt, v->sc);
		if (xsc)
			xmlAddChild (child, xsc);
	}

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

	return cur;
798 799
}

800
static xmlNodePtr
801
xml_write_names (XmlParseContext *ctxt, GList *names)
802
{
803 804 805
	char *txt, *expr_str;
	xmlNodePtr  namesContainer, nameNode;
	NamedExpression const *nexpr;
806

807
	namesContainer = xmlNewDocNode (ctxt->doc, ctxt->ns, "Names", NULL);
808

809 810
	for (; names != NULL ; names = names->next) {
		nexpr = names->data;
811

812
		g_return_val_if_fail (nexpr != NULL, NULL);
813

814
		nameNode = xmlNewChild (namesContainer, ctxt->ns, "Name", NULL);
815

816 817 818
		txt = xmlEncodeEntitiesReentrant (ctxt->doc, nexpr->name->str);
		xmlNewChild (nameNode, ctxt->ns, "name", txt);
		if (txt) xmlFree (txt);
819

820 821 822 823 824
		expr_str = expr_name_as_string (nexpr, NULL);
		txt = xmlEncodeEntitiesReentrant (ctxt->doc, expr_str);
		xmlNewChild (nameNode, ctxt->ns, "value", txt);
		if (txt) xmlFree (txt);
		g_free (expr_str);
825

826 827
		xmlNewChild (nameNode, ctxt->ns, "position",
			cell_pos_name (&nexpr->pos.eval));
828
	}
829

830
	return namesContainer;
831 832 833
}

static void
834 835
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree,
		Workbook *wb, Sheet *sheet)
836
{
837 838 839 840 841
	xmlNode *id;
	xmlNode *expr;
	xmlNode *position;
	xmlNode *name = e_xml_get_child_by_name (tree, "Names");
	xmlChar *name_str, *expr_str;
842

843 844
	if (name == NULL)
		return;
845

846 847 848
	for (name = name->xmlChildrenNode; name ; name = name->next) {
		ParseError  perr;
		ParsePos    pp;
849

850 851
		if (name->name == NULL || strcmp (name->name, "Name"))
			continue;
852

853 854 855
		id = e_xml_get_child_by_name (name, "name");
		expr = e_xml_get_child_by_name (name, "value");
		position = e_xml_get_child_by_name (name, "position");
856

857
		g_return_if_fail (id != NULL && expr != NULL);
858

859 860 861
		name_str = xmlNodeGetContent (id);
		expr_str = xmlNodeGetContent (expr);
		g_return_if_fail (name_str != NULL && expr_str != NULL);
862

863 864 865 866 867
		parse_pos_init (&pp, wb, sheet, 0, 0);
		if (position != NULL) {
			char *pos_txt = xmlNodeGetContent (position);
			if (pos_txt != NULL) {
				CellRef tmp;
868 869
				char const *res = cellref_a1_get (&tmp, pos_txt, &pp.eval);
				if (res != NULL && *res == '\0') {
870 871
					pp.eval.col = tmp.col;
					pp.eval.row = tmp.row;
872
				}
873
				xmlFree (pos_txt);
874 875
			}
		}
876

877
		parse_error_init (&perr);
878 879 880 881 882 883
		if (!expr_name_create (&pp, name_str, expr_str, &perr))
			g_warning (perr.message);
		parse_error_free (&perr);

		xmlFree (name_str);
		xmlFree (expr_str);
884 885 886
	}
}

887
static xmlNodePtr
888
xml_write_summary (XmlParseContext *ctxt, SummaryInfo *summary_info)
889 890
{
	GList *items, *m;
891
	char *tstr;
892 893
	xmlNodePtr cur;

894
	if (!summary_info)
895 896
		return NULL;

897
	m = items = summary_info_as_list (summary_info);
898 899 900 901 902 903 904 905 906 907 908 909 910

	if (!items)
		return NULL;

	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Summary", NULL);

	while (items) {
		xmlNodePtr   tmp;
		SummaryItem *sit = items->data;
		if (sit) {
			char *text;

			tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Item", NULL);
911 912 913
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, sit->name);
			xmlNewChild (tmp, ctxt->ns, "name", tstr);
			if (tstr) xmlFree (tstr);
914 915 916

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

static void
936
xml_read_summary (XmlParseContext *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
937
{
938
	xmlNodePtr child;
939

940 941 942
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);
	g_return_if_fail (summary_info != NULL);
943

944
	child = tree->xmlChildrenNode;
945
	while (child) {
946
		char *name = NULL;
Miguel de Icaza's avatar
Miguel de Icaza committed
947

948 949 950
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

951
			bits = child->xmlChildrenNode;
952 953
			while (bits) {
				SummaryItem *sit = NULL;
954

955
				if (!strcmp (bits->name, "name")) {
956
					name = xmlNodeGetContent (bits);
957 958 959 960 961
				} else {
					char *txt;
					g_return_if_fail (name);

					txt = xmlNodeGetContent (bits);
962 963 964 965
					if (txt != NULL){
						if (!strcmp (bits->name, "val-string"))
							sit = summary_item_new_string (name, txt);
						else if (!strcmp (bits->name, "val-int"))
Miguel de Icaza's avatar
Miguel de Icaza committed
966
							sit = summary_item_new_int (name, atoi (txt));
967

968
						if (sit)
969
							summary_info_add (summary_info, sit);
Daniel Veillard's avatar
Daniel Veillard committed
970
						xmlFree (txt);
971
					}
972
				}
973
				bits = bits->next;
974 975
			}
		}
Miguel de Icaza's avatar
Miguel de Icaza committed
976
		if (name){
977
			xmlFree (name);
Miguel de Icaza's avatar
Miguel de Icaza committed
978 979
			name = NULL;
		}
980 981 982 983
		child = child->next;
	}
}

Michael Meeks's avatar
Michael Meeks committed
984
static void
985
xml_node_set_print_hf (xmlNodePtr node, char const *name,
986
		       PrintHF const *hf)
Michael Meeks's avatar
Michael Meeks committed
987 988 989 990 991 992 993
{
	xmlNodePtr  child;

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

	child = xmlNewChild (node, NULL, name, NULL);
994 995 996
	xml_node_set_cstr (child, "Left", hf->left_format);
	xml_node_set_cstr (child, "Middle", hf->middle_format);
	xml_node_set_cstr (child, "Right", hf->right_format);
Michael Meeks's avatar
Michael Meeks committed
997 998 999
}

static void
1000
xml_node_get_print_hf (xmlNodePtr node, PrintHF *hf)
Michael Meeks's avatar
Michael Meeks committed
1001 1002
{
	char *txt;
1003

Michael Meeks's avatar
Michael Meeks committed
1004 1005 1006
	g_return_if_fail (hf != NULL);
	g_return_if_fail (node != NULL);

1007
	txt = xmlGetProp (node, "Left");
1008
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
1009 1010
		if (hf->left_format)
			g_free (hf->left_format);
1011 1012
		hf->left_format = g_strdup (txt);
		xmlFree (txt);
Arturo Espinosa's avatar
Arturo Espinosa committed
1013
	}
1014

1015
	txt = xmlGetProp (node, "Middle");
1016
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
1017 1018
		if (hf->middle_format)
			g_free (hf->middl