xml-io.c 80.2 KB
Newer Older
1 2 3
/*
 * xml-io.c: save/read gnumeric Sheets using an XML encoding.
 *
4 5 6
 * Authors:
 *   Daniel Veillard <Daniel.Veillard@w3.org>
 *   Miguel de Icaza <miguel@gnu.org>
7
 *   Jody Goldberg <jgoldberg@home.com>
8 9
 */
#include <config.h>
10
#include <gal/util/e-xml-utils.h>
11
#include <gnome.h>
12
#include <locale.h>
13
#include <math.h>
14
#include <limits.h>
15
#include "gnumeric.h"
16
#include "gnome-xml/parser.h"
Daniel Veillard's avatar
Daniel Veillard committed
17
#include "gnome-xml/parserInternals.h"
18
#include "gnome-xml/xmlmemory.h"
19
#include "style-color.h"
20
#include "style-border.h"
21
#include "style.h"
Jody Goldberg's avatar
Jody Goldberg committed
22 23
#include "sheet.h"
#include "sheet-style.h"
24
#include "sheet-object.h"
25
#include "sheet-object-cell-comment.h"
26
#include "print-info.h"
27
#include "xml-io.h"
28
#include "file.h"
29
#include "expr.h"
30
#include "expr-name.h"
31
#include "cell.h"
32
#include "sheet-merge.h"
33 34 35
#include "io-context.h"
#include "command-context.h"
#include "workbook-control.h"
36
#include "workbook-view.h"
37
#include "workbook.h"
38
#include "selection.h"
39
#include "clipboard.h"
Jody Goldberg's avatar
Jody Goldberg committed
40
#include "format.h"
Jody Goldberg's avatar
Jody Goldberg committed
41
#include "ranges.h"
Chyla Zbigniew's avatar
Chyla Zbigniew committed
42
#include "file.h"
43

44 45 46
/* Precision to use when saving point measures. */
#define POINT_SIZE_PRECISION 3

Chyla Zbigniew's avatar
Chyla Zbigniew committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
static FileOpenerId xml_opener_id = FILE_OPENER_ID_INVAID;
static FileSaverId xml_saver_id = FILE_SAVER_ID_INVAID;

FileOpenerId
gnumeric_xml_get_opener_id (void)
{
	return xml_opener_id;
}

FileSaverId
gnumeric_xml_get_saver_id (void)
{
	return xml_saver_id;
}

62 63 64
XmlParseContext *
xml_parse_ctx_new_full (xmlDocPtr             doc,
			xmlNsPtr              ns,
Michael Meeks's avatar
Michael Meeks committed
65
			GnumericXMLVersion    version,
66 67 68 69 70 71
			XmlSheetObjectReadFn  read_fn,
			XmlSheetObjectWriteFn write_fn,
			gpointer              user_data)
{
	XmlParseContext *ctxt = g_new0 (XmlParseContext, 1);

Michael Meeks's avatar
Michael Meeks committed
72
	ctxt->version   = version;
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
	ctxt->doc       = doc;
	ctxt->ns        = ns;
	ctxt->expr_map  = g_hash_table_new (g_direct_hash, g_direct_equal);

	ctxt->write_fn  = write_fn;
	ctxt->read_fn   = read_fn;
	ctxt->user_data = user_data;

	return ctxt;
}

XmlParseContext *
xml_parse_ctx_new (xmlDocPtr doc,
		   xmlNsPtr  ns)
{
Michael Meeks's avatar
Michael Meeks committed
88
	return xml_parse_ctx_new_full (
Jody Goldberg's avatar
Jody Goldberg committed
89
		doc, ns, GNUM_XML_V6, NULL, NULL, NULL);
90
}
91

92 93 94 95 96 97 98 99
void
xml_parse_ctx_destroy (XmlParseContext *ctxt)
{
	g_return_if_fail (ctxt != NULL);

	g_hash_table_destroy (ctxt->expr_map);
	g_free (ctxt);
}
100

101 102 103
/*
 * Internal stuff: xml helper functions.
 */
104

105 106 107 108
static void
xml_arg_set (GtkArg *arg, gchar *string)
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
109 110 111 112 113 114 115
	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
116
		if (!strcmp (string, "TRUE"))
JP Rosevear's avatar
JP Rosevear committed
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
			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;
