xml-io.c 90.4 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>
8
 *   Jody Goldberg <jgoldberg@home.com>
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 <gnome-xml/parser.h>
#include <gnome-xml/parserInternals.h>
#include <gnome-xml/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 568 569 570 571 572 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 638 639 640 641 642 643 644 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
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;
				ParsePos pos, *pp;
				char *val, *tstr;
			
				pp = parse_pos_init (&pos, sheet->workbook, sheet, 0, 0);
				val = expr_tree_as_string (sci->u.expr.dep.expression, pp);
				
				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);
					if ((expr = expr_parse_string (s, pp, NULL, NULL)) == NULL)
						fprintf (stderr, "xml_read_style_condition: empty/invalid expression. condition damaged!\n");
					xmlFree (s);
				} else
					g_warning ("StyleConditionExpression without Expression!");
			
				sc = style_condition_new_expr (ctxt->sheet, op, expr);
				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
	xmlNodePtr  cur;
804
	char       *tstr;
805

806 807 808 809
#if 0  /* Don't return, We need to have a names node in the worksheet
	   * all the time becasue xml_search_child looks for a node down the
	   * tree and it will find the first "Names" node in the sheet #1
	   */
810 811
	if (!names)
		return NULL;
812
#endif
813 814 815 816 817

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

	while (names) {
		xmlNodePtr   tmp;
818
		NamedExpression    *expr_name = names->data;
819 820 821 822 823
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

		tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Name", NULL);
824 825 826
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, expr_name->name->str);
		xmlNewChild (tmp, ctxt->ns, "name", tstr);
		if (tstr) xmlFree (tstr);
827

828
		text = expr_name_as_string (expr_name, NULL);
829 830 831
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
		xmlNewChild (tmp, ctxt->ns, "value", tstr);
		if (tstr) xmlFree (tstr);
832 833 834 835 836
		g_free (text);

		xmlAddChild (cur, tmp);
		names = g_list_next (names);
	}
837

838 839 840 841
	return cur;
}

static void
842 843
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree,
		Workbook *wb, Sheet *sheet)
844
{
845 846 847 848 849
	xmlNode *id;
	xmlNode *expr;
	xmlNode *position;
	xmlNode *name = e_xml_get_child_by_name (tree, "Names");
	xmlChar *name_str, *expr_str;
850

851 852
	if (name == NULL)
		return;
853

854 855 856
	for (name = name->xmlChildrenNode; name ; name = name->next) {
		ParseError  perr;
		ParsePos    pp;
857

858 859
		if (name->name == NULL || strcmp (name->name, "Name"))
			continue;
860

861 862 863
		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");
864

865
		g_return_if_fail (id != NULL && expr != NULL);
866

867 868 869
		name_str = xmlNodeGetContent (id);
		expr_str = xmlNodeGetContent (expr);
		g_return_if_fail (name_str != NULL && expr_str != NULL);
870

871 872 873 874 875
		parse_pos_init (&pp, wb, sheet, 0, 0);
		if (position != NULL) {
			char *pos_txt = xmlNodeGetContent (position);
			if (pos_txt != NULL) {
				CellRef tmp;
876 877
				char const *res = cellref_a1_get (&tmp, pos_txt, &pp.eval);
				if (res != NULL && *res == '\0') {
878 879
					pp.eval.col = tmp.col;
					pp.eval.row = tmp.row;
880
				}
881
				xmlFree (pos_txt);
882 883
			}
		}
884 885 886 887 888 889 890

		if (!expr_name_create (&pp, name_str, expr_str, &perr))
			g_warning (perr.message);
		parse_error_free (&perr);

		xmlFree (name_str);
		xmlFree (expr_str);
891 892 893
	}
}

894
static xmlNodePtr
895
xml_write_summary (XmlParseContext *ctxt, SummaryInfo *summary_info)
896 897
{
	GList *items, *m;
898
	char *tstr;
899 900
	xmlNodePtr cur;

901
	if (!summary_info)
902 903
		return NULL;

904
	m = items = summary_info_as_list (summary_info);
905 906 907 908 909 910 911 912 913 914 915 916 917

	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);
