xml-io.c 77.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 8 9 10
 */

#include <config.h>
#include <gnome.h>
11
#include <locale.h>
12
#include <math.h>
13
#include <limits.h>
14
#include "gnumeric.h"
15
#include "gnome-xml/parser.h"
Daniel Veillard's avatar
Daniel Veillard committed
16
#include "gnome-xml/parserInternals.h"
17
#include "gnome-xml/xmlmemory.h"
18
#include "color.h"
19
#include "border.h"
20
#include "sheet-object.h"
21
#include "sheet-object-graphic.h"
22 23 24
#ifdef ENABLE_BONOBO
#	include "sheet-object-bonobo.h"
#endif
25
#include "print-info.h"
26
#include "xml-io.h"
Jody Goldberg's avatar
Jody Goldberg committed
27
#include "rendered-value.h"
28
#include "file.h"
29
#include "expr.h"
30
#include "expr-name.h"
31
#include "cell.h"
32
#include "cell-comment.h"
33
#include "workbook.h"
34 35
#include "workbook-view.h"
#include "selection.h"
36
#include "clipboard.h"
37
#include "command-context.h"
38 39 40 41

/*
 * A parsing context.
 */
42
struct _XmlParseContext {
43 44 45 46
	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
47
	Workbook *wb;		/* the associated workbook */
48
	GHashTable *style_table;/* old style styles compatibility */
Jody Goldberg's avatar
Jody Goldberg committed
49 50 51 52 53
	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
				 */
54 55 56 57 58 59 60 61 62 63 64 65 66 67 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
	XmlSheetObjectWriteFn write_fn;
	XmlSheetObjectReadFn  read_fn;
	gpointer              user_data;
};

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

95 96 97
/*
 * Internal stuff: xml helper functions.
 */
98

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

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

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

171 172 173
/*
 * Get a value for a node either carried as an attibute or as
 * the content of a child.
174 175
 *
 * Returns a g_malloc'ed string.  Caller must free.
176
 */
177
static char *
178
xml_value_get (xmlNodePtr node, const char *name)
179
{
180
	char *ret, *val;
181 182
	xmlNodePtr child;

183 184 185 186
	val = (char *) xmlGetProp (node, name);
	if (val != NULL) {
		ret = g_strdup (val);
		xmlFree (val);
Morten Welinder's avatar
Morten Welinder committed
187
		return ret;
188
	}
189 190 191
	child = node->childs;

	while (child != NULL) {
192 193 194 195
		if (!strcmp (child->name, name)) {
		        /*
			 * !!! Inefficient, but ...
			 */
196 197 198 199 200 201
			val = xmlNodeGetContent(child);
			if (val != NULL) {
				ret = g_strdup (val);
				xmlFree (val);
				return ret;
			}
202
		}
203 204 205 206
		child = child->next;
	}

	return NULL;
207 208
}

209 210 211 212
/*
 * Get a String value for a node either carried as an attibute or as
 * the content of a child.
 */
213
static String *
214
xml_get_value_string (xmlNodePtr node, const char *name)
215
{
216
	char *val;
217 218
	String *ret;

219 220 221 222 223
	val = xml_value_get (node, name);
	if (val == NULL) return NULL;
        ret = string_get (val);
	g_free (val);
	return ret;
224 225
}

226 227 228 229
/*
 * Get an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
230
static gboolean
231
xml_get_value_int (xmlNodePtr node, const char *name, int *val)
232
{
233
	char *ret;
234
	int i;
235
	int res;
236

237
	ret = xml_value_get (node, name);
238
	if (ret == NULL) return 0;
239
	res = sscanf (ret, "%d", &i);
240 241
	g_free (ret);

242 243
	if (res == 1) {
	        *val = i;
244
		return TRUE;
245
	}
246
	return FALSE;
247 248
}

249
#if 0
250 251 252 253
/*
 * Get a float value for a node either carried as an attibute or as
 * the content of a child.
 */