142 143 144 145 146 147 148
	}
}

static char *
xml_arg_get (GtkArg *arg)
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
149 150 151 152 153 154 155 156 157 158 159 160 161 162
	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
163
		return g_strdup_printf("%li", GTK_VALUE_LONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
164
	case GTK_TYPE_ULONG:
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
165
		return g_strdup_printf("%lu", GTK_VALUE_ULONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
166 167 168 169 170 171
	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));
172 173 174 175
	}

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

177 178 179
/*
 * Get a value for a node either carried as an attibute or as
 * the content of a child.
180 181
 *
 * Returns a g_malloc'ed string.  Caller must free.
182
 */
183
static char *
184
xml_value_get (xmlNodePtr node, const char *name)
185
{
186
	char *ret, *val;
187 188
	xmlNodePtr child;

189 190 191 192
	val = (char *) xmlGetProp (node, name);
	if (val != NULL) {
		ret = g_strdup (val);
		xmlFree (val);
Morten Welinder's avatar
Morten Welinder committed
193
		return ret;
194
	}
195
	child = node->xmlChildrenNode;
196 197

	while (child != NULL) {
198 199 200 201
		if (!strcmp (child->name, name)) {
		        /*
			 * !!! Inefficient, but ...
			 */
202 203 204 205 206 207
			val = xmlNodeGetContent(child);
			if (val != NULL) {
				ret = g_strdup (val);
				xmlFree (val);
				return ret;
			}
208
		}
209 210 211 212
		child = child->next;
	}

	return NULL;
213 214
}

215 216 217 218
/*
 * Get a String value for a node either carried as an attibute or as
 * the content of a child.
 */
219
String *
220
xml_get_value_string (xmlNodePtr node, const char *name)
221
{
222
	char *val;
223 224
	String *ret;

225 226 227 228 229
	val = xml_value_get (node, name);
	if (val == NULL) return NULL;
        ret = string_get (val);
	g_free (val);
	return ret;
230 231
}

232 233 234 235
/*
 * Get an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
236
gboolean
237
xml_get_value_int (xmlNodePtr node, const char *name, int *val)
238
{
239
	char *ret;
240
	int i;
241
	int res;
242

243
	ret = xml_value_get (node, name);
244
	if (ret == NULL) return 0;
245
	res = sscanf (ret, "%d", &i);
246 247
	g_free (ret);

248 249
	if (res == 1) {
	        *val = i;
250
		return TRUE;
251
	}
252
	return FALSE;
253 254
}

255
#if 0
256 257 258 259
/*
 * Get a float value for a node either carried as an attibute or as
 * the content of a child.
 */
260
static int
261
xml_get_value_float (xmlNodePtr node, const char *name, float *val)
262
{
263 264
	int res;
	char *ret;
265 266
	float f;

267
	ret = xml_value_get (node, name);
268
	if (ret == NULL) return 0;
269
	res = sscanf (ret, "%f", &f);
270 271
	g_free (ret);

272 273
	if (res == 1) {
	        *val = f;
274 275 276
		return 1;
	}
	return 0;
277
}
278
#endif
279 280 281 282 283

/*
 * Get a double value for a node either carried as an attibute or as
 * the content of a child.
 */
284
static int
285
xml_get_value_double (xmlNodePtr node, const char *name, double *val)
286
{
287 288
	int res;
	char *ret;
289

290
	ret = xml_value_get (node, name);
291
	if (ret == NULL) return 0;
292
	res = sscanf (ret, "%lf", val);
293
	g_free (ret);
294

295
	return (res == 1);
296 297 298 299 300 301
}

/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
302 303
void
xml_set_value_cstr (xmlNodePtr node, const char *name, const char *val)
304
{
305
	char *ret;
306 307 308 309
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
	if (ret != NULL){
310
		xmlFree (ret);
311 312 313
		xmlSetProp (node, name, val);
		return;
	}
314
	child = node->xmlChildrenNode;
315 316 317 318 319 320 321 322
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, val);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, val);
323 324
}

325 326 327 328
/*
 * Set a String value for a node either carried as an attibute or as
 * the content of a child.
 */
