xml-io.c 84.6 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
#include <sys/types.h>
#include <sys/stat.h>
44
#include <unistd.h>
45
#include <fcntl.h>
46
#include <errno.h>
47
#include <zlib.h>
48
#include <string.h>
Jody Goldberg's avatar
Jody Goldberg committed
49
#include <gal/util/e-xml-utils.h>
50
#include <gal/widgets/e-colors.h>
51 52 53
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
Jody Goldberg's avatar
Jody Goldberg committed
54 55 56
#include <locale.h>
#include <math.h>
#include <limits.h>
57 58 59
#ifdef ENABLE_BONOBO
#include <bonobo/bonobo-exception.h>
#endif
60

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

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

68 69
/* ------------------------------------------------------------------------- */

70 71
static GnumFileOpener *xml_opener = NULL;
static GnumFileSaver  *xml_saver = NULL;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
72

73
GnumFileOpener *
74
gnumeric_xml_get_opener (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
75
{
76
	return xml_opener;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
77 78
}

79
GnumFileSaver *
80
gnumeric_xml_get_saver (void)
Chyla Zbigniew's avatar
Chyla Zbigniew committed
81
{
82
	return xml_saver;
Chyla Zbigniew's avatar
Chyla Zbigniew committed
83 84
}

85 86
/* ------------------------------------------------------------------------- */

87 88 89
XmlParseContext *
xml_parse_ctx_new_full (xmlDocPtr             doc,
			xmlNsPtr              ns,
Michael Meeks's avatar
Michael Meeks committed
90
			GnumericXMLVersion    version,
91 92 93 94 95 96
			XmlSheetObjectReadFn  read_fn,
			XmlSheetObjectWriteFn write_fn,
			gpointer              user_data)
{
	XmlParseContext *ctxt = g_new0 (XmlParseContext, 1);

97 98 99 100 101
	ctxt->version      = version;
	ctxt->doc          = doc;
	ctxt->ns           = ns;
	ctxt->expr_map     = g_hash_table_new (g_direct_hash, g_direct_equal);
	ctxt->shared_exprs = g_ptr_array_new ();
102

103 104 105
	ctxt->write_fn     = write_fn;
	ctxt->read_fn      = read_fn;
	ctxt->user_data    = user_data;
106 107 108 109 110 111 112 113

	return ctxt;
}

XmlParseContext *
xml_parse_ctx_new (xmlDocPtr doc,
		   xmlNsPtr  ns)
{
Michael Meeks's avatar
Michael Meeks committed
114
	return xml_parse_ctx_new_full (
Jody Goldberg's avatar
Jody Goldberg committed
115
		doc, ns, GNUM_XML_LATEST, NULL, NULL, NULL);
116
}
117

118 119 120 121 122 123
void
xml_parse_ctx_destroy (XmlParseContext *ctxt)
{
	g_return_if_fail (ctxt != NULL);

	g_hash_table_destroy (ctxt->expr_map);
124 125
	g_ptr_array_free (ctxt->shared_exprs, TRUE);

126 127
	g_free (ctxt);
}
128

129 130
/* ------------------------------------------------------------------------- */

131 132 133
/*
 * Internal stuff: xml helper functions.
 */
134

135
static void
136
xml_arg_set (GtkArg *arg, const gchar *string)
137 138
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
139 140 141 142 143 144 145
	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
146
		if (!strcmp (string, "TRUE"))
JP Rosevear's avatar
JP Rosevear committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
			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;
172 173 174 175 176 177 178
	}
}

static char *
xml_arg_get (GtkArg *arg)
{
	switch (arg->type) {
JP Rosevear's avatar
JP Rosevear committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192
	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
193
		return g_strdup_printf("%li", GTK_VALUE_LONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
194
	case GTK_TYPE_ULONG:
Jon Kåre Hellan's avatar
Jon Kåre Hellan committed
195
		return g_strdup_printf("%lu", GTK_VALUE_ULONG (*arg));
JP Rosevear's avatar
JP Rosevear committed
196 197 198 199 200 201
	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));
202 203 204 205
	}

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

207 208
/* Get an xmlChar * value for a node carried as an attibute
 * result must be xmlFree
209
 */
210
xmlChar *
211
xml_node_get_cstr (xmlNodePtr node, char const *name)
212
{
213
	return name ? xmlGetProp (node, name) : xmlNodeGetContent (node);
214
}
215

