xml-io.c 87.4 KB
Newer Older
1
/* vim: set sw=8: */
2 3 4
/*
 * xml-io.c: save/read gnumeric Sheets using an XML encoding.
 *
5 6 7
 * Authors:
 *   Daniel Veillard <Daniel.Veillard@w3.org>
 *   Miguel de Icaza <miguel@gnu.org>
8
 *   Jody Goldberg <jgoldberg@home.com>
9 10
 */
#include <config.h>
Jody Goldberg's avatar
Jody Goldberg committed
11
#include "xml-io.h"
12
#include "style-color.h"
13
#include "style-border.h"
14
#include "style.h"
Jody Goldberg's avatar
Jody Goldberg committed
15 16
#include "sheet.h"
#include "sheet-style.h"
17
#include "sheet-object.h"
18
#include "sheet-object-cell-comment.h"
19
#include "print-info.h"
20
#include "file.h"
21
#include "expr.h"
22
#include "expr-name.h"
23
#include "cell.h"
24
#include "value.h"
25
#include "sheet-merge.h"
26 27 28
#include "io-context.h"
#include "command-context.h"
#include "workbook-control.h"
29
#include "workbook-view.h"
30
#include "workbook.h"
31
#include "selection.h"
32
#include "clipboard.h"
Jody Goldberg's avatar
Jody Goldberg committed
33
#include "format.h"
Jody Goldberg's avatar
Jody Goldberg committed
34
#include "ranges.h"
Chyla Zbigniew's avatar
Chyla Zbigniew committed
35
#include "file.h"
Jody Goldberg's avatar
Jody Goldberg committed
36
#include "str.h"
37
#include "plugin-util.h"
Jody Goldberg's avatar
Jody Goldberg committed
38

Jody Goldberg's avatar
Jody Goldberg committed
39 40 41
#include <gnome-xml/parser.h>
#include <gnome-xml/parserInternals.h>
#include <gnome-xml/xmlmemory.h>
42 43 44
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
45
#include <errno.h>
46
#include <zlib.h>
47
#include <string.h>
Jody Goldberg's avatar
Jody Goldberg committed
48
#include <gal/util/e-xml-utils.h>
49
#include <gal/widgets/e-colors.h>
50 51 52
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
Jody Goldberg's avatar
Jody Goldberg committed
53 54 55
#include <locale.h>
#include <math.h>
#include <limits.h>
56 57 58
#ifdef ENABLE_BONOBO
#include <bonobo/bonobo-exception.h>
#endif
59

60 61 62
/* Precision to use when saving point measures. */
#define POINT_SIZE_PRECISION 3

63
/* FIXME - tune the values below */
64 65
#define XML_INPUT_BUFFER_SIZE      4096
#define N_ELEMENTS_BETWEEN_UPDATES 20
66

67 68
static GnumFileOpener *xml_opener = NULL;
static GnumFileSaver  *xml_saver = NULL;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
69