254
static int
255
xml_get_value_float (xmlNodePtr node, const char *name, float *val)
256
{
257 258
	int res;
	char *ret;
259 260
	float f;

261
	ret = xml_value_get (node, name);
262
	if (ret == NULL) return 0;
263
	res = sscanf (ret, "%f", &f);
264 265
	g_free (ret);

266 267
	if (res == 1) {
	        *val = f;
268 269 270
		return 1;
	}
	return 0;
271
}
272
#endif
273 274 275 276 277

/*
 * Get a double value for a node either carried as an attibute or as
 * the content of a child.
 */
278
static int
279
xml_get_value_double (xmlNodePtr node, const char *name, double *val)
280
{
281 282
	int res;
	char *ret;
283

284
	ret = xml_value_get (node, name);
285
	if (ret == NULL) return 0;
286
	res = sscanf (ret, "%lf", val);
287
	g_free (ret);
288

289
	return (res == 1);
290 291
}

292
#if 0
293 294 295
/*
 * Get a set of coodinates for a node, carried as the content of a child.
 */
296
static int
297
xml_get_coordinate (xmlNodePtr node, const char *name, double *x, double *y)
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 %lf)", x, y)
305
	g_free (ret);
306 307

	return (res == 2)
308
}
309
#endif
310 311 312 313 314

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

315
static int
316
xml_get_coordinates (xmlNodePtr node, const char *name,
317 318
		   double *x1, double *y1, double *x2, double *y2)
{
319 320
	int res;
	char *ret;
321

322
	ret = xml_value_get (node, name);
323
	if (ret == NULL) return 0;
324
	res = sscanf (ret, "(%lf %lf)(%lf %lf)", x1, y1, x2, y2);
325 326 327
	g_free (ret);

	if (res == 4)
328
		return 1;
329

330
	return 0;
331 332
}

333
#if 0
334 335 336
/*
 * Get a GnomeCanvasPoints for a node, carried as the content of a child.
 */
337
static GnomeCanvasPoints *
338
xml_get_gnome_canvas_points (xmlNodePtr node, const char *name)
339
{
340
	char *val;
341 342 343 344
	GnomeCanvasPoints *ret = NULL;
	int res;
	const char *ptr;
	int index = 0, i;
345
	float coord[20];	/* TODO: must be dynamic !!!! */
346

347
	val = xml_value_get (node, name);
348
	if (val == NULL) return NULL;
349 350 351 352 353
	ptr = val;
	do {
		while ((*ptr) && (*ptr != '('))
			ptr++;
		if (*ptr == 0)
354
			break;
355
		THIS_DOES_NOT_LOOK_RIGHT = 1;
356
		res = sscanf (ptr, "(%lf %lf)", &coord[index], &coord[index + 1]);
357 358 359 360 361
		if (res != 2)
			break;
		index += 2;
		ptr++;
	} while (res > 0);
362
	g_free (val);
363 364 365 366 367 368 369

	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];
370
	return ret;
371
}
372
#endif
373 374 375 376 377

/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
378
static void
379
xml_set_gnome_canvas_points (xmlNodePtr node, const char *name,
380
			     const GnomeCanvasPoints *val)
381 382 383
{
	xmlNodePtr child;
	char *str, *base;
384
	char *tstr;
385
	int i;
386
	int width1 = 30 + DBL_DIG;
387 388 389

	if (val == NULL)
		return;
390
	if (val->num_points < 0 || val->num_points > (INT_MAX - 1) / width1)
391
		return;
392
	base = str = g_malloc (val->num_points * width1 + 1);
393 394 395
	if (str == NULL)
		return;
	for (i = 0; i < val->num_points; i++){
396 397 398
		sprintf (str, "(%f %f)", val->coords[2 * i],
			 val->coords[2 * i + 1]);
		str += strlen (str);
399
	}
400
	*str = 0;
401 402 403 404 405

	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, base);
406
			g_free (base);
407 408 409 410
			return;
		}
		child = child->next;
	}