216 217
/* Set a string value for a node carried as an attibute */
void
218
xml_node_set_cstr (xmlNodePtr node, char const *name, char const *val)
219
{
220 221 222 223
	if (name)
		xmlSetProp (node, name, val);
	else
		xmlNodeSetContent (node, val);
224 225
}

226
/* Get an integer value for a node carried as an attibute */
227
gboolean
228
xml_node_get_int (xmlNodePtr node, char const *name, int *val)
229
{
230
	xmlChar *buf;
231
	int res;
232

233 234 235 236 237
	buf = xml_node_get_cstr (node, name);
	if (buf == NULL)
		return FALSE;
	res = sscanf (buf, "%d", val);
	xmlFree (buf);
238

239
	return (res == 1);
240 241
}

242
/* Set an integer value for a node carried as an attibute */
243
void
244
xml_node_set_int (xmlNodePtr node, char const *name, int val)
245
{
246 247
	char str[4 * sizeof (int)];
	sprintf (str, "%d", val);
Jody Goldberg's avatar
Jody Goldberg committed
248
	xml_node_set_cstr (node, name, str);
249 250
}

251 252
/* Get a double value for a node carried as an attibute */
gboolean
253
xml_node_get_double (xmlNodePtr node, char const *name, double *val)
254
{
255
	int res;
256
	xmlChar *buf;
257

258 259
	buf = xml_node_get_cstr (node, name);
	if (buf == NULL)
260
		return FALSE;
261 262
	res = sscanf (buf, "%lf", val);
	xmlFree (buf);
263 264

	return (res == 1);
265 266 267 268 269 270
}

/*
 * Set a double value for a node either carried as an attibute or as
 * the content of a child.
 */
271
void
272
xml_node_set_double (xmlNodePtr node, char const *name, double val,
273
		     int precision)
274
{
275
	char str[101 + DBL_DIG];
276

277 278
	if (precision < 0 || precision > DBL_DIG)
		precision = DBL_DIG;
279

280
	if (fabs (val) < 1e9 && fabs (val) > 1e-5)
281
		snprintf (str, 100 + DBL_DIG, "%.*g", precision, val);
282 283
	else
		snprintf (str, 100 + DBL_DIG, "%f", val);
284

Jody Goldberg's avatar
Jody Goldberg committed
285
	xml_node_set_cstr (node, name, str);
286 287
}

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
static gboolean
xml_node_get_cellpos (xmlNodePtr node, char const *name, CellPos *val)
{
	xmlChar *buf;
	int dummy;
	gboolean res;

	buf = xml_node_get_cstr (node, name);
	if (val == NULL)
		return FALSE;
	res = parse_cell_name (buf, &val->col, &val->row, TRUE, &dummy);
	xmlFree (buf);
	return res;
}

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

309 310 311 312
/*
 * Set a double value for a node with POINT_SIZE_PRECISION digits precision.
 */
static void
313
xml_node_set_points (xmlNodePtr node, char const *name, double val)
314
{
315
	xml_node_set_double (node, name, val, POINT_SIZE_PRECISION);
316 317
}

318
static void
319
xml_node_set_print_unit (xmlNodePtr node, char const *name,
320
			 PrintUnit const *pu)
321 322
{
	xmlNodePtr  child;
Jody Goldberg's avatar
Jody Goldberg committed
323
	char       *txt = "points";
324
	char       *tstr;
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343

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

344
	child = xmlNewChild (node, NULL, name, NULL);
345

346
	xml_node_set_points (child, "Points", pu->points);
347 348

	tstr = xmlEncodeEntitiesReentrant (node->doc, txt);
349
	xml_node_set_cstr (child, "PrefUnit", tstr);
350
	if (tstr) xmlFree (tstr);
351 352 353
}

