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

42 43 44
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
45
#include <errno.h>
46
#include <zlib.h>
Jody Goldberg's avatar
Jody Goldberg committed
47 48 49 50 51
#include <gal/util/e-xml-utils.h>
#include <gnome.h>
#include <locale.h>
#include <math.h>
#include <limits.h>
52

53 54 55
/* Precision to use when saving point measures. */
#define POINT_SIZE_PRECISION 3

56
/* FIXME - tune the values below */
57 58 59 60
#define XML_INPUT_BUFFER_SIZE             0x1000
#define XML_OUTPUT_BUFFER_SIZE            0x4000
#define N_INPUT_ELEMENTS_BETWEEN_UPDATES  20
#define N_OUTPUT_ELEMENTS_BETWEEN_UPDATES 40
61

62 63
static GnumFileOpener *xml_opener = NULL;
static GnumFileSaver  *xml_saver = NULL;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
64

65
GnumFileOpener *
66
gnumeric_xml_get_opener (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
67
{
68
	return xml_opener;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
69 70
}

71
GnumFileSaver *
72
gnumeric_xml_get_saver (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
73
{
74
	return xml_saver;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
75 76
}

77 78 79
XmlParseContext *
xml_parse_ctx_new_full (xmlDocPtr             doc,
			xmlNsPtr              ns,
Michael Meeks's avatar
Michael Meeks committed
80
			GnumericXMLVersion    version,
81 82 83 84 85 86
			XmlSheetObjectReadFn  read_fn,
			XmlSheetObjectWriteFn write_fn,
			gpointer              user_data)
{
	XmlParseContext *ctxt = g_new0 (XmlParseContext, 1);

Michael Meeks's avatar
Michael Meeks committed
87
	ctxt->version   = version;
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
	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
103
	return xml_parse_ctx_new_full (
Jody Goldberg's avatar
Jody Goldberg committed
104
		doc, ns, GNUM_XML_V6, NULL, NULL, NULL);
105
}
106

107 108 109 110 111 112 113 114
void
xml_parse_ctx_destroy (XmlParseContext *ctxt)
{
	g_return_if_fail (ctxt != NULL);

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

116 117 118
/*
 * Internal stuff: xml helper functions.
 */
119

120 121 122 123
static void
xml_arg_set (GtkArg *arg, gchar *string)
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
124 125 126 127 128 129 130
	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
131
		if (!strcmp (string, "TRUE"))
JP Rosevear's avatar
JP Rosevear committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
			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;
157 158 159 160 161 162 163
	}
}

static char *
xml_arg_get (GtkArg *arg)
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177
	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
178
		return g_strdup_printf("%li", GTK_VALUE_LONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
179
	case GTK_TYPE_ULONG:
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
180
		return g_strdup_printf("%lu", GTK_VALUE_ULONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
181 182 183 184 185 186
	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));
187 188 189 190
	}

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

192 193 194
/*
 * Get a value for a node either carried as an attibute or as
 * the content of a child.
195 196
 *
 * Returns a g_malloc'ed string.  Caller must free.
197
 */
198
static char *
199
xml_value_get (xmlNodePtr node, const char *name)
200
{
201
	char *ret, *val;
202 203
	xmlNodePtr child;

204 205 206 207
	val = (char *) xmlGetProp (node, name);
	if (val != NULL) {
		ret = g_strdup (val);
		xmlFree (val);
Morten Welinder's avatar
Morten Welinder committed
208
		return ret;
209
	}
210
	child = node->xmlChildrenNode;
211 212

	while (child != NULL) {
213 214 215 216
		if (!strcmp (child->name, name)) {
		        /*
			 * !!! Inefficient, but ...
			 */
217 218 219 220 221 222
			val = xmlNodeGetContent(child);
			if (val != NULL) {
				ret = g_strdup (val);
				xmlFree (val);
				return ret;
			}
223
		}
224 225 226 227
		child = child->next;
	}

	return NULL;
228 229
}