411 412 413 414

	tstr = xmlEncodeEntitiesReentrant (node->doc, base);
	xmlNewChild (node, NULL, name, tstr);
	if (tstr) xmlFree (tstr);
415
	g_free (base);
416 417
}

418 419 420 421
/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
422 423
static void
xml_set_value (xmlNodePtr node, const char *name, const char *val)
424
{
425
	char *ret;
426 427 428 429
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
	if (ret != NULL){
430
		xmlFree (ret);
431 432 433 434 435 436 437 438 439 440 441 442
		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);
443 444
}

445 446 447 448
/*
 * Set a String value for a node either carried as an attibute or as
 * the content of a child.
 */
449
static void
450
xml_set_value_string (xmlNodePtr node, const char *name, const String *val)
451
{
452
	char *ret;
453 454 455
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
456
	if (ret != NULL) {
457
		xmlFree (ret);
458 459 460 461 462 463 464 465 466 467 468 469
		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);
470 471
}

472 473 474 475
/*
 * Set an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
476
static void
477
xml_set_value_int (xmlNodePtr node, const char *name, int val)
478
{
479
	char *ret;
480
	xmlNodePtr child;
481
	char str[4 * sizeof (int)];
482

483
	sprintf (str, "%d", val);
484 485
	ret = xmlGetProp (node, name);
	if (ret != NULL){
486
		xmlFree (ret);
487 488 489 490 491 492 493 494 495 496 497 498
		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);
499 500 501 502 503 504
}

/*
 * Set a double value for a node either carried as an attibute or as
 * the content of a child.
 */
505
static void
506
xml_set_value_double (xmlNodePtr node, const char *name, double val)
507
{
508
	char *ret;
509
	xmlNodePtr child;
510
	char str[101 + DBL_DIG];
511

512 513 514 515
	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);
516

517 518
	ret = xmlGetProp (node, name);
	if (ret != NULL){
519
		xmlFree (ret);
520 521 522 523 524 525 526 527 528 529 530 531
		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);
532 533
}

534 535 536 537 538
static void
xml_set_print_unit (xmlNodePtr node, const char *name,
		    const PrintUnit * const pu)
{
	xmlNodePtr  child;
Jody Goldberg's avatar
Jody Goldberg committed
539
	char       *txt = "points";
540
	char       *tstr;
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559

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

560 561 562 563
	tstr = xmlEncodeEntitiesReentrant (node->doc, name);
	child = xmlNewChild (node, NULL, "PrintUnit", tstr);
	if (tstr) xmlFree (tstr);

564
	xml_set_value_double (child, "Points", pu->points);
565 566 567 568

	tstr = xmlEncodeEntitiesReentrant (node->doc, txt);
	xml_set_value (child, "PrefUnit", tstr);
	if (tstr) xmlFree (tstr);
569 570 571 572 573 574
}

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

576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
	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;
591
		g_free (txt);
592 593 594
	}
}

595
/*
596
 * Search a child by name, if needed go down the tree to find it.
597
 */
598 599
static xmlNodePtr
xml_search_child (xmlNodePtr node, const char *name)
600 601 602 603 604 605 606 607 608 609 610 611
{
	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){
612
		ret = xml_search_child (child, name);
613 614 615 616 617
		if (ret != NULL)
			return ret;
		child = child->next;
	}
	return NULL;
618 619
}

620 621 622
static gboolean
xml_read_range (xmlNodePtr tree, Range *res)
{
Jody Goldberg's avatar
Jody Goldberg committed
623
	return
624 625 626 627 628 629 630
	    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
631
xml_write_range (xmlNodePtr tree, const Range *value)
632 633 634 635 636 637 638 639
{
	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
640
xml_read_selection_info (XmlParseContext *ctxt, Sheet *sheet, xmlNodePtr tree)
641 642 643 644 645 646 647 648 649 650
{
	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))
651 652 653 654
			sheet_selection_add_range (sheet,
						   r.start.col, r.start.row,
						   r.start.col, r.start.row,
						   r.end.col, r.end.row);
655 656 657 658 659 660 661 662
	}

	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