static void
354
xml_node_get_print_unit (xmlNodePtr node, PrintUnit * const pu)
355 356
{
	char       *txt;
357

358 359 360
	g_return_if_fail (pu != NULL);
	g_return_if_fail (node != NULL);

361
	xml_node_get_double (node, "Points", &pu->points);
362
	txt = xmlGetProp  (node, "PrefUnit");
363 364 365
	if (txt) {
		if (!g_strcasecmp (txt, "points"))
			pu->desired_display = UNIT_POINTS;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
366
		else if (!strcmp (txt, "mm"))
367
			pu->desired_display = UNIT_MILLIMETER;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
368
		else if (!strcmp (txt, "cm"))
369
			pu->desired_display = UNIT_CENTIMETER;
Jody Goldberg's avatar
Doh!  
Jody Goldberg committed
370
		else if (!strcmp (txt, "in"))
371
			pu->desired_display = UNIT_INCH;
372
		xmlFree (txt);
373 374 375
	}
}

376
static gboolean
377
xml_node_get_range (xmlNodePtr tree, Range *r)
378
{
379
	gboolean res =
380 381 382 383
	    xml_node_get_int (tree, "startCol", &r->start.col) &&
	    xml_node_get_int (tree, "startRow", &r->start.row) &&
	    xml_node_get_int (tree, "endCol",   &r->end.col) &&
	    xml_node_get_int (tree, "endRow",   &r->end.row);
384

385 386
	/* Older versions of gnumeric had some boundary problems */
	range_ensure_sanity (r);
387 388

	return res;
389 390 391
}

static void
392
xml_node_set_range (xmlNodePtr tree, Range const *r)
393
{
Jody Goldberg's avatar
Jody Goldberg committed
394 395
	g_return_if_fail (range_is_sane (r));

396 397 398 399
	xml_node_set_int (tree, "startCol", r->start.col);
	xml_node_set_int (tree, "startRow", r->start.row);
	xml_node_set_int (tree, "endCol",   r->end.col);
	xml_node_set_int (tree, "endRow",   r->end.row);
400 401 402
}

static void
403
xml_read_selection_info (XmlParseContext *ctxt, xmlNodePtr tree)
404 405 406
{
	Range r;
	int row, col;
407
	Sheet *sheet = ctxt->sheet;
408
	xmlNodePtr sel, selections = e_xml_get_child_by_name (tree, "Selections");
409

410 411 412
	if (selections == NULL)
		return;

413
	sheet_selection_reset (sheet);
414
	for (sel = selections->xmlChildrenNode; sel; sel = sel->next)
415
		if (xml_node_get_range (sel, &r))
416 417 418 419
			sheet_selection_add_range (sheet,
						   r.start.col, r.start.row,
						   r.start.col, r.start.row,
						   r.end.col, r.end.row);
420

421 422
	if (xml_node_get_int (selections, "CursorCol", &col) &&
	    xml_node_get_int (selections, "CursorRow", &row))
423
		sheet_set_edit_pos (sheet, col, row);
424 425 426
}

static void
427
xml_write_selection_info (XmlParseContext *ctxt, Sheet const *sheet, xmlNodePtr tree)
428 429 430 431 432 433 434 435
{
	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) {
436
		Range const *r = ptr->data;
437
		xmlNodePtr child = xmlNewChild (tree, ctxt->ns, "Selection", NULL);
438
		xml_node_set_range (child, r);
439 440 441
	}
	g_list_free (copy);

442 443
	xml_node_set_int (tree, "CursorCol", sheet->edit_pos_real.col);
	xml_node_set_int (tree, "CursorRow", sheet->edit_pos_real.row);
444 445
}

446 447 448 449 450 451 452 453
/*
 * 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 ...
 */
454
static int
455
xml_get_color_value (xmlNodePtr node, char const *name, StyleColor **color)
456
{
457
	char *ret;
458 459
	int red, green, blue;

460
	ret = xmlGetProp (node, name);
461
	if (ret == NULL) return 0;
462
	if (sscanf (ret, "%X:%X:%X", &red, &green, &blue) == 3){
463
		*color = style_color_new (red, green, blue);
464
		g_free (ret);
465 466
		return 1;
	}
467
	xmlFree (ret);
468
	return 0;
469 470
}

471
GdkColor *
472
xml_node_get_gdkcolor (xmlNodePtr node, char const *name)
473 474 475 476 477 478 479 480 481 482 483 484 485
{
	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
486
		return NULL;
487 488
}

489 490 491 492
/*
 * Set a color value for a node either carried as an attibute or as
 * the content of a child.
 */
