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

#include <config.h>
#include <stdio.h>
#include <gnome.h>
12
#include <locale.h>
13
#include <math.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
#include "print-info.h"
23
#include "xml-io.h"
Jody Goldberg's avatar
Jody Goldberg committed
24
#include "rendered-value.h"
25
#include "file.h"
26
#include "expr.h"
27
#include "expr-name.h"
28
#include "cell.h"
29
#include "cell-comment.h"
30
#include "workbook.h"
31 32
#include "workbook-view.h"
#include "selection.h"
33
#include "clipboard.h"
34
#include "command-context.h"
35 36 37 38

/*
 * A parsing context.
 */
39 40 41 42 43
typedef struct {
	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
44
	Workbook *wb;		/* the associated workbook */
45
	GHashTable *style_table;/* old style styles compatibility */
Jody Goldberg's avatar
Jody Goldberg committed
46 47 48 49 50
	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
				 */
51 52
} parse_xml_context_t;

53 54 55
/*
 * Internal stuff: xml helper functions.
 */
56

57 58 59 60
static void
xml_arg_set (GtkArg *arg, gchar *string)
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
61 62 63 64 65 66 67
	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
68
		if (!strcmp (string, "TRUE"))
JP Rosevear's avatar
JP Rosevear committed
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
			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;
94 95 96 97 98 99 100
	}
}

static char *
xml_arg_get (GtkArg *arg)
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
101 102 103 104 105 106 107 108 109 110 111 112 113 114
	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
115
		return g_strdup_printf("%li", GTK_VALUE_LONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
116
	case GTK_TYPE_ULONG:
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
117
		return g_strdup_printf("%lu", GTK_VALUE_ULONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
118 119 120 121 122 123
	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));
124 125 126 127
	}

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

129 130 131
/*
 * Get a value for a node either carried as an attibute or as
 * the content of a child.
132 133
 *
 * Returns a g_malloc'ed string.  Caller must free.
134
 */
135
static char *
136
xml_value_get (xmlNodePtr node, const char *name)
137
{
138
	char *ret, *val;
139 140
	xmlNodePtr child;

141 142 143 144
	val = (char *) xmlGetProp (node, name);
	if (val != NULL) {
		ret = g_strdup (val);
		xmlFree (val);
Morten Welinder's avatar
Morten Welinder committed
145
		return ret;
146
	}
147 148 149
	child = node->childs;

	while (child != NULL) {
150 151 152 153
		if (!strcmp (child->name, name)) {
		        /*
			 * !!! Inefficient, but ...
			 */
154 155 156 157 158 159
			val = xmlNodeGetContent(child);
			if (val != NULL) {
				ret = g_strdup (val);
				xmlFree (val);
				return ret;
			}
160
		}
161 162 163 164
		child = child->next;
	}

	return NULL;
165 166
}

167 168 169 170
/*
 * Get a String value for a node either carried as an attibute or as
 * the content of a child.
 */
171
static String *
172
xml_get_value_string (xmlNodePtr node, const char *name)
173
{
174
	char *val;
175 176
	String *ret;

177 178 179 180 181
	val = xml_value_get (node, name);
	if (val == NULL) return NULL;
        ret = string_get (val);
	g_free (val);
	return ret;
182 183
}

184 185 186 187
/*
 * Get an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
188
static gboolean
189
xml_get_value_int (xmlNodePtr node, const char *name, int *val)
190
{
191
	char *ret;
192
	int i;
193
	int res;
194

195
	ret = xml_value_get (node, name);
196
	if (ret == NULL) return 0;
197
	res = sscanf (ret, "%d", &i);
198 199
	g_free (ret);

200 201
	if (res == 1) {
	        *val = i;
202
		return TRUE;
203
	}
204
	return FALSE;
205 206
}

207
#if 0
208 209 210 211
/*
 * Get a float value for a node either carried as an attibute or as
 * the content of a child.
 */
212
static int
213
xml_get_value_float (xmlNodePtr node, const char *name, float *val)
214
{
215 216
	int res;
	char *ret;
217 218
	float f;

219
	ret = xml_value_get (node, name);
220
	if (ret == NULL) return 0;
221
	res = sscanf (ret, "%f", &f);
222 223
	g_free (ret);

224 225
	if (res == 1) {
	        *val = f;
226 227 228
		return 1;
	}
	return 0;
229
}
230
#endif
231 232 233 234 235