663
xml_write_selection_info (XmlParseContext *ctxt, Sheet *sheet, xmlNodePtr tree)
664 665 666 667 668 669 670 671 672 673 674 675 676 677
{
	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);

678 679
	xml_set_value_int (tree, "CursorCol", sheet->cursor.edit_pos.col);
	xml_set_value_int (tree, "CursorRow", sheet->cursor.edit_pos.row);
680 681
}

682 683 684 685 686 687 688 689
/*
 * 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 ...
 */
690
static int
691
xml_get_color_value (xmlNodePtr node, const char *name, StyleColor **color)
692
{
693
	char *ret;
694 695
	int red, green, blue;

696
	ret = xml_value_get (node, name);
697
	if (ret == NULL) return 0;
698
	if (sscanf (ret, "%X:%X:%X", &red, &green, &blue) == 3){
699
		*color = style_color_new (red, green, blue);
700
		g_free (ret);
701 702
		return 1;
	}
703
	g_free (ret);
704
	return 0;
705 706 707 708 709 710
}

/*
 * Set a color value for a node either carried as an attibute or as
 * the content of a child.
 */
711 712
static void
xml_set_color_value (xmlNodePtr node, const char *name, StyleColor *val)
713
{
714
	char *ret;
715
	xmlNodePtr child;
716
	char str[4 * sizeof (val->color)];
717

718
	sprintf (str, "%X:%X:%X", val->color.red, val->color.green, val->color.blue);
719 720
	ret = xmlGetProp (node, name);
	if (ret != NULL){
721
		xmlFree (ret);
722 723 724 725 726 727 728 729 730 731 732 733
		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);
734
}
735 736 737 738 739

/**
 **
 ** Private functions : mapping between in-memory structure and XML tree
 **
740
 **/
741
#if 0
742 743
static int
style_is_default_fore (StyleColor *color)
744
{
745 746
	if (!color)
		return TRUE;
747

748 749 750 751
	if (color->color.red == 0 && color->color.green == 0 && color->color.blue == 0)
		return TRUE;
	else
		return FALSE;
752 753
}

754 755
static int
style_is_default_back (StyleColor *color)
756
{
757 758
	if (!color)
		return TRUE;
759

760 761 762 763
	if (color->color.red == 0xffff && color->color.green == 0xffff && color->color.blue == 0xffff)
		return TRUE;
	else
		return FALSE;
764
}
765
#endif
766 767 768 769

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

Michael Meeks's avatar
Michael Meeks committed
771
static char *StyleSideNames[6] =
772 773 774 775
{
 	"Top",
 	"Bottom",
 	"Left",
Michael Meeks's avatar
Michael Meeks committed
776 777 778
 	"Right",
	"Diagonal",
	"Rev-Diagonal"
Arturo Espinosa's avatar
Arturo Espinosa committed
779
};
780

781
static xmlNodePtr
782
xml_write_style_border (XmlParseContext *ctxt,
783
			const MStyle *style)
784 785 786
{
	xmlNodePtr cur;
	xmlNodePtr side;
787
	int        i;
788

789 790 791
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_REV_DIAGONAL; i++) {
		MStyleBorder const *border;
		if (mstyle_is_element_set (style, i) &&
792
		    NULL != (border = mstyle_get_border (style, i))) {
Arturo Espinosa's avatar
Arturo Espinosa committed
793
			break;
794
		}
795
	}
796
	if (i > MSTYLE_BORDER_REV_DIAGONAL)
Arturo Espinosa's avatar
Arturo Espinosa committed
797
		return NULL;
798

799
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
800