230 231 232 233
/*
 * Get a String value for a node either carried as an attibute or as
 * the content of a child.
 */
234
String *
235
xml_get_value_string (xmlNodePtr node, const char *name)
236
{
237
	char *val;
238 239
	String *ret;

240 241 242 243 244
	val = xml_value_get (node, name);
	if (val == NULL) return NULL;
        ret = string_get (val);
	g_free (val);
	return ret;
245 246
}

247 248 249 250
/*
 * Get an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
251
gboolean
252
xml_get_value_int (xmlNodePtr node, const char *name, int *val)
253
{
254
	char *ret;
255
	int i;
256
	int res;
257

258
	ret = xml_value_get (node, name);
259
	if (ret == NULL) return 0;
260
	res = sscanf (ret, "%d", &i);
261 262
	g_free (ret);

263 264
	if (res == 1) {
	        *val = i;
265
		return TRUE;
266
	}
267
	return FALSE;
268 269
}

270
#if 0
271 272 273 274
/*
 * Get a float value for a node either carried as an attibute or as
 * the content of a child.
 */
275
static int
276
xml_get_value_float (xmlNodePtr node, const char *name, float *val)
277
{
278 279
	int res;
	char *ret;
280 281
	float f;

282
	ret = xml_value_get (node, name);
283
	if (ret == NULL) return 0;
284
	res = sscanf (ret, "%f", &f);
285 286
	g_free (ret);

287 288
	if (res == 1) {
	        *val = f;
289 290 291
		return 1;
	}
	return 0;
292
}
293
#endif
294 295 296 297 298

/*
 * Get a double value for a node either carried as an attibute or as
 * the content of a child.
 */
299
static int
300
xml_get_value_double (xmlNodePtr node, const char *name, double *val)
301
{
302 303
	int res;
	char *ret;
304

305
	ret = xml_value_get (node, name);
306
	if (ret == NULL) return 0;
307
	res = sscanf (ret, "%lf", val);
308
	g_free (ret);
309

310
	return (res == 1);
311 312 313 314 315 316
}

/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
317 318
void
xml_set_value_cstr (xmlNodePtr node, const char *name, const char *val)
319
{
320
	char *ret;
321 322 323 324
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
	if (ret != NULL){
325
		xmlFree (ret);
326 327 328
		xmlSetProp (node, name, val);
		return;
	}
329
	child = node->xmlChildrenNode;
330 331 332 333 334 335 336 337
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, val);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, val);
338 339
}

340 341 342 343
/*
 * Set a String value for a node either carried as an attibute or as
 * the content of a child.
 */
344
void
345
xml_set_value_string (xmlNodePtr node, const char *name, const String *val)
346
{
347
	char *ret;
348 349 350
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
351
	if (ret != NULL) {
352
		xmlFree (ret);
353 354 355
		xmlSetProp (node, name, val->str);
		return;
	}
356
	child = node->xmlChildrenNode;
357 358 359 360 361 362 363 364
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, val->str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, val->str);
365 366
}

367 368 369 370
/*
 * Set an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
371
void
372
xml_set_value_int (xmlNodePtr node, const char *name, int val)
373
{
374
	char *ret;
375
	xmlNodePtr child;
376
	char str[4 * sizeof (int)];
377

378
	sprintf (str, "%d", val);
379 380
	ret = xmlGetProp (node, name);
	if (ret != NULL){
381
		xmlFree (ret);
382 383 384
		xmlSetProp (node, name, str);
		return;
	}
385
	child = node->xmlChildrenNode;
386 387 388 389 390 391 392 393
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
394 395 396 397 398 399
}

/*
 * Set a double value for a node either carried as an attibute or as
 * the content of a child.
 */
400
static void
401 402
xml_set_value_double (xmlNodePtr node, const char *name, double val,
		      int precision)