/*
 * Get a double value for a node either carried as an attibute or as
 * the content of a child.
 */
236
static int
237
xml_get_value_double (xmlNodePtr node, const char *name, double *val)
238
{
239 240
	int res;
	char *ret;
241

242
	ret = xml_value_get (node, name);
243
	if (ret == NULL) return 0;
244
	res = sscanf (ret, "%lf", val);
245
	g_free (ret);
246

247
	return (res == 1);
248 249
}

250
#if 0
251 252 253
/*
 * Get a set of coodinates for a node, carried as the content of a child.
 */
254
static int
255
xml_get_coordinate (xmlNodePtr node, const char *name, double *x, double *y)
256
{
257 258
	int res;
	char *ret;
259
	float X, Y;
260

261
	ret = xml_value_get (node, name);
262
	if (ret == NULL) return 0;
263
	res = sscanf (ret, "(%lf %lf)", x, y)
264
	g_free (ret);
265 266

	return (res == 2)
267
}
268
#endif
269 270 271 272 273

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

274
static int
275
xml_get_coordinates (xmlNodePtr node, const char *name,
276 277
		   double *x1, double *y1, double *x2, double *y2)
{
278 279
	int res;
	char *ret;
280

281
	ret = xml_value_get (node, name);
282
	if (ret == NULL) return 0;
283
	res = sscanf (ret, "(%lf %lf)(%lf %lf)", x1, y1, x2, y2);
284 285 286
	g_free (ret);

	if (res == 4)
287
		return 1;
288

289
	return 0;
290 291
}

292
#if 0
293 294 295
/*
 * Get a GnomeCanvasPoints for a node, carried as the content of a child.
 */
296
static GnomeCanvasPoints *
297
xml_get_gnome_canvas_points (xmlNodePtr node, const char *name)
298
{
299
	char *val;
300 301 302 303
	GnomeCanvasPoints *ret = NULL;
	int res;
	const char *ptr;
	int index = 0, i;
304
	float coord[20];	/* TODO: must be dynamic !!!! */
305

306
	val = xml_value_get (node, name);
307
	if (val == NULL) return NULL;
308 309 310 311 312
	ptr = val;
	do {
		while ((*ptr) && (*ptr != '('))
			ptr++;
		if (*ptr == 0)
313
			break;
314
		res = sscanf (ptr, "(%lf %lf)", &coord[index], &coord[index + 1]);
315 316 317 318 319
		if (res != 2)
			break;
		index += 2;
		ptr++;
	} while (res > 0);
320
	g_free (val);
321 322 323 324 325 326 327

	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];
328
	return ret;
329
}
330
#endif
331 332 333 334 335

/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
336
static void
337
xml_set_gnome_canvas_points (xmlNodePtr node, const char *name,
338
			     GnomeCanvasPoints *val)
339 340 341
{
	xmlNodePtr child;
	char *str, *base;
342
	char *tstr;
343 344 345 346 347 348
	int i;

	if (val == NULL)
		return;
	if ((val->num_points < 0) || (val->num_points > 5000))
		return;
349
	base = str = g_malloc (val->num_points * 30 * sizeof (char) + 1);
350 351 352
	if (str == NULL)
		return;
	for (i = 0; i < val->num_points; i++){
353 354 355
		sprintf (str, "(%f %f)", val->coords[2 * i],
			 val->coords[2 * i + 1]);
		str += strlen (str);
356
	}
357
	*str = 0;
358 359 360 361 362

	child = node->childs;
	while (child != NULL){
		if (!strcmp (child->name, name)){
			xmlNodeSetContent (child, base);
363
			g_free (base);
364 365 366 367
			return;
		}
		child = child->next;
	}
368 369 370 371

	tstr = xmlEncodeEntitiesReentrant (node->doc, base);
	xmlNewChild (node, NULL, name, tstr);
	if (tstr) xmlFree (tstr);
372
	g_free (base);
373 374
}