329
void
330
xml_set_value_string (xmlNodePtr node, const char *name, const String *val)
331
{
332
	char *ret;
333 334 335
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
336
	if (ret != NULL) {
337
		xmlFree (ret);
338 339 340
		xmlSetProp (node, name, val->str);
		return;
	}
341
	child = node->xmlChildrenNode;
342 343 344 345 346 347 348 349
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, val->str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, val->str);
350 351
}

352 353 354 355
/*
 * Set an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
356
void
357
xml_set_value_int (xmlNodePtr node, const char *name, int val)
358
{
359
	char *ret;
360
	xmlNodePtr child;
361
	char str[4 * sizeof (int)];
362

363
	sprintf (str, "%d", val);
364 365
	ret = xmlGetProp (node, name);
	if (ret != NULL){
366
		xmlFree (ret);
367 368 369
		xmlSetProp (node, name, str);
		return;
	}
370
	child = node->xmlChildrenNode;
371 372 373 374 375 376 377 378
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
379 380 381 382 383 384
}

/*
 * Set a double value for a node either carried as an attibute or as
 * the content of a child.
 */
385
static void
386 387
xml_set_value_double (xmlNodePtr node, const char *name, double val,
		      int precision)
388
{
389
	char *ret;
390
	xmlNodePtr child;
391
	char str[101 + DBL_DIG];
392

393 394
	if (precision < 0 || precision > DBL_DIG)
		precision = DBL_DIG;
395

396
	if (fabs (val) < 1e9 && fabs (val) > 1e-5)
397
		snprintf (str, 100 + DBL_DIG, "%.*g", precision, val);
398 399
	else
		snprintf (str, 100 + DBL_DIG, "%f", val);
400

401 402
	ret = xmlGetProp (node, name);
	if (ret != NULL){
403
		xmlFree (ret);
404 405 406
		xmlSetProp (node, name, str);
		return;
	}
407
	child = node->xmlChildrenNode;
408 409 410 411 412 413 414 415
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
416 417
}

418 419 420 421 422 423 424 425 426
/*
 * Set a double value for a node with POINT_SIZE_PRECISION digits precision.
 */
static void
xml_set_value_points (xmlNodePtr node, const char *name, double val)
{
	xml_set_value_double (node, name, val, POINT_SIZE_PRECISION);
}

427 428 429 430 431
static void
xml_set_print_unit (xmlNodePtr node, const char *name,
		    const PrintUnit * const pu)
{
	xmlNodePtr  child;
Jody Goldberg's avatar
Jody Goldberg committed
432
	char       *txt = "points";
433
	char       *tstr;
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452

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

453
	child = xmlNewChild (node, NULL, name, NULL);
454

455
	xml_set_value_points (child, "Points", pu->points);
456 457

	tstr = xmlEncodeEntitiesReentrant (node->doc, txt);
458
	xml_set_value_cstr (child, "PrefUnit", tstr);
459
	if (tstr) xmlFree (tstr);
460 461 462 463 464 465
}

static void
xml_get_print_unit (xmlNodePtr node, PrintUnit * const pu)
{
	char       *txt;
466

467 468 469 470 471 472 473 474 475 476 477 478 479 480
	g_return_if_fail (pu != NULL);
	g_return_if_fail (node != NULL);

	xml_get_value_double (node, "Points", &pu->points);
	txt = xml_value_get  (node, "PrefUnit");
	if (txt) {
		if (!g_strcasecmp (txt, "points"))
			pu->desired_display = UNIT_POINTS;
		else if (!g_strcasecmp (txt, "mm"))
			pu->desired_display = UNIT_MILLIMETER;
		else if (!g_strcasecmp (txt, "cm"))
			pu->desired_display = UNIT_CENTIMETER;
		else if (!g_strcasecmp (txt, "in"))
			pu->desired_display = UNIT_INCH;
481
		g_free (txt);
482 483 484
	}
}

485
static gboolean
486
xml_read_range (xmlNodePtr tree, Range *r)
487
{
488 489 490 491 492 493 494 495 496 497 498 499
	gboolean res =
	    xml_get_value_int (tree, "startCol", &r->start.col) &&
	    xml_get_value_int (tree, "startRow", &r->start.row) &&
	    xml_get_value_int (tree, "endCol",   &r->end.col) &&
	    xml_get_value_int (tree, "endRow",   &r->end.row);

	/* Add some silent sanity checking to cleanup problems
	 * with older versions of gnumeric that had some boundary problems
	 */
	range_check_sanity (r);

	return res;
500 501 502
}