493
static void
494
xml_node_set_color (xmlNodePtr node, char const *name, StyleColor *val)
495
{
496 497
	char str[4 * sizeof (val->color)];
	sprintf (str, "%X:%X:%X", val->color.red, val->color.green, val->color.blue);
Jody Goldberg's avatar
Jody Goldberg committed
498
	xml_node_set_cstr (node, name, str);
499
}
500

501
void
502
xml_node_set_gdkcolor (xmlNodePtr node, char const *name, const GdkColor *color)
503 504 505 506 507 508 509
{
	StyleColor *style_color;

	if (!color)
		return;

	style_color = style_color_new (color->red, color->green, color->blue);
510
	xml_node_set_color (node, name, style_color);
511 512 513
	style_color_unref (style_color);
}

514 515 516
/*
 * Create an XML subtree of doc equivalent to the given StyleBorder.
 */
517
static char const *StyleSideNames[6] =
518 519 520 521
{
 	"Top",
 	"Bottom",
 	"Left",
Michael Meeks's avatar
Michael Meeks committed
522 523 524
 	"Right",
	"Diagonal",
	"Rev-Diagonal"
Arturo Espinosa's avatar
Arturo Espinosa committed
525
};
526

527
static xmlNodePtr
528
xml_write_style_border (XmlParseContext *ctxt,
529
			const MStyle *style)
530 531 532
{
	xmlNodePtr cur;
	xmlNodePtr side;
533
	int        i;
534

535
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
536
		StyleBorder const *border;
537
		if (mstyle_is_element_set (style, i) &&
538
		    NULL != (border = mstyle_get_border (style, i))) {
Arturo Espinosa's avatar
Arturo Espinosa committed
539
			break;
540
		}
541
	}
542
	if (i > MSTYLE_BORDER_DIAGONAL)
Arturo Espinosa's avatar
Arturo Espinosa committed
543
		return NULL;
544

545
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "StyleBorder", NULL);
546

547
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
Jody Goldberg's avatar
Jody Goldberg committed
548
		StyleBorder const *border;
Jody Goldberg's avatar
Jody Goldberg committed
549
		if (mstyle_is_element_set (style, i) &&
550
		    NULL != (border = mstyle_get_border (style, i))) {
Jody Goldberg's avatar
Jody Goldberg committed
551 552
			StyleBorderType t = border->line_type;
			StyleColor *col   = border->color;
553 554
 			side = xmlNewChild (cur, ctxt->ns,
					    StyleSideNames [i - MSTYLE_BORDER_TOP],
555
 					    NULL);
556
			xml_node_set_int (side, "Style", t);
557
			if (t != STYLE_BORDER_NONE)
558
				xml_node_set_color (side, "Color", col);
559
 		}
560 561
	}
	return cur;
562 563 564 565 566
}

/*
 * Create a StyleBorder equivalent to the XML subtree of doc.
 */
567
static void
568
xml_read_style_border (XmlParseContext *ctxt, xmlNodePtr tree, MStyle *mstyle)
569 570
{
	xmlNodePtr side;
571
	int        i;
572 573 574

	if (strcmp (tree->name, "StyleBorder")){
		fprintf (stderr,
575
			 "xml_read_style_border: invalid element type %s, 'StyleBorder' expected`\n",
576 577
			 tree->name);
	}
578

579
	for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
580
 		if ((side = e_xml_get_child_by_name (tree,
581
					      StyleSideNames [i - MSTYLE_BORDER_TOP])) != NULL) {
582 583
			int		 t;
			StyleColor      *color = NULL;
Jody Goldberg's avatar
Jody Goldberg committed
584
			StyleBorder    *border;
585
			xml_node_get_int (side, "Style", &t);
586 587
			if (t != STYLE_BORDER_NONE)
				xml_get_color_value (side, "Color", &color);
588
			border = style_border_fetch ((StyleBorderType)t, color,
Michael Meeks's avatar
Michael Meeks committed
589
						     style_border_get_orientation (i));
590
			mstyle_set_border (mstyle, i, border);
591
 		}
592
	}
593 594 595 596 597
}

/*
 * Create an XML subtree of doc equivalent to the given Style.
 */
598
xmlNodePtr
599
xml_write_style (XmlParseContext *ctxt,
600
		 MStyle *style)