70
GnumFileOpener *
71
gnumeric_xml_get_opener (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
72
{
73
	return xml_opener;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
74 75
}

76
GnumFileSaver *
77
gnumeric_xml_get_saver (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
78
{
79
	return xml_saver;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
80 81
}

82 83 84
XmlParseContext *
xml_parse_ctx_new_full (xmlDocPtr             doc,
			xmlNsPtr              ns,
Michael Meeks's avatar
Michael Meeks committed
85
			GnumericXMLVersion    version,
86 87 88 89 90 91
			XmlSheetObjectReadFn  read_fn,
			XmlSheetObjectWriteFn write_fn,
			gpointer              user_data)
{
	XmlParseContext *ctxt = g_new0 (XmlParseContext, 1);

Michael Meeks's avatar
Michael Meeks committed
92
	ctxt->version   = version;
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
	ctxt->doc       = doc;
	ctxt->ns        = ns;
	ctxt->expr_map  = g_hash_table_new (g_direct_hash, g_direct_equal);

	ctxt->write_fn  = write_fn;
	ctxt->read_fn   = read_fn;
	ctxt->user_data = user_data;

	return ctxt;
}

XmlParseContext *
xml_parse_ctx_new (xmlDocPtr doc,
		   xmlNsPtr  ns)
{
Michael Meeks's avatar
Michael Meeks committed
108
	return xml_parse_ctx_new_full (
Jody Goldberg's avatar
Jody Goldberg committed
109
		doc, ns, GNUM_XML_V6, NULL, NULL, NULL);
110
}
111

112 113 114 115 116 117 118 119
void
xml_parse_ctx_destroy (XmlParseContext *ctxt)
{
	g_return_if_fail (ctxt != NULL);

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

121 122 123
/*
 * Internal stuff: xml helper functions.
 */
124

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

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

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

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

209 210 211 212
	val = (char *) xmlGetProp (node, name);
	if (val != NULL) {
		ret = g_strdup (val);
		xmlFree (val);
Morten Welinder's avatar
Morten Welinder committed
213
		return ret;
214
	}
215
	child = node->xmlChildrenNode;
216 217

	while (child != NULL) {
218 219 220 221
		if (!strcmp (child->name, name)) {
		        /*
			 * !!! Inefficient, but ...
			 */
222 223 224 225 226 227
			val = xmlNodeGetContent(child);
			if (val != NULL) {
				ret = g_strdup (val);
				xmlFree (val);
				return ret;
			}
228
		}
229 230 231 232
		child = child->next;
	}

	return NULL;
233 234
}

235 236 237 238
/*
 * Get a String value for a node either carried as an attibute or as
 * the content of a child.
 */
239
String *
240
xml_get_value_string (xmlNodePtr node, const char *name)
241
{
242
	char *val;
243 244
	String *ret;

245 246 247 248 249
	val = xml_value_get (node, name);
	if (val == NULL) return NULL;
        ret = string_get (val);
	g_free (val);
	return ret;
250 251
}

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

263
	ret = xml_value_get (node, name);
264
	if (ret == NULL) return 0;
265
	res = sscanf (ret, "%d", &i);
266 267
	g_free (ret);

268 269
	if (res == 1) {
	        *val = i;
270
		return TRUE;
271
	}
272
	return FALSE;
273 274
}

275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
static gboolean
xml_get_prop_cellpos (xmlNodePtr node, char const *name, CellPos *val)
{
	char *str;
	int dummy;
	gboolean res;

	str = (char *) xmlGetProp (node, name);
	if (val == NULL)
		return FALSE;
	res = parse_cell_name (str, &val->col, &val->row, TRUE, &dummy);
	xmlFree (str);
	return res;
}

static void
xml_set_prop_cellpos (xmlNodePtr node, char const *name, CellPos const *val)
{
	xmlSetProp (node, name, cell_pos_name (val));
}

296
#if 0
297 298 299 300
/*
 * Get a float value for a node either carried as an attibute or as
 * the content of a child.
 */
301
static int
302
xml_get_value_float (xmlNodePtr node, const char *name, float *val)
303
{
304 305
	int res;
	char *ret;
306 307
	float f;

308
	ret = xml_value_get (node, name);
309
	if (ret == NULL) return 0;
310
	res = sscanf (ret, "%f", &f);
311 312
	g_free (ret);

313 314
	if (res == 1) {
	        *val = f;
315 316 317
		return 1;
	}
	return 0;
318
}
319
#endif
320 321 322 323 324

/*
 * Get a double value for a node either carried as an attibute or as
 * the content of a child.
 */
325
gboolean
326
xml_get_value_double (xmlNodePtr node, const char *name, double *val)
327
{
328 329
	int res;
	char *ret;
330

331
	ret = xml_value_get (node, name);
332 333
	if (ret == NULL)
		return FALSE;
334
	res = sscanf (ret, "%lf", val);
335
	g_free (ret);
336

337
	return (res == 1);
338 339 340 341 342 343
}

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

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

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

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

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

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

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

414
	xmlSetProp (node, name, str);
415 416
}

417 418 419 420 421 422
/*
 * Set a double value for a node with POINT_SIZE_PRECISION digits precision.
 */
static void
xml_set_value_points (xmlNodePtr node, const char *name, double val)
{
423
	xml_set_value_double (node, name, val, POINT_SIZE_PRECISION);
424 425
}

426 427 428 429 430
static void
xml_set_print_unit (xmlNodePtr node, const char *name,
		    const PrintUnit * const pu)
{
	xmlNodePtr  child;
Jody Goldberg's avatar
Jody Goldberg committed
431
	char       *txt = "points";
432
	char       *tstr;
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451

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

452
	child = xmlNewChild (node, NULL, name, NULL);
453

454
	xml_set_value_points (child, "Points", pu->points);
455 456

	tstr = xmlEncodeEntitiesReentrant (node->doc, txt);
457
	xml_set_value_cstr (child, "PrefUnit", tstr);
458
	if (tstr) xmlFree (tstr);
459 460 461 462 463 464
}

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

