xml-io.c 81.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 10 11
 */

#include <config.h>
#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 "color.h"
20
#include "border.h"
21
#include "sheet-object.h"
22
#include "sheet-object-graphic.h"
23 24 25
#ifdef ENABLE_BONOBO
#	include "sheet-object-bonobo.h"
#endif
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 "cell-comment.h"
34
#include "workbook.h"
35 36
#include "workbook-view.h"
#include "selection.h"
37
#include "clipboard.h"
38
#include "command-context.h"
Jody Goldberg's avatar
Jody Goldberg committed
39
#include "format.h"
40 41 42 43

/*
 * A parsing context.
 */
44 45 46 47 48 49 50 51
typedef enum
{
    GNUM_XML_V1,
    GNUM_XML_V2,
    GNUM_XML_V3,	/* >= 0.52 */
    GNUM_XML_V4,	/* >= 0.57 */
} GnumericXMLVersion;

52
struct _XmlParseContext {
53 54 55 56
	xmlDocPtr doc;		/* Xml document */
	xmlNsPtr ns;		/* Main name space */
	xmlNodePtr parent;	/* used only for g_hash_table_foreach callbacks */
	Sheet *sheet;		/* the associated sheet */
Jody Goldberg's avatar
Jody Goldberg committed
57
	Workbook *wb;		/* the associated workbook */
58
	GHashTable *style_table;/* old style styles compatibility */
Jody Goldberg's avatar
Jody Goldberg committed
59 60 61 62 63
	GHashTable *expr_map;	/*
				 * Emitted expressions with ref count > 1
				 * When writing this is map from expr pointer -> index
				 * when reading this is a map from index -> expr pointer
				 */
64 65 66
	XmlSheetObjectWriteFn write_fn;
	XmlSheetObjectReadFn  read_fn;
	gpointer              user_data;
67
	GnumericXMLVersion    version;
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
};

XmlParseContext *
xml_parse_ctx_new_full (xmlDocPtr             doc,
			xmlNsPtr              ns,
			XmlSheetObjectReadFn  read_fn,
			XmlSheetObjectWriteFn write_fn,
			gpointer              user_data)
{
	XmlParseContext *ctxt = g_new0 (XmlParseContext, 1);

	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)
{
	return xml_parse_ctx_new_full (doc, ns, NULL, NULL, NULL);
}
       
void
xml_parse_ctx_destroy (XmlParseContext *ctxt)
{
	g_return_if_fail (ctxt != NULL);

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

106 107 108
/*
 * Internal stuff: xml helper functions.
 */
109

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

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

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

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

194 195 196 197
	val = (char *) xmlGetProp (node, name);
	if (val != NULL) {
		ret = g_strdup (val);
		xmlFree (val);
Morten Welinder's avatar
Morten Welinder committed
198
		return ret;
199
	}
200 201 202
	child = node->childs;

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

	return NULL;
218 219
}

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

230 231 232 233 234
	val = xml_value_get (node, name);
	if (val == NULL) return NULL;
        ret = string_get (val);
	g_free (val);
	return ret;
235 236
}

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

248
	ret = xml_value_get (node, name);
249
	if (ret == NULL) return 0;
250
	res = sscanf (ret, "%d", &i);
251 252
	g_free (ret);

253 254
	if (res == 1) {
	        *val = i;
255
		return TRUE;
256
	}
257
	return FALSE;
258 259
}

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

272
	ret = xml_value_get (node, name);
273
	if (ret == NULL) return 0;
274
	res = sscanf (ret, "%f", &f);
275 276
	g_free (ret);

277 278
	if (res == 1) {
	        *val = f;
279 280 281
		return 1;
	}
	return 0;
282
}
283
#endif
284 285 286 287 288

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

295
	ret = xml_value_get (node, name);
296
	if (ret == NULL) return 0;
297
	res = sscanf (ret, "%lf", val);
298
	g_free (ret);
299

300
	return (res == 1);
301 302
}

303
#if 0
304 305 306
/*
 * Get a set of coodinates for a node, carried as the content of a child.
 */