601
{
602
	xmlNodePtr  cur, child;
603
	char       *tstr;
604

605
	cur = xmlNewDocNode (ctxt->doc, ctxt->ns, "Style", NULL);
606

607
	if (mstyle_is_element_set (style, MSTYLE_ALIGN_H))
608
		xml_node_set_int (cur, "HAlign", mstyle_get_align_h (style));
609
	if (mstyle_is_element_set (style, MSTYLE_ALIGN_V))
610
		xml_node_set_int (cur, "VAlign", mstyle_get_align_v (style));
611
	if (mstyle_is_element_set (style, MSTYLE_WRAP_TEXT))
612
		xml_node_set_int (cur, "WrapText", mstyle_get_wrap_text (style));
613
	if (mstyle_is_element_set (style, MSTYLE_ORIENTATION))
614
		xml_node_set_int (cur, "Orient", mstyle_get_orientation (style));
615
	if (mstyle_is_element_set (style, MSTYLE_PATTERN))
616
		xml_node_set_int (cur, "Shade", mstyle_get_pattern (style));
Jody Goldberg's avatar
Jody Goldberg committed
617
	if (mstyle_is_element_set (style, MSTYLE_INDENT))
618
		xml_node_set_int (cur, "Indent", mstyle_get_indent (style));
619 620 621 622
	if (mstyle_is_element_set (style, MSTYLE_CONTENT_LOCKED))
		xml_node_set_int (cur, "Locked", mstyle_get_content_locked (style));
	if (mstyle_is_element_set (style, MSTYLE_CONTENT_HIDDEN))
		xml_node_set_int (cur, "Hidden", mstyle_get_content_hidden (style));
623 624

	if (mstyle_is_element_set (style, MSTYLE_COLOR_FORE))
625
		xml_node_set_color (cur, "Fore", mstyle_get_color (style, MSTYLE_COLOR_FORE));
626
	if (mstyle_is_element_set (style, MSTYLE_COLOR_BACK))
627
		xml_node_set_color (cur, "Back", mstyle_get_color (style, MSTYLE_COLOR_BACK));
628
	if (mstyle_is_element_set (style, MSTYLE_COLOR_PATTERN))
629
		xml_node_set_color (cur, "PatternColor", mstyle_get_color (style, MSTYLE_COLOR_PATTERN));
Jody Goldberg's avatar
Jody Goldberg committed
630 631
	if (mstyle_is_element_set (style, MSTYLE_FORMAT)) {
		char *fmt = style_format_as_XL (mstyle_get_format (style), FALSE);
632
		xml_node_set_cstr (cur, "Format", fmt);
Jody Goldberg's avatar
Jody Goldberg committed
633 634
		g_free (fmt);
	}
635 636

	if (mstyle_is_element_set (style, MSTYLE_FONT_NAME) ||
637
	    mstyle_is_element_set (style, MSTYLE_FONT_SIZE) ||
638 639
	    mstyle_is_element_set (style, MSTYLE_FONT_BOLD) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_ITALIC) ||
640 641
	    mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
	    mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH)) {
642
		char const *fontname;
643 644 645 646 647

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

649 650 651 652
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, fontname);
		child = xmlNewChild (cur, ctxt->ns, "Font", tstr);
		if (tstr) xmlFree (tstr);

653
		if (mstyle_is_element_set (style, MSTYLE_FONT_SIZE))
654
			xml_node_set_points (child, "Unit",
655 656
					      mstyle_get_font_size (style));
		if (mstyle_is_element_set (style, MSTYLE_FONT_BOLD))
657
			xml_node_set_int (child, "Bold",
658 659
					   mstyle_get_font_bold (style));
		if (mstyle_is_element_set (style, MSTYLE_FONT_ITALIC))
660
			xml_node_set_int (child, "Italic",
661
					   mstyle_get_font_italic (style));
Jody Goldberg's avatar
Jody Goldberg committed
662
		if (mstyle_is_element_set (style, MSTYLE_FONT_UNDERLINE))
663
			xml_node_set_int (child, "Underline",
Jody Goldberg's avatar
Jody Goldberg committed
664
					   (int)mstyle_get_font_uline (style));
665
		if (mstyle_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH))
666
			xml_node_set_int (child, "StrikeThrough",
667
					   mstyle_get_font_strike (style));
668 669 670 671 672
	}

	child = xml_write_style_border (ctxt, style);
	if (child)
		xmlAddChild (cur, child);