918 919 920
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, sit->name);
			xmlNewChild (tmp, ctxt->ns, "name", tstr);
			if (tstr) xmlFree (tstr);
921 922 923

			if (sit->type == SUMMARY_INT) {
				text = g_strdup_printf ("%d", sit->v.i);
924 925 926
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-int", tstr);
				if (tstr) xmlFree (tstr);
927 928
			} else {
				text = summary_item_as_text (sit);
929 930 931
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-string", tstr);
				if (tstr) xmlFree (tstr);
932 933
			}
			g_free (text);
934
			xmlAddChild (cur, tmp);
935 936 937 938 939 940 941 942
		}
		items = g_list_next (items);
	}
	g_list_free (m);
	return cur;
}

static void
943
xml_read_summary (XmlParseContext *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
944
{
945
	xmlNodePtr child;
946

947 948 949
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);
	g_return_if_fail (summary_info != NULL);
950

951
	child = tree->xmlChildrenNode;
952
	while (child) {
953
		char *name = NULL;
Miguel de Icaza's avatar
Miguel de Icaza committed
954

955 956 957
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

958
			bits = child->xmlChildrenNode;
959 960
			while (bits) {
				SummaryItem *sit = NULL;
961

962
				if (!strcmp (bits->name, "name")) {
963
					name = xmlNodeGetContent (bits);
964 965 966 967 968
				} else {
					char *txt;
					g_return_if_fail (name);

					txt = xmlNodeGetContent (bits);
969 970 971 972
					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
973
							sit = summary_item_new_int (name, atoi (txt));
974

975
						if (sit)
976
							summary_info_add (summary_info, sit);
Daniel Veillard's avatar
Daniel Veillard committed
977
						xmlFree (txt);
978
					}
979
				}
980
				bits = bits->next;
981 982
			}
		}
Miguel de Icaza's avatar
Miguel de Icaza committed
983
		if (name){
984
			xmlFree (name);
Miguel de Icaza's avatar
Miguel de Icaza committed
985 986
			name = NULL;
		}
987 988 989 990
		child = child->next;
	}
}

Michael Meeks's avatar
Michael Meeks committed
991
static void
992
xml_node_set_print_hf (xmlNodePtr node, char const *name,
993
		       PrintHF const *hf)
Michael Meeks's avatar
Michael Meeks committed
994 995 996 997 998 999 1000
{
	xmlNodePtr  child;

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

	child = xmlNewChild (node, NULL, name, NULL);
1001 1002 1003
	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
1004 1005 1006
}

static void
1007
xml_node_get_print_hf (xmlNodePtr node, PrintHF *hf)
Michael Meeks's avatar
Michael Meeks committed
1008 1009
{
	char *txt;
1010

Michael Meeks's avatar
Michael Meeks committed
1011 1012 1013
	g_return_if_fail (hf != NULL);
	g_return_if_fail (node != NULL);

1014
	txt = xmlGetProp (node, "Left");
1015
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
1016 1017
		if (hf->left_format)
			g_free (hf->left_format);
1018 1019
		hf->left_format = g_strdup (txt);
		xmlFree (txt);
Arturo Espinosa's avatar
Arturo Espinosa committed
1020
	}
1021

1022
	txt = xmlGetProp (node, "Middle");
1023
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
1024 1025
		if (hf->middle_format)
			g_free (hf->middle_format);
1026 1027
		hf->middle_format = g_strdup (txt);
		xmlFree (txt);
Arturo Espinosa's avatar
Arturo Espinosa committed
1028
	}
Michael Meeks's avatar
Michael Meeks committed
1029

1030
	txt = xmlGetProp (node, "Right");
1031
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
1032 1033
		if (hf->right_format)
			g_free (hf->right_format);
1034 1035
		hf->right_format = g_strdup (txt);
		xmlFree (txt);
Arturo Espinosa's avatar
Arturo Espinosa committed
1036
	}
Michael Meeks's avatar
Michael Meeks committed
1037 1038
}

JP Rosevear's avatar