307
static int
308
xml_get_coordinate (xmlNodePtr node, const char *name, double *x, double *y)
309
{
310 311
	int res;
	char *ret;
312

313
	ret = xml_value_get (node, name);
314
	if (ret == NULL) return 0;
315
	res = sscanf (ret, "(%lf %lf)", x, y)
316
	g_free (ret);
317 318

	return (res == 2)
319
}
320
#endif
321 322 323 324 325

/*
 * Get a pair of coodinates for a node, carried as the content of a child.
 */

326
static int
327
xml_get_coordinates (xmlNodePtr node, const char *name,
328 329
		   double *x1, double *y1, double *x2, double *y2)
{
330 331
	int res;
	char *ret;
332

333
	ret = xml_value_get (node, name);
334
	if (ret == NULL) return 0;
335
	res = sscanf (ret, "(%lf %lf)(%lf %lf)", x1, y1, x2, y2);
336 337 338
	g_free (ret);

	if (res == 4)
339
		return 1;
340

341
	return 0;
342 343
}

344
#if 0
345 346 347
/*
 * Get a GnomeCanvasPoints for a node, carried as the content of a child.
 */
348
static GnomeCanvasPoints *
349
xml_get_gnome_canvas_points (xmlNodePtr node, const char *name)
350
{
351
	char *val;
352 353 354 355
	GnomeCanvasPoints *ret = NULL;
	int res;
	const char *ptr;
	int index = 0, i;
356
	float coord[20];	/* TODO: must be dynamic !!!! */
357

358
	val = xml_value_get (node, name);
359
	if (val == NULL) return NULL;
360 361 362 363 364
	ptr = val;
	do {
		while ((*ptr) && (*ptr != '('))
			ptr++;
		if (*ptr == 0)
365
			break;
366
		THIS_DOES_NOT_LOOK_RIGHT = 1;
367
		res = sscanf (ptr, "(%lf %lf)", &coord[index], &coord[index + 1]);
368 369 370 371 372
		if (res != 2)
			break;
		index += 2;
		ptr++;
	} while (res > 0);
373
	g_free (val);
374 375 376 377 378 379 380

	if (index >= 2)
		ret = gnome_canvas_points_new (index / 2);
	if (ret == NULL)
		return NULL;
	for (i = 0; i < index; i++)
		ret->coords[i] = coord[i];
381
	return ret;
382
}
383
#endif
384 385 386 387 388

/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
389
static void
390
xml_set_gnome_canvas_points (xmlNodePtr node, const char *name,
391
			     const GnomeCanvasPoints *val)
392 393 394
{
	xmlNodePtr child;
	char *str, *base;
395
	char *tstr;
396
	int i;
397
	int width1 = 30 + DBL_DIG;
398 399 400

	if (val == NULL)
		return;
401
	if (val->num_points < 0 || val->num_points > (INT_MAX - 1) / width1)
402
		return;
403
	base = str = g_malloc (val->num_points * width1 + 1);
404 405 406
	if (str == NULL)
		return;
	for (i = 0; i < val->num_points; i++){
407 408 409
		sprintf (str, "(%f %f)", val->coords[2 * i],
			 val->coords[2 * i + 1]);
		str += strlen (str);
410
	}
411
	*str = 0;
412 413 414 415 416

	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, base);
417
			g_free (base);
418 419 420 421
			return;
		}
		child = child->next;
	}
422 423 424 425

	tstr = xmlEncodeEntitiesReentrant (node->doc, base);
	xmlNewChild (node, NULL, name, tstr);
	if (tstr) xmlFree (tstr);
426
	g_free (base);
427 428
}

429 430 431 432
/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
433 434
static void
xml_set_value (xmlNodePtr node, const char *name, const char *val)
435
{
436
	char *ret;
437 438 439 440
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
	if (ret != NULL){
441
		xmlFree (ret);
442 443 444 445 446 447 448 449 450 451 452 453
		xmlSetProp (node, name, val);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, val);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, val);
454 455
}

456 457 458 459
/*
 * Set a String value for a node either carried as an attibute or as
 * the content of a child.
 */
460
void
461
xml_set_value_string (xmlNodePtr node, const char *name, const String *val)
462
{
463
	char *ret;
464 465 466
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
467
	if (ret != NULL) {
468
		xmlFree (ret);
469 470 471 472 473 474 475 476 477 478 479 480
		xmlSetProp (node, name, val->str);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, val->str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, val->str);
481 482
}

