xml-io.c 79.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"
Jody Goldberg's avatar
Jody Goldberg committed
28
#include "rendered-value.h"
29
#include "file.h"
30
#include "expr.h"
31
#include "expr-name.h"
32
#include "cell.h"
33
#include "sheet-merge.h"
34 35 36
#include "io-context.h"
#include "command-context.h"
#include "workbook-control.h"
37
#include "workbook-view.h"
38
#include "workbook.h"
39
#include "selection.h"
40
#include "clipboard.h"
Jody Goldberg's avatar
Jody Goldberg committed
41
#include "format.h"
Jody Goldberg's avatar
Jody Goldberg committed
42
#include "ranges.h"
Chyla Zbigniew's avatar
Chyla Zbigniew committed
43
#include "file.h"
44

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

Chyla Zbigniew's avatar
Chyla Zbigniew committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
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;
}

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

Michael Meeks's avatar
Michael Meeks committed
73
	ctxt->version   = version;
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
	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
89
	return xml_parse_ctx_new_full (
Jody Goldberg's avatar
Jody Goldberg committed
90
		doc, ns, GNUM_XML_V6, NULL, NULL, NULL);
91
}
92

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

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

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

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

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

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

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

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

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

	return NULL;
214 215
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

419 420 421 422 423 424 425 426 427
/*
 * 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);
}

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

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

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

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

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

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

468 469 470 471 472 473 474 475 476 477 478 479 480 481
	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;
482
		g_free (txt);
483 484 485
	}
}

486
static gboolean
487
xml_read_range (xmlNodePtr tree, Range *r)
488
{
489 490 491 492 493 494 495 496 497 498 499 500
	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;
501 502 503
}

static void
504
xml_write_range (xmlNodePtr tree, const Range *value)
505 506 507 508 509 510 511 512
{
	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
513
xml_read_selection_info (XmlParseContext *ctxt, Sheet *sheet, xmlNodePtr tree)
514 515 516
{
	Range r;
	int row, col;
517
	xmlNodePtr sel, selections = e_xml_get_child_by_name (tree, "Selections");
518 519 520
	if (selections == NULL)
		return;

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

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

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

555 556 557 558 559 560 561 562
/*
 * 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 ...
 */
563
static int
564
xml_get_color_value (xmlNodePtr node, const char *name, StyleColor **color)
565
{
566
	char *ret;
567 568
	int red, green, blue;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

734 735 736 737 738 739 740 741 742 743 744 745
	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));
	if (mstyle_is_element_set (style, MSTYLE_FIT_IN_CELL))
		xml_set_value_int (cur, "Fit", mstyle_get_fit_in_cell (style));
	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)) {
746
/*		if (!style_is_default_fore (mstyle_get_color (style, MSTYLE_COLOR_FORE)))*/
747 748 749
			xml_set_color_value (cur, "Fore", mstyle_get_color (style, MSTYLE_COLOR_FORE));
	}
	if (mstyle_is_element_set (style, MSTYLE_COLOR_BACK)) {
750
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_BACK)))*/
751 752
			xml_set_color_value (cur, "Back", mstyle_get_color (style, MSTYLE_COLOR_BACK));
	}
753
	if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN)) {
754
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_PATTERN)))*/
755 756
			xml_set_color_value (cur, "PatternColor", mstyle_get_color (style, MSTYLE_COLOR_PATTERN));
	}
Jody Goldberg's avatar
Jody Goldberg committed
757 758
	if (mstyle_is_element_set (style, MSTYLE_FORMAT)) {
		char *fmt = style_format_as_XL (mstyle_get_format (style), FALSE);
759
		xml_set_value_cstr (cur, "Format", fmt);
Jody Goldberg's avatar
Jody Goldberg committed
760 761
		g_free (fmt);
	}
762 763

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

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

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

780
		if (mstyle_is_element_set (style, MSTYLE_FONT_SIZE))
781
			xml_set_value_points (child, "Unit",
782 783 784 785 786 787 788
					      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
789 790 791
		if (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE))
			xml_set_value_int (child, "Underline",
					   (int)mstyle_get_font_uline (style));
792 793 794
		if (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
			xml_set_value_int (child, "StrikeThrough",
					   mstyle_get_font_strike (style));
795 796 797 798 799
	}

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

	return cur;
802 803
}

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

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

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

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

		g_return_val_if_fail (expr_name != NULL, NULL);

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

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

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

842 843 844 845
	return cur;
}

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

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

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

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

863 864 865 866 867 868 869 870 871 872 873
				if (!strcmp (bits->name, "name")) {
					name = xmlNodeGetContent (bits);
				} else {
					char     *txt;
					char     *error;
					g_return_if_fail (name != NULL);

					txt = xmlNodeGetContent (bits);
					g_return_if_fail (txt != NULL);
					g_return_if_fail (!strcmp (bits->name, "value"));

874
					if (!expr_name_create (wb, sheet, name, txt, &error))
875 876
						g_warning (error);

Daniel Veillard's avatar
Daniel Veillard committed
877
					xmlFree (txt);
878
				}
879
				bits = bits->next;
880 881 882 883 884 885
			}
		}
		child = child->next;
	}
}

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

893
	if (!summary_info)
894 895
		return NULL;

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

	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);
910 911 912
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, sit->name);
			xmlNewChild (tmp, ctxt->ns, "name", tstr);
			if (tstr) xmlFree (tstr);
913 914 915

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

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

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

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

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

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

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

					txt = xmlNodeGetContent (bits);
961 962 963 964
					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
965
							sit = summary_item_new_int (name, atoi (txt));
966

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

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

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

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

static void
Arturo Espinosa's avatar
Arturo Espinosa committed
999
xml_get_print_hf (xmlNodePtr node, PrintHF *const hf)
Michael Meeks's avatar
Michael Meeks committed
1000 1001
{
	char *txt;
1002

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

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

Michael Meeks's avatar
Michael Meeks committed
1013
	txt = xml_value_get (node, "Middle");
1014
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
1015 1016
		if (hf->middle_format)
			g_free (hf->middle_format);
1017
		hf->middle_format = txt;
Arturo Espinosa's avatar
Arturo Espinosa committed
1018
	}
Michael Meeks's avatar
Michael Meeks committed
1019 1020

	txt = xml_value_get (node, "Right");
1021
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
1022 1023
		if (hf->right_format)
			g_free (hf->right_format);
1024
		hf->right_format = txt;
Arturo Espinosa's avatar
Arturo Espinosa committed
1025
	}
Michael Meeks's avatar
Michael Meeks committed
1026 1027
}

1028
static void
1029
xml_write_attribute (XmlParseContext *ctxt, xmlNodePtr attr, GtkArg *arg)
1030 1031 1032
{
	xmlChar *tstr;
	gchar *