375 376 377 378
/*
 * Set a string value for a node either carried as an attibute or as
 * the content of a child.
 */
379 380
static void
xml_set_value (xmlNodePtr node, const char *name, const char *val)
381
{
382
	char *ret;
383 384 385 386
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
	if (ret != NULL){
387
		xmlFree (ret);
388 389 390 391 392 393 394 395 396 397 398 399
		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);
400 401
}

402 403 404 405
/*
 * Set a String value for a node either carried as an attibute or as
 * the content of a child.
 */
406 407
static void
xml_set_value_string (xmlNodePtr node, const char *name, String *val)
408
{
409
	char *ret;
410 411 412 413
	xmlNodePtr child;

	ret = xmlGetProp (node, name);
	if (ret != NULL){
414
		xmlFree (ret);
415 416 417 418 419 420 421 422 423 424 425 426
		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);
427 428
}

429 430 431 432
/*
 * Set an integer value for a node either carried as an attibute or as
 * the content of a child.
 */
433
static void
434
xml_set_value_int (xmlNodePtr node, const char *name, int val)
435
{
436
	char *ret;
437 438 439 440 441 442
	xmlNodePtr child;
	char str[101];

	snprintf (str, 100, "%d", val);
	ret = xmlGetProp (node, name);
	if (ret != NULL){
443
		xmlFree (ret);
444 445 446 447 448 449 450 451 452 453 454 455
		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);
456 457
}

458
#if 0
459 460 461 462
/*
 * Set a float value for a node either carried as an attibute or as
 * the content of a child.
 */
463
static void
464
xml_set_value_float (xmlNodePtr node, const char *name, float val)
465
{
466
	char *ret;
467 468 469 470 471 472
	xmlNodePtr child;
	char str[101];

	snprintf (str, 100, "%f", val);
	ret = xmlGetProp (node, name);
	if (ret != NULL){
473
		xmlFree (ret);
474 475 476 477 478 479 480 481 482 483 484 485
		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);
486
}
487
#endif
488 489 490 491 492

/*
 * Set a double value for a node either carried as an attibute or as
 * the content of a child.
 */
493
static void
494
xml_set_value_double (xmlNodePtr node, const char *name, double val)
495
{
496
	char *ret;
497
	xmlNodePtr child;
498
	char str[101 + DBL_DIG];
499

500 501 502 503
	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);
504

505 506
	ret = xmlGetProp (node, name);
	if (ret != NULL){
507
		xmlFree (ret);
508 509 510 511 512 513 514 515 516 517 518 519
		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);
520 521
}

522 523 524 525 526
static void
xml_set_print_unit (xmlNodePtr node, const char *name,
		    const PrintUnit * const pu)
{
	xmlNodePtr  child;
Jody Goldberg's avatar
Jody Goldberg committed
527
	char       *txt = "points";
528
	char       *tstr;
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547

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

548 549 550 551
	tstr = xmlEncodeEntitiesReentrant (node->doc, name);
	child = xmlNewChild (node, NULL, "PrintUnit", tstr);
	if (tstr) xmlFree (tstr);

552
	xml_set_value_double (child, "Points", pu->points);
553 554 555 556

	tstr = xmlEncodeEntitiesReentrant (node->doc, txt);
	xml_set_value (child, "PrefUnit", tstr);
	if (tstr) xmlFree (tstr);
557 558 559 560 561 562
}

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

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
	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;
579
		g_free (txt);
580 581 582
	}
}

583
/*
584
 * Search a child by name, if needed go down the tree to find it.
585
 */
586 587
static xmlNodePtr
xml_search_child (xmlNodePtr node, const char *name)
588 589 590 591 592 593 594 595 596 597 598 599
{
	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){
600
		ret = xml_search_child (child, name);
601 602 603 604 605
		if (ret != NULL)
			return ret;
		child = child->next;
	}
	return NULL;
606 607
}

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

	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
xml_write_selection_info (parse_xml_context_t *ctxt, Sheet *sheet, xmlNodePtr tree)
{
	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);

666 667
	xml_set_value_int (tree, "CursorCol", sheet->cursor.edit_pos.col);
	xml_set_value_int (tree, "CursorRow", sheet->cursor.edit_pos.row);