466 467 468 469 470 471 472 473
	g_return_if_fail (pu != NULL);
	g_return_if_fail (node != NULL);

	xml_get_value_double (node, "Points", &pu->points);
	txt = xml_value_get  (node, "PrefUnit");
	if (txt) {
		if (!g_strcasecmp (txt, "points"))
			pu->desired_display = UNIT_POINTS;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
474
		else if (!strcmp (txt, "mm"))
475
			pu->desired_display = UNIT_MILLIMETER;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
476
		else if (!strcmp (txt, "cm"))
477
			pu->desired_display = UNIT_CENTIMETER;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
478
		else if (!strcmp (txt, "in"))
479
			pu->desired_display = UNIT_INCH;
480
		g_free (txt);
481 482 483
	}
}

484
static gboolean
485
xml_read_range (xmlNodePtr tree, Range *r)
486
{
487 488 489 490 491 492
	gboolean res =
	    xml_get_value_int (tree, "startCol", &r->start.col) &&
	    xml_get_value_int (tree, "startRow", &r->start.row) &&
	    xml_get_value_int (tree, "endCol",   &r->end.col) &&
	    xml_get_value_int (tree, "endRow",   &r->end.row);

493 494
	/* Older versions of gnumeric had some boundary problems */
	range_ensure_sanity (r);
495 496

	return res;
497 498 499
}

static void
Jody Goldberg's avatar
Jody Goldberg committed
500
xml_write_range (xmlNodePtr tree, Range const *r)
501
{
Jody Goldberg's avatar
Jody Goldberg committed
502 503 504 505 506 507
	g_return_if_fail (range_is_sane (r));

	xml_set_value_int (tree, "startCol", r->start.col);
	xml_set_value_int (tree, "startRow", r->start.row);
	xml_set_value_int (tree, "endCol",   r->end.col);
	xml_set_value_int (tree, "endRow",   r->end.row);
508 509 510
}

static void
511
xml_read_selection_info (XmlParseContext *ctxt, xmlNodePtr tree)
512 513 514
{
	Range r;
	int row, col;
515
	Sheet *sheet = ctxt->sheet;
516
	xmlNodePtr sel, selections = e_xml_get_child_by_name (tree, "Selections");
517

518 519 520
	if (selections == NULL)
		return;

521
	sheet_selection_reset (sheet);
522
	for (sel = selections->xmlChildrenNode; sel; sel = sel->next)
523
		if (xml_read_range (sel, &r))
524 525 526 527
			sheet_selection_add_range (sheet,
						   r.start.col, r.start.row,
						   r.start.col, r.start.row,
						   r.end.col, r.end.row);
528

529 530
	if (xml_get_value_int (selections, "CursorCol", &col) &&
	    xml_get_value_int (selections, "CursorRow", &row))
531
		sheet_set_edit_pos (sheet, col, row);
532 533 534
}

static void
535
xml_write_selection_info (XmlParseContext *ctxt, Sheet const *sheet, xmlNodePtr tree)
536 537 538 539 540 541 542 543
{
	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) {
544
		Range const *r = ptr->data;
545
		xmlNodePtr child = xmlNewChild (tree, ctxt->ns, "Selection", NULL);
546
		xml_write_range (child, r);
547 548 549
	}
	g_list_free (copy);

550 551
	xml_set_value_int (tree, "CursorCol", sheet->edit_pos_real.col);
	xml_set_value_int (tree, "CursorRow", sheet->edit_pos_real.row);
552 553
}

554 555 556 557 558 559 560 561
/*
 * 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 ...
 */
562
static int
563
xml_get_color_value (xmlNodePtr node, const char *name, StyleColor **color)
564
{
565
	char *ret;
566 567
	int red, green, blue;

568
	ret = xml_value_get (node, name);
569
	if (ret == NULL) return 0;
570
	if (sscanf (ret, "%X:%X:%X", &red, &green, &blue) == 3){
571
		*color = style_color_new (red, green, blue);
572
		g_free (ret);
573 574
		return 1;
	}
575
	g_free (ret);
576
	return 0;
577 578
}