483 484 485 486
/*
 * Set an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
487
void
488
xml_set_value_int (xmlNodePtr node, const char *name, int val)
489
{
490
	char *ret;
491
	xmlNodePtr child;
492
	char str[4 * sizeof (int)];
493

494
	sprintf (str, "%d", val);
495 496
	ret = xmlGetProp (node, name);
	if (ret != NULL){
497
		xmlFree (ret);
498 499 500 501 502 503 504 505 506 507 508 509
		xmlSetProp (node, name, str);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
510 511 512 513 514 515
}

/*
 * Set a double value for a node either carried as an attibute or as
 * the content of a child.
 */
516
static void
517
xml_set_value_double (xmlNodePtr node, const char *name, double val)
518
{
519
	char *ret;
520
	xmlNodePtr child;
521
	char str[101 + DBL_DIG];
522

523 524 525 526
	if (fabs (val) < 1e9 && fabs (val) > 1e-5)
		snprintf (str, 100 + DBL_DIG, "%.*g", DBL_DIG, val);
	else
		snprintf (str, 100 + DBL_DIG, "%f", val);
527

528 529
	ret = xmlGetProp (node, name);
	if (ret != NULL){
530
		xmlFree (ret);
531 532 533 534 535 536 537 538 539 540 541 542
		xmlSetProp (node, name, str);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
543 544
}

545 546 547 548 549
static void
xml_set_print_unit (xmlNodePtr node, const char *name,
		    const PrintUnit * const pu)
{
	xmlNodePtr  child;
Jody Goldberg's avatar
Jody Goldberg committed
550
	char       *txt = "points";
551
	char       *tstr;
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570

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

571 572 573 574
	tstr = xmlEncodeEntitiesReentrant (node->doc, name);
	child = xmlNewChild (node, NULL, "PrintUnit", tstr);
	if (tstr) xmlFree (tstr);

575
	xml_set_value_double (child, "Points", pu->points);
576 577 578 579

	tstr = xmlEncodeEntitiesReentrant (node->doc, txt);
	xml_set_value (child, "PrefUnit", tstr);
	if (tstr) xmlFree (tstr);
580 581 582 583 584 585
}

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

587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
	g_return_if_fail (pu != NULL);
	g_return_if_fail (node != NULL);
	g_return_if_fail (node->childs != 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;
602
		g_free (txt);
603 604 605
	}
}

606
/*
607
 * Search a child by name, if needed go down the tree to find it.
608
 */
609
xmlNodePtr
610
xml_search_child (xmlNodePtr node, const char *name)
611 612 613 614 615 616 617 618 619 620 621 622
{
	xmlNodePtr ret;
	xmlNodePtr child;

	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name))
			return child;
		child = child->next;
	}
	child = node->childs;
	while (child != NULL){
623
		ret = xml_search_child (child, name);
624 625 626 627 628
		if (ret != NULL)
			return ret;
		child = child->next;
	}
	return NULL;
629 630
}

631 632 633
static gboolean
xml_read_range (xmlNodePtr tree, Range *res)
{
Jody Goldberg's avatar
Jody Goldberg committed
634
	return
635 636 637 638 639 640 641
	    xml_get_value_int (tree, "startCol", &res->start.col) &&
	    xml_get_value_int (tree, "startRow", &res->start.row) &&
	    xml_get_value_int (tree, "endCol",   &res->end.col) &&
	    xml_get_value_int (tree, "endRow",   &res->end.row);
}