403
{
404
	char *ret;
405
	xmlNodePtr child;
406
	char str[101 + DBL_DIG];
407

408 409
	if (precision < 0 || precision > DBL_DIG)
		precision = DBL_DIG;
410

411
	if (fabs (val) < 1e9 && fabs (val) > 1e-5)
412
		snprintf (str, 100 + DBL_DIG, "%.*g", precision, val);
413 414
	else
		snprintf (str, 100 + DBL_DIG, "%f", val);
415

416 417
	ret = xmlGetProp (node, name);
	if (ret != NULL){
418
		xmlFree (ret);
419 420 421
		xmlSetProp (node, name, str);
		return;
	}
422
	child = node->xmlChildrenNode;
423 424 425 426 427 428 429 430
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
431 432
}

433 434 435 436 437 438 439 440 441
/*
 * 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);
}

442 443 444 445 446
static void
xml_set_print_unit (xmlNodePtr node, const char *name,
		    const PrintUnit * const pu)
{
	xmlNodePtr  child;
Jody Goldberg's avatar
Jody Goldberg committed
447
	char       *txt = "points";
448
	char       *tstr;
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467

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

468
	child = xmlNewChild (node, NULL, name, NULL);
469

470
	xml_set_value_points (child, "Points", pu->points);
471 472

	tstr = xmlEncodeEntitiesReentrant (node->doc, txt);
473
	xml_set_value_cstr (child, "PrefUnit", tstr);
474
	if (tstr) xmlFree (tstr);
475 476 477 478 479 480
}

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

482 483 484 485 486 487 488 489
	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;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
490
		else if (!strcmp (txt, "mm"))
491
			pu->desired_display = UNIT_MILLIMETER;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
492
		else if (!strcmp (txt, "cm"))
493
			pu->desired_display = UNIT_CENTIMETER;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
494
		else if (!strcmp (txt, "in"))
495
			pu->desired_display = UNIT_INCH;
496
		g_free (txt);
497 498 499
	}
}

500
static gboolean
501
xml_read_range (xmlNodePtr tree, Range *r)
502
{
503 504 505 506 507 508
	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);

509 510
	/* Older versions of gnumeric had some boundary problems */
	range_ensure_sanity (r);
511 512

	return res;
513 514 515
}

static void
516
xml_write_range (xmlNodePtr tree, const Range *value)
517 518 519 520 521 522 523 524
{
	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
525
xml_read_selection_info (XmlParseContext *ctxt, Sheet *sheet, xmlNodePtr tree)
526 527 528
{
	Range r;
	int row, col;
529
	xmlNodePtr sel, selections = e_xml_get_child_by_name (tree, "Selections");
530 531 532
	if (selections == NULL)
		return;

533
	sheet_selection_reset (sheet);
534
	for (sel = selections->xmlChildrenNode; sel; sel = sel->next)
535
		if (xml_read_range (sel, &r))
536 537 538 539
			sheet_selection_add_range (sheet,
						   r.start.col, r.start.row,
						   r.start.col, r.start.row,
						   r.end.col, r.end.row);
540

541 542
	if (xml_get_value_int (selections, "CursorCol", &col) &&
	    xml_get_value_int (selections, "CursorRow", &row))
543
		sheet_set_edit_pos (sheet, col, row);
544 545 546
}

static void
547
xml_write_selection_info (XmlParseContext *ctxt, Sheet const *sheet, xmlNodePtr tree)
548 549 550 551 552 553 554 555
{
	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) {
556
		Range const *r = ptr->data;
557
		xmlNodePtr child = xmlNewChild (tree, ctxt->ns, "Selection", NULL);
558
		xml_write_range (child, r);
559 560 561
	}
	g_list_free (copy);

562 563
	xml_set_value_int (tree, "CursorCol", sheet->edit_pos_real.col);
	xml_set_value_int (tree, "CursorRow", sheet->edit_pos_real.row);
564 565
}

566 567 568 569 570 571 572 573
/*
 * 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 ...
 */