Michael Meeks's avatar
Michael Meeks committed
801
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_REV_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
802 803
		MStyleBorder const *border;
		if (mstyle_is_element_set (style, i) &&
804
		    NULL != (border = mstyle_get_border (style, i))) {
Jody Goldberg's avatar
Jody Goldberg committed
805 806
			StyleBorderType t = border->line_type;
			StyleColor *col   = border->color;
807 808
 			side = xmlNewChild (cur, ctxt->ns,
					    StyleSideNames [i - MSTYLE_BORDER_TOP],
809 810
 					    NULL);
			xml_set_value_int (side, "Style", t);
811 812
			if (t != STYLE_BORDER_NONE)
				xml_set_color_value (side, "Color", col);
813
 		}
814 815
	}
	return cur;
816 817 818 819 820
}

/*
 * Create a StyleBorder equivalent to the XML subtree of doc.
 */
821
static void
822
xml_read_style_border (XmlParseContext *ctxt, xmlNodePtr tree, MStyle *mstyle)
823 824
{
	xmlNodePtr side;
825
	int        i;
826 827 828

	if (strcmp (tree->name, "StyleBorder")){
		fprintf (stderr,
829
			 "xml_read_style_border: invalid element type %s, 'StyleBorder' expected`\n",
830 831
			 tree->name);
	}
832

Michael Meeks's avatar
Michael Meeks committed
833
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_REV_DIAGONAL; i++) {
834 835
 		if ((side = xml_search_child (tree,
					      StyleSideNames [i - MSTYLE_BORDER_TOP])) != NULL) {
836 837 838 839
			int		 t;
			StyleColor      *color = NULL;
			MStyleBorder    *border;
			xml_get_value_int (side, "Style", &t);
840 841
			if (t != STYLE_BORDER_NONE)
				xml_get_color_value (side, "Color", &color);
842
			border = style_border_fetch ((StyleBorderType)t, color,
Michael Meeks's avatar
Michael Meeks committed
843
						     style_border_get_orientation (i));
844
			mstyle_set_border (mstyle, i, border);
845
 		}
846
	}
847 848 849 850 851
}

/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
852
static xmlNodePtr
853
xml_write_style (XmlParseContext *ctxt,
854
		 MStyle *style)
855
{
856
	xmlNodePtr  cur, child;
857
	char       *tstr;
858

859
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
860

861 862 863 864 865 866 867 868 869 870 871 872
	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)) {
873
/*		if (!style_is_default_fore (mstyle_get_color (style, MSTYLE_COLOR_FORE)))*/
874 875 876
			xml_set_color_value (cur, "Fore", mstyle_get_color (style, MSTYLE_COLOR_FORE));
	}
	if (mstyle_is_element_set (style, MSTYLE_COLOR_BACK)) {
877
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_BACK)))*/
878 879
			xml_set_color_value (cur, "Back", mstyle_get_color (style, MSTYLE_COLOR_BACK));
	}
880
	if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN)) {
881
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_PATTERN)))*/
882 883
			xml_set_color_value (cur, "PatternColor", mstyle_get_color (style, MSTYLE_COLOR_PATTERN));
	}
884 885 886 887 888 889 890 891 892 893 894 895 896
	if (mstyle_is_element_set (style, MSTYLE_FORMAT))
		xml_set_value (cur, "Format", mstyle_get_format (style)->format);

	if (mstyle_is_element_set (style, MSTYLE_FONT_NAME) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_BOLD) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_ITALIC) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_SIZE)) {
		const char *fontname;

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

898 899 900 901
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, fontname);
		child = xmlNewChild (cur, ctxt->ns, "Font", tstr);
		if (tstr) xmlFree (tstr);

902 903 904 905 906 907 908 909 910
		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
911 912 913
		if (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE))
			xml_set_value_int (child, "Underline",
					   (int)mstyle_get_font_uline (style));
914 915 916
		if (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
			xml_set_value_int (child, "StrikeThrough",
					   mstyle_get_font_strike (style));
917 918 919 920 921
	}

	child = xml_write_style_border (ctxt, style);
	if (child)
		xmlAddChild (cur, child);
922 923

	return cur;
924 925
}