579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
GdkColor *xml_get_value_color (xmlNodePtr node, const char *name)
{
	GdkColor   *color;
	StyleColor *style_color;

	if (xml_get_color_value (node, name, &style_color)) {
		color = g_new0 (GdkColor, 1);
		color->red   = style_color->color.red;
		color->green = style_color->color.green;
		color->blue  = style_color->color.blue;
		e_color_alloc_gdk (color);
		style_color_unref (style_color);
		return (color);
	} else
		return (NULL);
}

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

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

625 626 627 628 629 630 631 632 633 634 635 636 637
void
xml_set_value_color (xmlNodePtr node, const char *name, const GdkColor *color)
{
	StyleColor *style_color;

	if (!color)
		return;

	style_color = style_color_new (color->red, color->green, color->blue);
	xml_set_color_value (node, name, style_color);
	style_color_unref (style_color);
}

638 639 640 641
/**
 **
 ** Private functions : mapping between in-memory structure and XML tree
 **
642
 **/
643
#if 0
644 645
static int
style_is_default_fore (StyleColor *color)
646
{
647 648
	if (!color)
		return TRUE;
649

650 651 652 653
	if (color->color.red == 0 && color->color.green == 0 && color->color.blue == 0)
		return TRUE;
	else
		return FALSE;
654 655
}

656 657
static int
style_is_default_back (StyleColor *color)
658
{
659 660
	if (!color)
		return TRUE;
661

662 663 664 665
	if (color->color.red == 0xffff && color->color.green == 0xffff && color->color.blue == 0xffff)
		return TRUE;
	else
		return FALSE;
666
}
667
#endif
668 669 670 671

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

Michael Meeks's avatar
Michael Meeks committed
673
static char *StyleSideNames[6] =
674 675 676 677
{
 	"Top",
 	"Bottom",
 	"Left",
Michael Meeks's avatar
Michael Meeks committed
678 679 680
 	"Right",
	"Diagonal",
	"Rev-Diagonal"
Arturo Espinosa's avatar
Arturo Espinosa committed
681
};
682

683
static xmlNodePtr
684
xml_write_style_border (XmlParseContext *ctxt,
685
			const MStyle *style)
686 687 688
{
	xmlNodePtr cur;
	xmlNodePtr side;
689
	int        i;
690

691
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
692
		StyleBorder const *border;
693
		if (mstyle_is_element_set (style, i) &&
694
		    NULL != (border = mstyle_get_border (style, i))) {
Arturo Espinosa's avatar
Arturo Espinosa committed
695
			break;
696
		}
697
	}
698
	if (i > MSTYLE_BORDER_DIAGONAL)
Arturo Espinosa's avatar
Arturo Espinosa committed
699
		return NULL;
700

701
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
702

703
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
704
		StyleBorder const *border;
Jody Goldberg's avatar
Jody Goldberg committed
705
		if (mstyle_is_element_set (style, i) &&
706
		    NULL != (border = mstyle_get_border (style, i))) {
Jody Goldberg's avatar
Jody Goldberg committed
707 708
			StyleBorderType t = border->line_type;
			StyleColor *col   = border->color;
709 710
 			side = xmlNewChild (cur, ctxt->ns,
					    StyleSideNames [i - MSTYLE_BORDER_TOP],
711 712
 					    NULL);
			xml_set_value_int (side, "Style", t);
713 714
			if (t != STYLE_BORDER_NONE)
				xml_set_color_value (side, "Color", col);
715
 		}
716 717
	}
	return cur;
718 719 720 721 722
}

/*
 * Create a StyleBorder equivalent to the XML subtree of doc.
 */
723
static void
724
xml_read_style_border (XmlParseContext *ctxt, xmlNodePtr tree, MStyle *mstyle)
725 726
{
	xmlNodePtr side;
727
	int        i;
728 729 730

	if (strcmp (tree->name, "StyleBorder")){
		fprintf (stderr,
731
			 "xml_read_style_border: invalid element type %s, 'StyleBorder' expected`\n",
732 733
			 tree->name);
	}
734

735
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
736
 		if ((side = e_xml_get_child_by_name (tree,
737
					      StyleSideNames [i - MSTYLE_BORDER_TOP])) != NULL) {
738 739
			int		 t;
			StyleColor      *color = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
740
			StyleBorder    *border;
741
			xml_get_value_int (side, "Style", &t);
742 743
			if (t != STYLE_BORDER_NONE)
				xml_get_color_value (side, "Color", &color);
744
			border = style_border_fetch ((StyleBorderType)t, color,
Michael Meeks's avatar
Michael Meeks committed
745
						     style_border_get_orientation (i));
746
			mstyle_set_border (mstyle, i, border);
747
 		}
748
	}