static void
503
xml_write_range (xmlNodePtr tree, const Range *value)
504 505 506 507 508 509 510 511
{
	xml_set_value_int (tree, "startCol", value->start.col);
	xml_set_value_int (tree, "startRow", value->start.row);
	xml_set_value_int (tree, "endCol",   value->end.col);
	xml_set_value_int (tree, "endRow",   value->end.row);
}

static void
512
xml_read_selection_info (XmlParseContext *ctxt, Sheet *sheet, xmlNodePtr tree)
513 514 515
{
	Range r;
	int row, col;
516
	xmlNodePtr sel, selections = e_xml_get_child_by_name (tree, "Selections");
517 518 519
	if (selections == NULL)
		return;

520
	sheet_selection_reset (sheet);
521
	for (sel = selections->xmlChildrenNode; sel; sel = sel->next) {
522
		if (xml_read_range (sel, &r))
523 524 525 526
			sheet_selection_add_range (sheet,
						   r.start.col, r.start.row,
						   r.start.col, r.start.row,
						   r.end.col, r.end.row);
527 528 529 530 531 532 533 534
	}

	if (xml_get_value_int (tree, "CursorCol", &col) &&
	    xml_get_value_int (tree, "CursorRow", &row))
		sheet_cursor_set (sheet, col, row, col, row, col, row);
}

static void
535
xml_write_selection_info (XmlParseContext *ctxt, Sheet *sheet, xmlNodePtr tree)
536 537 538 539 540 541 542 543
{
	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) {
544
		Range const *r = ptr->data;
545
		xmlNodePtr child = xmlNewChild (tree, ctxt->ns, "Selection", NULL);
546
		xml_write_range (child, r);
547 548 549
	}
	g_list_free (copy);

550 551
	xml_set_value_int (tree, "CursorCol", sheet->edit_pos.col);
	xml_set_value_int (tree, "CursorRow", sheet->edit_pos.row);
552 553
}

554 555 556 557 558 559 560 561
/*
 * Get a color value for a node either carried as an attibute or as
 * the content of a child.
 *
 * TODO PBM: at parse time one doesn't have yet a widget, so we have
 *           to retrieve the default colormap, but this may be a bad
 *           option ...
 */
562
static int
563
xml_get_color_value (xmlNodePtr node, const char *name, StyleColor **color)
564
{
565
	char *ret;
566 567
	int red, green, blue;

568
	ret = xml_value_get (node, name);
569
	if (ret == NULL) return 0;
570
	if (sscanf (ret, "%X:%X:%X", &red, &green, &blue) == 3){
571
		*color = style_color_new (red, green, blue);
572
		g_free (ret);
573 574
		return 1;
	}
575
	g_free (ret);
576
	return 0;
577 578 579 580 581 582
}

/*
 * Set a color value for a node either carried as an attibute or as
 * the content of a child.
 */
583 584
static void
xml_set_color_value (xmlNodePtr node, const char *name, StyleColor *val)
585
{
586
	char *ret;
587
	xmlNodePtr child;
588
	char str[4 * sizeof (val->color)];
589

590
	sprintf (str, "%X:%X:%X", val->color.red, val->color.green, val->color.blue);
591 592
	ret = xmlGetProp (node, name);
	if (ret != NULL){
593
		xmlFree (ret);
594 595 596
		xmlSetProp (node, name, str);
		return;
	}
597
	child = node->xmlChildrenNode;
598 599 600 601 602 603 604 605
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
606
}
607 608 609 610 611

/**
 **
 ** Private functions : mapping between in-memory structure and XML tree
 **
612
 **/
613
#if 0
614 615
static int
style_is_default_fore (StyleColor *color)
616
{
617 618
	if (!color)
		return TRUE;
619

620 621 622 623
	if (color->color.red == 0 && color->color.green == 0 && color->color.blue == 0)
		return TRUE;
	else
		return FALSE;
624 625
}

626 627
static int
style_is_default_back (StyleColor *color)
628
{
629 630
	if (!color)
		return TRUE;
631

632 633 634 635
	if (color->color.red == 0xffff && color->color.green == 0xffff && color->color.blue == 0xffff)
		return TRUE;
	else
		return FALSE;
636
}
637
#endif
638 639 640 641