574
static int
575
xml_get_color_value (xmlNodePtr node, const char *name, StyleColor **color)
576
{
577
	char *ret;
578 579
	int red, green, blue;

580
	ret = xml_value_get (node, name);
581
	if (ret == NULL) return 0;
582
	if (sscanf (ret, "%X:%X:%X", &red, &green, &blue) == 3){
583
		*color = style_color_new (red, green, blue);
584
		g_free (ret);
585 586
		return 1;
	}
587
	g_free (ret);
588
	return 0;
589 590 591 592 593 594
}

/*
 * Set a color value for a node either carried as an attibute or as
 * the content of a child.
 */
595 596
static void
xml_set_color_value (xmlNodePtr node, const char *name, StyleColor *val)
597
{
598
	char *ret;
599
	xmlNodePtr child;
600
	char str[4 * sizeof (val->color)];
601

602
	sprintf (str, "%X:%X:%X", val->color.red, val->color.green, val->color.blue);
603 604
	ret = xmlGetProp (node, name);
	if (ret != NULL){
605
		xmlFree (ret);
606 607 608
		xmlSetProp (node, name, str);
		return;
	}
609
	child = node->xmlChildrenNode;
610 611 612 613 614 615 616 617
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
618
}
619 620 621 622 623

/**
 **
 ** Private functions : mapping between in-memory structure and XML tree
 **
624
 **/
625
#if 0
626 627
static int
style_is_default_fore (StyleColor *color)
628
{
629 630
	if (!color)
		return TRUE;
631

632 633 634 635
	if (color->color.red == 0 && color->color.green == 0 && color->color.blue == 0)
		return TRUE;
	else
		return FALSE;
636 637
}

638 639
static int
style_is_default_back (StyleColor *color)
640
{
641 642
	if (!color)
		return TRUE;
643

644 645 646 647
	if (color->color.red == 0xffff && color->color.green == 0xffff && color->color.blue == 0xffff)
		return TRUE;
	else
		return FALSE;
648
}
649
#endif
650 651 652 653

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

Michael Meeks's avatar
Michael Meeks committed
655
static char *StyleSideNames[6] =
656 657 658 659
{
 	"Top",
 	"Bottom",
 	"Left",
Michael Meeks's avatar
Michael Meeks committed
660 661 662
 	"Right",
	"Diagonal",
	"Rev-Diagonal"
Arturo Espinosa's avatar
Arturo Espinosa committed
663
};
664

665
static xmlNodePtr
666
xml_write_style_border (XmlParseContext *ctxt,
667
			const MStyle *style)
668 669 670
{
	xmlNodePtr cur;
	xmlNodePtr side;
671
	int        i;
672

673
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
674
		StyleBorder const *border;
675
		if (mstyle_is_element_set (style, i) &&
676
		    NULL != (border = mstyle_get_border (style, i))) {
Arturo Espinosa's avatar
Arturo Espinosa committed
677
			break;
678
		}
679
	}
680
	if (i > MSTYLE_BORDER_DIAGONAL)
Arturo Espinosa's avatar
Arturo Espinosa committed
681
		return NULL;
682

683
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
684

685
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
686
		StyleBorder const *border;
Jody Goldberg's avatar
Jody Goldberg committed
687
		if (mstyle_is_element_set (style, i) &&
688
		    NULL != (border = mstyle_get_border (style, i))) {
Jody Goldberg's avatar
Jody Goldberg committed
689 690
			StyleBorderType t = border->line_type;
			StyleColor *col   = border->color;
691 692
 			side = xmlNewChild (cur, ctxt->ns,
					    StyleSideNames [i - MSTYLE_BORDER_TOP],
693 694
 					    NULL);
			xml_set_value_int (side, "Style", t);
695 696
			if (t != STYLE_BORDER_NONE)
				xml_set_color_value (side, "Color", col);
697
 		}
698 699
	}
	return cur;
700 701 702 703 704
}

/*
 * Create a StyleBorder equivalent to the XML subtree of doc.
 */