673 674

	return cur;
675 676
}

677
static xmlNodePtr
678
xml_write_names (XmlParseContext *ctxt, GList *names)
679
{
680
	xmlNodePtr  cur;
681
	char       *tstr;
682

683 684 685 686
#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
	   */
687 688
	if (!names)
		return NULL;
689
#endif
690 691 692 693 694

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

	while (names) {
		xmlNodePtr   tmp;
695
		NamedExpression    *expr_name = names->data;
696 697 698 699 700
		char        *text;

		g_return_val_if_fail (expr_name != NULL, NULL);

		tmp = xmlNewDocNode (ctxt->doc, ctxt->ns, "Name", NULL);
701 702 703
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, expr_name->name->str);
		xmlNewChild (tmp, ctxt->ns, "name", tstr);
		if (tstr) xmlFree (tstr);
704 705

		text = expr_name_value (expr_name);
706 707 708
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
		xmlNewChild (tmp, ctxt->ns, "value", tstr);
		if (tstr) xmlFree (tstr);
709 710 711 712 713
		g_free (text);

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

715 716 717 718
	return cur;
}

static void
719
xml_read_names (XmlParseContext *ctxt, xmlNodePtr tree, Workbook *wb,
720
		Sheet *sheet)
721 722 723 724 725 726
{
	xmlNodePtr child;

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

727
	child = tree->xmlChildrenNode;
728
	while (child) {
729
		char *name  = NULL;
730 731 732
		if (child->name && !strcmp (child->name, "Name")) {
			xmlNodePtr bits;

733
			bits = child->xmlChildrenNode;
734
			while (bits) {
735

736 737 738
				if (!strcmp (bits->name, "name")) {
					name = xmlNodeGetContent (bits);
				} else {
739
					char     *txt;
740
					ParseError  perr;
741 742 743 744 745 746
					g_return_if_fail (name != NULL);

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

747 748 749
					if (!expr_name_create (wb, sheet, name, txt, &perr))
						g_warning (perr.message);
					parse_error_free (&perr);
750

Daniel Veillard's avatar
Daniel Veillard committed
751
					xmlFree (txt);
752
				}
753
				bits = bits->next;
754 755 756 757 758 759
			}
		}
		child = child->next;
	}
}

760
static xmlNodePtr
761
xml_write_summary (XmlParseContext *ctxt, SummaryInfo *summary_info)
762 763
{
	GList *items, *m;
764
	char *tstr;
765 766
	xmlNodePtr cur;

767
	if (!summary_info)
768 769
		return NULL;

770
	m = items = summary_info_as_list (summary_info);
771 772 773 774 775 776 777 778 779 780 781 782 783

	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);
784 785 786
			tstr = xmlEncodeEntitiesReentrant (ctxt->doc, sit->name);
			xmlNewChild (tmp, ctxt->ns, "name", tstr);
			if (tstr) xmlFree (tstr);
787 788 789

			if (sit->type == SUMMARY_INT) {
				text = g_strdup_printf ("%d", sit->v.i);
790 791 792
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-int", tstr);
				if (tstr) xmlFree (tstr);
793 794
			} else {
				text = summary_item_as_text (sit);
795 796 797
				tstr = xmlEncodeEntitiesReentrant (ctxt->doc, text);
				xmlNewChild (tmp, ctxt->ns, "val-string", tstr);
				if (tstr) xmlFree (tstr);
798 799
			}
			g_free (text);
800
			xmlAddChild (cur, tmp);
801 802 803 804 805 806 807 808
		}
		items = g_list_next (items);
	}
	g_list_free (m);
	return cur;
}

