]> Pileus Git - ~andy/gtk/blob - gtk/gtktextbufferserialize.c
Improve consistency of signal and property names
[~andy/gtk] / gtk / gtktextbufferserialize.c
1 /* gtktextbufferserialize.c
2  *
3  * Copyright (C) 2001 Havoc Pennington
4  * Copyright (C) 2004 Nokia Corporation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /* FIXME: We should use other error codes for the
23  * parts that deal with the format errors
24  */
25
26 #include <config.h>
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include "gdk-pixbuf/gdk-pixdata.h"
33 #include "gtktextbufferserialize.h"
34
35 #include "gtkintl.h"
36
37
38 typedef struct
39 {
40   GString *tag_table_str;
41   GString *text_str;
42   GHashTable *tags;
43   GtkTextIter start, end;
44
45   gint n_pixbufs;
46   GList *pixbufs;
47   gint tag_id;
48   GHashTable *tag_id_tags;
49 } SerializationContext;
50
51 static gchar *
52 serialize_value (GValue *value)
53 {
54   if (g_value_type_transformable (value->g_type, G_TYPE_STRING))
55     {
56       GValue text_value = { 0 };
57       gchar *tmp;
58
59       g_value_init (&text_value, G_TYPE_STRING);
60       g_value_transform (value, &text_value);
61
62       tmp = g_markup_escape_text (g_value_get_string (&text_value), -1);
63       g_value_unset (&text_value);
64
65       return tmp;
66     }
67   else if (value->g_type == GDK_TYPE_COLOR)
68     {
69       GdkColor *color = g_value_get_boxed (value);
70
71       return g_strdup_printf ("%x:%x:%x", color->red, color->green, color->blue);
72     }
73   else if (g_type_is_a (value->g_type, GDK_TYPE_DRAWABLE))
74     {
75       /* Don't do anything */
76     }
77   else
78     {
79       g_warning ("Type %s is not serializable\n", g_type_name (value->g_type));
80     }
81
82   return NULL;
83 }
84
85 static gboolean
86 deserialize_value (const gchar *str,
87                    GValue      *value)
88 {
89   if (g_value_type_transformable (G_TYPE_STRING, value->g_type))
90     {
91       GValue text_value = { 0 };
92       gboolean retval;
93
94       g_value_init (&text_value, G_TYPE_STRING);
95       g_value_set_static_string (&text_value, str);
96
97       retval = g_value_transform (&text_value, value);
98       g_value_unset (&text_value);
99
100       return retval;
101     }
102   else if (value->g_type == G_TYPE_BOOLEAN)
103     {
104       gboolean v;
105
106       v = strcmp (str, "TRUE") == 0;
107
108       g_value_set_boolean (value, v);
109
110       return TRUE;
111     }
112   else if (value->g_type == G_TYPE_INT)
113     {
114       gchar *tmp;
115       int v;
116
117       v = strtol (str, &tmp, 10);
118
119       if (tmp == NULL || tmp == str)
120         return FALSE;
121
122       g_value_set_int (value, v);
123
124       return TRUE;
125     }
126   else if (value->g_type == G_TYPE_DOUBLE)
127     {
128       gchar *tmp;
129       gdouble v;
130
131       v = g_ascii_strtod (str, &tmp);
132
133       if (tmp == NULL || tmp == str)
134         return FALSE;
135
136       g_value_set_double (value, v);
137
138       return TRUE;
139     }
140   else if (value->g_type == GDK_TYPE_COLOR)
141     {
142       GdkColor color;
143       const gchar *old;
144       gchar *tmp;
145
146       old = str;
147       color.red = strtol (old, &tmp, 16);
148
149       if (tmp == NULL || tmp == old)
150         return FALSE;
151
152       old = tmp;
153       if (*old++ != ':')
154         return FALSE;
155
156       color.green = strtol (old, &tmp, 16);
157       if (tmp == NULL || tmp == old)
158         return FALSE;
159
160       old = tmp;
161       if (*old++ != ':')
162         return FALSE;
163
164       color.blue = strtol (old, &tmp, 16);
165
166       if (tmp == NULL || tmp == old || *tmp != '\0')
167         return FALSE;
168
169       g_value_set_boxed (value, &color);
170
171       return TRUE;
172     }
173   else if (G_VALUE_HOLDS_ENUM (value))
174     {
175       GEnumClass *class = G_ENUM_CLASS (g_type_class_peek (value->g_type));
176       GEnumValue *enum_value;
177
178       enum_value = g_enum_get_value_by_name (class, str);
179
180       if (enum_value)
181         {
182           g_value_set_enum (value, enum_value->value);
183           return TRUE;
184         }
185
186       return FALSE;
187     }
188   else
189     {
190       g_warning ("Type %s can not be deserialized\n", g_type_name (value->g_type));
191     }
192
193   return FALSE;
194 }
195
196 /* Checks if a param is set, or if it's the default value */
197 static gboolean
198 is_param_set (GObject    *object,
199               GParamSpec *pspec,
200               GValue     *value)
201 {
202   /* We need to special case some attributes here */
203   if (strcmp (pspec->name, "background-gdk") == 0)
204     {
205       gboolean is_set;
206
207       g_object_get (object, "background-set", &is_set, NULL);
208
209       if (is_set)
210         {
211           g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
212
213           g_object_get_property (object, pspec->name, value);
214
215           return TRUE;
216         }
217
218       return FALSE;
219     }
220   else if (strcmp (pspec->name, "foreground-gdk") == 0)
221     {
222       gboolean is_set;
223
224       g_object_get (object, "foreground-set", &is_set, NULL);
225
226       if (is_set)
227         {
228           g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
229
230           g_object_get_property (object, pspec->name, value);
231
232           return TRUE;
233         }
234
235       return FALSE;
236     }
237   else
238     {
239       gboolean is_set;
240       gchar *is_set_name;
241
242       is_set_name = g_strdup_printf ("%s-set", pspec->name);
243
244       if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), is_set_name) == NULL)
245         {
246           g_free (is_set_name);
247           return FALSE;
248         }
249       else
250         {
251           g_object_get (object, is_set_name, &is_set, NULL);
252
253           if (!is_set)
254             {
255               g_free (is_set_name);
256               return FALSE;
257             }
258
259           g_free (is_set_name);
260
261           g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec));
262
263           g_object_get_property (object, pspec->name, value);
264
265           if (g_param_value_defaults (pspec, value))
266             {
267               g_value_unset (value);
268
269               return FALSE;
270             }
271         }
272       return TRUE;
273     }
274 }
275
276 static void
277 serialize_tag (gpointer key,
278                gpointer data,
279                gpointer user_data)
280 {
281   SerializationContext *context = user_data;
282   GtkTextTag *tag = data;
283   gchar *tag_name;
284   gint tag_id;
285   GParamSpec **pspecs;
286   guint n_pspecs;
287   int i;
288
289   g_string_append (context->tag_table_str, "  <tag ");
290
291   /* Handle anonymous tags */
292   if (tag->name)
293     {
294       tag_name = g_markup_escape_text (tag->name, -1);
295       g_string_append_printf (context->tag_table_str, "name=\"%s\"", tag_name);
296       g_free (tag_name);
297     }
298   else
299     {
300       tag_id = GPOINTER_TO_INT (g_hash_table_lookup (context->tag_id_tags, tag));
301
302       g_string_append_printf (context->tag_table_str, "id=\"%d\"", tag_id);
303     }
304
305   g_string_append_printf (context->tag_table_str, " priority=\"%d\">\n", tag->priority);
306
307   /* Serialize properties */
308   pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (tag), &n_pspecs);
309
310   for (i = 0; i < n_pspecs; i++)
311     {
312       GValue value = { 0 };
313       gchar *tmp, *tmp2;
314
315       if (!(pspecs[i]->flags & G_PARAM_READABLE) ||
316           !(pspecs[i]->flags & G_PARAM_WRITABLE))
317         continue;
318
319       if (!is_param_set (G_OBJECT (tag), pspecs[i], &value))
320         continue;
321
322       /* Now serialize the attr */
323       tmp2 = serialize_value (&value);
324
325       if (tmp2)
326         {
327           tmp = g_markup_escape_text (pspecs[i]->name, -1);
328           g_string_append_printf (context->tag_table_str, "   <attr name=\"%s\" ", tmp);
329           g_free (tmp);
330
331           tmp = g_markup_escape_text (g_type_name (pspecs[i]->value_type), -1);
332           g_string_append_printf (context->tag_table_str, "type=\"%s\" value=\"%s\" />\n", tmp, tmp2);
333
334           g_free (tmp);
335           g_free (tmp2);
336         }
337
338       g_value_unset (&value);
339     }
340
341   g_free (pspecs);
342
343   g_string_append (context->tag_table_str, "  </tag>\n");
344 }
345
346 static void
347 serialize_tags (SerializationContext *context)
348 {
349   g_string_append (context->tag_table_str, " <text_view_markup>\n");
350   g_string_append (context->tag_table_str, " <tags>\n");
351   g_hash_table_foreach (context->tags, serialize_tag, context);
352   g_string_append (context->tag_table_str, " </tags>\n");
353 }
354
355 #if 0
356 static void
357 dump_tag_list (const gchar *str,
358                GList       *list)
359 {
360   g_print ("%s: ", str);
361
362   if (!list)
363     g_print ("(empty)");
364   else
365     {
366       while (list)
367         {
368           g_print ("%s ", ((GtkTextTag *)list->data)->name);
369           list = list->next;
370         }
371     }
372
373   g_print ("\n");
374 }
375 #endif
376
377 static void
378 find_list_delta (GSList  *old_list,
379                  GSList  *new_list,
380                  GList  **added,
381                  GList  **removed)
382 {
383   GSList *tmp;
384   GList *tmp_added, *tmp_removed;
385
386   tmp_added = NULL;
387   tmp_removed = NULL;
388
389   /* Find added tags */
390   tmp = new_list;
391   while (tmp)
392     {
393       if (!g_slist_find (old_list, tmp->data))
394         tmp_added = g_list_prepend (tmp_added, tmp->data);
395
396       tmp = tmp->next;
397     }
398
399   *added = tmp_added;
400
401   /* Find removed tags */
402   tmp = old_list;
403   while (tmp)
404     {
405       if (!g_slist_find (new_list, tmp->data))
406         tmp_removed = g_list_prepend (tmp_removed, tmp->data);
407
408       tmp = tmp->next;
409     }
410
411   /* We reverse the list here to match the xml semantics */
412   *removed = g_list_reverse (tmp_removed);
413 }
414
415 static void
416 serialize_section_header (GString     *str,
417                           const gchar *name,
418                           gint         length)
419 {
420   g_return_if_fail (strlen (name) == 26);
421
422   g_string_append (str, name);
423
424   g_string_append_c (str, length >> 24);
425
426   g_string_append_c (str, (length >> 16) & 0xff);
427   g_string_append_c (str, (length >> 8) & 0xff);
428   g_string_append_c (str, length & 0xff);
429 }
430
431 static void
432 serialize_text (GtkTextBuffer        *buffer,
433                 SerializationContext *context)
434 {
435   GtkTextIter iter, old_iter;
436   GSList *tag_list, *new_tag_list;
437   GQueue *active_tags;
438   int i;
439
440   g_string_append (context->text_str, "<text>");
441
442   iter = context->start;
443   tag_list = NULL;
444   active_tags = g_queue_new ();
445
446   do
447     {
448       GList *added, *removed;
449       GList *tmp;
450       gchar *tmp_text, *escaped_text;
451
452       new_tag_list = gtk_text_iter_get_tags (&iter);
453       find_list_delta (tag_list, new_tag_list, &added, &removed);
454
455       /* Handle removed tags */
456       tmp = removed;
457       while (tmp)
458         {
459           GtkTextTag *tag = tmp->data;
460
461           g_string_append (context->text_str, "</apply_tag>");
462
463           /* We might need to drop some of the tags and re-add them afterwards */
464           while (g_queue_peek_head (active_tags) != tag &&
465                      !g_queue_is_empty (active_tags))
466             {
467               added = g_list_prepend (added, g_queue_pop_head (active_tags));
468               g_string_append_printf (context->text_str, "</apply_tag>");
469             }
470
471           g_queue_pop_head (active_tags);
472
473           tmp = tmp->next;
474         }
475
476       /* Handle added tags */
477       tmp = added;
478       while (tmp)
479         {
480           GtkTextTag *tag = tmp->data;
481           gchar *tag_name;
482
483           /* Add it to the tag hash table */
484           g_hash_table_insert (context->tags, tag, tag);
485
486           if (tag->name)
487             {
488               tag_name = g_markup_escape_text (tag->name, -1);
489
490               g_string_append_printf (context->text_str, "<apply_tag name=\"%s\">", tag_name);
491               g_free (tag_name);
492             }
493           else
494             {
495               gpointer tag_id;
496
497               /* We've got an anonymous tag, find out if it's been
498                  used before */
499               if (!g_hash_table_lookup_extended (context->tag_id_tags, tag, NULL, &tag_id))
500                 {
501                   tag_id = GINT_TO_POINTER (context->tag_id++);
502
503                   g_hash_table_insert (context->tag_id_tags, tag, tag_id);
504                 }
505
506               g_string_append_printf (context->text_str, "<apply_tag id=\"%d\">", GPOINTER_TO_INT (tag_id));
507             }
508           g_queue_push_head (active_tags, tag);
509
510           tmp = tmp->next;
511         }
512
513       g_slist_free (tag_list);
514       tag_list = new_tag_list;
515
516       old_iter = iter;
517
518       /* Now try to go to either the next tag toggle, or if a pixbuf appears */
519       while (TRUE)
520         {
521           gunichar ch = gtk_text_iter_get_char (&iter);
522
523           if (ch == 0xFFFC)
524             {
525               GdkPixbuf *pixbuf = gtk_text_iter_get_pixbuf (&iter);
526
527               if (pixbuf)
528                 {
529                   /* Append the text before the pixbuf */
530                   tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
531                   escaped_text = g_markup_escape_text (tmp_text, -1);
532                   g_free (tmp_text);
533
534                   /* Forward so we don't get the 0xfffc char */
535                   gtk_text_iter_forward_char (&iter);
536                   old_iter = iter;
537
538                   g_string_append (context->text_str, escaped_text);
539                   g_free (escaped_text);
540
541                   g_string_append_printf (context->text_str, "<pixbuf index=\"%d\" />", context->n_pixbufs);
542
543                   context->n_pixbufs++;
544                   context->pixbufs = g_list_prepend (context->pixbufs, pixbuf);
545                 }
546             }
547           else if (ch == 0)
548             {
549                 break;
550             }
551           else
552             gtk_text_iter_forward_char (&iter);
553
554           if (gtk_text_iter_toggles_tag (&iter, NULL))
555             break;
556         }
557
558       /* We might have moved too far */
559       if (gtk_text_iter_compare (&iter, &context->end) > 0)
560         iter = context->end;
561
562       /* Append the text */
563       tmp_text = gtk_text_iter_get_slice (&old_iter, &iter);
564       escaped_text = g_markup_escape_text (tmp_text, -1);
565       g_free (tmp_text);
566
567       g_string_append (context->text_str, escaped_text);
568       g_free (escaped_text);
569     }
570   while (!gtk_text_iter_equal (&iter, &context->end));
571
572   /* Close any open tags */
573   for (i = 0; i < g_queue_get_length (active_tags); i++) {
574     g_string_append (context->text_str, "</apply_tag>");
575   }
576   g_queue_free (active_tags);
577   g_string_append (context->text_str, "</text>\n</text_view_markup>\n");
578 }
579
580 static void
581 serialize_pixbufs (SerializationContext *context,
582                    GString              *text)
583 {
584   GList *list;
585
586   for (list = context->pixbufs; list != NULL; list = list->next)
587     {
588       GdkPixbuf *pixbuf = list->data;
589       GdkPixdata pixdata;
590       guint8 *tmp;
591       guint len;
592
593       gdk_pixdata_from_pixbuf (&pixdata, pixbuf, FALSE);
594       tmp = gdk_pixdata_serialize (&pixdata, &len);
595
596       serialize_section_header (text, "GTKTEXTBUFFERPIXBDATA-0001", len);
597       g_string_append_len (text, (gchar *) tmp, len);
598       g_free (tmp);
599     }
600 }
601
602 guint8 *
603 _gtk_text_buffer_serialize_rich_text (GtkTextBuffer     *register_buffer,
604                                       GtkTextBuffer     *content_buffer,
605                                       const GtkTextIter *start,
606                                       const GtkTextIter *end,
607                                       gsize             *length,
608                                       gpointer           user_data)
609 {
610   SerializationContext context;
611   GString *text;
612
613   context.tags = g_hash_table_new (NULL, NULL);
614   context.text_str = g_string_new (NULL);
615   context.tag_table_str = g_string_new (NULL);
616   context.start = *start;
617   context.end = *end;
618   context.n_pixbufs = 0;
619   context.pixbufs = NULL;
620   context.tag_id = 0;
621   context.tag_id_tags = g_hash_table_new (NULL, NULL);
622
623   /* We need to serialize the text before the tag table so we know
624      what tags are used */
625   serialize_text (content_buffer, &context);
626   serialize_tags (&context);
627
628   text = g_string_new (NULL);
629   serialize_section_header (text, "GTKTEXTBUFFERCONTENTS-0001",
630                             context.tag_table_str->len + context.text_str->len);
631
632   g_string_append_len (text, context.tag_table_str->str, context.tag_table_str->len);
633   g_string_append_len (text, context.text_str->str, context.text_str->len);
634
635   context.pixbufs = g_list_reverse (context.pixbufs);
636   serialize_pixbufs (&context, text);
637
638   g_hash_table_destroy (context.tags);
639   g_list_free (context.pixbufs);
640   g_string_free (context.text_str, TRUE);
641   g_string_free (context.tag_table_str, TRUE);
642   g_hash_table_destroy (context.tag_id_tags);
643
644   *length = text->len;
645
646   return (guint8 *) g_string_free (text, FALSE);
647 }
648
649 typedef enum
650 {
651   STATE_START,
652   STATE_TEXT_VIEW_MARKUP,
653   STATE_TAGS,
654   STATE_TAG,
655   STATE_ATTR,
656   STATE_TEXT,
657   STATE_APPLY_TAG,
658   STATE_PIXBUF
659 } ParseState;
660
661 typedef struct
662 {
663   gchar *text;
664   GdkPixbuf *pixbuf;
665   GSList *tags;
666 } TextSpan;
667
668 typedef struct
669 {
670   GtkTextTag *tag;
671   gint prio;
672 } TextTagPrio;
673
674 typedef struct
675 {
676   GSList *states;
677
678   GList *headers;
679
680   GtkTextBuffer *buffer;
681
682   /* Tags that are defined in <tag> elements */
683   GHashTable *defined_tags;
684
685   /* Tags that are anonymous */
686   GHashTable *anonymous_tags;
687
688   /* Tag name substitutions */
689   GHashTable *substitutions;
690
691   /* Current tag */
692   GtkTextTag *current_tag;
693
694   /* Priority of current tag */
695   gint current_tag_prio;
696
697   /* Id of current tag */
698   gint current_tag_id;
699
700   /* Tags and their priorities */
701   GList *tag_priorities;
702
703   GSList *tag_stack;
704
705   GList *spans;
706
707   gboolean create_tags;
708
709   gboolean parsed_text;
710   gboolean parsed_tags;
711 } ParseInfo;
712
713 static void
714 set_error (GError              **err,
715            GMarkupParseContext  *context,
716            int                   error_domain,
717            int                   error_code,
718            const char           *format,
719            ...)
720 {
721   int line, ch;
722   va_list args;
723   char *str;
724
725   g_markup_parse_context_get_position (context, &line, &ch);
726
727   va_start (args, format);
728   str = g_strdup_vprintf (format, args);
729   va_end (args);
730
731   g_set_error (err, error_domain, error_code,
732                ("Line %d character %d: %s"),
733                line, ch, str);
734
735   g_free (str);
736 }
737
738 static void
739 push_state (ParseInfo  *info,
740             ParseState  state)
741 {
742   info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
743 }
744
745 static void
746 pop_state (ParseInfo *info)
747 {
748   g_return_if_fail (info->states != NULL);
749
750   info->states = g_slist_remove (info->states, info->states->data);
751 }
752
753 static ParseState
754 peek_state (ParseInfo *info)
755 {
756   g_return_val_if_fail (info->states != NULL, STATE_START);
757
758   return GPOINTER_TO_INT (info->states->data);
759 }
760
761 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
762
763
764 static gboolean
765 check_id_or_name (GMarkupParseContext  *context,
766                   const gchar          *element_name,
767                   const gchar         **attribute_names,
768                   const gchar         **attribute_values,
769                   gint                 *id,
770                   const gchar         **name,
771                   GError              **error)
772 {
773   gboolean has_id = FALSE;
774   gboolean has_name = FALSE;
775   int i;
776
777   *id = 0;
778   *name = NULL;
779
780   for (i = 0; attribute_names[i] != NULL; i++)
781     {
782       if (strcmp (attribute_names[i], "name") == 0)
783         {
784           *name = attribute_values[i];
785
786           if (has_id)
787             {
788               set_error (error, context,
789                          G_MARKUP_ERROR,
790                          G_MARKUP_ERROR_PARSE,
791                          _("Both \"id\" and \"name\" were found on the <%s> element"),
792                          element_name);
793               return FALSE;
794             }
795
796           if (has_name)
797             {
798               set_error (error, context,
799                          G_MARKUP_ERROR,
800                          G_MARKUP_ERROR_PARSE,
801                          _("The attribute \"%s\" was found twice on the <%s> element"),
802                          "name", element_name);
803               return FALSE;
804             }
805
806           has_name = TRUE;
807         }
808       else if (strcmp (attribute_names[i], "id") == 0)
809         {
810           gchar *tmp;
811
812           if (has_name)
813             {
814               set_error (error, context,
815                          G_MARKUP_ERROR,
816                          G_MARKUP_ERROR_PARSE,
817                          _("Both \"id\" and \"name\" were found on the <%s> element"),
818                          element_name);
819               return FALSE;
820             }
821
822           if (has_id)
823             {
824               set_error (error, context,
825                          G_MARKUP_ERROR,
826                          G_MARKUP_ERROR_PARSE,
827                          _("The attribute \"%s\" was found twice on the <%s> element"),
828                          "id", element_name);
829               return FALSE;
830             }
831
832           has_id = TRUE;
833
834           /* Try parsing the integer */
835           *id = strtol (attribute_values[i], &tmp, 10);
836
837           if (tmp == NULL || tmp == attribute_values[i])
838             {
839               set_error (error, context,
840                          G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
841                          _("<%s> element has invalid id \"%s\""), attribute_values[i]);
842               return FALSE;
843             }
844         }
845     }
846
847   if (!has_id && !has_name)
848     {
849       set_error (error, context,
850                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
851                  _("<%s> element has neither a \"name\" nor an \"id\" element"), element_name);
852       return FALSE;
853     }
854
855   return TRUE;
856 }
857
858 typedef struct
859 {
860   const char  *name;
861   const char **retloc;
862 } LocateAttr;
863
864 static gboolean
865 locate_attributes (GMarkupParseContext  *context,
866                    const char           *element_name,
867                    const char          **attribute_names,
868                    const char          **attribute_values,
869                    gboolean              allow_unknown_attrs,
870                    GError              **error,
871                    const char           *first_attribute_name,
872                    const char          **first_attribute_retloc,
873                    ...)
874 {
875   va_list args;
876   const char *name;
877   const char **retloc;
878   int n_attrs;
879 #define MAX_ATTRS 24
880   LocateAttr attrs[MAX_ATTRS];
881   gboolean retval;
882   int i;
883
884   g_return_val_if_fail (first_attribute_name != NULL, FALSE);
885   g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
886
887   retval = TRUE;
888
889   n_attrs = 1;
890   attrs[0].name = first_attribute_name;
891   attrs[0].retloc = first_attribute_retloc;
892   *first_attribute_retloc = NULL;
893
894   va_start (args, first_attribute_retloc);
895
896   name = va_arg (args, const char*);
897   retloc = va_arg (args, const char**);
898
899   while (name != NULL)
900     {
901       g_return_val_if_fail (retloc != NULL, FALSE);
902
903       g_assert (n_attrs < MAX_ATTRS);
904
905       attrs[n_attrs].name = name;
906       attrs[n_attrs].retloc = retloc;
907       n_attrs += 1;
908       *retloc = NULL;
909
910       name = va_arg (args, const char*);
911       retloc = va_arg (args, const char**);
912     }
913
914   va_end (args);
915
916   if (!retval)
917     return retval;
918
919   i = 0;
920   while (attribute_names[i])
921     {
922       int j;
923       gboolean found;
924
925       found = FALSE;
926       j = 0;
927       while (j < n_attrs)
928         {
929           if (strcmp (attrs[j].name, attribute_names[i]) == 0)
930             {
931               retloc = attrs[j].retloc;
932
933               if (*retloc != NULL)
934                 {
935                   set_error (error, context,
936                              G_MARKUP_ERROR,
937                              G_MARKUP_ERROR_PARSE,
938                              _("Attribute \"%s\" repeated twice on the same <%s> element"),
939                              attrs[j].name, element_name);
940                   retval = FALSE;
941                   goto out;
942                 }
943
944               *retloc = attribute_values[i];
945               found = TRUE;
946             }
947
948           ++j;
949         }
950
951       if (!found && !allow_unknown_attrs)
952         {
953           set_error (error, context,
954                      G_MARKUP_ERROR,
955                      G_MARKUP_ERROR_PARSE,
956                      _("Attribute \"%s\" is invalid on <%s> element in this context"),
957                      attribute_names[i], element_name);
958           retval = FALSE;
959           goto out;
960         }
961
962       ++i;
963     }
964
965  out:
966   return retval;
967 }
968
969 static gboolean
970 check_no_attributes (GMarkupParseContext  *context,
971                      const char           *element_name,
972                      const char          **attribute_names,
973                      const char          **attribute_values,
974                      GError              **error)
975 {
976   if (attribute_names[0] != NULL)
977     {
978       set_error (error, context,
979                  G_MARKUP_ERROR,
980                  G_MARKUP_ERROR_PARSE,
981                  _("Attribute \"%s\" is invalid on <%s> element in this context"),
982                  attribute_names[0], element_name);
983       return FALSE;
984     }
985
986   return TRUE;
987 }
988
989 static GtkTextTag *
990 tag_exists (GMarkupParseContext *context,
991             const gchar         *name,
992             gint                 id,
993             ParseInfo           *info,
994             GError             **error)
995 {
996   const gchar *real_name;
997
998   if (info->create_tags)
999     {
1000       /* If we have an anonymous tag, just return it directly */
1001       if (!name)
1002         return g_hash_table_lookup (info->anonymous_tags,
1003                                     GINT_TO_POINTER (id));
1004
1005       /* First, try the substitutions */
1006       real_name = g_hash_table_lookup (info->substitutions, name);
1007
1008       if (real_name)
1009         return gtk_text_tag_table_lookup (info->buffer->tag_table, real_name);
1010
1011       /* Next, try the list of defined tags */
1012       if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1013         return gtk_text_tag_table_lookup (info->buffer->tag_table, name);
1014
1015       set_error (error, context,
1016                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1017                  _("Tag \"%s\" has not been defined."), name);
1018
1019       return NULL;
1020     }
1021   else
1022     {
1023       GtkTextTag *tag;
1024
1025       if (!name)
1026         {
1027           set_error (error, context,
1028                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1029                      _("Anonymous tag found and tags can not be created."));
1030           return NULL;
1031         }
1032
1033       tag = gtk_text_tag_table_lookup (info->buffer->tag_table, name);
1034
1035       if (tag)
1036         return tag;
1037
1038       set_error (error, context,
1039                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1040                  _("Tag \"%s\" does not exist in buffer and tags can not be created."), name);
1041
1042       return NULL;
1043     }
1044 }
1045
1046 typedef struct
1047 {
1048   const gchar *id;
1049   gint length;
1050   const gchar *start;
1051 } Header;
1052
1053 static GdkPixbuf *
1054 get_pixbuf_from_headers (GList   *headers,
1055                          int      id,
1056                          GError **error)
1057 {
1058   Header *header;
1059   GdkPixdata pixdata;
1060   GdkPixbuf *pixbuf;
1061
1062   header = g_list_nth_data (headers, id);
1063
1064   if (!header)
1065     return NULL;
1066
1067   if (!gdk_pixdata_deserialize (&pixdata, header->length,
1068                                 (const guint8 *) header->start, error))
1069     return NULL;
1070
1071   pixbuf = gdk_pixbuf_from_pixdata (&pixdata, TRUE, error);
1072
1073   return pixbuf;
1074 }
1075
1076 static void
1077 parse_apply_tag_element (GMarkupParseContext  *context,
1078                          const gchar          *element_name,
1079                          const gchar         **attribute_names,
1080                          const gchar         **attribute_values,
1081                          ParseInfo            *info,
1082                          GError              **error)
1083 {
1084   const gchar *name, *priority;
1085   gint id;
1086   GtkTextTag *tag;
1087
1088   g_assert (peek_state (info) == STATE_TEXT ||
1089             peek_state (info) == STATE_APPLY_TAG);
1090
1091   if (ELEMENT_IS ("apply_tag"))
1092     {
1093       if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1094                               "priority", &priority, NULL))
1095         return;
1096
1097       if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1098                              &id, &name, error))
1099         return;
1100
1101
1102       tag = tag_exists (context, name, id, info, error);
1103
1104       if (!tag)
1105         return;
1106
1107       info->tag_stack = g_slist_prepend (info->tag_stack, tag);
1108
1109       push_state (info, STATE_APPLY_TAG);
1110     }
1111   else if (ELEMENT_IS ("pixbuf"))
1112     {
1113       int int_id;
1114       GdkPixbuf *pixbuf;
1115       TextSpan *span;
1116       const gchar *pixbuf_id;
1117
1118       if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1119                               "index", &pixbuf_id, NULL))
1120         return;
1121
1122       int_id = atoi (pixbuf_id);
1123       pixbuf = get_pixbuf_from_headers (info->headers, int_id, error);
1124
1125       span = g_new0 (TextSpan, 1);
1126       span->pixbuf = pixbuf;
1127       span->tags = NULL;
1128
1129       info->spans = g_list_prepend (info->spans, span);
1130
1131       if (!pixbuf)
1132         return;
1133
1134       push_state (info, STATE_PIXBUF);
1135     }
1136   else
1137     set_error (error, context,
1138                G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1139                _("Element <%s> is not allowed below <%s>"),
1140                element_name, peek_state(info) == STATE_TEXT ? "text" : "apply_tag");
1141 }
1142
1143 static void
1144 parse_attr_element (GMarkupParseContext  *context,
1145                     const gchar          *element_name,
1146                     const gchar         **attribute_names,
1147                     const gchar         **attribute_values,
1148                     ParseInfo            *info,
1149                     GError              **error)
1150 {
1151   const gchar *name, *type, *value;
1152   GType gtype;
1153   GValue gvalue = { 0 };
1154   GParamSpec *pspec;
1155
1156   g_assert (peek_state (info) == STATE_TAG);
1157
1158   if (ELEMENT_IS ("attr"))
1159     {
1160       if (!locate_attributes (context, element_name, attribute_names, attribute_values, FALSE, error,
1161                               "name", &name, "type", &type, "value", &value, NULL))
1162         return;
1163
1164       gtype = g_type_from_name (type);
1165
1166       if (gtype == G_TYPE_INVALID)
1167         {
1168           set_error (error, context,
1169                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1170                      _("\"%s\" is not a valid attribute type"), type);
1171           return;
1172         }
1173
1174       if (!(pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (info->current_tag), name)))
1175         {
1176           set_error (error, context,
1177                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1178                      _("\"%s\" is not a valid attribute name"), name);
1179           return;
1180         }
1181
1182       g_value_init (&gvalue, gtype);
1183
1184       if (!deserialize_value (value, &gvalue))
1185         {
1186           set_error (error, context,
1187                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1188                      _("\"%s\" could not be converted to a value of type \"%s\" for attribute \"%s\""),
1189                      value, type, name);
1190           return;
1191         }
1192
1193       if (g_param_value_validate (pspec, &gvalue))
1194         {
1195           set_error (error, context,
1196                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1197                      _("\"%s\" is not a valid value for attribute \"%s\""),
1198                      value, name);
1199           g_value_unset (&gvalue);
1200           return;
1201         }
1202
1203       g_object_set_property (G_OBJECT (info->current_tag),
1204                              name, &gvalue);
1205
1206       g_value_unset (&gvalue);
1207
1208       push_state (info, STATE_ATTR);
1209     }
1210   else
1211     {
1212       set_error (error, context,
1213                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1214                  _("Element <%s> is not allowed below <%s>"),
1215                  element_name, "tag");
1216     }
1217 }
1218
1219
1220 static gchar *
1221 get_tag_name (ParseInfo   *info,
1222               const gchar *tag_name)
1223 {
1224   gchar *name;
1225   gint i;
1226
1227   name = g_strdup (tag_name);
1228
1229   if (!info->create_tags)
1230     return name;
1231
1232   i = 0;
1233
1234   while (gtk_text_tag_table_lookup (info->buffer->tag_table, name) != NULL)
1235     {
1236       g_free (name);
1237       name = g_strdup_printf ("%s-%d", tag_name, ++i);
1238     }
1239
1240   if (i != 0)
1241     {
1242       g_hash_table_insert (info->substitutions, g_strdup (tag_name), g_strdup (name));
1243     }
1244
1245   return name;
1246 }
1247
1248 static void
1249 parse_tag_element (GMarkupParseContext  *context,
1250                    const gchar          *element_name,
1251                    const gchar         **attribute_names,
1252                    const gchar         **attribute_values,
1253                    ParseInfo            *info,
1254                    GError              **error)
1255 {
1256   const gchar *name, *priority;
1257   gchar *tag_name;
1258   gint id;
1259   gint prio;
1260   gchar *tmp;
1261
1262   g_assert (peek_state (info) == STATE_TAGS);
1263
1264   if (ELEMENT_IS ("tag"))
1265     {
1266       if (!locate_attributes (context, element_name, attribute_names, attribute_values, TRUE, error,
1267                               "priority", &priority, NULL))
1268         return;
1269
1270       if (!check_id_or_name (context, element_name, attribute_names, attribute_values,
1271                              &id, &name, error))
1272         return;
1273
1274       if (name)
1275         {
1276           if (g_hash_table_lookup (info->defined_tags, name) != NULL)
1277             {
1278               set_error (error, context,
1279                          G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1280                          _("Tag \"%s\" already defined"), name);
1281               return;
1282             }
1283         }
1284
1285       prio = strtol (priority, &tmp, 10);
1286
1287       if (tmp == NULL || tmp == priority)
1288         {
1289           set_error (error, context,
1290                      G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1291                      _("Tag \"%s\" has invalid priority \"%s\""), name, priority);
1292           return;
1293         }
1294
1295       if (name)
1296         {
1297           tag_name = get_tag_name (info, name);
1298           info->current_tag = gtk_text_tag_new (tag_name);
1299           g_free (tag_name);
1300         }
1301       else
1302         {
1303           info->current_tag = gtk_text_tag_new (NULL);
1304           info->current_tag_id = id;
1305         }
1306
1307       info->current_tag_prio = prio;
1308
1309       push_state (info, STATE_TAG);
1310     }
1311   else
1312     {
1313       set_error (error, context,
1314                  G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1315                  _("Element <%s> is not allowed below <%s>"),
1316                  element_name, "tags");
1317     }
1318 }
1319
1320 static void
1321 start_element_handler (GMarkupParseContext  *context,
1322                        const gchar          *element_name,
1323                        const gchar         **attribute_names,
1324                        const gchar         **attribute_values,
1325                        gpointer              user_data,
1326                        GError              **error)
1327 {
1328   ParseInfo *info = user_data;
1329
1330   switch (peek_state (info))
1331     {
1332     case STATE_START:
1333       if (ELEMENT_IS ("text_view_markup"))
1334         {
1335           if (!check_no_attributes (context, element_name,
1336                                     attribute_names, attribute_values, error))
1337             return;
1338
1339           push_state (info, STATE_TEXT_VIEW_MARKUP);
1340           break;
1341         }
1342       else
1343         set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1344                    _("Outermost element in text must be <text_view_markup> not <%s>"),
1345                    element_name);
1346       break;
1347     case STATE_TEXT_VIEW_MARKUP:
1348       if (ELEMENT_IS ("tags"))
1349         {
1350           if (info->parsed_tags)
1351             {
1352               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1353                          _("A <%s> element has already been specified"), "tags");
1354               return;
1355             }
1356
1357           if (!check_no_attributes (context, element_name,
1358                                     attribute_names, attribute_values, error))
1359             return;
1360
1361           push_state (info, STATE_TAGS);
1362           break;
1363         }
1364       else if (ELEMENT_IS ("text"))
1365         {
1366           if (info->parsed_text)
1367             {
1368               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1369                          _("A <%s> element has already been specified"), "text");
1370               return;
1371             }
1372           else if (!info->parsed_tags)
1373             {
1374               set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1375                          _("A <text> element can't occur before a <tags> element"));
1376               return;
1377             }
1378
1379           if (!check_no_attributes (context, element_name,
1380                                     attribute_names, attribute_values, error))
1381             return;
1382
1383           push_state (info, STATE_TEXT);
1384           break;
1385         }
1386       else
1387         set_error (error, context,
1388                    G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
1389                  _("Element <%s> is not allowed below <%s>"),
1390                    element_name, "text_view_markup");
1391       break;
1392     case STATE_TAGS:
1393       parse_tag_element (context, element_name,
1394                          attribute_names, attribute_values,
1395                          info, error);
1396       break;
1397     case STATE_TAG:
1398       parse_attr_element (context, element_name,
1399                           attribute_names, attribute_values,
1400                           info, error);
1401       break;
1402     case STATE_TEXT:
1403     case STATE_APPLY_TAG:
1404       parse_apply_tag_element (context, element_name,
1405                                attribute_names, attribute_values,
1406                                info, error);
1407       break;
1408     default:
1409       g_assert_not_reached ();
1410       break;
1411     }
1412 }
1413
1414 static gint
1415 sort_tag_prio (TextTagPrio *a,
1416                TextTagPrio *b)
1417 {
1418   if (a->prio < b->prio)
1419     return -1;
1420   else if (a->prio > b->prio)
1421     return 1;
1422   else
1423     return 0;
1424 }
1425
1426 static void
1427 end_element_handler (GMarkupParseContext  *context,
1428                      const gchar          *element_name,
1429                      gpointer              user_data,
1430                      GError              **error)
1431 {
1432   ParseInfo *info = user_data;
1433   gchar *tmp;
1434   GList *list;
1435
1436   switch (peek_state (info))
1437     {
1438     case STATE_TAGS:
1439       pop_state (info);
1440       g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1441
1442       info->parsed_tags = TRUE;
1443
1444       /* Sort list and add the tags */
1445       info->tag_priorities = g_list_sort (info->tag_priorities,
1446                                           (GCompareFunc)sort_tag_prio);
1447       list = info->tag_priorities;
1448       while (list)
1449         {
1450           TextTagPrio *prio = list->data;
1451
1452           if (info->create_tags)
1453             gtk_text_tag_table_add (info->buffer->tag_table, prio->tag);
1454
1455           g_object_unref (prio->tag);
1456           prio->tag = NULL;
1457
1458           list = list->next;
1459         }
1460
1461       break;
1462     case STATE_TAG:
1463       pop_state (info);
1464       g_assert (peek_state (info) == STATE_TAGS);
1465
1466       if (info->current_tag->name)
1467         {
1468           /* Add tag to defined tags hash */
1469           tmp = g_strdup (info->current_tag->name);
1470           g_hash_table_insert (info->defined_tags,
1471                                tmp, tmp);
1472         }
1473       else
1474         {
1475           g_hash_table_insert (info->anonymous_tags,
1476                                GINT_TO_POINTER (info->current_tag_id),
1477                                info->current_tag);
1478         }
1479
1480       if (info->create_tags)
1481         {
1482           TextTagPrio *prio;
1483
1484           /* add the tag to the list */
1485           prio = g_new0 (TextTagPrio, 1);
1486           prio->prio = info->current_tag_prio;
1487           prio->tag = info->current_tag;
1488
1489           info->tag_priorities = g_list_prepend (info->tag_priorities, prio);
1490         }
1491
1492       info->current_tag = NULL;
1493       break;
1494     case STATE_ATTR:
1495       pop_state (info);
1496       g_assert (peek_state (info) == STATE_TAG);
1497       break;
1498     case STATE_APPLY_TAG:
1499       pop_state (info);
1500       g_assert (peek_state (info) == STATE_APPLY_TAG ||
1501                 peek_state (info) == STATE_TEXT);
1502
1503       /* Pop tag */
1504       info->tag_stack = g_slist_delete_link (info->tag_stack,
1505                                              info->tag_stack);
1506
1507       break;
1508     case STATE_TEXT:
1509       pop_state (info);
1510       g_assert (peek_state (info) == STATE_TEXT_VIEW_MARKUP);
1511
1512       info->spans = g_list_reverse (info->spans);
1513       info->parsed_text = TRUE;
1514       break;
1515     case STATE_TEXT_VIEW_MARKUP:
1516       pop_state (info);
1517       g_assert (peek_state (info) == STATE_START);
1518       break;
1519     case STATE_PIXBUF:
1520       pop_state (info);
1521       g_assert (peek_state (info) == STATE_APPLY_TAG ||
1522                 peek_state (info) == STATE_TEXT);
1523       break;
1524     default:
1525       g_assert_not_reached ();
1526       break;
1527     }
1528 }
1529
1530 static gboolean
1531 all_whitespace (const char *text,
1532                 int         text_len)
1533 {
1534   const char *p;
1535   const char *end;
1536
1537   p = text;
1538   end = text + text_len;
1539
1540   while (p != end)
1541     {
1542       if (!g_ascii_isspace (*p))
1543         return FALSE;
1544
1545       p = g_utf8_next_char (p);
1546     }
1547
1548   return TRUE;
1549 }
1550
1551 static void
1552 text_handler (GMarkupParseContext  *context,
1553               const gchar          *text,
1554               gsize                 text_len,
1555               gpointer              user_data,
1556               GError              **error)
1557 {
1558   ParseInfo *info = user_data;
1559   TextSpan *span;
1560
1561   if (all_whitespace (text, text_len) &&
1562       peek_state (info) != STATE_TEXT &&
1563       peek_state (info) != STATE_APPLY_TAG)
1564     return;
1565
1566   switch (peek_state (info))
1567     {
1568     case STATE_START:
1569       g_assert_not_reached (); /* gmarkup shouldn't do this */
1570       break;
1571     case STATE_TEXT:
1572     case STATE_APPLY_TAG:
1573       if (text_len == 0)
1574         return;
1575
1576       span = g_new0 (TextSpan, 1);
1577       span->text = g_strndup (text, text_len);
1578       span->tags = g_slist_copy (info->tag_stack);
1579
1580       info->spans = g_list_prepend (info->spans, span);
1581       break;
1582     default:
1583       g_assert_not_reached ();
1584       break;
1585     }
1586 }
1587
1588 static void
1589 parse_info_init (ParseInfo     *info,
1590                  GtkTextBuffer *buffer,
1591                  gboolean       create_tags,
1592                  GList         *headers)
1593 {
1594   info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
1595
1596   info->create_tags = create_tags;
1597   info->headers = headers;
1598   info->defined_tags = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1599   info->substitutions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1600   info->anonymous_tags = g_hash_table_new_full (NULL, NULL, NULL, NULL);
1601   info->tag_stack = NULL;
1602   info->spans = NULL;
1603   info->parsed_text = FALSE;
1604   info->parsed_tags = FALSE;
1605   info->current_tag = NULL;
1606   info->current_tag_prio = -1;
1607   info->tag_priorities = NULL;
1608
1609   info->buffer = buffer;
1610 }
1611
1612 static void
1613 text_span_free (TextSpan *span)
1614 {
1615   g_free (span->text);
1616   g_slist_free (span->tags);
1617   g_free (span);
1618 }
1619
1620 static void
1621 parse_info_free (ParseInfo *info)
1622 {
1623   GSList *slist;
1624   GList *list;
1625
1626   slist = info->tag_stack;
1627   while (slist)
1628     {
1629       g_free (slist->data);
1630
1631       slist = slist->next;
1632     }
1633
1634   g_slist_free (info->tag_stack);
1635   g_slist_free (info->states);
1636
1637   g_hash_table_destroy (info->substitutions);
1638   g_hash_table_destroy (info->defined_tags);
1639
1640   if (info->current_tag)
1641     g_object_unref (info->current_tag);
1642
1643   list = info->spans;
1644   while (list)
1645     {
1646       text_span_free (list->data);
1647
1648       list = list->next;
1649     }
1650   g_list_free (info->spans);
1651
1652   list = info->tag_priorities;
1653   while (list)
1654     {
1655       TextTagPrio *prio = list->data;
1656
1657       if (prio->tag)
1658         g_object_unref (prio->tag);
1659       g_free (prio);
1660
1661       list = list->next;
1662     }
1663   g_list_free (info->tag_priorities);
1664
1665 }
1666
1667 static void
1668 insert_text (ParseInfo   *info,
1669              GtkTextIter *iter)
1670 {
1671   GtkTextIter start_iter;
1672   GtkTextMark *mark;
1673   GList *tmp;
1674   GSList *tags;
1675
1676   start_iter = *iter;
1677
1678   mark = gtk_text_buffer_create_mark (info->buffer, "deserialize_insert_point",
1679                                       &start_iter, TRUE);
1680
1681   tmp = info->spans;
1682   while (tmp)
1683     {
1684       TextSpan *span = tmp->data;
1685
1686       if (span->text)
1687         gtk_text_buffer_insert (info->buffer, iter, span->text, -1);
1688       else
1689         {
1690           gtk_text_buffer_insert_pixbuf (info->buffer, iter, span->pixbuf);
1691           g_object_unref (span->pixbuf);
1692         }
1693       gtk_text_buffer_get_iter_at_mark (info->buffer, &start_iter, mark);
1694
1695       /* Apply tags */
1696       tags = span->tags;
1697       while (tags)
1698         {
1699           GtkTextTag *tag = tags->data;
1700
1701           gtk_text_buffer_apply_tag (info->buffer, tag,
1702                                      &start_iter, iter);
1703
1704           tags = tags->next;
1705         }
1706
1707       gtk_text_buffer_move_mark (info->buffer, mark, iter);
1708
1709       tmp = tmp->next;
1710     }
1711
1712   gtk_text_buffer_delete_mark (info->buffer, mark);
1713 }
1714
1715
1716
1717 static int
1718 read_int (const guchar *start)
1719 {
1720   int result;
1721
1722   result =
1723     start[0] << 24 |
1724     start[1] << 16 |
1725     start[2] << 8 |
1726     start[3];
1727
1728   return result;
1729 }
1730
1731 static gboolean
1732 header_is (Header      *header,
1733            const gchar *id)
1734 {
1735   return (strncmp (header->id, id, strlen (id)) == 0);
1736 }
1737
1738 static GList *
1739 read_headers (const gchar *start,
1740               gint         len,
1741               GError     **error)
1742 {
1743   int i = 0;
1744   int section_len;
1745   Header *header;
1746   GList *headers = NULL;
1747
1748   while (i < len)
1749     {
1750       if (i + 30 >= len)
1751         goto error;
1752
1753       if (strncmp (start + i, "GTKTEXTBUFFERCONTENTS-0001", 26) == 0 ||
1754           strncmp (start + i, "GTKTEXTBUFFERPIXBDATA-0001", 26) == 0)
1755         {
1756           section_len = read_int ((const guchar *) start + i + 26);
1757
1758           if (i + 30 + section_len > len)
1759             goto error;
1760
1761           header = g_new0 (Header, 1);
1762           header->id = start + i;
1763           header->length = section_len;
1764           header->start = start + i + 30;
1765
1766           i += 30 + section_len;
1767
1768           headers = g_list_prepend (headers, header);
1769         }
1770       else
1771         break;
1772     }
1773
1774   return g_list_reverse (headers);
1775
1776  error:
1777   g_list_foreach (headers, (GFunc) g_free, NULL);
1778   g_list_free (headers);
1779
1780   g_set_error (error,
1781                G_MARKUP_ERROR,
1782                G_MARKUP_ERROR_PARSE,
1783                _("Serialized data is malformed"));
1784
1785   return NULL;
1786 }
1787
1788 static gboolean
1789 deserialize_text (GtkTextBuffer *buffer,
1790                   GtkTextIter   *iter,
1791                   const gchar   *text,
1792                   gint           len,
1793                   gboolean       create_tags,
1794                   GError       **error,
1795                   GList         *headers)
1796 {
1797   GMarkupParseContext *context;
1798   ParseInfo info;
1799   gboolean retval = FALSE;
1800
1801   static const GMarkupParser rich_text_parser = {
1802     start_element_handler,
1803     end_element_handler,
1804     text_handler,
1805     NULL,
1806     NULL
1807   };
1808
1809   parse_info_init (&info, buffer, create_tags, headers);
1810
1811   context = g_markup_parse_context_new (&rich_text_parser,
1812                                         0, &info, NULL);
1813
1814   if (!g_markup_parse_context_parse (context,
1815                                      text,
1816                                      len,
1817                                      error))
1818     goto out;
1819
1820   if (!g_markup_parse_context_end_parse (context, error))
1821     goto out;
1822
1823   retval = TRUE;
1824
1825   /* Now insert the text */
1826   insert_text (&info, iter);
1827
1828  out:
1829   parse_info_free (&info);
1830
1831   g_markup_parse_context_free (context);
1832
1833   return retval;
1834 }
1835
1836 gboolean
1837 _gtk_text_buffer_deserialize_rich_text (GtkTextBuffer *register_buffer,
1838                                         GtkTextBuffer *content_buffer,
1839                                         GtkTextIter   *iter,
1840                                         const guint8  *text,
1841                                         gsize          length,
1842                                         gboolean       create_tags,
1843                                         gpointer       user_data,
1844                                         GError       **error)
1845 {
1846   GList *headers;
1847   Header *header;
1848   gboolean retval;
1849
1850   headers = read_headers ((gchar *) text, length, error);
1851
1852   if (!headers)
1853     return FALSE;
1854
1855   header = headers->data;
1856   if (!header_is (header, "GTKTEXTBUFFERCONTENTS-0001"))
1857     {
1858       g_set_error (error,
1859                    G_MARKUP_ERROR,
1860                    G_MARKUP_ERROR_PARSE,
1861                    _("Serialized data is malformed. First section isn't GTKTEXTBUFFERCONTENTS-0001"));
1862
1863       retval = FALSE;
1864       goto out;
1865     }
1866
1867   retval = deserialize_text (content_buffer, iter,
1868                              header->start, header->length,
1869                              create_tags, error, headers->next);
1870
1871  out:
1872   g_list_foreach (headers, (GFunc)g_free, NULL);
1873   g_list_free (headers);
1874
1875   return retval;
1876 }