static void
642
xml_write_range (xmlNodePtr tree, const Range *value)
643 644 645 646 647 648 649 650
{
	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
651
xml_read_selection_info (XmlParseContext *ctxt, Sheet *sheet, xmlNodePtr tree)
652 653 654 655 656 657 658 659 660 661
{
	Range r;
	int row, col;
	xmlNodePtr sel, selections = xml_search_child (tree, "Selections");
	if (selections == NULL)
		return;

	sheet_selection_reset_only (sheet);
	for (sel = selections->childs; sel; sel = sel->next) {
		if (xml_read_range (sel, &r))
662 663 664 665
			sheet_selection_add_range (sheet,
						   r.start.col, r.start.row,
						   r.start.col, r.start.row,
						   r.end.col, r.end.row);
666 667 668 669 670 671 672 673
	}

	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
674
xml_write_selection_info (XmlParseContext *ctxt, Sheet *sheet, xmlNodePtr tree)
675 676 677 678 679 680 681 682 683 684 685 686 687 688
{
	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) {
		SheetSelection *sel = ptr->data;
		xmlNodePtr child = xmlNewChild (tree, ctxt->ns, "Selection", NULL);
		xml_write_range (child, &sel->user);
	}
	g_list_free (copy);

689 690
	xml_set_value_int (tree, "CursorCol", sheet->cursor.edit_pos.col);
	xml_set_value_int (tree, "CursorRow", sheet->cursor.edit_pos.row);
691 692
}

693 694 695 696 697 698 699 700
/*
 * 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 ...
 */
701
static int
702
xml_get_color_value (xmlNodePtr node, const char *name, StyleColor **color)
703
{
704
	char *ret;
705 706
	int red, green, blue;

707
	ret = xml_value_get (node, name);
708
	if (ret == NULL) return 0;
709
	if (sscanf (ret, "%X:%X:%X", &red, &green, &blue) == 3){
710
		*color = style_color_new (red, green, blue);
711
		g_free (ret);
712 713
		return 1;
	}
714
	g_free (ret);
715
	return 0;
716 717 718 719 720 721
}

/*
 * Set a color value for a node either carried as an attibute or as
 * the content of a child.
 */
722 723
static void
xml_set_color_value (xmlNodePtr node, const char *name, StyleColor *val)
724
{
725
	char *ret;
726
	xmlNodePtr child;
727
	char str[4 * sizeof (val->color)];
728

729
	sprintf (str, "%X:%X:%X", val->color.red, val->color.green, val->color.blue);
730 731
	ret = xmlGetProp (node, name);
	if (ret != NULL){
732
		xmlFree (ret);
733 734 735 736 737 738 739 740 741 742 743 744
		xmlSetProp (node, name, str);
		return;
	}
	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, str);
			return;
		}
		child = child->next;
	}
	xmlSetProp (node, name, str);
745
}
746 747 748 749 750

/**
 **
 ** Private functions : mapping between in-memory structure and XML tree
 **
751
 **/
752
#if 0
753 754
static int
style_is_default_fore (StyleColor *color)
755
{
756 757
	if (!color)
		return TRUE;
758

759 760 761 762
	if (color->color.red == 0 && color->color.green == 0 && color->color.blue == 0)
		return TRUE;
	else
		return FALSE;
763 764
}

765 766
static int
style_is_default_back (StyleColor *color)
767
{
768 769
	if (!color)
		return TRUE;
770

771 772 773 774
	if (color->color.red == 0xffff && color->color.green == 0xffff && color->color.blue == 0xffff)
		return TRUE;
	else
		return FALSE;
775
}
776
#endif
777 778 779 780

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

Michael Meeks's avatar
Michael Meeks committed
782
static char *StyleSideNames[6] =
783 784 785 786
{
 	"Top",
 	"Bottom",
 	"Left",
Michael Meeks's avatar
Michael Meeks committed
787 788 789
 	"Right",
	"Diagonal",
	"Rev-Diagonal"
Arturo Espinosa's avatar
Arturo Espinosa committed
790
};
791

792
static xmlNodePtr
793
xml_write_style_border (XmlParseContext *ctxt,
794
			const MStyle *style)
795 796 797
{
	xmlNodePtr cur;
	xmlNodePtr side;
798
	int        i;
799

800 801 802
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_REV_DIAGONAL; i++) {
		MStyleBorder const *border;
		if (mstyle_is_element_set (style, i) &&
803
		    NULL != (border = mstyle_get_border (style, i))) {
Arturo Espinosa's avatar
Arturo Espinosa committed
804
			break;
805
		}
806
	}
807
	if (i > MSTYLE_BORDER_REV_DIAGONAL)
Arturo Espinosa's avatar
Arturo Espinosa committed
808
		return NULL;
809

810
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
811

