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