/*
 * Create an XML subtree of doc equivalent to the given StyleBorder.
 */
642

Michael Meeks's avatar
Michael Meeks committed
643
static char *StyleSideNames[6] =
644 645 646 647
{
 	"Top",
 	"Bottom",
 	"Left",
Michael Meeks's avatar
Michael Meeks committed
648 649 650
 	"Right",
	"Diagonal",
	"Rev-Diagonal"
Arturo Espinosa's avatar
Arturo Espinosa committed
651
};
652

653
static xmlNodePtr
654
xml_write_style_border (XmlParseContext *ctxt,
655
			const MStyle *style)
656 657 658
{
	xmlNodePtr cur;
	xmlNodePtr side;
659
	int        i;
660

661
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
662
		StyleBorder const *border;
663
		if (mstyle_is_element_set (style, i) &&
664
		    NULL != (border = mstyle_get_border (style, i))) {
Arturo Espinosa's avatar
Arturo Espinosa committed
665
			break;
666
		}
667
	}
668
	if (i > MSTYLE_BORDER_DIAGONAL)
Arturo Espinosa's avatar
Arturo Espinosa committed
669
		return NULL;
670

671
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
672

673
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
674
		StyleBorder const *border;
Jody Goldberg's avatar
Jody Goldberg committed
675
		if (mstyle_is_element_set (style, i) &&
676
		    NULL != (border = mstyle_get_border (style, i))) {
Jody Goldberg's avatar
Jody Goldberg committed
677 678
			StyleBorderType t = border->line_type;
			StyleColor *col   = border->color;
679 680
 			side = xmlNewChild (cur, ctxt->ns,
					    StyleSideNames [i - MSTYLE_BORDER_TOP],
681 682
 					    NULL);
			xml_set_value_int (side, "Style", t);
683 684
			if (t != STYLE_BORDER_NONE)
				xml_set_color_value (side, "Color", col);
685
 		}
686 687
	}
	return cur;
688 689 690 691 692
}

/*
 * Create a StyleBorder equivalent to the XML subtree of doc.
 */
693
static void
694
xml_read_style_border (XmlParseContext *ctxt, xmlNodePtr tree, MStyle *mstyle)
695 696
{
	xmlNodePtr side;
697
	int        i;
698 699 700

	if (strcmp (tree->name, "StyleBorder")){
		fprintf (stderr,
701
			 "xml_read_style_border: invalid element type %s, 'StyleBorder' expected`\n",
702 703
			 tree->name);
	}
704

705
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
706
 		if ((side = e_xml_get_child_by_name (tree,
707
					      StyleSideNames [i - MSTYLE_BORDER_TOP])) != NULL) {
708 709
			int		 t;
			StyleColor      *color = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
710
			StyleBorder    *border;
711
			xml_get_value_int (side, "Style", &t);
712 713
			if (t != STYLE_BORDER_NONE)
				xml_get_color_value (side, "Color", &color);
714
			border = style_border_fetch ((StyleBorderType)t, color,
Michael Meeks's avatar
Michael Meeks committed
715
						     style_border_get_orientation (i));
716
			mstyle_set_border (mstyle, i, border);
717
 		}
718
	}
719 720 721 722 723
}

/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
724
xmlNodePtr
725
xml_write_style (XmlParseContext *ctxt,
726
		 MStyle *style)
727
{
728
	xmlNodePtr  cur, child;
729
	char       *tstr;
730

731
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
732

733 734 735 736
	if (mstyle_is_element_set (style, MSTYLE_ALIGN_H))
		xml_set_value_int (cur, "HAlign", mstyle_get_align_h (style));
	if (mstyle_is_element_set (style, MSTYLE_ALIGN_V))
		xml_set_value_int (cur, "VAlign", mstyle_get_align_v (style));
737 738
	if (mstyle_is_element_set (style, MSTYLE_WRAP_TEXT))
		xml_set_value_int (cur, "WrapText", mstyle_get_wrap_text (style));
739 740 741 742 743 744
	if (mstyle_is_element_set (style, MSTYLE_ORIENTATION))
		xml_set_value_int (cur, "Orient", mstyle_get_orientation (style));
	if (mstyle_is_element_set (style, MSTYLE_PATTERN))
		xml_set_value_int (cur, "Shade", mstyle_get_pattern (style));

	if (mstyle_is_element_set (style, MSTYLE_COLOR_FORE)) {
745
/*		if (!style_is_default_fore (mstyle_get_color (style, MSTYLE_COLOR_FORE)))*/
746 747 748
			xml_set_color_value (cur, "Fore", mstyle_get_color (style, MSTYLE_COLOR_FORE));
	}
	if (mstyle_is_element_set (style, MSTYLE_COLOR_BACK)) {
749
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_BACK)))*/
750 751
			xml_set_color_value (cur, "Back", mstyle_get_color (style, MSTYLE_COLOR_BACK));
	}