749 750 751 752 753
}

/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
754
xmlNodePtr
755
xml_write_style (XmlParseContext *ctxt,
756
		 MStyle *style)
757
{
758
	xmlNodePtr  cur, child;
759
	char       *tstr;
760

761
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
762

763 764 765 766
	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));
767 768
	if (mstyle_is_element_set (style, MSTYLE_WRAP_TEXT))
		xml_set_value_int (cur, "WrapText", mstyle_get_wrap_text (style));
769 770 771 772
	if (mstyle_is_element_set (style, MSTYLE_ORIENTATION))
		xml_set_value_int (cur, "Orient", mstyle_get_orientation (style));
	if (mstyle_is_element_set (style, MSTYLE_PATTERN))
		xml_set_value_int (cur, "Shade", mstyle_get_pattern (style));
Jody Goldberg's avatar
Jody Goldberg committed
773 774
	if (mstyle_is_element_set (style, MSTYLE_INDENT))
		xml_set_value_int (cur, "Indent", mstyle_get_indent (style));
775 776

	if (mstyle_is_element_set (style, MSTYLE_COLOR_FORE)) {
777
/*		if (!style_is_default_fore (mstyle_get_color (style, MSTYLE_COLOR_FORE)))*/
778 779 780
			xml_set_color_value (cur, "Fore", mstyle_get_color (style, MSTYLE_COLOR_FORE));
	}
	if (mstyle_is_element_set (style, MSTYLE_COLOR_BACK)) {
781
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_BACK)))*/
782 783
			xml_set_color_value (cur, "Back", mstyle_get_color (style, MSTYLE_COLOR_BACK));
	}
784
	if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN)) {
785
/*		if (!style_is_default_back (mstyle_get_color (style, MSTYLE_COLOR_PATTERN)))*/
786 787
			xml_set_color_value (cur, "PatternColor", mstyle_get_color (style, MSTYLE_COLOR_PATTERN));
	}
Jody Goldberg's avatar
Jody Goldberg committed
788 789
	if (mstyle_is_element_set (style, MSTYLE_FORMAT)) {
		char *fmt = style_format_as_XL (mstyle_get_format (style), FALSE);
790
		xml_set_value_cstr (cur, "Format", fmt);
Jody Goldberg's avatar
Jody Goldberg committed
791 792
		g_free (fmt);
	}
793 794

	if (mstyle_is_element_set (style, MSTYLE_FONT_NAME) ||
795
	    mstyle_is_element_set (style, MSTYLE_FONT_SIZE) ||
796 797
	    mstyle_is_element_set (style, MSTYLE_FONT_BOLD) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_ITALIC) ||
798 799
	    mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH)) {
800 801 802 803 804 805
		const char *fontname;

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

807 808 809 810
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, fontname);
		child = xmlNewChild (cur, ctxt->ns, "Font", tstr);
		if (tstr) xmlFree (tstr);

811
		if (mstyle_is_element_set (style, MSTYLE_FONT_SIZE))
812
			xml_set_value_points (child, "Unit",
813 814 815 816 817 818 819
					      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
820 821 822
		if (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE))
			xml_set_value_int (child, "Underline",
					   (int)mstyle_get_font_uline (style));
823 824 825
		if (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
			xml_set_value_int (child, "StrikeThrough",
					   mstyle_get_font_strike (style));
826 827 828 829 830
	}

	child = xml_write_style_border (ctxt, style);
	if (child)
		xmlAddChild (cur, child);
831 832

	return cur;
833 834
}