926
static xmlNodePtr
927
xml_write_names (XmlParseContext *ctxt, GList *names)
928
{
929
	xmlNodePtr  cur;
930
	char       *tstr;
931 932 933 934 935 936 937 938

	if (!names)
		return NULL;

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

	while (names) {
		xmlNodePtr   tmp;
939
		NamedExpression    *expr_name = names->data;
940 941 942 943 944
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

		tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Name", NULL);
945 946 947
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, expr_name->name->str);
		xmlNewChild (tmp, ctxt->ns, "name", tstr);
		if (tstr) xmlFree (tstr);
948 949

		text = expr_name_value (expr_name);
950 951 952
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
		xmlNewChild (tmp, ctxt->ns, "value", tstr);
		if (tstr) xmlFree (tstr);
953 954 955 956 957
		g_free (text);

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

959 960 961 962
	return cur;
}

static void
963
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree, Workbook *wb,
964
		Sheet *sheet)
965 966 967 968 969 970 971 972
{
	xmlNodePtr child;

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

	child = tree->childs;
	while (child) {
973
		char *name  = NULL;
974 975 976 977 978
		if (child->name && !strcmp (child->name, "Name")) {
			xmlNodePtr bits;

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

980 981 982 983 984 985 986 987 988 989 990
				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"));

991
					if (!expr_name_create (wb, sheet, name, txt, &error))
992 993
						g_warning (error);

Daniel Veillard's avatar
Daniel Veillard committed
994
					xmlFree (txt);
995
				}
996
				bits = bits->next;
997 998 999 1000 1001 1002
			}
		}
		child = child->next;
	}
}

1003
static xmlNodePtr
1004
xml_write_summary (XmlParseContext *ctxt, SummaryInfo *summary_info)
1005 1006
{
	GList *items, *m;
1007
	char *tstr;
1008 1009
	xmlNodePtr cur;

1010
	if (!summary_info)
1011 1012
		return NULL;

1013
	m = items = summary_info_as_list (summary_info);
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026

	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);
1027 1028 1029
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, sit->name);
			xmlNewChild (tmp, ctxt->ns, "name", tstr);
			if (tstr) xmlFree (tstr);
1030 1031 1032

			if (sit->type == SUMMARY_INT) {
				text = g_strdup_printf ("%d", sit->v.i);
1033 1034 1035
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-int", tstr);
				if (tstr) xmlFree (tstr);
1036 1037
			} else {
				text = summary_item_as_text (sit);
1038 1039 1040
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-string", tstr);
				if (tstr) xmlFree (tstr);
1041 1042
			}
			g_free (text);
1043
			xmlAddChild (cur, tmp);
1044 1045 1046 1047 1048 1049 1050 1051
		}
		items = g_list_next (items);
	}
	g_list_free (m);
	return cur;
}

static void
1052
xml_read_summary (XmlParseContext *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
1053
{
1054
	xmlNodePtr child;
1055

1056 1057 1058
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);
	g_return_if_fail (summary_info != NULL);
1059 1060 1061

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

1064 1065 1066 1067 1068 1069
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

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

1071
				if (!strcmp (bits->name, "name")) {
1072
					name = xmlNodeGetContent (bits);
1073 1074 1075 1076 1077
				} else {
					char *txt;
					g_return_if_fail (name);

					txt = xmlNodeGetContent (bits);
1078 1079 1080 1081
					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
1082
							sit = summary_item_new_int (name, atoi (txt));
1083

1084
						if (sit)
1085
							summary_info_add (summary_info, sit);
Daniel Veillard's avatar
Daniel Veillard committed
1086
						xmlFree (txt);
1087
					}
1088
				}
1089
				bits = bits->next;
1090 1091
			}
		}
Miguel de Icaza's avatar
Miguel de Icaza committed
1092
		if (name){
1093
			xmlFree (name);
Miguel de Icaza's avatar
Miguel de Icaza committed
1094 1095
			name = NULL;
		}
1096 1097 1098 1099
		child = child->next;
	}
}

Michael Meeks's avatar
Michael Meeks committed
1100 1101 1102 1103 1104 1105 1106