Michael Meeks's avatar
Michael Meeks committed
812
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_REV_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
813 814
		MStyleBorder const *border;
		if (mstyle_is_element_set (style, i) &&
815
		    NULL != (border = mstyle_get_border (style, i))) {
Jody Goldberg's avatar
Jody Goldberg committed
816 817
			StyleBorderType t = border->line_type;
			StyleColor *col   = border->color;
818 819
 			side = xmlNewChild (cur, ctxt->ns,
					    StyleSideNames [i - MSTYLE_BORDER_TOP],
820 821
 					    NULL);
			xml_set_value_int (side, "Style", t);
822 823
			if (t != STYLE_BORDER_NONE)
				xml_set_color_value (side, "Color", col);
824
 		}
825 826
	}
	return cur;
827 828 829 830 831
}

/*
 * Create a StyleBorder equivalent to the XML subtree of doc.
 */
832
static void
833
xml_read_style_border (XmlParseContext *ctxt, xmlNodePtr tree, MStyle *mstyle)
834 835
{
	xmlNodePtr side;
836
	int        i;
837 838 839

	if (strcmp (tree->name, "StyleBorder")){
		fprintf (stderr,
840
			 "xml_read_style_border: invalid element type %s, 'StyleBorder' expected`\n",
841 842
			 tree->name);
	}
843

Michael Meeks's avatar
Michael Meeks committed
844
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_REV_DIAGONAL; i++) {
845 846
 		if ((side = xml_search_child (tree,
					      StyleSideNames [i - MSTYLE_BORDER_TOP])) != NULL) {
847 848 849 850
			int		 t;
			StyleColor      *color = NULL;
			MStyleBorder    *border;
			xml_get_value_int (side, "Style", &t);
851 852
			if (t != STYLE_BORDER_NONE)
				xml_get_color_value (side, "Color", &color);
853
			border = style_border_fetch ((StyleBorderType)t, color,
Michael Meeks's avatar
Michael Meeks committed
854
						     style_border_get_orientation (i));
855
			mstyle_set_border (mstyle, i, border);
856
 		}
857
	}
858 859 860 861 862
}

/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
863
xmlNodePtr
864
xml_write_style (XmlParseContext *ctxt,
865
		 MStyle *style)
866
{
867
	xmlNodePtr  cur, child;
868
	char       *tstr;
869

870
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
871

872 873 874 875 876 877 878 879 880 881 882 883
	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)) {
884
/*		if (!style_is_default_fore (mstyle_get_color (style, MSTYLE_COLOR_FORE)))*/
885 886 887
			xml_set_color_value (cur, "Fore", mstyle_get_color (style, MSTYLE_COLOR_FORE));
	}
	if (mstyle_is_element_set (style, MSTYLE_COLOR_BACK)) {
888
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_BACK)))*/
889 890
			xml_set_color_value (cur, "Back", mstyle_get_color (style, MSTYLE_COLOR_BACK));
	}
891
	if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN)) {
892
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_PATTERN)))*/
893 894
			xml_set_color_value (cur, "PatternColor", mstyle_get_color (style, MSTYLE_COLOR_PATTERN));
	}
Jody Goldberg's avatar
Jody Goldberg committed
895 896 897 898 899
	if (mstyle_is_element_set (style, MSTYLE_FORMAT)) {
		char *fmt = style_format_as_XL (mstyle_get_format (style), FALSE);
		xml_set_value (cur, "Format", fmt);
		g_free (fmt);
	}
900 901

	if (mstyle_is_element_set (style, MSTYLE_FONT_NAME) ||
902
	    mstyle_is_element_set (style, MSTYLE_FONT_SIZE) ||
903 904
	    mstyle_is_element_set (style, MSTYLE_FONT_BOLD) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_ITALIC) ||
905 906
	    mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH)) {
907 908 909 910 911 912
		const char *fontname;

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

914 915 916 917
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, fontname);
		child = xmlNewChild (cur, ctxt->ns, "Font", tstr);
		if (tstr) xmlFree (tstr);

918 919 920 921 922 923 924 925 926
		if (mstyle_is_element_set (style, MSTYLE_FONT_SIZE))
			xml_set_value_double (child, "Unit",
					      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
927 928 929
		if (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE))
			xml_set_value_int (child, "Underline",
					   (int)mstyle_get_font_uline (style));