668 669
}

670 671 672 673 674 675 676 677
/*
 * 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 ...
 */
678
static int
679
xml_get_color_value (xmlNodePtr node, const char *name, StyleColor **color)
680
{
681
	char *ret;
682 683
	int red, green, blue;

684
	ret = xml_value_get (node, name);
685
	if (ret == NULL) return 0;
686
	if (sscanf (ret, "%X:%X:%X", &red, &green, &blue) == 3){
687
		*color = style_color_new (red, green, blue);
688
		g_free (ret);
689 690
		return 1;
	}
691
	g_free (ret);
692
	return 0;
693 694 695 696 697 698
}

/*
 * Set a color value for a node either carried as an attibute or as
 * the content of a child.
 */
699 700
static void
xml_set_color_value (xmlNodePtr node, const char *name, StyleColor *val)
701
{
702
	char *ret;
703 704 705 706 707 708
	xmlNodePtr child;
	char str[101];

	snprintf (str, 100, "%X:%X:%X", val->color.red, val->color.green, val->color.blue);
	ret = xmlGetProp (node, name);
	if (ret != NULL){
709
		xmlFree (ret);
710 711 712 713 714 715 716 717 718 719 720 721
		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);
722
}
723 724 725 726 727

/**
 **
 ** Private functions : mapping between in-memory structure and XML tree
 **
728
 **/
729
#if 0
730 731
static int
style_is_default_fore (StyleColor *color)
732
{
733 734
	if (!color)
		return TRUE;
735

736 737 738 739
	if (color->color.red == 0 && color->color.green == 0 && color->color.blue == 0)
		return TRUE;
	else
		return FALSE;
740 741
}

742 743
static int
style_is_default_back (StyleColor *color)
744
{
745 746
	if (!color)
		return TRUE;
747

748 749 750 751
	if (color->color.red == 0xffff && color->color.green == 0xffff && color->color.blue == 0xffff)
		return TRUE;
	else
		return FALSE;
752
}
753
#endif
754 755 756 757

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

Michael Meeks's avatar
Michael Meeks committed
759
static char *StyleSideNames[6] =
760 761 762 763
{
 	"Top",
 	"Bottom",
 	"Left",
Michael Meeks's avatar
Michael Meeks committed
764 765 766
 	"Right",
	"Diagonal",
	"Rev-Diagonal"
Arturo Espinosa's avatar
Arturo Espinosa committed
767
};
768

769
static xmlNodePtr
770 771
xml_write_style_border (parse_xml_context_t *ctxt,
			const MStyle *style)
772 773 774
{
	xmlNodePtr cur;
	xmlNodePtr side;
775
	int        i;
776

777 778 779
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_REV_DIAGONAL; i++) {
		MStyleBorder const *border;
		if (mstyle_is_element_set (style, i) &&
780
		    NULL != (border = mstyle_get_border (style, i))) {
Arturo Espinosa's avatar
Arturo Espinosa committed
781
			break;
782
		}
783
	}
784
	if (i > MSTYLE_BORDER_REV_DIAGONAL)
Arturo Espinosa's avatar
Arturo Espinosa committed
785
		return NULL;
786

787
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
788

Michael Meeks's avatar
Michael Meeks committed
789
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_REV_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
790 791
		MStyleBorder const *border;
		if (mstyle_is_element_set (style, i) &&
792
		    NULL != (border = mstyle_get_border (style, i))) {
Jody Goldberg's avatar
Jody Goldberg committed
793 794
			StyleBorderType t = border->line_type;
			StyleColor *col   = border->color;
795 796
 			side = xmlNewChild (cur, ctxt->ns,
					    StyleSideNames [i - MSTYLE_BORDER_TOP],
797
 					    NULL);
798
 			xml_set_color_value (side, "Color", col);
799
			xml_set_value_int (side, "Style", t);
800
 		}
801 802
	}
	return cur;
803 804 805 806 807
}

/*
 * Create a StyleBorder equivalent to the XML subtree of doc.
 */