705
static void
706
xml_read_style_border (XmlParseContext *ctxt, xmlNodePtr tree, MStyle *mstyle)
707 708
{
	xmlNodePtr side;
709
	int        i;
710 711 712

	if (strcmp (tree->name, "StyleBorder")){
		fprintf (stderr,
713
			 "xml_read_style_border: invalid element type %s, 'StyleBorder' expected`\n",
714 715
			 tree->name);
	}
716

717
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
718
 		if ((side = e_xml_get_child_by_name (tree,
719
					      StyleSideNames [i - MSTYLE_BORDER_TOP])) != NULL) {
720 721
			int		 t;
			StyleColor      *color = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
722
			StyleBorder    *border;
723
			xml_get_value_int (side, "Style", &t);
724 725
			if (t != STYLE_BORDER_NONE)
				xml_get_color_value (side, "Color", &color);
726
			border = style_border_fetch ((StyleBorderType)t, color,
Michael Meeks's avatar
Michael Meeks committed
727
						     style_border_get_orientation (i));
728
			mstyle_set_border (mstyle, i, border);
729
 		}
730
	}
731 732 733 734 735
}

/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
736
xmlNodePtr
737
xml_write_style (XmlParseContext *ctxt,
738
		 MStyle *style)
739
{
740
	xmlNodePtr  cur, child;
741
	char       *tstr;
742

743
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
744

745 746 747 748
	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));
749 750
	if (mstyle_is_element_set (style, MSTYLE_WRAP_TEXT))
		xml_set_value_int (cur, "WrapText", mstyle_get_wrap_text (style));
751 752 753 754
	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));
Jody Goldberg's avatar
Jody Goldberg committed
755 756
	if (mstyle_is_element_set (style, MSTYLE_INDENT))
		xml_set_value_int (cur, "Indent", mstyle_get_indent (style));
757 758

	if (mstyle_is_element_set (style, MSTYLE_COLOR_FORE)) {
759
/*		if (!style_is_default_fore (mstyle_get_color (style, MSTYLE_COLOR_FORE)))*/
760 761 762
			xml_set_color_value (cur, "Fore", mstyle_get_color (style, MSTYLE_COLOR_FORE));
	}
	if (mstyle_is_element_set (style, MSTYLE_COLOR_BACK)) {
763
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_BACK)))*/
764 765
			xml_set_color_value (cur, "Back", mstyle_get_color (style, MSTYLE_COLOR_BACK));
	}
766
	if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN)) {
767
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_PATTERN)))*/
768 769
			xml_set_color_value (cur, "PatternColor", mstyle_get_color (style, MSTYLE_COLOR_PATTERN));
	}
Jody Goldberg's avatar
Jody Goldberg committed
770 771
	if (mstyle_is_element_set (style, MSTYLE_FORMAT)) {
		char *fmt = style_format_as_XL (mstyle_get_format (style), FALSE);
772
		xml_set_value_cstr (cur, "Format", fmt);
Jody Goldberg's avatar
Jody Goldberg committed
773 774
		g_free (fmt);
	}
775 776

	if (mstyle_is_element_set (style, MSTYLE_FONT_NAME) ||
777
	    mstyle_is_element_set (style, MSTYLE_FONT_SIZE) ||
778 779
	    mstyle_is_element_set (style, MSTYLE_FONT_BOLD) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_ITALIC) ||
780 781
	    mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH)) {
782 783 784 785 786 787
		const char *fontname;

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

789 790 791 792
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, fontname);
		child = xmlNewChild (cur, ctxt->ns, "Font", tstr);
		if (tstr) xmlFree (tstr);

793
		if (mstyle_is_element_set (style, MSTYLE_FONT_SIZE))
794
			xml_set_value_points (child, "Unit",
795 796 797 798 799 800 801
					      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
802 803 804
		if (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE))
			xml_set_value_int (child, "Underline",
					   (int)mstyle_get_font_uline (style));
805 806 807
		if (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
			xml_set_value_int (child, "StrikeThrough",
					   mstyle_get_font_strike (style));
808 809 810 811 812
	}

	child = xml_write_style_border (ctxt, style);
	if (child)
		xmlAddChild (cur, child);
