xml-io.c 85.6 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 45
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <zlib.h>
Jody Goldberg's avatar
Jody Goldberg committed
46 47 48 49 50
#include <gal/util/e-xml-utils.h>
#include <gnome.h>
#include <locale.h>
#include <math.h>
#include <limits.h>
51

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

55
/* FIXME - tune the values below */
56 57
#define XML_INPUT_BUFFER_SIZE      4096
#define N_ELEMENTS_BETWEEN_UPDATES 20
58

59 60
static GnumFileOpener *xml_opener = NULL;
static GnumFileSaver  *xml_saver = NULL;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
61

62
GnumFileOpener *
63
gnumeric_xml_get_opener (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
64
{
65
	return xml_opener;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
66 67
}

68
GnumFileSaver *
69
gnumeric_xml_get_saver (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
70
{
71
	return xml_saver;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
72 73
}

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

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

104 105 106 107 108 109 110 111
void
xml_parse_ctx_destroy (XmlParseContext *ctxt)
{
	g_return_if_fail (ctxt != NULL);

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

113 114 115
/*
 * Internal stuff: xml helper functions.
 */
116

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

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

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

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

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

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

	return NULL;
225 226
}

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

237 238 239 240 241
	val = xml_value_get (node, name);
	if (val == NULL) return NULL;
        ret = string_get (val);
	g_free (val);
	return ret;
242 243
}

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

255
	ret = xml_value_get (node, name);
256
	if (ret == NULL) return 0;
257
	res = sscanf (ret, "%d", &i);
258 259
	g_free (ret);

260 261
	if (res == 1) {
	        *val = i;
262
		return TRUE;
263
	}
264
	return FALSE;
265 266
}

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

279
	ret = xml_value_get (node, name);
280
	if (ret == NULL) return 0;
281
	res = sscanf (ret, "%f", &f);
282 283
	g_free (ret);

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

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

302
	ret = xml_value_get (node, name);
303
	if (ret == NULL) return 0;
304
	res = sscanf (ret, "%lf", val);
305
	g_free (ret);
306

307
	return (res == 1);
308 309 310 311 312 313
}

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

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

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

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

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

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

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

405 406
	if (precision < 0 || precision > DBL_DIG)
		precision = DBL_DIG;
407

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

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

430 431 432 433 434 435 436 437 438
/*
 * 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);
}

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

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

465
	child = xmlNewChild (node, NULL, name, NULL);
466

467
	xml_set_value_points (child, "Points", pu->points);
468 469

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

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

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

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

506 507
	/* Older versions of gnumeric had some boundary problems */
	range_ensure_sanity (r);
508 509

	return res;
510 511 512
}

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

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

538 539
	if (xml_get_value_int (selections, "CursorCol", &col) &&
	    xml_get_value_int (selections, "CursorRow", &row))
540
		sheet_set_edit_pos (sheet, col, row);
541 542 543
}

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

559 560
	xml_set_value_int (tree, "CursorCol", sheet->edit_pos_real.col);
	xml_set_value_int (tree, "CursorRow", sheet->edit_pos_real.row);
561 562
}

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

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

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

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

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

629 630 631 632
	if (color->color.red == 0 && color->color.green == 0 && color->color.blue == 0)
		return TRUE;
	else
		return FALSE;
633 634
}

635 636
static int
style_is_default_back (StyleColor *color)
637
{
638 639
	if (!color)
		return TRUE;
640

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

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

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

662
static xmlNodePtr
663
xml_write_style_border (XmlParseContext *ctxt,
664
			const MStyle *style)
665 666 667
{
	xmlNodePtr cur;
	xmlNodePtr side;
668
	int        i;
669

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

680
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
681

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

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

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

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

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

740
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
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));
746 747
	if (mstyle_is_element_set (style, MSTYLE_WRAP_TEXT))
		xml_set_value_int (cur, "WrapText", mstyle_get_wrap_text (style));
748 749 750 751
	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
752 753
	if (mstyle_is_element_set (style, MSTYLE_INDENT))
		xml_set_value_int (cur, "Indent", mstyle_get_indent (style));
754 755

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

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

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

786 787 788 789
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, fontname);
		child = xmlNewChild (cur, ctxt->ns, "Font", tstr);
		if (tstr) xmlFree (tstr);

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

	child = xml_write_style_border (ctxt, style);
	if (child)
		xmlAddChild (cur, child);
810 811

	return cur;
812 813
}

814
static xmlNodePtr
815
xml_write_names (XmlParseContext *ctxt, GList *names)
816
{
817
	xmlNodePtr  cur;
818
	char       *tstr;
819

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

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

	while (names) {
		xmlNodePtr   tmp;
832
		NamedExpression    *expr_name = names->data;
833 834 835 836 837
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

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

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

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

852 853 854 855
	return cur;
}

static void
856
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree, Workbook *wb,
857
		Sheet *sheet)
858 859 860 861 862 863
{
	xmlNodePtr child;

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

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

870
			bits = child->xmlChildrenNode;
871
			while (bits) {
872

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

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

884 885 886
					if (!expr_name_create (wb, sheet, name, txt, &perr))
						g_warning (perr.message);
					parse_error_free (&perr);
887

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

897
static xmlNodePtr
898
xml_write_summary (XmlParseContext *ctxt, SummaryInfo *summary_info)
899 900
{
	GList *items, *m;
901
	char *tstr;
902 903
	xmlNodePtr cur;

904
	if (!summary_info)
905 906
		return NULL;

907
	m = items = summary_info_as_list (summary_info);
908 909 910 911 912 913 914 915 916 917 918 919 920

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

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

static void
946
xml_read_summary (XmlParseContext *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
947
{
948
	xmlNodePtr child;
949

950 951 952
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);
	g_return_if_fail (summary_info != NULL);
953

954
	child = tree->xmlChildrenNode;
955
	while (child) {
956
		char *name = NULL;
Miguel de Icaza's avatar
Miguel de Icaza committed
957

958 959 960
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

961
			bits = child->xmlChildrenNode;
962 963
			while (bits) {
				SummaryItem *sit = NULL;
964

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

					txt = xmlNodeGetContent (bits);
972 973 974 975
					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
976
							sit = summary_item_new_int (name, atoi (txt));
977

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

Michael Meeks's avatar
Michael Meeks committed
994 995 996 997 998 999 1000 1001 1002 1003
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