808
static void
809
xml_read_style_border (parse_xml_context_t *ctxt, xmlNodePtr tree, MStyle *mstyle)
810 811
{
	xmlNodePtr side;
812
	int        i;
813 814 815

	if (strcmp (tree->name, "StyleBorder")){
		fprintf (stderr,
816
			 "xml_read_style_border: invalid element type %s, 'StyleBorder' expected`\n",
817 818
			 tree->name);
	}
819

Michael Meeks's avatar
Michael Meeks committed
820
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_REV_DIAGONAL; i++) {
821 822
 		if ((side = xml_search_child (tree,
					      StyleSideNames [i - MSTYLE_BORDER_TOP])) != NULL) {
823 824 825 826 827
			int		 t;
			StyleColor      *color = NULL;
			MStyleBorder    *border;
			xml_get_value_int (side, "Style", &t);
			xml_get_color_value (side, "Color", &color);
828
			border = style_border_fetch ((StyleBorderType)t, color,
Michael Meeks's avatar
Michael Meeks committed
829
						     style_border_get_orientation (i));
830 831
			if (border)
				mstyle_set_border (mstyle, i, border);
832
 		}
833
	}
834 835 836 837 838
}

/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
839
static xmlNodePtr
840 841
xml_write_style (parse_xml_context_t *ctxt,
		 MStyle *style)
842
{
843
	xmlNodePtr  cur, child;
844
	char       *tstr;
845

846
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
847

848 849 850 851 852 853 854 855 856 857 858 859
	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)) {
860
/*		if (!style_is_default_fore (mstyle_get_color (style, MSTYLE_COLOR_FORE)))*/
861 862 863
			xml_set_color_value (cur, "Fore", mstyle_get_color (style, MSTYLE_COLOR_FORE));
	}
	if (mstyle_is_element_set (style, MSTYLE_COLOR_BACK)) {
864
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_BACK)))*/
865 866
			xml_set_color_value (cur, "Back", mstyle_get_color (style, MSTYLE_COLOR_BACK));
	}
867
	if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN)) {
868
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_PATTERN)))*/
869 870
			xml_set_color_value (cur, "PatternColor", mstyle_get_color (style, MSTYLE_COLOR_PATTERN));
	}
871 872 873 874 875 876 877 878 879 880 881 882 883
	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";
884

885 886 887 888
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, fontname);
		child = xmlNewChild (cur, ctxt->ns, "Font", tstr);
		if (tstr) xmlFree (tstr);

889 890 891 892 893 894 895 896 897
		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
898 899 900
		if (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE))
			xml_set_value_int (child, "Underline",
					   (int)mstyle_get_font_uline (style));
901 902 903
		if (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
			xml_set_value_int (child, "StrikeThrough",
					   mstyle_get_font_strike (style));
904 905 906 907 908
	}

	child = xml_write_style_border (ctxt, style);
	if (child)
		xmlAddChild (cur, child);
909 910

	return cur;
911 912
}

913
static xmlNodePtr
914
xml_write_names (parse_xml_context_t *ctxt, GList *names)
915
{
916
	xmlNodePtr  cur;
917
	char       *tstr;
918 919 920 921 922 923 924 925

	if (!names)
		return NULL;

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

	while (names) {
		xmlNodePtr   tmp;
926
		NamedExpression    *expr_name = names->data;
927 928 929 930 931
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

		tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Name", NULL);
932 933 934
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, expr_name->name->str);
		xmlNewChild (tmp, ctxt->ns, "name", tstr);
		if (tstr) xmlFree (tstr);
935 936

		text = expr_name_value (expr_name);
937 938 939
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
		xmlNewChild (tmp, ctxt->ns, "value", tstr);
		if (tstr) xmlFree (tstr);
940 941 942 943 944
		g_free (text);

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

946 947 948 949
	return cur;
}

static void
950 951
xml_read_names (parse_xml_context_t *ctxt, xmlNodePtr tree, Workbook *wb,
		Sheet *sheet)