static void
809
xml_read_summary (XmlParseContext *ctxt, xmlNodePtr tree, SummaryInfo *summary_info)
810
{
811
	xmlNodePtr child;
812

813 814 815
	g_return_if_fail (ctxt != NULL);
	g_return_if_fail (tree != NULL);
	g_return_if_fail (summary_info != NULL);
816

817
	child = tree->xmlChildrenNode;
818
	while (child) {
819
		char *name = NULL;
Miguel de Icaza's avatar
Miguel de Icaza committed
820

821 822 823
		if (child->name && !strcmp (child->name, "Item")) {
			xmlNodePtr bits;

824
			bits = child->xmlChildrenNode;
825 826
			while (bits) {
				SummaryItem *sit = NULL;
827

828
				if (!strcmp (bits->name, "name")) {
829
					name = xmlNodeGetContent (bits);
830 831 832 833 834
				} else {
					char *txt;
					g_return_if_fail (name);

					txt = xmlNodeGetContent (bits);
835 836 837 838
					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
839
							sit = summary_item_new_int (name, atoi (txt));
840

841
						if (sit)
842
							summary_info_add (summary_info, sit);
Daniel Veillard's avatar
Daniel Veillard committed
843
						xmlFree (txt);
844
					}
845
				}
846
				bits = bits->next;
847 848
			}
		}
Miguel de Icaza's avatar
Miguel de Icaza committed
849
		if (name){
850
			xmlFree (name);
Miguel de Icaza's avatar
Miguel de Icaza committed
851 852
			name = NULL;
		}
853 854 855 856
		child = child->next;
	}
}

Michael Meeks's avatar
Michael Meeks committed
857
static void
858
xml_node_set_print_hf (xmlNodePtr node, char const *name,
859
		       PrintHF const *hf)
Michael Meeks's avatar
Michael Meeks committed
860 861 862 863 864 865 866
{
	xmlNodePtr  child;

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

	child = xmlNewChild (node, NULL, name, NULL);
867 868 869
	xml_node_set_cstr (child, "Left", hf->left_format);
	xml_node_set_cstr (child, "Middle", hf->middle_format);
	xml_node_set_cstr (child, "Right", hf->right_format);
Michael Meeks's avatar
Michael Meeks committed
870 871 872
}

static void
873
xml_node_get_print_hf (xmlNodePtr node, PrintHF *hf)
Michael Meeks's avatar
Michael Meeks committed
874 875
{
	char *txt;
876

Michael Meeks's avatar
Michael Meeks committed
877 878 879
	g_return_if_fail (hf != NULL);
	g_return_if_fail (node != NULL);

880
	txt = xmlGetProp (node, "Left");
881
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
882 883
		if (hf->left_format)
			g_free (hf->left_format);
884 885
		hf->left_format = g_strdup (txt);
		xmlFree (txt);
Arturo Espinosa's avatar
Arturo Espinosa committed
886
	}
887

888
	txt = xmlGetProp (node, "Middle");
889
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
890 891
		if (hf->middle_format)
			g_free (hf->middle_format);
892 893
		hf->middle_format = g_strdup (txt);
		xmlFree (txt);
Arturo Espinosa's avatar
Arturo Espinosa committed
894
	}
Michael Meeks's avatar
Michael Meeks committed
895

896
	txt = xmlGetProp (node, "Right");
897
	if (txt) {
Arturo Espinosa's avatar
Arturo Espinosa committed
898 899
		if (hf->right_format)
			g_free (hf->right_format);
900 901
		hf->right_format = g_strdup (txt);
		xmlFree (txt);
Arturo Espinosa's avatar
Arturo Espinosa committed
902
	}
Michael Meeks's avatar
Michael Meeks committed
903 904
}

905
static void
906
xml_write_attribute (XmlParseContext *ctxt, xmlNodePtr attr, GtkArg *arg)
907 908 909
{
	xmlChar *tstr;
	gchar *str;
Jody Goldberg's avatar
Jody Goldberg committed
910

911 912 913 914 915 916 917 918 919 920 921
	switch (arg->type) {
	case GTK_TYPE_CHAR:
	case GTK_TYPE_UCHAR:
	case GTK_TYPE_BOOL:
	case GTK_TYPE_INT:
	case GTK_TYPE_UINT:
	case GTK_TYPE_LONG:
	case GTK_TYPE_ULONG:
	case GTK_TYPE_FLOAT:
	case GTK_TYPE_DOUBLE:
	case GTK_TYPE_STRING:
Jody Goldberg's avatar
Jody Goldberg committed
922 923
		str = xml_arg_get (arg);
		tstr = xmlEncodeEntitiesReentrant (ctxt->doc, str);
924 925 926 927 928 929
		xmlNewChild (attr, ctxt->ns, "value", tstr);
		if (tstr) {
			xmlFree (tstr);
		}
		g_free (str);
		break;
Jody Goldberg's avatar
Jody Goldberg committed
930
	}
931 932
}

933
static xmlNodePtr