835
static xmlNodePtr
836
xml_write_names (XmlParseContext *ctxt, GList *names)
837
{
838
	xmlNodePtr  cur;
839
	char       *tstr;
840

841 842 843 844
#if 0  /* Don't return, We need to have a names node in the worksheet
	   * all the time becasue xml_search_child looks for a node down the
	   * tree and it will find the first "Names" node in the sheet #1
	   */
845 846
	if (!names)
		return NULL;
847
#endif
848 849 850 851 852

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

	while (names) {
		xmlNodePtr   tmp;
853
		NamedExpression    *expr_name = names->data;
854 855 856 857 858
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

		tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Name", NULL);
859 860 861
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, expr_name->name->str);
		xmlNewChild (tmp, ctxt->ns, "name", tstr);
		if (tstr) xmlFree (tstr);
862 863

		text = expr_name_value (expr_name);
864 865 866
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
		xmlNewChild (tmp, ctxt->ns, "value", tstr);
		if (tstr) xmlFree (tstr);
867 868 869 870 871
		g_free (text);

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

873 874 875 876
	return cur;
}

static void
877
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree, Workbook *wb,
878
		Sheet *sheet)
879 880 881 882 883 884
{
	xmlNodePtr child;

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

885
	child = tree->xmlChildrenNode;
886
	while (child) {
887
		char *name  = NULL;
888 889 890
		if (child->name && !strcmp (child->name, "Name")) {
			xmlNodePtr bits;

891
			bits = child->xmlChildrenNode;
892
			while (bits) {
893

894 895 896
				if (!strcmp (bits->name, "name")) {
					name = xmlNodeGetContent (bits);
				} else {
897
					char     *txt;
898
					ParseError  perr;
899 900 901 902 903 904
					g_return_if_fail (name != NULL);

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

905 906 907
					if (!expr_name_create (wb, sheet, name, txt, &perr))
						g_warning (perr.message);
					parse_error_free (&perr);
908

Daniel Veillard's avatar
Daniel Veillard committed
909
					xmlFree (txt);
910
				}
911
				bits = bits->next;
912 913 914 915 916 917
			}
		}
		child = child->next;
	}
}

918
static xmlNodePtr
919
xml_write_summary (XmlParseContext *ctxt, SummaryInfo *summary_info)
920 921
{
	GList *items, *m;
922
	char *tstr;
923 924
	xmlNodePtr cur;

925
	if (!summary_info)
926 927
		return NULL;

928
	m = items = summary_info_as_list (summary_info);
929 930 931 932 933 934 935 936 937 938 939 940 941

	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);
942 943 944
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, sit->name);
			xmlNewChild (tmp, ctxt->ns, "name", tstr);
			if (tstr) xmlFree (tstr);
945 946 947

			if (sit->type == SUMMARY_INT) {
				text = g_strdup_printf ("%d", sit->v.i);
948 949 950
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-int", tstr);
				if (tstr) xmlFree (tstr);
951 952
			} else {
				text = summary_item_as_text (sit);
953 954 955
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-string", tstr);
				if (tstr) xmlFree (tstr);
956 957
			}
			g_free (text);
958
			xmlAddChild (cur, tmp);
959 960 961 962 963 964 965 966
		}
		items = g_list_next (items);
	}
	g_list_free (m);
	return cur;
}

static void
967
xml_read_summary (XmlParseContext *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
968
{
969
	xmlNodePtr child;
970

971 972 973
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);
	g_return_if_fail (summary_info != NULL);
974

975
	child = tree->xmlChildrenNode;
976
	while (child) {
977
		char *name = NULL;
Miguel de Icaza's avatar
Miguel de Icaza committed
978

979 980 981
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

982
			bits = child->xmlChildrenNode;
983 984
			while (bits) {
				SummaryItem *sit = NULL;
985

986
				if (!strcmp (bits->name, "name")) {
987
					name = xmlNodeGetContent (bits);
988 989 990 991 992
				} else {
					char *txt;
					g_return_if_fail (name);

					txt = xmlNodeGetContent (bits);
993 994 995 996
					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
997
							sit = summary_item_new_int (name, atoi (txt));
998

999
						if (sit)
1000
							summary_info_add (summary_info, sit);
Daniel Veillard's avatar
Daniel Veillard committed
1001
						xmlFree (txt);
1002
					}
1003
				}
1004
				bits = bits->next;
1005 1006
			}
		}
Miguel de Icaza's avatar
Miguel de Icaza committed
1007
		if (name){
1008
			xmlFree (name);
Miguel de Icaza's avatar
Miguel de Icaza committed
1009 1010
			name = NULL;
		}
1011 1012 1013 1014
		child = child->next;
	}
}