813 814

	return cur;
815 816
}

817
static xmlNodePtr
818
xml_write_names (XmlParseContext *ctxt, GList *names)
819
{
820
	xmlNodePtr  cur;
821
	char       *tstr;
822

823 824 825 826
#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
	   */
827 828
	if (!names)
		return NULL;
829
#endif
830 831 832 833 834

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

	while (names) {
		xmlNodePtr   tmp;
835
		NamedExpression    *expr_name = names->data;
836 837 838 839 840
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

		tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Name", NULL);
841 842 843
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, expr_name->name->str);
		xmlNewChild (tmp, ctxt->ns, "name", tstr);
		if (tstr) xmlFree (tstr);
844 845

		text = expr_name_value (expr_name);
846 847 848
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
		xmlNewChild (tmp, ctxt->ns, "value", tstr);
		if (tstr) xmlFree (tstr);
849 850 851 852 853
		g_free (text);

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

855 856 857 858
	return cur;
}

static void
859
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree, Workbook *wb,
860
		Sheet *sheet)
861 862 863 864 865 866
{
	xmlNodePtr child;

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

867
	child = tree->xmlChildrenNode;
868
	while (child) {
869
		char *name  = NULL;
870 871 872
		if (child->name && !strcmp (child->name, "Name")) {
			xmlNodePtr bits;

873
			bits = child->xmlChildrenNode;
874
			while (bits) {
875

876 877 878
				if (!strcmp (bits->name, "name")) {
					name = xmlNodeGetContent (bits);
				} else {
879
					char     *txt;
880
					ParseError  perr;
881 882 883 884 885 886
					g_return_if_fail (name != NULL);

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

887 888 889
					if (!expr_name_create (wb, sheet, name, txt, &perr))
						g_warning (perr.message);
					parse_error_free (&perr);
890

Daniel Veillard's avatar
Daniel Veillard committed
891
					xmlFree (txt);
892
				}
893
				bits = bits->next;
894 895 896 897 898 899
			}
		}
		child = child->next;
	}
}

900
static xmlNodePtr
901
xml_write_summary (XmlParseContext *ctxt, SummaryInfo *summary_info)
902 903
{
	GList *items, *m;
904
	char *tstr;
905 906
	xmlNodePtr cur;

907
	if (!summary_info)
908 909
		return NULL;

910
	m = items = summary_info_as_list (summary_info);
911 912 913 914 915 916 917 918 919 920 921 922 923

	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);
924 925 926
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, sit->name);
			xmlNewChild (tmp, ctxt->ns, "name", tstr);
			if (tstr) xmlFree (tstr);
927 928 929

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

static void
949
xml_read_summary (XmlParseContext *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
950
{
951
	xmlNodePtr child;
952

953 954 955
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);
	g_return_if_fail (summary_info != NULL);
956

957
	child = tree->xmlChildrenNode;
958
	while (child) {
959
		char *name = NULL;
Miguel de Icaza's avatar
Miguel de Icaza committed
960

961 962 963
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

964
			bits = child->xmlChildrenNode;
965 966
			while (bits) {
				SummaryItem *sit = NULL;
967

968
				if (!strcmp (bits->name, "name")) {
969
					name = xmlNodeGetContent (bits);
970 971 972 973 974
				} else {
					char *txt;
					g_return_if_fail (name);

					txt = xmlNodeGetContent (bits);
975 976 977 978
					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
979
							sit = summary_item_new_int (name, atoi (txt));
980

981
						if (sit)
982
							summary_info_add (summary_info, sit);
Daniel Veillard's avatar
Daniel Veillard committed
983
						xmlFree (txt);
984
					}
985
				}
986
				bits = bits->next;
987 988
			}
		}
Miguel de Icaza's avatar
Miguel de Icaza committed
989
		if (name){
990
			xmlFree (name);
Miguel de Icaza's avatar
Miguel de Icaza committed
991 992
			name = NULL;
		}
993 994 995 996
		child = child->next;
	}
}