1 /*
2  * go-doc.c : A GOffice Document
3  *
4  * Copyright (C) 2004-2006 Jody Goldberg (jody@gnome.org)
5  * Copyright (C) 2008-2009 Morten Welinder (terra@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) version 3.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20  * USA
21  */
22 
23 #include <goffice/goffice-config.h>
24 #include <goffice/app/goffice-app.h>
25 #include <goffice/app/go-doc-impl.h>
26 
27 #include <gsf/gsf-doc-meta-data.h>
28 #include <gsf/gsf-impl-utils.h>
29 #include <gsf/gsf-libxml.h>
30 #include <glib/gi18n.h>
31 
32 #include <string.h>
33 
34 struct _GODocPrivate {
35 	GHashTable	*imagebuf; /* used when loading/saving images */
36 
37 	/* color maps */
38 	GSList	*resourcesbuf; /* used when loading/saving color maps and themes */
39 
40 	guint64 state, saved_state, last_used_state;
41 };
42 
43 /**
44  * _GODoc:
45  * @base: parent object.
46  * @uri: URI.
47  * @meta_data: metadata.
48  * @modified: whether the document has been modified.
49  * @first_modification_time: date of the first modification.
50  * @pristine: whether the document is unchanged since it was created.
51  * @images: images used inside the document.
52 **/
53 
54 enum {
55 	PROP_0,
56 	PROP_URI,
57 	PROP_DIRTY,
58 	PROP_DIRTY_TIME,
59 	PROP_PRISTINE,
60 	PROP_MODTIME,
61 	PROP_STATE,
62 	PROP_SAVED_STATE
63 };
64 enum {
65 	METADATA_CHANGED,
66 	METADATA_UPDATE,
67 	LAST_SIGNAL
68 };
69 static guint signals [LAST_SIGNAL] = { 0 };
70 static GObjectClass *go_doc_parent_class;
71 
72 static void
go_doc_get_property(GObject * obj,guint property_id,GValue * value,GParamSpec * pspec)73 go_doc_get_property (GObject *obj, guint property_id,
74 		     GValue *value, GParamSpec *pspec)
75 {
76 	GODoc *doc = (GODoc *)obj;
77 
78 	switch (property_id) {
79 	case PROP_URI:
80 		g_value_set_string (value, doc->uri);
81 		break;
82 
83 	case PROP_DIRTY:
84 		g_value_set_boolean (value, go_doc_is_dirty (doc));
85 		break;
86 
87 	case PROP_DIRTY_TIME:
88 		g_value_set_int64 (value, go_doc_get_dirty_time (doc));
89 		break;
90 
91 	case PROP_PRISTINE:
92 		g_value_set_boolean (value, go_doc_is_pristine (doc));
93 		break;
94 
95 	case PROP_MODTIME:
96 		g_value_set_boxed (value, go_doc_get_modtime (doc));
97 		break;
98 
99 	case PROP_STATE:
100 		g_value_set_uint64 (value, go_doc_get_state (doc));
101 		break;
102 
103 	case PROP_SAVED_STATE:
104 		g_value_set_uint64 (value, go_doc_get_saved_state (doc));
105 		break;
106 
107 	default:
108 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
109 		break;
110 	}
111 }
112 
113 static void
go_doc_set_property(GObject * obj,guint property_id,const GValue * value,GParamSpec * pspec)114 go_doc_set_property (GObject *obj, guint property_id,
115 		       const GValue *value, GParamSpec *pspec)
116 {
117 	GODoc *doc = (GODoc *)obj;
118 
119 	switch (property_id) {
120 	case PROP_URI:
121 		go_doc_set_uri (doc, g_value_get_string (value));
122 		break;
123 
124  	case PROP_DIRTY:
125 		go_doc_set_dirty (doc, g_value_get_boolean (value));
126 		break;
127 
128  	case PROP_DIRTY_TIME:
129 		go_doc_set_dirty_time (doc, g_value_get_int64 (value));
130 		break;
131 
132  	case PROP_PRISTINE:
133 		go_doc_set_pristine (doc, g_value_get_boolean (value));
134 		break;
135 
136 	case PROP_MODTIME:
137 		go_doc_set_modtime (doc, g_value_get_boxed (value));
138 		break;
139 
140 	case PROP_STATE:
141 		go_doc_set_state (doc, g_value_get_uint64 (value));
142 		break;
143 
144 	case PROP_SAVED_STATE:
145 		go_doc_set_saved_state (doc, g_value_get_uint64 (value));
146 		break;
147 
148 	default:
149 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
150 		break;
151 	}
152 }
153 
154 static void
go_doc_finalize(GObject * obj)155 go_doc_finalize (GObject *obj)
156 {
157 	GODoc *doc = (GODoc *)obj;
158 
159 	g_object_unref (doc->meta_data);
160 	doc->meta_data = NULL;
161 	g_free (doc->uri);
162 	doc->uri = NULL;
163 
164 	go_doc_set_modtime (doc, NULL);
165 
166 	if (doc->images)
167 		g_hash_table_destroy (doc->images);
168 	doc->images = NULL;
169 	g_free (doc->priv);
170 	doc->priv = NULL;
171 
172 	go_doc_parent_class->finalize (obj);
173 }
174 
175 static void
go_doc_init(GODoc * obj)176 go_doc_init (GODoc *obj)
177 {
178 	GODoc *doc = (GODoc *)obj;
179 
180 	doc->meta_data	 = gsf_doc_meta_data_new ();
181 	doc->uri	 = NULL;
182 	doc->modified	 = FALSE;
183 	doc->pristine	 = TRUE;
184 	doc->priv = g_new0 (struct _GODocPrivate, 1);
185 }
186 
187 static void
go_doc_class_init(GObjectClass * object_class)188 go_doc_class_init (GObjectClass *object_class)
189 {
190 	go_doc_parent_class = g_type_class_peek_parent (object_class);
191 
192 	object_class->set_property = go_doc_set_property;
193 	object_class->get_property = go_doc_get_property;
194 	object_class->finalize = go_doc_finalize;
195 
196         g_object_class_install_property (object_class, PROP_URI,
197 		 g_param_spec_string ("uri", _("URI"),
198 			_("The URI associated with this document."),
199 			NULL, GSF_PARAM_STATIC | G_PARAM_READWRITE));
200 
201         g_object_class_install_property (object_class, PROP_DIRTY,
202 		 g_param_spec_boolean ("dirty", _("Dirty"),
203 			_("Whether the document has been changed."),
204 			FALSE, GSF_PARAM_STATIC | G_PARAM_READWRITE));
205 
206         g_object_class_install_property (object_class, PROP_DIRTY_TIME,
207 		 g_param_spec_int64 ("dirty-time", _("Dirty Time"),
208 			_("When the document was first changed."),
209 			G_MININT64, G_MAXINT64, 0,
210 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
211 
212         g_object_class_install_property (object_class, PROP_PRISTINE,
213 		 g_param_spec_boolean ("pristine", _("Pristine"),
214 			_("Whether the document is unchanged since it was created."),
215 			FALSE, GSF_PARAM_STATIC | G_PARAM_READWRITE));
216         g_object_class_install_property (object_class, PROP_MODTIME,
217 		 g_param_spec_boxed ("modtime",
218 				     _("Modification time"),
219 				     _("The known file system modification time"),
220 				     G_TYPE_DATE_TIME,
221 				     GSF_PARAM_STATIC |
222 				     G_PARAM_READWRITE));
223         g_object_class_install_property (object_class, PROP_STATE,
224 		 g_param_spec_uint64 ("state", _("State"),
225 			_("Current state of document"),
226 			0, G_MAXUINT64, 0,
227 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
228         g_object_class_install_property (object_class, PROP_STATE,
229 		 g_param_spec_uint64 ("saved-state", _("Saved state"),
230 			_("State of document when last saved"),
231 			0, G_MAXUINT64, 0,
232 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
233 
234 
235 	signals [METADATA_UPDATE] = g_signal_new ("metadata-update",
236 		GO_TYPE_DOC,	G_SIGNAL_RUN_LAST,
237 		G_STRUCT_OFFSET (GODocClass, meta_data.update),
238 		NULL, NULL,
239 		g_cclosure_marshal_VOID__VOID,
240 		G_TYPE_NONE,	0, G_TYPE_NONE);
241 	signals [METADATA_CHANGED] = g_signal_new ("metadata-changed",
242 		GO_TYPE_DOC,	G_SIGNAL_RUN_LAST,
243 		G_STRUCT_OFFSET (GODocClass, meta_data.changed),
244 		NULL, NULL,
245 		g_cclosure_marshal_VOID__VOID,
246 		G_TYPE_NONE, 0, G_TYPE_NONE);
247 }
248 
GSF_CLASS(GODoc,go_doc,go_doc_class_init,go_doc_init,G_TYPE_OBJECT)249 GSF_CLASS (GODoc, go_doc,
250 	   go_doc_class_init, go_doc_init,
251 	   G_TYPE_OBJECT)
252 
253 /**
254  * go_doc_set_uri:
255  * @doc: the document to modify
256  * @uri: the uri for this worksheet.
257  *
258  * Returns: TRUE if the name was set succesfully.
259  **/
260 gboolean
261 go_doc_set_uri (GODoc *doc, char const *uri)
262 {
263 	char *new_uri;
264 
265 	g_return_val_if_fail (doc != NULL, FALSE);
266 
267 	if (go_str_compare (uri, doc->uri) == 0)
268 		return TRUE;
269 
270 	new_uri = g_strdup (uri);
271 	g_free (doc->uri);
272 	doc->uri = new_uri;
273 
274 /* FIXME FIXME FIXME should we allow signal to return FALSE and disable the assignment ? */
275 	g_object_notify (G_OBJECT (doc), "uri");
276 
277 	return TRUE;
278 }
279 
280 char const *
go_doc_get_uri(GODoc const * doc)281 go_doc_get_uri (GODoc const *doc)
282 {
283 	g_return_val_if_fail (GO_IS_DOC (doc), NULL);
284 	return doc->uri;
285 }
286 
287 /**
288  * go_doc_set_dirty:
289  * @doc: #GODoc
290  * @is_dirty:bool
291  *
292  * Changes the dirty state of @doc to @is_dirty and clears the pristine state
293  * no matter what.
294  **/
295 void
go_doc_set_dirty(GODoc * doc,gboolean is_dirty)296 go_doc_set_dirty (GODoc *doc, gboolean is_dirty)
297 {
298 	g_return_if_fail (GO_IS_DOC (doc));
299 
300 	is_dirty = !!is_dirty;
301 	if (is_dirty == doc->modified)
302 		return;
303 
304 	doc->modified = is_dirty;
305 	g_object_notify (G_OBJECT (doc), "dirty");
306 
307 	go_doc_set_dirty_time (doc, is_dirty ? g_get_real_time () : 0);
308 
309 	/* Dirtiness changed so no longer pristine.  */
310 	go_doc_set_pristine (doc, FALSE);
311 }
312 
313 /**
314  * go_doc_is_dirty:
315  * @doc: #GODoc
316  *
317  * Returns: TRUE if @doc has been modified.
318  **/
319 gboolean
go_doc_is_dirty(GODoc const * doc)320 go_doc_is_dirty (GODoc const *doc)
321 {
322 	g_return_val_if_fail (GO_IS_DOC (doc), FALSE);
323 
324 	return doc->modified;
325 }
326 
327 /**
328  * go_doc_set_dirty_time:
329  * @doc: #GODoc
330  * @t: a timestamp from g_get_real_time
331  *
332  * Changes the dirty time, i.e., the time the document was first marked
333  * dirty.
334  **/
335 void
go_doc_set_dirty_time(GODoc * doc,gint64 t)336 go_doc_set_dirty_time (GODoc *doc, gint64 t)
337 {
338 	g_return_if_fail (GO_IS_DOC (doc));
339 
340 	if (doc->first_modification_time == t)
341 		return;
342 
343 	doc->first_modification_time = t;
344 	g_object_notify (G_OBJECT (doc), "dirty-time");
345 }
346 
347 /**
348  * go_doc_get_dirty_time:
349  * @doc: #GODoc
350  *
351  * Returns: the time (as in g_get_real_time()) the document was first marked
352  * dirty.
353  **/
354 gint64
go_doc_get_dirty_time(GODoc const * doc)355 go_doc_get_dirty_time (GODoc const *doc)
356 {
357 	g_return_val_if_fail (GO_IS_DOC (doc), 0);
358 	return doc->first_modification_time;
359 }
360 
361 /**
362  * go_doc_set_pristine:
363  * @doc: #GODoc
364  * @pristine: a gboolean.
365  *
366  * Sets the indication of whether this document is unchanged since it was
367  * created.  Note: if both "dirty" and "pristine" are being set, set
368  * "pristine" last.
369  **/
370 void
go_doc_set_pristine(GODoc * doc,gboolean pristine)371 go_doc_set_pristine (GODoc *doc, gboolean pristine)
372 {
373 	g_return_if_fail (GO_IS_DOC (doc));
374 
375 	pristine = !!pristine;
376 	if (pristine == doc->pristine)
377 		return;
378 
379 	doc->pristine = pristine;
380 	g_object_notify (G_OBJECT (doc), "pristine");
381 }
382 
383 /**
384  * go_doc_is_pristine:
385  * @doc: #GODoc
386  *
387  * This checks to see if the doc has ever been
388  * used ( approximately )
389  *
390  * Return value: %TRUE if we can discard this doc.
391  **/
392 gboolean
go_doc_is_pristine(GODoc const * doc)393 go_doc_is_pristine (GODoc const *doc)
394 {
395 	g_return_val_if_fail (GO_IS_DOC (doc), FALSE);
396 	return doc->pristine;
397 }
398 
399 /**
400  * go_doc_get_meta_data:
401  * @doc: #GODoc
402  *
403  * Returns: (transfer none): @doc's metadata
404  **/
405 GsfDocMetaData *
go_doc_get_meta_data(GODoc const * doc)406 go_doc_get_meta_data (GODoc const *doc)
407 {
408 	g_return_val_if_fail (GO_IS_DOC (doc), NULL);
409 	return doc->meta_data;
410 }
411 
412 /**
413  * go_doc_set_meta_data:
414  * @doc: #GODoc
415  * @data: #GsfDocMetaData
416  *
417  * Adds a ref to @data.
418  **/
419 void
go_doc_set_meta_data(GODoc * doc,GsfDocMetaData * data)420 go_doc_set_meta_data (GODoc *doc, GsfDocMetaData *data)
421 {
422 	g_return_if_fail (GO_IS_DOC (doc));
423 
424 	g_object_ref (data);
425 	g_object_unref (doc->meta_data);
426 	doc->meta_data = data;
427 	g_signal_emit (G_OBJECT (doc), signals [METADATA_CHANGED], 0);
428 }
429 
430 /**
431  * go_doc_update_meta_data:
432  * @doc: #GODoc
433  *
434  * Signal that @doc's metadata should be updated
435  * 	- statistics (sheet count, word count)
436  * 	- content (sheet names, bookmarks)
437  * 	- reloading linked items
438  **/
439 void
go_doc_update_meta_data(GODoc * doc)440 go_doc_update_meta_data (GODoc *doc)
441 {
442 	g_return_if_fail (GO_IS_DOC (doc));
443 
444 	/* update linked properties and automatic content */
445 	g_signal_emit (G_OBJECT (doc), signals [METADATA_UPDATE], 0);
446 }
447 
448 /**
449  * go_doc_set_modtime:
450  * @doc: #GODoc
451  * @modtime: (allow-none): new file system time stamp
452  *
453  * Sets the last known file system time stamp for the document, %NULL
454  * if unknown.
455  **/
456 void
go_doc_set_modtime(GODoc * doc,GDateTime * modtime)457 go_doc_set_modtime (GODoc *doc, GDateTime *modtime)
458 {
459 	g_return_if_fail (GO_IS_DOC (doc));
460 
461 	if (modtime == doc->modtime)
462 		return;
463 
464 	if (modtime)
465 		g_date_time_ref (modtime);
466 	if (doc->modtime)
467 		g_date_time_unref (doc->modtime);
468 	doc->modtime = modtime;
469 
470 	g_object_notify (G_OBJECT (doc), "modtime");
471 }
472 
473 /**
474  * go_doc_get_modtime:
475  * @doc: #GODoc
476  *
477  * Returns: (transfer none): the last known file system time stamp for
478  * the document, or %NULL if unknown.
479  **/
480 GDateTime *
go_doc_get_modtime(GODoc const * doc)481 go_doc_get_modtime (GODoc const *doc)
482 {
483 	g_return_val_if_fail (GO_IS_DOC (doc), NULL);
484 
485 	return doc->modtime;
486 }
487 
488 
489 /**
490  * go_doc_set_state:
491  * @doc: #GODoc
492  * @state: state of document
493  *
494  * Sets the current state of the document as a serial number that is intended
495  * to be incremented for each document change.
496  *
497  * Setting the state will set the document's dirty status also, assuming both
498  * the state and the saved state are known.
499  **/
500 void
go_doc_set_state(GODoc * doc,guint64 state)501 go_doc_set_state (GODoc *doc, guint64 state)
502 {
503 	g_return_if_fail (GO_IS_DOC (doc));
504 
505 	if (doc->priv->state == state)
506 		return;
507 
508 	doc->priv->state = state;
509 	g_object_notify (G_OBJECT (doc), "state");
510 	go_doc_set_dirty (doc, state != doc->priv->saved_state);
511 }
512 
513 /**
514  * go_doc_bump_state:
515  * @doc: #GODoc
516  *
517  * Sets the current state of the document to a fresh id.
518  **/
519 void
go_doc_bump_state(GODoc * doc)520 go_doc_bump_state (GODoc *doc)
521 {
522 	g_return_if_fail (GO_IS_DOC (doc));
523 	go_doc_set_state (doc, ++(doc->priv->last_used_state));
524 }
525 
526 
527 /**
528  * go_doc_get_state:
529  * @doc: #GODoc
530  *
531  * Returns: the current state of the document.
532  **/
533 guint64
go_doc_get_state(GODoc * doc)534 go_doc_get_state (GODoc *doc)
535 {
536 	g_return_val_if_fail (GO_IS_DOC (doc), 0);
537 
538 	return doc->priv->state;
539 }
540 
541 /**
542  * go_doc_set_saved_state:
543  * @doc: #GODoc
544  * @state: state at the time of last save
545  *
546  * Sets the state at the last time the document was saved.
547  **/
548 void
go_doc_set_saved_state(GODoc * doc,guint64 state)549 go_doc_set_saved_state (GODoc *doc, guint64 state)
550 {
551 	g_return_if_fail (GO_IS_DOC (doc));
552 
553 	if (doc->priv->saved_state == state)
554 		return;
555 
556 	doc->priv->saved_state = state;
557 	g_object_notify (G_OBJECT (doc), "saved-state");
558 	go_doc_set_dirty (doc, state != doc->priv->state);
559 }
560 
561 
562 /**
563  * go_doc_get_saved_state:
564  * @doc: #GODoc
565  *
566  * Returns: the state at the last time the document was saved.
567  **/
568 guint64
go_doc_get_saved_state(GODoc * doc)569 go_doc_get_saved_state (GODoc *doc)
570 {
571 	g_return_val_if_fail (GO_IS_DOC (doc), 0);
572 
573 	return doc->priv->saved_state;
574 }
575 
576 
577 /**
578  * go_doc_get_image:
579  * @doc: a #GODoc
580  * @id: the image name
581  *
582  * Return value: (transfer none): the #GOImage is one exist with name @id. The caller does not own a
583  * reference.
584  **/
585 GOImage *
go_doc_get_image(GODoc * doc,char const * id)586 go_doc_get_image (GODoc *doc, char const *id)
587 {
588 	return (doc->images != NULL)?
589 		(GOImage *) g_hash_table_lookup (doc->images, id):
590 		NULL;
591 }
592 
593 /**
594  * go_doc_add_image:
595  * @doc: a #GODoc
596  * @id: the image name or %NULL
597  * @image: a #GOImage
598  *
599  * Adds @image to the document if no such image already exists. The name of
600  * the returned image might be different from @id, even if given.
601  * Returns: (transfer none): either @image, in which case the document adds a reference on it, or
602  * an identical image for which the owner does not own a reference.
603  **/
604 GOImage *
go_doc_add_image(GODoc * doc,char const * id,GOImage * image)605 go_doc_add_image (GODoc *doc, char const *id, GOImage *image)
606 {
607 	GOImage *img;
608 	int i = 0;
609 	char *new_id;
610 	GHashTableIter iter;
611 	char const *key;
612 
613 	if (doc->images == NULL)
614 		doc->images = g_hash_table_new_full (g_str_hash, g_str_equal,
615 						     g_free, g_object_unref);
616 
617 	/* check if the image is already there */
618 	g_hash_table_iter_init (&iter, doc->images);
619 	while (g_hash_table_iter_next (&iter, (void**) &key, (void**) &img))
620 		if (!go_image_differ (image, img))
621 			return img;
622 
623 	if (!id || !*id)
624 		id = _("Image");
625 	/* now check if the id is not a duplicate */
626 	if (g_hash_table_lookup (doc->images, id)) {
627 		while (1) {
628 			new_id = g_strdup_printf ("%s(%d)", id, i++);
629 			if (g_hash_table_lookup (doc->images, new_id))
630 				g_free (new_id);
631 			else
632 				break;
633 		}
634 	} else
635 		new_id = g_strdup (id);
636 	go_image_set_name (image, new_id),
637 	g_hash_table_insert (doc->images, new_id, g_object_ref (image));
638 	return image;
639 }
640 
641 /**
642  * go_doc_get_images:
643  * @doc: #GODoc
644  *
645  * Returns: (transfer none): the images registered inside the document
646  **/
647 GHashTable *
go_doc_get_images(GODoc * doc)648 go_doc_get_images (GODoc *doc) {
649 	g_return_val_if_fail (doc, NULL);
650 	return doc->images;
651 }
652 
653 void
go_doc_init_write(GODoc * doc,GsfXMLOut * output)654 go_doc_init_write (GODoc *doc, GsfXMLOut *output)
655 {
656 	g_return_if_fail (GO_IS_DOC (doc));
657 	g_return_if_fail (doc->priv->imagebuf == NULL);
658 
659 	doc->priv->imagebuf = g_hash_table_new_full (g_str_hash, g_str_equal,
660 					       g_free, NULL);
661 	g_object_set_data (G_OBJECT (gsf_xml_out_get_output (output)),
662 			   "document", doc);
663 }
664 
665 void
go_doc_init_read(GODoc * doc,GsfInput * input)666 go_doc_init_read (GODoc *doc, GsfInput *input)
667 {
668 	g_return_if_fail (GO_IS_DOC (doc));
669 	g_return_if_fail (doc->priv->imagebuf == NULL);
670 
671 	doc->priv->imagebuf = g_hash_table_new_full (g_str_hash, g_str_equal,
672 					       g_free, g_object_unref);
673 	g_object_set_data (G_OBJECT (input), "document", doc);
674 }
675 
676 static void
save_image_cb(G_GNUC_UNUSED gpointer key,gpointer img_,gpointer user)677 save_image_cb (G_GNUC_UNUSED gpointer key, gpointer img_, gpointer user)
678 {
679 	go_image_save ((GOImage *) img_, (GsfXMLOut *) user);
680 }
681 
682 void
go_doc_write(GODoc * doc,GsfXMLOut * output)683 go_doc_write (GODoc *doc, GsfXMLOut *output)
684 {
685 	GSList *ptr;
686 	if (g_hash_table_size (doc->priv->imagebuf) > 0 ||
687 	    doc->priv->resourcesbuf != NULL) {
688 		gsf_xml_out_start_element (output, "GODoc");
689 		g_hash_table_foreach (doc->priv->imagebuf, save_image_cb, output);
690 		for (ptr = doc->priv->resourcesbuf; ptr; ptr = ptr->next) {
691 			gsf_xml_out_start_element (output, G_OBJECT_TYPE_NAME (ptr->data));
692 			go_persist_sax_save (GO_PERSIST (ptr->data), output);
693 			gsf_xml_out_end_element (output);
694 		}
695 		g_slist_free (doc->priv->resourcesbuf);
696 		doc->priv->resourcesbuf = NULL;
697 		gsf_xml_out_end_element (output);
698 	}
699 	g_hash_table_destroy (doc->priv->imagebuf);
700 	doc->priv->imagebuf = NULL;
701 }
702 
703 /**
704  * go_doc_save_image:
705  * @doc: a #GODoc
706  * @id: the Id of the #GOImage to save
707  *
708  * Saves the image with the document. Each image will be saved only
709  * once.
710  **/
711 void
go_doc_save_image(GODoc * doc,char const * id)712 go_doc_save_image (GODoc *doc, char const *id)
713 {
714 	if (!doc)
715 		return;
716 	if (!g_hash_table_lookup (doc->priv->imagebuf, id)) {
717 		GOImage *image = go_doc_get_image (doc, id);
718 		if (image)
719 			g_hash_table_replace (doc->priv->imagebuf,
720 					      g_strdup (id),
721 					      image);
722 	}
723 }
724 
725 static void
load_image(GsfXMLIn * xin,xmlChar const ** attrs)726 load_image (GsfXMLIn *xin, xmlChar const **attrs)
727 {
728 	GODoc *doc = GO_DOC (xin->user_state);
729 	GOImage *image = NULL;
730 	xmlChar const **attr;
731 	GType type = 0;
732 	if (!attrs || !*attrs)
733 		return;
734 	for (attr = attrs; *attr; attr += 2)
735 		if (!strcmp (*attr, "name"))
736 			image = (GOImage *) g_hash_table_lookup (doc->priv->imagebuf, attr[1]);
737 		else if (!strcmp (*attr, "type"))
738 			type = g_type_from_name (attr[1]);
739 	if (!image) /* this should not occur, but if it does, we might want to load the image? */
740 		return;
741 	g_return_if_fail (type == 0 || G_OBJECT_TYPE (image) == type);
742 	go_image_load_attrs (image, xin, attrs);
743 	g_object_set_data (G_OBJECT (doc), "new image", image);
744 }
745 
746 static void
load_image_data(GsfXMLIn * xin,G_GNUC_UNUSED GsfXMLBlob * unknown)747 load_image_data (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
748 {
749 	GODoc *doc = GO_DOC (xin->user_state);
750 	GOImage *image = GO_IMAGE (g_object_get_data (G_OBJECT (doc), "new image")), *real;
751 	g_return_if_fail (image != NULL);
752 	go_image_load_data (image, xin);
753 	real = go_doc_add_image (doc, go_image_get_name (image), image);
754 	g_hash_table_remove (doc->priv->imagebuf, (gpointer) go_image_get_name (image));
755 	/*
756 	 * We have an issue if the image already existed and this can
757 	 * happen on pasting or if one day, we implement merging two
758 	 * documents (import a workbook as new sheets in an existing
759 	 * workbook). At the moment, I don't see any way to tell the
760 	 * clients to reference the image stored in the document instead
761 	 * of the one created by go_doc_image_fetch, so let's just make
762 	 * certain they share the same id. Anyway this should not happen
763 	 * very often (may be with a company logo?) and it is not so
764 	 * harmful since the duplication will not survive
765 	 * serialization. (Jean)
766 	 */
767 	if (real != image) {
768 		go_image_set_name (image, go_image_get_name (real));
769 		g_object_unref (image);
770 	}
771 	g_object_set_data (G_OBJECT (doc), "new image", NULL);
772 }
773 
774 static void
load_color_map(GsfXMLIn * xin,xmlChar const ** attrs)775 load_color_map (GsfXMLIn *xin, xmlChar const **attrs)
776 {
777 	GogAxisColorMap *map = NULL;
778 	for (; attrs && *attrs; attrs +=2)
779 		if (!strcmp ((char const *) *attrs, "id")) {
780 			map = GOG_AXIS_COLOR_MAP (gog_axis_color_map_get_from_id ((char const *) attrs[1]));
781 			break;
782 		}
783 	if (map)
784 		go_persist_prep_sax (GO_PERSIST (map), xin, attrs);
785 }
786 
787 static void
load_theme(GsfXMLIn * xin,xmlChar const ** attrs)788 load_theme (GsfXMLIn *xin, xmlChar const **attrs)
789 {
790 	GogTheme *theme = NULL;
791 	for (; attrs && *attrs; attrs +=2)
792 		if (!strcmp ((char const *) *attrs, "id")) {
793 			theme = GOG_THEME (gog_theme_registry_lookup ((char const *) attrs[1]));
794 			break;
795 		}
796 	if (theme)
797 		go_persist_prep_sax (GO_PERSIST (theme), xin, attrs);
798 }
799 
800 void
go_doc_read(GODoc * doc,GsfXMLIn * xin,xmlChar const ** attrs)801 go_doc_read (GODoc *doc, GsfXMLIn *xin, xmlChar const **attrs)
802 {
803 	static GsfXMLInNode const dtd[] = {
804 		GSF_XML_IN_NODE 	(DOC, DOC,
805 					 -1, "GODoc",
806 					 FALSE, NULL, NULL),
807 		GSF_XML_IN_NODE 	(DOC, IMAGE,
808 					 -1, "GOImage",
809 					 GSF_XML_CONTENT,
810 					 &load_image, &load_image_data),
811 		GSF_XML_IN_NODE 	(DOC, COLOR_MAP,
812 					 -1, "GogAxisColorMap",
813 					 GSF_XML_NO_CONTENT,
814 					 &load_color_map, NULL),
815 		GSF_XML_IN_NODE 	(DOC, THEME,
816 					 -1, "GogTheme",
817 					 GSF_XML_NO_CONTENT,
818 					 &load_theme, NULL),
819 		GSF_XML_IN_NODE_END
820 	};
821 	static GsfXMLInDoc *xmldoc = NULL;
822 	if (NULL == xmldoc) {
823 		xmldoc = gsf_xml_in_doc_new (dtd, NULL);
824 		go_xml_in_doc_dispose_on_exit (&xmldoc);
825 	}
826 	gsf_xml_in_push_state (xin, xmldoc, doc, NULL, attrs);
827 }
828 
829 void
go_doc_end_read(GODoc * doc)830 go_doc_end_read	(GODoc *doc)
831 {
832 	g_hash_table_destroy (doc->priv->imagebuf);
833 	doc->priv->imagebuf = NULL;
834 }
835 
836 /**
837  * go_doc_image_fetch:
838  * @doc: a #GODoc
839  * @id: the name for the new image.
840  * @type: the type of the #GOImage to create if needed.
841  *
842  * Searches for a #GOImage with name @id in the document image buffer and
843  * creates one if needed. The caller does not own a reference on the returned
844  * #GOImage.
845  * This function must be called after a call to go_doc_init_read(), otherwise
846  * it will emit a critical and return NULL.
847  * Returns: (transfer none): the found or created #GOImage.
848  **/
849 GOImage *
go_doc_image_fetch(GODoc * doc,char const * id,GType type)850 go_doc_image_fetch (GODoc *doc, char const *id, GType type)
851 {
852 	GOImage *image;
853 	g_return_val_if_fail (doc && doc->priv->imagebuf, NULL);
854 	image = g_hash_table_lookup (doc->priv->imagebuf, id);
855 	if (!image) {
856 		g_return_val_if_fail (g_type_is_a (type, GO_TYPE_IMAGE), NULL);
857 		image = g_object_new (type, NULL);
858 		if (!GO_IS_IMAGE (image)) {
859 			if (image)
860 				g_object_unref (image);
861 			g_critical ("Invalid image type");
862 			return NULL;
863 		}
864 		go_image_set_name (image, id);
865 		g_hash_table_replace (doc->priv->imagebuf,
866 				      g_strdup (go_image_get_name (image)),
867 				      image);
868 	}
869 	return image;
870 }
871 
872 /**
873  * go_doc_save_resource:
874  * @doc: a #GODoc
875  * @gp: the #GOPersist to save
876  *
877  * Saves the resource with the document. Each resource will be saved only
878  * once.
879  **/
880 void
go_doc_save_resource(GODoc * doc,GOPersist const * gp)881 go_doc_save_resource (GODoc *doc, GOPersist const *gp)
882 {
883 	GSList *ptr;
884 	for (ptr = doc->priv->resourcesbuf; ptr; ptr = ptr->next)
885 		if (ptr->data == gp) /* already marked for saving */
886 			return;
887 	doc->priv->resourcesbuf = g_slist_prepend (doc->priv->resourcesbuf, (void *) gp);
888 }
889