752
	if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN)) {
753
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_PATTERN)))*/
754 755
			xml_set_color_value (cur, "PatternColor", mstyle_get_color (style, MSTYLE_COLOR_PATTERN));
	}
Jody Goldberg's avatar
Jody Goldberg committed
756 757
	if (mstyle_is_element_set (style, MSTYLE_FORMAT)) {
		char *fmt = style_format_as_XL (mstyle_get_format (style), FALSE);
758
		xml_set_value_cstr (cur, "Format", fmt);
Jody Goldberg's avatar
Jody Goldberg committed
759 760
		g_free (fmt);
	}
761 762

	if (mstyle_is_element_set (style, MSTYLE_FONT_NAME) ||
763
	    mstyle_is_element_set (style, MSTYLE_FONT_SIZE) ||
764 765
	    mstyle_is_element_set (style, MSTYLE_FONT_BOLD) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_ITALIC) ||
766 767
	    mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH)) {
768 769 770 771 772 773
		const char *fontname;

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

775 776 777 778
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, fontname);
		child = xmlNewChild (cur, ctxt->ns, "Font", tstr);
		if (tstr) xmlFree (tstr);

779
		if (mstyle_is_element_set (style, MSTYLE_FONT_SIZE))
780
			xml_set_value_points (child, "Unit",
781 782 783 784 785 786 787
					      mstyle_get_font_size (style));
		if (mstyle_is_element_set (style, MSTYLE_FONT_BOLD))
			xml_set_value_int (child, "Bold",
					   mstyle_get_font_bold (style));
		if (mstyle_is_element_set (style, MSTYLE_FONT_ITALIC))
			xml_set_value_int (child, "Italic",
					   mstyle_get_font_italic (style));
Jody Goldberg's avatar
Jody Goldberg committed
788 789 790
		if (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE))
			xml_set_value_int (child, "Underline",
					   (int)mstyle_get_font_uline (style));
791 792 793
		if (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
			xml_set_value_int (child, "StrikeThrough",
					   mstyle_get_font_strike (style));
794 795 796 797 798
	}

	child = xml_write_style_border (ctxt, style);
	if (child)
		xmlAddChild (cur, child);
799 800

	return cur;
801 802
}

803
static xmlNodePtr
804
xml_write_names (XmlParseContext *ctxt, GList *names)
805
{
806
	xmlNodePtr  cur;
807
	char       *tstr;
808

809 810 811 812
#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
	   */
813 814
	if (!names)
		return NULL;
815
#endif
816 817 818 819 820

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

	while (names) {
		xmlNodePtr   tmp;
821
		NamedExpression    *expr_name = names->data;
822 823 824 825 826
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

		tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Name", NULL);
827 828 829
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, expr_name->name->str);
		xmlNewChild (tmp, ctxt->ns, "name", tstr);
		if (tstr) xmlFree (tstr);
830 831

		text = expr_name_value (expr_name);
832 833 834
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
		xmlNewChild (tmp, ctxt->ns, "value", tstr);
		if (tstr) xmlFree (tstr);
835 836 837 838 839
		g_free (text);

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

841 842 843 844
	return cur;
}

static void
845
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree, Workbook *wb,
846
		Sheet *sheet)
847 848 849 850 851 852
{
	xmlNodePtr child;

	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);

853
	child = tree->xmlChildrenNode;
854
	while (child) {
855
		char *name  = NULL;
856 857 858
		if (child->name && !strcmp (child->name, "Name")) {
			xmlNodePtr bits;

859
			bits = child->xmlChildrenNode;
860
			while (bits) {
861