952 953 954 955 956 957 958 959
{
	xmlNodePtr child;

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

	child = tree->childs;
	while (child) {
960
		char *name  = NULL;
961 962 963 964 965
		if (child->name && !strcmp (child->name, "Name")) {
			xmlNodePtr bits;

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

967 968 969 970 971 972 973 974 975 976 977
				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"));

978
					if (!expr_name_create (wb, sheet, name, txt, &error))
979 980
						g_warning (error);

Daniel Veillard's avatar
Daniel Veillard committed
981
					xmlFree (txt);
982
				}
983
				bits = bits->next;
984 985 986 987 988 989
			}
		}
		child = child->next;
	}
}

990
static xmlNodePtr
991
xml_write_summary (parse_xml_context_t *ctxt, SummaryInfo *summary_info)
992 993
{
	GList *items, *m;
994
	char *tstr;
995 996
	xmlNodePtr cur;

997
	if (!summary_info)
998 999
		return NULL;

1000
	m = items = summary_info_as_list (summary_info);
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013

	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);
1014 1015 1016
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, sit->name);
			xmlNewChild (tmp, ctxt->ns, "name", tstr);
			if (tstr) xmlFree (tstr);
1017 1018 1019

			if (sit->type == SUMMARY_INT) {
				text = g_strdup_printf ("%d", sit->v.i);
1020 1021 1022
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-int", tstr);
				if (tstr) xmlFree (tstr);
1023 1024
			} else {
				text = summary_item_as_text (sit);
1025 1026 1027
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-string", tstr);
				if (tstr) xmlFree (tstr);
1028 1029
			}
			g_free (text);
1030
			xmlAddChild (cur, tmp);
1031 1032 1033 1034 1035 1036 1037 1038
		}
		items = g_list_next (items);
	}
	g_list_free (m);
	return cur;
}

static void
1039
xml_read_summary (parse_xml_context_t *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
1040
{
1041
	xmlNodePtr child;
1042

1043 1044 1045
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);
	g_return_if_fail (summary_info != NULL);
1046 1047 1048

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

1051 1052 1053 1054 1055 1056
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

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

1058
				if (!strcmp (bits->name, "name")) {
1059
					name = xmlNodeGetContent (bits);
1060 1061 1062 1063 1064
				} else {
					char *txt;
					g_return_if_fail (name);

					txt = xmlNodeGetContent (bits);
1065 1066 1067 1068
					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
1069
							sit = summary_item_new_int (name, atoi (txt));
1070

1071
						if (sit)
1072
							summary_info_add (summary_info, sit);
Daniel Veillard's avatar
Daniel Veillard committed
1073
						xmlFree (txt);
1074
					}
1075
				}
1076
				bits = bits->next;
1077 1078
			}
		}
Miguel de Icaza's avatar
Miguel de Icaza committed
1079
		if (name){
1080
			xmlFree (name);
Miguel de Icaza's avatar
Miguel de Icaza committed
1081 1082
			name = NULL;
		}
1083 1084 1085 1086
		child = child->next;
	}
}

Michael Meeks's avatar
Michael Meeks committed
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
static void
xml_set_print_hf (xmlNodePtr node, const char *name,
		  const PrintHF * const hf)
{
	xmlNodePtr  child;

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

	child = xmlNewChild (node, NULL, name, NULL);
1097 1098 1099
	xml_set_value (child, "Left", hf->left_format);
	xml_set_value (child, "Middle", hf->middle_format);
	xml_set_value (child, "Right", hf->right_format);
Michael Meeks's avatar
Michael Meeks committed
1100 1101 1102
}

static void
Arturo Espinosa's avatar
Arturo Espinosa committed
1103
xml_get_print_hf (xmlNodePtr node, PrintHF *const hf)
Michael Meeks's avatar
Michael Meeks committed
1104 1105
{
	char *txt;
1106

Michael Meeks's avatar
Michael Meeks committed
1107 1108 1109 1110
	g_return_if_fail (hf != NULL);
	g_return_if_fail (node != NULL);

	txt = xml_value_get (node, "Left");
1111
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
1112 1113
		if (hf->left_format)
			g_free (hf->left_format);
1114
		hf->left_format = txt;
Arturo Espinosa's avatar
Arturo Espinosa committed
1115
	}
1116

Michael Meeks's avatar
Michael Meeks committed
1117
	txt = xml_value_get (node, "Middle");
1118
	if (txt) {