930 931 932
		if (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
			xml_set_value_int (child, "StrikeThrough",
					   mstyle_get_font_strike (style));
933 934 935 936 937
	}

	child = xml_write_style_border (ctxt, style);
	if (child)
		xmlAddChild (cur, child);
938 939

	return cur;
940 941
}

942
static xmlNodePtr
943
xml_write_names (XmlParseContext *ctxt, GList *names)
944
{
945
	xmlNodePtr  cur;
946
	char       *tstr;
947 948 949 950 951 952 953 954

	if (!names)
		return NULL;

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

	while (names) {
		xmlNodePtr   tmp;
955
		NamedExpression    *expr_name = names->data;
956 957 958 959 960
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

		tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Name", NULL);
961 962 963
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, expr_name->name->str);
		xmlNewChild (tmp, ctxt->ns, "name", tstr);
		if (tstr) xmlFree (tstr);
964 965

		text = expr_name_value (expr_name);
966 967 968
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
		xmlNewChild (tmp, ctxt->ns, "value", tstr);
		if (tstr) xmlFree (tstr);
969 970 971 972 973
		g_free (text);

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

975 976 977 978
	return cur;
}

static void
979
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree, Workbook *wb,
980
		Sheet *sheet)
981 982 983 984 985 986 987 988
{
	xmlNodePtr child;

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

	child = tree->childs;
	while (child) {
989
		char *name  = NULL;
990 991 992 993 994
		if (child->name && !strcmp (child->name, "Name")) {
			xmlNodePtr bits;

			bits = child->childs;
			while (bits) {
995

996 997 998 999 1000 1001 1002 1003 1004 1005 1006
				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"));

1007
					if (!expr_name_create (wb, sheet, name, txt, &error))
1008 1009
						g_warning (error);

Daniel Veillard's avatar
Daniel Veillard committed
1010
					xmlFree (txt);
1011
				}
1012
				bits = bits->next;
1013 1014 1015 1016 1017 1018
			}
		}
		child = child->next;
	}
}

1019
static xmlNodePtr
1020
xml_write_summary (XmlParseContext *ctxt, SummaryInfo *summary_info)
1021 1022
{
	GList *items, *m;
1023
	char *tstr;
1024 1025
	xmlNodePtr cur;

1026
	if (!summary_info)
1027 1028
		return NULL;

1029
	m = items = summary_info_as_list (summary_info);
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

	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);
1043 1044 1045
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, sit->name);
			xmlNewChild (tmp, ctxt->ns, "name", tstr);
			if (tstr) xmlFree (tstr);
1046 1047 1048

			if (sit->type == SUMMARY_INT) {
				text = g_strdup_printf ("%d", sit->v.i);
1049 1050 1051
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-int", tstr);
				if (tstr) xmlFree (tstr);
1052 1053
			} else {
				text = summary_item_as_text (sit);
1054 1055 1056
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-string", tstr);
				if (tstr) xmlFree (tstr);
1057 1058
			}
			g_free (text);
1059
			xmlAddChild (cur, tmp);
1060 1061 1062 1063 1064 1065 1066 1067
		}
		items = g_list_next (items);
	}
	g_list_free (m);
	return cur;
}

static void
1068
xml_read_summary (XmlParseContext *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
1069
{
1070
	xmlNodePtr child;
1071

1072 1073 1074
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);
	g_return_if_fail (summary_info != NULL);
1075 1076 1077

	child = tree->childs;
	while (child) {
1078
		char *name = NULL;
Miguel de Icaza's avatar
Miguel de Icaza committed
1079

1080 1081 1082 1083 1084 1085
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

			bits = child->childs;
			while (bits) {
				SummaryItem *sit = NULL;
1086

1087
				if (!strcmp (bits->name, "name")) {
1088
					name = xmlNodeGetContent (bits);
1089 1090 1091 1092 1093
				} else {
					char *txt;
					g_return_if_fail (name);

					txt = xmlNodeGetContent (bits);
1094 1095 1096 1097
					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
1098
							sit = summary_item_new_int (name, atoi (txt));
1099

1100
						if (sit)
Arturo Espinosa's avatar